open import Data.Tuple as Tuple using (_⨯_ ; _,_)
open import Logic.Classical
open import Logic
import      Lvl
open import Type
open import Structure.Setoid

-- Elementary Plane Geometry.
-- An axiomatic approach to plane geometry in first order logic.
-- The axiomatic system used here is usually called "Tarski's axioms", and plane geometry is also known as two-dimensional Euclidean geometry.
module Geometry.Axioms
  {ℓₚ ℓₚₑ ℓₗₗₑ ℓₗᵢₗ}
  (Point : Type{ℓₚ}) ⦃ Point-equiv : Equiv{ℓₚₑ}(Point) ⦄
  (Equidistanced : (Point ⨯ Point) → (Point ⨯ Point) → Stmt{ℓₗₗₑ}) -- Two pairs of points have the same distance between each other.
  (Aligned : Point → Point → Point → Stmt{ℓₗᵢₗ})                   -- Three points are aligned in a weak order. The second point is between the first and the third in a line.
  ⦃ classical : ∀{ℓ}{P : Stmt{ℓ}} → Classical(P) ⦄
  where

open import Data.Tuple.Equivalence
open import Functional
open import Functional.Combinations
open import Logic.Predicate
open import Logic.Propositional
open import Logic.Propositional.Theorems
open import Sets.ExtensionalPredicateSet renaming (_≡_ to _≡ₛ_)
open import Structure.Relator.Equivalence
open import Structure.Relator.Ordering
open import Structure.Relator.Properties
open import Structure.Relator
open import Structure.Setoid.Uniqueness
open import Syntax.Function
open import Syntax.Implication
open import Syntax.Transitivity
open import Syntax.Type

private variable ℓ ℓ₁ ℓ₂ ℓₗ ℓₗ₁ ℓₗ₂ : Lvl.Level
private variable p a b c d e p₁ p₂ pᵢ pₒ pᵣ a₁ a₂ b₁ b₂ c₁ c₂ d₁ d₂ e₁ e₂ i m₁ m₂ : Point
private variable P Q : Point → Stmt{ℓ}

-- Three points are aligned in a strict order. The second point is between the first and the third in a line without being the first or the second.
Between : Point → Point → Point → Stmt
Between a b c = (Aligned a b c) ∧ ((a ≢ b) ∧ (b ≢ c))

-- Three points are collinear when they all are points of a single line.
Collinear : Point → Point → Point → Stmt
Collinear a b c = (Aligned a b c) ∨ (Aligned b c a) ∨ (Aligned c a b)

Aligned₄ : Point → Point → Point → Point → Stmt
Aligned₄ = combine₄Fn₃Op₂ Aligned (_∧_)

-- The equivalence on points.
_≡ₚ_ = Equiv._≡_ Point-equiv

-- Source: Tarski's axioms of geometry (https://en.wikipedia.org/wiki/Tarski%27s_axioms @ 2020-06-14).
-- TODO: Are there any modifications of the axioms when working in constructive logic?
record Axioms : Typeω where
  field
    ⦃ Equidistanced-relator ⦄ : BinaryRelator(Equidistanced)
    ⦃ Aligned-relator ⦄ : TrinaryRelator(Aligned)
    -- ⦃ Point-equivalence-classical ⦄ : Classical₂(_≡ₚ_)
    -- ⦃ Equidistanced-classical ⦄ : Classical₂(Equidistanced)
    -- ⦃ Aligned-classical ⦄ : Classical₃(Aligned)

    -- The distance between p₁ and p₂ is the same as the distance between p₂ and p₁.
    -- The order of the points in the equidistance relation does not matter.
    -- Example:
    --   • (p₁) <--- (p₂)
    --   • (p₁) ---> (p₂)
    --   These two drawings depict lines between points p₁ and p₂. They have the same length.
    --   So there is no need to draw an arrow head on the lines when referring to the length of a line.
    -- Also called: A1 in Metamathematische Methoden in der Geometrie.
    Equidistanced-flipped : Equidistanced(p₁ , p₂)(p₂ , p₁)

    -- Equidistance is a transitive relation.
    -- Example:
    --   la: (a₁) ---- (a₂)
    --   lb: (b₁) ---- (b₂)
    --   lc: (c₁) ---- (c₂)
    --   Here is a drawing of three lines.
    --   The line la have the same length as lb, and the line la have the same length as lc. Then lb have the same length as lc.
    -- Also called: A2 in Metamathematische Methoden in der Geometrie.
    Equidistanced-symmetric-transitivity : Equidistanced(a₁ , a₂)(b₁ , b₂) → Equidistanced(a₁ , a₂)(c₁ , c₂) → Equidistanced(b₁ , b₂)(c₁ , c₂)

    -- If two points have the same distance as the distance between a single point with itself, then they are the same point.
    -- Essentially, if two points have no distance between each other, they are the same point.
    -- Example:
    --   (p₁) ---------- (p₂)
    --   (p)
    --   Here p₁ and p₂ are arbitrary points, and a line between them is depicted.
    --   The line from p to p is also depicted (as a point).
    --   Currently, these lines do not have the same length, and for p₁ and p₂ to have the same distance as the single point line consisting of p, the line must collapse to a single point, making p₁ and p₂ the same point.
    -- Also called:
    -- • Identity axiom for the `Equidistance` relation.
    -- • A3 in Metamathematische Methoden in der Geometrie.
    Equidistanced-point : Equidistanced(p₁ , p₂)(p , p) → (p₁ ≡ p₂)

    -- Given two lines, there exists a line extending the first line so that the extension part is of the same length as the second line.
    -- Essentially, given a starting point and a direction, it is always possible to construct a line of the same length as an another already existing line.
    -- Or: It is always possible to extend a line by the length of another line.
    -- Example:
    --   Given two lines:
    --   (p) <-- (a₁)
    --   (a₂) ------> (b₂)
    --   it is possible to construct an extension of the first line like this:
    --   (b₁) <------(p)-- (a₁)
    --   The extension from a₁ to b₁ have the same direction as the first line (a₁ to p) and the same length as the second line (from a₂ to b₂).
    -- Also called:
    -- • A4 in Metamathematische Methoden in der Geometrie.
    segment-construction : ∃(b₁ ↦ (Aligned a₁ p b₁) ∧ Equidistanced(p , b₁)(a₂ , b₂))

    -- Given two bisected triangles, if all except one of the non-bisected sides of the first triangle have the same length as the second one's, then the unknown side of the first triangle is also congruent to the unknown side of the second.
    --
    -- Note: If any of the points other than (a,b) or (a,d) are equal, then the result follows from the assumptions.
    --         (d)\__
    --        /  \_  \__
    --      /      \_   \__
    --    /          \     \
    --  (a)__________(b)___(c)
    --
    -- Also called:
    -- • A5 in Metamathematische Methoden in der Geometrie.
    five-segment : (a₁ ≢ b₁) → (Aligned a₁ b₁ c₁) → (Aligned a₂ b₂ c₂) → Equidistanced(a₁ , b₁)(a₂ , b₂) → Equidistanced(b₁ , c₁)(b₂ , c₂) → Equidistanced(a₁ , d₁)(a₂ , d₂) → Equidistanced(b₁ , d₁)(b₂ , d₂) → Equidistanced(c₁ , d₁)(c₂ , d₂)

    -- If a point is between two identical points, then they are the same point.
    -- Alternatively, if a point is in a line consisting of only a single point, then the point is the single point.
    -- Example:
    --   (p₁) ------(a)--- (p₂)
    --   Here, a line from p₁ to p₂ is depicted, and a point a is in this line.
    --   But if p₁ and p₂ would be the same point p, then the line would collapse to a single point, making a and p the same.
    -- Also called:
    -- • Identity axiom for the `Aligned` relation.
    -- • A6 in Metamathematische Methoden in der Geometrie.
    Aligned-point : (Aligned p a p) → (a ≡ p)

    -- Given two connected lines and one point in each of the lines, two lines connecting the points and the endpoints of the other line have an intersection point.
    -- Example:
    --         (c)
    --       /     \
    --    (m₁)\   /(m₂)
    --    /   _(i)_   \
    --  (a)__/     \__(b)
    -- Also called:
    -- • Axiom of Pasch.
    -- • Inner Pasch.
    -- • A7 in Metamathematische Methoden in der Geometrie.
    Aligned-intersection : (Aligned a m₁ c) → (Aligned b m₂ c) → ∃(i ↦ (Aligned m₁ i b) ∧ (Aligned m₂ i a))

    -- Also called:
    -- • Axiom schema of Continuity
    -- • A11' in Metamathematische Methoden in der Geometrie.
    Aligned-continuity : ∃(a ↦ ∀{x y} → P(x) → Q(y) → (Aligned a x y)) → ∃(a ↦ ∀{x y} → P(x) → Q(y) → (Aligned x a y))

    -- A triangle exists.
    -- This excludes the possibility of this theory describing 0 or 1-dimensional spaces when using the standard interpretation of the axioms.
    -- Example:
    --    (c)
    --   /   \
    -- (a)___(b)
    -- Also called:
    -- • A8 in Metamathematische Methoden in der Geometrie.
    lower-dimension₂ : ∃{Obj = Point ⨯ Point ⨯ Point}(\(a , b , c) → (¬ Aligned a b c) ∧ (¬ Aligned b c a) ∧ (¬ Aligned c a b))

    -- Also called:
    -- • A9 in Metamathematische Methoden in der Geometrie.
    upper-dimension₂ : Equidistanced(a , p₁)(a , p₂) → Equidistanced(b , p₁)(b , p₂) → Equidistanced(c , p₁)(c , p₂) → (p₁ ≢ p₂) → (Aligned a b c) ∨ (Aligned b c a) ∨ (Aligned c a b)

    -- Given three points, they are either collinear (forming a line), or there is a circumcentered point for the triangle of the three points (a point where the distance between this point and all three vertices are equal).
    -- Example:
    --   Here is an example of the different cases depicted:
    --   • (a) ----(b)------ (c)
    --   • (b) ----(c)------ (a)
    --   • (c) ----(a)------ (b)
    --   •      (a)\__
    --         /  ‖   \__
    --       /     ‖      \
    --     (b)______‖______(c)
    --       \___   ‖  ___/
    --           \_(m)_/
    --     or when the triangle is equilateral:
    --        (a)
    --        / \
    --       /   \
    --      / (m) \
    --     /_______\
    --   (b)       (c)
    -- Also called:
    -- • Axiom of Euclid.
    -- • Variant of A10 in Metamathematische Methoden in der Geometrie.
    center-of-3 : (Collinear a b c) ∨ ∃(m ↦ Equidistanced(a , m)(b , m) ∧ Equidistanced(a , m)(c , m))

module _ ⦃ axioms : Axioms ⦄ where
  open Axioms(axioms)

  -- A line constructed by the points `a` and `b` have an intersection point on a circle with the center point `c` and radius point `pᵣ` when a part of the line.
  -- Also called:
  -- • CA in Metamathematische Methoden in der Geometrie.
  -- circle-line-intersection : (Aligned₄ c pᵢ pᵣ pₒ) → Equidistanced(c , a)(c , pᵢ) → Equidistanced(c , b)(c , pₒ) → ∃(i ↦ Equidistanced(c , i)(c , pᵣ) ∧ (Aligned a i b))

  instance
    -- Identical pairs of points have the same distance between each other.
    -- Also called: 2.1 in Metamathematische Methoden in der Geometrie.
    Equidistanced-reflexivity : Reflexivity(Equidistanced)
    Reflexivity.proof Equidistanced-reflexivity {p₁ , p₂} = Equidistanced-symmetric-transitivity (Equidistanced-flipped {p₂}{p₁}) (Equidistanced-flipped {p₂}{p₁})

  instance
    -- Also called: 2.2 in Metamathematische Methoden in der Geometrie.
    Equidistanced-symmetry : Symmetry(Equidistanced)
    Symmetry.proof Equidistanced-symmetry p = Equidistanced-symmetric-transitivity p (reflexivity(Equidistanced))

  instance
    -- Also called: 2.3 in Metamathematische Methoden in der Geometrie.
    Equidistanced-transitivity : Transitivity(Equidistanced)
    Transitivity.proof Equidistanced-transitivity p q = Equidistanced-symmetric-transitivity (symmetry(Equidistanced) p) q

  instance
    -- Also called: 2.7 in Metamathematische Methoden in der Geometrie.
    Equidistanced-equivalence : Equivalence(Equidistanced)
    Equidistanced-equivalence = intro

  -- The distance between a point and itself is the same for all points.
  -- Also called: 2.8 in Metamathematische Methoden in der Geometrie.
  Equidistanced-points : Equidistanced(a , a) (b , b)
  Equidistanced-points {a}{b}
    with [∃]-intro p ⦃ [∧]-intro _ bpaa ⦄ ← segment-construction{a}{b} {a}{a}
    =
      • (b ≡ b) ∧ (p ≡ b)            :-[ [∧]-intro (reflexivity(_≡_)) (symmetry(_≡_) (Equidistanced-point bpaa)) ]
      • Equidistanced(b , p) (b , p) :-[ reflexivity(Equidistanced) ]
      ⇒₂-[ substitute₂ᵣ(Equidistanced) ]
      Equidistanced(b , p) (b , b) ⇒-[ Equidistanced-symmetric-transitivity bpaa ]
      Equidistanced(a , a) (b , b) ⇒-end

  -- Addition of two distances when the points are in a line.
  -- Also called: 2.11 in Metamathematische Methoden in der Geometrie.
  segment-sum : (Aligned a₁ b₁ c₁) → (Aligned a₂ b₂ c₂) → Equidistanced(a₁ , b₁)(a₂ , b₂) → Equidistanced(b₁ , c₁)(b₂ , c₂) → Equidistanced(a₁ , c₁)(a₂ , c₂)
  segment-sum {a₁ = a₁}{b₁ = b₁} abc₁ abc₂ ab₁ab₂ bc₁bc₂ with excluded-middle(a₁ ≡ b₁)
  ... | [∨]-introₗ a₁b₁
    with a₂b₂ ← Equidistanced-point (symmetry(Equidistanced) (substitute₂ₗ(Equidistanced) ([∧]-intro a₁b₁ (reflexivity(_≡ₚ_))) ab₁ab₂))
    = substitute₂(Equidistanced) ([∧]-intro (symmetry(_≡ₚ_) a₁b₁) (reflexivity(_≡ₚ_))) ([∧]-intro (symmetry(_≡ₚ_) a₂b₂) (reflexivity(_≡ₚ_))) bc₁bc₂
  ... | [∨]-introᵣ na₁b₁ =
    Equidistanced-flipped
    🝖 (five-segment
      na₁b₁
      abc₁
      abc₂
      ab₁ab₂
      bc₁bc₂
      Equidistanced-points
      (Equidistanced-flipped 🝖 ab₁ab₂ 🝖 Equidistanced-flipped)
    )
    🝖 Equidistanced-flipped

  -- The segment extension axiom constructs unique points when the given pair of points forms a line segment to extend.
  -- Also called: 2.12 in Metamathematische Methoden in der Geometrie.
  segment-construction-uniqueness : (a₁ ≢ p) → Unique(b₁ ↦ (Aligned a₁ p b₁) ∧ Equidistanced(p , b₁)(a₂ , b₂))
  segment-construction-uniqueness na₁p ([∧]-intro al-a₁p₁x eq-p₁xa₂b₂) ([∧]-intro al-a₁p₁y eq-p₁ya₂b₂)
    with pxpy ← transitivity(Equidistanced) eq-p₁xa₂b₂ (symmetry(Equidistanced) eq-p₁ya₂b₂)
    with a₁xa₁y ← segment-sum al-a₁p₁x al-a₁p₁y (reflexivity(Equidistanced)) pxpy
    = Equidistanced-point (five-segment na₁p al-a₁p₁x al-a₁p₁y (reflexivity(Equidistanced)) pxpy (reflexivity(Equidistanced)) (reflexivity(Equidistanced)))

  -- Two points are always aligned.
  -- Also called: 3.1 in Metamathematische Methoden in der Geometrie.
  Aligned-reflexivityᵣ : Aligned a b b
  Aligned-reflexivityᵣ {a}{b}
    with [∃]-intro p ⦃ [∧]-intro alig equi ⦄ ← segment-construction{a}{b} {a}{a}
    = substitute₃-unary₃(Aligned) (symmetry(_≡_) (Equidistanced-point equi)) alig

  -- A single point is always aligned with itself.
  Aligned-reflexivity : Aligned a a a
  Aligned-reflexivity = Aligned-reflexivityᵣ

  -- When three points are aligned in one direction, they are also aligned in the opposite direction.
  -- Also called: 3.2 in Metamathematische Methoden in der Geometrie.
  Aligned-symmetry : Aligned a b c → Aligned c b a
  Aligned-symmetry {a}{b}{c} alig
    with [∃]-intro p ⦃ [∧]-intro bpb cpa ⦄ ← Aligned-intersection{a}{b}{_}{b}{c} alig Aligned-reflexivityᵣ
    with pb ← Aligned-point bpb
    = substitute₃-unary₂(Aligned) pb cpa

  -- Two points are always aligned.
  -- Also called: 3.3 in Metamathematische Methoden in der Geometrie.
  Aligned-reflexivityₗ : Aligned a a b
  Aligned-reflexivityₗ = Aligned-symmetry Aligned-reflexivityᵣ

  -- Also called: 3.4 in Metamathematische Methoden in der Geometrie.
  Aligned-antisymmetryₗ : (Aligned a b c) → (Aligned b a c) → (a ≡ b)
  Aligned-antisymmetryₗ {a}{b}{c} al-abc al-bac
    with [∃]-intro i ⦃ [∧]-intro al-bib al-aia ⦄ ← Aligned-intersection{a}{b}{c} al-abc al-bac
    with ia ← Aligned-point al-aia
    with ib ← Aligned-point al-bib
    = symmetry(_≡_) ia 🝖 ib

  Aligned-antisymmetryᵣ : (Aligned a b c) → (Aligned a c b) → (b ≡ c)
  Aligned-antisymmetryᵣ al-abc al-acb = symmetry(_≡_) (Aligned-antisymmetryₗ (Aligned-symmetry al-abc) (Aligned-symmetry al-acb))

  -- Also called: 3.6(1) in Metamathematische Methoden in der Geometrie.
  Aligned-transitivityᵣ-exchange : (Aligned a b c) → (Aligned a c d) → (Aligned b c d)
  Aligned-transitivityᵣ-exchange abc acd
    with [∃]-intro i ⦃ [∧]-intro bid cic ⦄ ← Aligned-intersection (Aligned-symmetry abc) (Aligned-symmetry acd)
    with ic ← Aligned-point cic
    = substitute₃-unary₂(Aligned) ic bid

  -- Also called: 3.5(1) in Metamathematische Methoden in der Geometrie.
  Aligned-transitivityₗ-exchange : (Aligned a b d) → (Aligned b c d) → (Aligned a b c)
  Aligned-transitivityₗ-exchange abd bcd = Aligned-symmetry (Aligned-transitivityᵣ-exchange (Aligned-symmetry bcd) (Aligned-symmetry abd))

  -- Also called: 3.7(1) in Metamathematische Methoden in der Geometrie.
  Aligned-semitransitivityᵣ : (Aligned a b c) → (Aligned b c d) → (b ≢ c) → (Aligned a c d)
  Aligned-semitransitivityᵣ {a}{b}{c}{d} abc bcd nbc
    with [∃]-intro x ⦃ [∧]-intro acx cxcd ⦄ ← segment-construction{a}{c}{c}{d}
    with bcx ← Aligned-transitivityᵣ-exchange abc acx
    with xd ← segment-construction-uniqueness nbc ([∧]-intro bcx cxcd) ([∧]-intro bcd (reflexivity(Equidistanced)))
    = substitute₃-unary₃(Aligned) xd acx

  -- Also called: 3.5(2) in Metamathematische Methoden in der Geometrie.
  Aligned-transitivityₗ-merge : (Aligned a b d) → (Aligned b c d) → (Aligned a c d)
  Aligned-transitivityₗ-merge {a}{b}{d}{c} abd bcd with excluded-middle(b ≡ₚ c)
  ... | [∨]-introₗ bc  = substitute₃-unary₂(Aligned) bc abd
  ... | [∨]-introᵣ nbc = Aligned-semitransitivityᵣ (Aligned-transitivityₗ-exchange abd bcd) bcd nbc

  -- Also called: 3.6(2) in Metamathematische Methoden in der Geometrie.
  Aligned-transitivityᵣ-merge : (Aligned a b c) → (Aligned a c d) → (Aligned a b d)
  Aligned-transitivityᵣ-merge abc acd = Aligned-symmetry (Aligned-transitivityₗ-merge (Aligned-symmetry acd) (Aligned-symmetry abc))

  -- Also called: 3.7(2) in Metamathematische Methoden in der Geometrie.
  Aligned-semitransitivityₗ : (Aligned a b c) → (Aligned b c d) → (b ≢ c) → (Aligned a b d)
  Aligned-semitransitivityₗ abc bcd nbc = Aligned-symmetry(Aligned-semitransitivityᵣ (Aligned-symmetry bcd) (Aligned-symmetry abc) (nbc ∘ symmetry(_≡ₚ_)))

  Aligned₄-intro-[123,134] : (Aligned a b c) → (Aligned a c d) → (Aligned₄ a b c d)
  Aligned₄-intro-[123,134] abc acd = abc , abd , acd , bcd where
    abd = Aligned-transitivityᵣ-merge    abc acd
    bcd = Aligned-transitivityᵣ-exchange abc acd

  Aligned₄-intro-[124,234] : (Aligned a b d) → (Aligned b c d) → (Aligned₄ a b c d)
  Aligned₄-intro-[124,234] abd bcd = abc , abd , acd , bcd where
    abc = Aligned-transitivityₗ-exchange abd bcd
    acd = Aligned-transitivityₗ-merge    abd bcd

  -- Existence of two distinct points.
  -- Also called: 3.13 in Metamathematische Methoden in der Geometrie.
  lower-dimension₁ : ∃{Obj = Point ⨯ Point}(\{(a , b) → a ≢ b})
  lower-dimension₁
    with [∃]-intro (a , b , _) ⦃ [∧]-intro ([∧]-intro p _) _ ⦄ ← lower-dimension₂
    = [∃]-intro (a , b) ⦃ ab ↦ p(substitute₃-unary₂(Aligned) ab Aligned-reflexivityₗ) ⦄

  -- Also called: 3.14 in Metamathematische Methoden in der Geometrie.
  extension-existence : ∃(e ↦ (Aligned a b e) ∧ (b ≢ e))
  extension-existence{a}{b}
    with [∃]-intro (x , y) ⦃ nab ⦄ ← lower-dimension₁
    with [∃]-intro e ⦃ [∧]-intro abe bexy ⦄ ← segment-construction{a}{b}{x}{y}
    = [∃]-intro e ⦃ [∧]-intro abe (nab ∘ (be ↦ Equidistanced-point(symmetry(Equidistanced) (substitute₂ₗ(Equidistanced) ([∧]-intro be (reflexivity(_≡ₚ_))) bexy)))) ⦄

  -- Also called: 3.17 in Metamathematische Methoden in der Geometrie.
  triangle-edge-lines-intersection : (Aligned a₁ b₁ c) → (Aligned a₂ b₂ c) → (Aligned a₁ p a₂) → ∃(i ↦ (Aligned p i c) ∧ (Aligned b₁ i b₂))
  triangle-edge-lines-intersection a₁b₁c a₂b₂c a₁pa₂
    with [∃]-intro j ⦃ [∧]-intro pjc b₂ja₁ ⦄ ← Aligned-intersection a₁pa₂ (Aligned-symmetry a₂b₂c)
    with [∃]-intro i ⦃ [∧]-intro b₁ib₂ jic ⦄ ← Aligned-intersection (Aligned-symmetry a₁b₁c) (b₂ja₁)
    = [∃]-intro i ⦃ [∧]-intro (Aligned-transitivityₗ-merge pjc jic) b₁ib₂ ⦄

  -- Also called: 4.6 in Metamathematische Methoden in der Geometrie.
  postulate equidistanced-points-are-aligned : Equidistanced₃(a₁ , b₁ , c₁)(a₂ , b₂ , c₂) → (Aligned a₁ b₁ c₁) → (Aligned a₂ b₂ c₂)

  -- Also called: 5.1 in Metamathematische Methoden in der Geometrie.
  postulate Aligned-order-cases₃-skip₂ : (a ≢ b) → (Aligned a b c₁) → (Aligned a b c₂) → ((Aligned a c₁ c₂) ∨ (Aligned a c₂ c₁))

  -- Also called: 5.2 in Metamathematische Methoden in der Geometrie.
  postulate Aligned-order-cases₃-skip₁ : (a ≢ b) → (Aligned a b c₁) → (Aligned a b c₂) → ((Aligned b c₁ c₂) ∨ (Aligned b c₂ c₁))

  -- Also called: 5.3 in Metamathematische Methoden in der Geometrie.
  postulate Aligned-order-cases₂-skip₃ : (Aligned a b₁ c) → (Aligned a b₂ c) → ((Aligned a b₁ b₂) ∨ (Aligned a b₂ b₁))

  -- The distance between the first pair of points are lesser or equal to the distance between the second pair of points.
  -- The definition states that there should exist a point bₘ such that a (b₁,bₘ) is a subline of (b₁,b₂) and such that the subline is of the same length as (a₁,a₂).
  -- Also called: 5.4 in Metamathematische Methoden in der Geometrie.
  LeDistanced : (Point ⨯ Point) → (Point ⨯ Point) → Stmt
  LeDistanced(a₁ , a₂)(b₁ , b₂) = ∃(bₘ ↦ (Aligned b₁ bₘ b₂) ∧ Equidistanced(a₁ , a₂)(b₁ , bₘ))

  -- Also called: 5.4 in Metamathematische Methoden in der Geometrie.
  GeDistanced : (Point ⨯ Point) → (Point ⨯ Point) → Stmt
  GeDistanced = swap LeDistanced

  -- Also called: 5.5 in Metamathematische Methoden in der Geometrie.
  postulate LeDistanced-alternative : LeDistanced(a₁ , a₂)(b₁ , b₂) ↔ ∃(aₒ ↦ (Aligned a₁ a₂ aₒ) ∧ Equidistanced(a₁ , aₒ)(b₁ , b₂))

  -- Also called: 5.7 in Metamathematische Methoden in der Geometrie.
  instance
    postulate LeDistanced-reflexivity : Reflexivity(LeDistanced)

  -- Also called: 5.8 in Metamathematische Methoden in der Geometrie.
  instance
    postulate LeDistanced-transitivity : Transitivity(LeDistanced)

  -- Also called: 5.9 in Metamathematische Methoden in der Geometrie.
  instance
    postulate LeDistanced-antisymmetry : Antisymmetry(LeDistanced)(Equidistanced)

  -- Also called: 5.10 in Metamathematische Methoden in der Geometrie.
  instance
    postulate LeDistanced-converseTotal : ConverseTotal(LeDistanced)

  -- Also called: 5.11 in Metamathematische Methoden in der Geometrie.
  instance
    postulate LeDistanced-minimal : Weak.Properties.LE.Minimum(LeDistanced)(p , p)

  -- Also called: 5.12 in Metamathematische Methoden in der Geometrie.
  postulate Collinear-aligned-ledistanced-equivalence : (Collinear a b c) → ((Aligned a b c) ↔ (LeDistanced(a , b)(a , c) ∧ LeDistanced(b , c)(a , c)))

  -- Also called: 5.14 in Metamathematische Methoden in der Geometrie.
  LtDistanced : (Point ⨯ Point) → (Point ⨯ Point) → Stmt
  LtDistanced(a₁ , a₂)(b₁ , b₂) = LeDistanced(a₁ , a₂)(b₁ , b₂) ∧ (¬ Equidistanced(a₁ , a₂)(b₁ , b₂))

  -- Also called: 5.14 in Metamathematische Methoden in der Geometrie.
  GtDistanced : (Point ⨯ Point) → (Point ⨯ Point) → Stmt
  GtDistanced = swap GeDistanced

  aligned-equidistanced-equality : (Aligned a b₁ b₂) → Equidistanced(a , b₁)(a , b₂) → (b₁ ≡ₚ b₂)
  aligned-equidistanced-equality ab₁b₂ ab₁ab₂ = {!!}

  aligned-equidistanced-last-equality : (a ≢ b) → (Aligned a b c₁) → (Aligned a b c₂) → Equidistanced(b , c₁)(b , c₂) → (c₁ ≡ c₂)
  aligned-equidistanced-last-equality nab abc₁ abc₂ bc₁bc₂ = {!Aligned-order-cases₃-skip₁ nab abc₁ abc₂!}

  Aligned-classical : Classical₃(Aligned)
  Classical.excluded-middle (Aligned-classical {a} {b} {c})
    with [∃]-intro d ⦃ [∧]-intro abd bdbc ⦄ ← segment-construction{a}{b}{b}{c}
    with excluded-middle(a ≡ₚ b) | excluded-middle(c ≡ₚ d)
  ... | _              | [∨]-introₗ cd  = [∨]-introₗ (substitute₃-unary₃(Aligned) (symmetry(_≡ₚ_) cd) abd)
  ... | [∨]-introₗ ab  | [∨]-introᵣ ncd = [∨]-introₗ (substitute₃-unary₂(Aligned) ab Aligned-reflexivityₗ)
  ... | [∨]-introᵣ nab | [∨]-introᵣ ncd = [∨]-introᵣ (abc ↦ ncd(aligned-equidistanced-last-equality nab abc abd (symmetry(Equidistanced) bdbc)))

  {-
  -- Also called: 6.14 in Metamathematische Methoden in der Geometrie.
  lineSet : (a : Point) → (b : Point) → ⦃ distinct : (a ≢ b) ⦄ → PredSet(Point)
  lineSet a b ∋ p = Collinear a p b
  PredSet.preserve-equiv (lineSet a b) = {!TrinaryRelator.unary₂(Collinear)!} -- TODO-}

  -- Example:
  --         (c)
  --       /     \
  --    (m₁)\   /(m₂)
  --    /   _(i)_   \
  --  (a)__/     \__(b)
  -- Also called: Outer Pasch.
  postulate Aligned-outer-intersection : (Aligned a i m₂) → (Aligned b m₂ c) → ∃(m₁ ↦ (Aligned a m₁ c) ∧ (Aligned b i m₁))
  {-Aligned-outer-intersection {a}{i}{m₂}{b}{c} aim2 bm2c with excluded-middle(Collinear b i m₂) ⦃ classical ⦄
  Aligned-outer-intersection {a}{i}{m₂}{b}{c} aim2 bm2c | [∨]-introₗ coll-bim2 with excluded-middle(Aligned b i m₂) ⦃ classical ⦄
  ... | [∨]-introₗ al-bim2 = [∃]-intro c ⦃ [∧]-intro {!!} ([∨]-elim ([∨]-elim (al-bim2 ↦ {!!}) (al-im2b ↦ {!!})) (al-m2bi ↦ {!!}) coll-bim2) ⦄
  ... | [∨]-introᵣ x = {!!}
  Aligned-outer-intersection {a}{i}{m₂}{b}{c} aim2 bm2c | [∨]-introᵣ x = {!!}-}
    {-with [∃]-intro ii ⦃ p ⦄ ← Aligned-intersection {!!} {!!}
    = {!!}-}

{-

  -- A point on the bisecting ray of the given line.
  -- Example:
  --        ⋮
  --        |
  --       (p)
  --      / | \
  --     /  |  \
  --   (a)-----(b)
  --        |
  --        ⋮
  BisectingRayPoint : Point → Point → Point → Stmt
  BisectingRayPoint a p b = Equidistanced(a , p)(b , p)

  -- The points are all aligned in a line with the second point being in the center of the line constructed from the other points.
  -- Example:
  --   (a)---(m)---(b)
  -- `m` is the centerpoint (in the middle) of `a` and `b`.
  CenterAligned : Point → Point → Point → Stmt
  CenterAligned a m b = (BisectingRayPoint a m b) ∧ (Aligned a m b)

  -- The points form a perpendicular triangle with b being the corner where the perpendicular angle is residing.
  -- Example:
  --       (a)
  --      / | \
  --     /  |  \
  --    /   |   \
  --  (c)--(b)--(cₘ)
  PerpendicularCorner : Point → Point → Point → Stmt
  PerpendicularCorner a b c = ∃(cₘ ↦ (CenterAligned c b cₘ) ∧ (BisectingRayPoint c a cₘ))

  -- The distance between the first pair of points are lesser or equal to the distance between the second pair of points.
  -- This is defined by considering points on a bisecting ray in the center of each of the two lines constructed from the pairs of points. If there is a function that maps points on the second bisecting ray to the first bisecting ray such that this mapping preserves the distance to one of the endpoints of its corresponding line, then the distance between the first pair is lesser than the second.
  -- Example:
  --    (bBisect)
  --       /|\
  --      / | \
  --     /  |  \
  --  (b₁)=====(b₂)

  --   (aBisect)
  --      /|\
  --     / | \
  --    |  |  |
  --  (a₁)===(a₂)
  --
  --   Here, b₁ and b₂ are equally distanced to bBisect.
  --   The definition then states that there exists a similiar construction aBisect on a₁ and a₂, and it does.
  --   And this second construction (aBisect) should have the same distance to its endpoints (for example a₂) as the first (bBisect) have (for example b₂), which it does.
  --   Now, in the extreme case where the distance between bBisect and the endpoints are shrunk, bBisect would collapse into being a center point of the line (b₁,b₂), but because the distance between a₁ and a₂ is smaller, aBisect will still be outside of the line (a₁,a₂). Though, it still exists.
  --   This would not be the case if the relation was reversed.
  LeDistanced2 : (Point ⨯ Point) → (Point ⨯ Point) → Stmt
  LeDistanced2(a₁ , a₂)(b₁ , b₂) =
    ∀{bBisect} → (BisectingRayPoint b₁ bBisect b₂) →
    ∃(aBisect  ↦ (BisectingRayPoint a₁ aBisect a₂) ∧ Equidistanced(a₂ , aBisect)(b₂ , bBisect))

  -- Aligned-Distanced-equivalence : (Aligned a b c) ↔ (∀{d} → LeDistanced2(a , d)(a , b) → LeDistanced2(c , d)(b , c) → (b ≡ d))

  -- Note: The only purpose of this definition is so that instance search works (because it do not work on functions, and negation is defined as a function).
  record DistinctPoints (a b : Point) : Type{ℓₚₑ} where
    constructor intro
    field proof : (a ≢ b)

  -- A line segment is a geometric figure defined by two distinct points
  -- The interpretation is that these two points connect each other, and the set of points of the figure are the points between these two points.
  record LineSegment : Type{ℓₚ Lvl.⊔ ℓₚₑ} where
    constructor lineSegment
    field
      from : Point
      to : Point
      ⦃ distinct ⦄ : DistinctPoints from to

    -- A point on the line segment.
    point : Point
    point = [∃]-witness (Aligned-intersection {to}{from} {from} {to}{from} Aligned-reflexivityᵣ Aligned-reflexivityᵣ)

    -- The line segment flipped.
    flip : LineSegment
    flip = record{from = to ; to = from ; distinct = intro(DistinctPoints.proof distinct ∘ symmetry(_≡_))}

    copyTo : Point → LineSegment
    copyTo base = {!!}

    -- TODO: This is apparently difficult to construct
    center : Point
    -- center = {!!}

    -- The bisector (TODO: which one?) line of this line segment. (TODO: Too unspecific. Where does it start and end?)
    bisector : LineSegment
    -- bisector = {!!}
  private variable l l₁ l₂ l₃ : LineSegment

  -- Point inclusion in line segment (a point is in a line segment).
  _∈₂_ : Point → LineSegment → Type
  p ∈₂ lineSegment a b = Aligned a p b

  -- Line segment congruence (two line segments are of equal length)
  _⎶_ : LineSegment → LineSegment → Type
  lineSegment a₁ a₂ ⎶ lineSegment b₁ b₂ = Equidistanced(a₁ , a₂)(b₁ , b₂)

  Perpendicular : LineSegment → LineSegment → Stmt
  Perpendicular(lineSegment a₁ a₂)(lineSegment b₁ b₂) = ∃(c ↦ (Collinear a₁ a₂ c) ∧ (Collinear b₁ b₂ c) ∧ (PerpendicularCorner a₂ c b₂))

  Parallel : LineSegment → LineSegment → Stmt
  Parallel a b = Perpendicular a (LineSegment.bisector b)

  point-in-lineSegment : LineSegment.point(l) ∈₂ l
  point-in-lineSegment{l} = [∧]-elimₗ([∃]-proof (Aligned-intersection Aligned-reflexivityᵣ Aligned-reflexivityᵣ))

  -- A triangle is a geometric figure defined by three unaligned points.
  -- The interpretation is that these three points connect each other, forming three line segments, and the set of points of the figure are the points inside the line segments.
  record Triangle : Type{ℓₚ Lvl.⊔ ℓₗᵢₗ} where
    constructor triangle
    field
      point₁ : Point
      point₂ : Point
      point₃ : Point
      ⦃ distinct₁ ⦄ : ¬ Aligned point₁ point₂ point₃
      ⦃ distinct₂ ⦄ : ¬ Aligned point₂ point₃ point₁
      ⦃ distinct₃ ⦄ : ¬ Aligned point₃ point₁ point₂

    side₁₂ : LineSegment
    side₁₂ = line point₁ point₂ ⦃ intro(contrapositiveᵣ (p1p2 ↦ substitute₃-unary₁(Aligned) (symmetry(_≡_) p1p2) Aligned-reflexivityₗ) distinct₁) ⦄

    side₂₃ : LineSegment
    side₂₃ = line point₂ point₃ ⦃ intro(contrapositiveᵣ (p2p3 ↦ substitute₃-unary₁(Aligned) (symmetry(_≡_) p2p3) Aligned-reflexivityₗ) distinct₂) ⦄

    side₃₁ : LineSegment
    side₃₁ = line point₃ point₁ ⦃ intro(contrapositiveᵣ (p3p1 ↦ substitute₃-unary₁(Aligned) (symmetry(_≡_) p3p1) Aligned-reflexivityₗ) distinct₃) ⦄
  private variable tri tri₁ tri₂ : Triangle

  -- A triangle exists.
  -- This comes directly from an axiom.
  Triangle-existence : Triangle
  Triangle-existence
    with [∃]-intro(x , y , z) ⦃ [∧]-intro ([∧]-intro p q) r ⦄ ← lower-dimension
    = triangle x y z ⦃ p ⦄ ⦃ q ⦄ ⦃ r ⦄

  Aligned-four : (Aligned a b c) → (Aligned p₁ b c) → (Aligned a b p₂) → (Aligned p₁ b p₂)
  Aligned-four abc p₁bc abp₂
    with [∃]-intro d ⦃ [∧]-intro bdp₂ bdc ⦄ ← Aligned-intersection (Aligned-symmetry abc) (Aligned-symmetry abp₂)
    with [∃]-intro e ⦃ [∧]-intro bep₁ bea ⦄ ← Aligned-intersection abc p₁bc
    = {!Aligned-transitivityᵣ abp₂ p₁bc!}
  Aligned-alternative-start : (Aligned a₁ b c)→ (Aligned a₂ b c) → ((Aligned a₁ a₂ b) ∨ (Aligned a₂ a₁ b))

  -- Note: The difference between segment-construction and this is that this is not extending the line (a₁,b₁). segment-equidistanced-copy constructs a new line which is of the same length as (a₂,b₂) but starting from a₁ and is in the direction of p.
  -- TODO: A proof idea is to use center-of-3 and eliminate the last case. For the different cases, one uses different segment-constructions as usual
  segment-equidistanced-copy : ∃(p ↦ ((Aligned a₁ b₁ p) ∨ (Aligned a₁ p b₁)) ∧ Equidistanced(a₁ , p)(a₂ , b₂))
  segment-equidistanced-copy {a₁}{b₁} {a₂}{b₂}
    with [∃]-intro ext ⦃ [∧]-intro al-b₁a₁ext d-a₁ext-a₂b₂ ⦄ ← segment-construction{b₁}{a₁} {a₂}{b₂}
    with [∃]-intro p   ⦃ [∧]-intro al-exta₁p  d-a₁p-exta₁  ⦄ ← segment-construction{ext}{a₁} {ext}{a₁}
    with center-of-3 {a₁}{b₁}{p}
  ... | [∨]-introₗ ([∨]-introₗ ([∨]-introₗ al-a₁b₁p)) = [∃]-intro p ⦃ [∧]-intro ([∨]-introₗ al-a₁b₁p) (d-a₁p-exta₁ 🝖 Equidistanced-flipped 🝖 d-a₁ext-a₂b₂) ⦄
  ... | [∨]-introₗ ([∨]-introₗ ([∨]-introᵣ al-b₁pa₁)) = [∃]-intro p ⦃ [∧]-intro ([∨]-introᵣ (Aligned-symmetry al-b₁pa₁)) (d-a₁p-exta₁ 🝖 Equidistanced-flipped 🝖 d-a₁ext-a₂b₂) ⦄
  ... | [∨]-introₗ ([∨]-introᵣ             al-pa₁b₁)  = [∃]-intro p ⦃ [∧]-intro ([∨]-introᵣ {!!}) (d-a₁p-exta₁ 🝖 Equidistanced-flipped 🝖 d-a₁ext-a₂b₂) ⦄ -- TODO: Use Aligned-four to prove (a₁ ≡ ext), which implies (a₁ ≡ p), so therefore ( Aligned a₁ a₁ b₁) by substitution. Note that this is the case when (a₂ ≡ b₂), but it is not used in the proof.
  ... | [∨]-introᵣ x = {!!} -- TODO: x essentially states that a₁ b₁ p is not in a line, which can be proven that they are like in the cases above by Aligned-alternative-start

  -- A circle is a geometric figure defined by two distinct points.
  -- The interpretation is that the first point is the center point, and the second point lies on the arc of the circle.
  -- The distance between the points is the radius of the circle, and all points within the radius from the center point defines the set of points of the circle.
  record Circle : Type{ℓₚ Lvl.⊔ ℓₚₑ} where
    constructor circle
    field
      center : Point
      outer : Point
      ⦃ distinct ⦄ : DistinctPoints center outer

    -- A line from the center point to the arc.
    radiusLineSegment : LineSegment
    radiusLineSegment = lineSegment center outer

    {- TODO
    -- A line from the center point to the arc in the direction of the given point.
    radiusLineSegmentTowards : Point → LineSegment
    radiusLineSegmentTowards p
      with [∃]-intro q ⦃ [∧]-intro cpq pqco ⦄ ← segment-construction{center}{p} {center}{outer}
      = lineSegment center q ⦃ intro(cq ↦ (
        • (
          • center ≡ q                          :-[ cq ]
          • Aligned center p q                  :-[ cpq ]
          ⇒₂-[ substitute₃-unary₁(Aligned) ]
          Aligned q p q                         ⇒-[ Aligned-point ]
          p ≡ q                                 ⇒-[ pq ↦ [∧]-intro pq (reflexivity(_≡_)) ]
          (p ≡ q) ∧ (q ≡ q)                     ⇒-end
        )
        • Equidistanced(p , q) (center , outer) :-[ pqco ]
        ⇒₂-[ substitute₂ₗ(Equidistanced) ]
        Equidistanced(q , q) (center , outer)   ⇒-[ symmetry(Equidistanced) ]
        Equidistanced(center , outer) (q , q)   ⇒-[ Equidistanced-point ]
        center ≡ outer                          ⇒-[ DistinctPoints.proof distinct ]
        ⊥                                       ⇒-end
      )) ⦄

    -- TODO: I am confused. Is the point (LineSegment.to(radiusLineTowards p)) always further away than p?
    radiusLineTowards-aligned : Aligned center p (Line.to(radiusLineSegmentTowards p))
    radiusLineTowards-aligned{p}
      with [∃]-intro q ⦃ [∧]-intro cpq pqco ⦄ ← segment-construction{center}{p} {center}{outer}
      = cpq

    radiusLineSegmentTowards-radius : Equidistanced(center , LineSegment.to(radiusLineSegmentTowards p)) (center , outer)
    radiusLineSegmentTowards-radius{p}
      with [∃]-intro q ⦃ [∧]-intro cpq pqco ⦄ ← segment-construction{center}{p} {center}{outer}
      = {!pqco!} -- TODO: pqco is actually stating that (p,q) and (center,outer) is equally distanced, which is not what we want, so radiusLineTowards should be modified to use segment-construction-alt
    -}

    -- TODO: Two circles' intersection points are either 0, 1, 2 or they are the same circle.
    -- intersection : ∀{C₁ C₂} → (∀{p} → ¬(p ∈ₒ C₁) ∧ ¬(p ∈ₒ C₂)) ∨ ∃!(p ↦ (p ∈ₒ C₁) ∧ (p ∈ₒ C₂)) ∨ ∃(p₁ ↦ ∃(p₂ ↦ (p ∈ₒ C₁) ∧ (p ∈ₒ C₂))) ∨ (C₁ ≡ C₂)

  -- Point inclusion in circle (a point is in a circle).
  _∈ₒ_ : Point → Circle → Type
  p ∈ₒ circle c o = LeDistanced(c , p)(c , o)
  -- TODO: Is this equivalent? ∃(op ↦ Equidistanced(c , o)(c , op) ∧ (Aligned c p op)) It extends the line (c,p) instead of shrinking the line (c,o)
-}


module Ag07 where

open import IO

data W (A : Set) (B : A → Set) : Set where
  sup : (a : A) → (b : B a → W A B) → W A B

module WBinTree where
  data Tree : Set where
    Leaf : Tree
    Node : Tree

  data Direction : Set where
    Left  : Direction
    Right : Direction

  data Bottom : Set where

  void : ∀ {A : Set} → Bottom → A
  void ()

  binTree : Tree → Set
  binTree Leaf = Bottom
  binTree Node = Direction

  BinTree : Set
  BinTree = W Tree binTree

  leaf : BinTree
  leaf = sup Leaf void

  node : BinTree → BinTree → BinTree
  node x y = sup Node λ {
    Left  → x ;
    Right → y }


open import Function      using (flip; _∘_; _$_)
open import Auto.Counting
open import Data.Nat      using (ℕ)
open import Data.List     using (List; _∷_; [])
open import Data.Product  using (∃; _,_; proj₂)
open import Data.Maybe
open import Data.Sum      using (inj₁; inj₂; isInj₂)

module Auto.Example.Sublists where

infix 3 _⊆_

data _⊆_ {a} {A : Set a} : List A → List A → Set a where
  stop : [] ⊆ []
  drop : ∀ {xs y ys} → xs ⊆ ys →     xs ⊆ y ∷ ys
  keep : ∀ {x xs ys} → xs ⊆ ys → x ∷ xs ⊆ x ∷ ys

refl : ∀ {a} {A : Set a} {xs : List A} → xs ⊆ xs
refl {xs = []}     = stop
refl {xs = x ∷ xs} = keep refl

trans : ∀ {a} {A : Set a} {xs ys zs : List A} → xs ⊆ ys → ys ⊆ zs → xs ⊆ zs
trans       p   stop    = p
trans       p  (drop q) = drop (trans p q)
trans (drop p) (keep q) = drop (trans p q)
trans (keep p) (keep q) = keep (trans p q)

hintdb₁ : HintDB
hintdb₁ = ε << quote drop
            << quote keep
            << quote trans

lemma₁ : {ws xs ys zs : List ℕ}
       → ws ⊆ 1 ∷ xs → xs ⊆ ys → ys ⊆ zs → ws ⊆ 1 ∷ 2 ∷ zs
lemma₁ = tactic (auto dfs 10 hintdb₁)

lemma₂ : {ws xs ys zs : List ℕ}
       → ws ⊆ 1 ∷ xs → xs ⊆ ys → ys ⊆ zs → ws ⊆     2 ∷ zs
lemma₂ = tactic (auto dfs 10 hintdb₁)

{-
db₂ : HintDB
db₂ = ε << quote trans
        << quote keep
        << quote drop

test₂ : {A : Set} {ws xs ys zs : List A} → ws ⊆ xs → ys ⊆ zs → ws ⊆ zs
test₂ = tactic (auto dfs 10 db₁)
-}


{-# OPTIONS --warning=error --safe --without-K #-}

open import Groups.Definition
open import Groups.Groups
open import Groups.Homomorphisms.Definition
open import Setoids.Setoids
open import Sets.EquivalenceRelations
open import Groups.Lemmas
open import Groups.Homomorphisms.Lemmas

module Groups.QuotientGroup.Definition {a b c d : _} {A : Set a} {B : Set b} {S : Setoid {a} {c} A} {T : Setoid {b} {d} B} {_·A_ : A → A → A} {_·B_ : B → B → B} (G : Group S _·A_) {H : Group T _·B_} {f : A → B} (fHom : GroupHom G H f) where

quotientGroupSetoid : (Setoid {a} {d} A)
quotientGroupSetoid = ansSetoid
  where
    open Setoid T
    open Group H
    open Equivalence eq
    ansSetoid : Setoid A
    Setoid._∼_ ansSetoid r s = (f (r ·A (Group.inverse G s))) ∼ 0G
    Equivalence.reflexive (Setoid.eq ansSetoid) {b} = transitive (GroupHom.wellDefined fHom (Group.invRight G)) (imageOfIdentityIsIdentity fHom)
    Equivalence.symmetric (Setoid.eq ansSetoid) {m} {n} pr = i
      where
        g : f (Group.inverse G (m ·A Group.inverse G n)) ∼ 0G
        g = transitive (homRespectsInverse fHom {m ·A Group.inverse G n}) (transitive (inverseWellDefined H pr) (invIdent H))
        h : f (Group.inverse G (Group.inverse G n) ·A Group.inverse G m) ∼ 0G
        h = transitive (GroupHom.wellDefined fHom (Equivalence.symmetric (Setoid.eq S) (invContravariant G))) g
        i : f (n ·A Group.inverse G m) ∼ 0G
        i = transitive (GroupHom.wellDefined fHom (Group.+WellDefined G (Equivalence.symmetric (Setoid.eq S) (invTwice G n)) (Equivalence.reflexive (Setoid.eq S)))) h
    Equivalence.transitive (Setoid.eq ansSetoid) {m} {n} {o} prmn prno = transitive (GroupHom.wellDefined fHom (Group.+WellDefined G (Equivalence.reflexive (Setoid.eq S)) (Equivalence.symmetric (Setoid.eq S) (Group.identLeft G)))) k
      where
        g : f (m ·A Group.inverse G n) ·B f (n ·A Group.inverse G o) ∼ 0G ·B 0G
        g = replaceGroupOp H reflexive reflexive prmn prno reflexive
        h : f (m ·A Group.inverse G n) ·B f (n ·A Group.inverse G o) ∼ 0G
        h = transitive g identLeft
        i : f ((m ·A Group.inverse G n) ·A (n ·A Group.inverse G o)) ∼ 0G
        i = transitive (GroupHom.groupHom fHom) h
        j : f (m ·A (((Group.inverse G n) ·A n) ·A Group.inverse G o)) ∼ 0G
        j = transitive (GroupHom.wellDefined fHom (fourWay+Associative G)) i
        k : f (m ·A ((Group.0G G) ·A Group.inverse G o)) ∼ 0G
        k = transitive (GroupHom.wellDefined fHom (Group.+WellDefined G (Equivalence.reflexive (Setoid.eq S)) (Group.+WellDefined G (Equivalence.symmetric (Setoid.eq S) (Group.invLeft G)) (Equivalence.reflexive (Setoid.eq S))))) j

quotientGroupByHom : Group (quotientGroupSetoid) _·A_
Group.+WellDefined (quotientGroupByHom) {x} {y} {m} {n} x~m y~n = ans
  where
    open Setoid T
    open Equivalence (Setoid.eq T)
    p1 : f ((x ·A y) ·A (Group.inverse G (m ·A n))) ∼ f ((x ·A y) ·A ((Group.inverse G n) ·A (Group.inverse G m)))
    p2 : f ((x ·A y) ·A ((Group.inverse G n) ·A (Group.inverse G m))) ∼ f (x ·A ((y ·A (Group.inverse G n)) ·A (Group.inverse G m)))
    p3 : f (x ·A ((y ·A (Group.inverse G n)) ·A (Group.inverse G m))) ∼ (f x) ·B f ((y ·A (Group.inverse G n)) ·A (Group.inverse G m))
    p4 : (f x) ·B f ((y ·A (Group.inverse G n)) ·A (Group.inverse G m)) ∼ (f x) ·B (f (y ·A (Group.inverse G n)) ·B f (Group.inverse G m))
    p5 : (f x) ·B (f (y ·A (Group.inverse G n)) ·B f (Group.inverse G m)) ∼ (f x) ·B (Group.0G H ·B f (Group.inverse G m))
    p6 : (f x) ·B (Group.0G H ·B f (Group.inverse G m)) ∼ (f x) ·B f (Group.inverse G m)
    p7 : (f x) ·B f (Group.inverse G m) ∼ f (x ·A (Group.inverse G m))
    p8 : f (x ·A (Group.inverse G m)) ∼ Group.0G H
    p1 = GroupHom.wellDefined fHom (Group.+WellDefined G (Equivalence.reflexive (Setoid.eq S)) (invContravariant G))
    p2 = GroupHom.wellDefined fHom (Equivalence.symmetric (Setoid.eq S) (fourWay+Associative G))
    p3 = GroupHom.groupHom fHom
    p4 = Group.+WellDefined H reflexive (GroupHom.groupHom fHom)
    p5 = Group.+WellDefined H reflexive (replaceGroupOp H (symmetric y~n) reflexive reflexive reflexive reflexive)
    p6 = Group.+WellDefined H reflexive (Group.identLeft H)
    p7 = symmetric (GroupHom.groupHom fHom)
    p8 = x~m
    ans : f ((x ·A y) ·A (Group.inverse G (m ·A n))) ∼ Group.0G H
    ans = transitive p1 (transitive p2 (transitive p3 (transitive p4 (transitive p5 (transitive p6 (transitive p7 p8))))))
Group.0G (quotientGroupByHom) = Group.0G G
Group.inverse (quotientGroupByHom) = Group.inverse G
Group.+Associative (quotientGroupByHom) {a} {b} {c} = ans
  where
    open Setoid T
    open Equivalence (Setoid.eq T)
    ans : f ((a ·A (b ·A c)) ·A (Group.inverse G ((a ·A b) ·A c))) ∼ Group.0G H
    ans = transitive (GroupHom.wellDefined fHom (transferToRight'' G (Group.+Associative G))) (imageOfIdentityIsIdentity fHom)
Group.identRight (quotientGroupByHom) {a} = ans
  where
    open Group G
    open Setoid T
    open Equivalence eq
    transitiveG = Equivalence.transitive (Setoid.eq S)
    ans : f ((a ·A 0G) ·A inverse a) ∼ Group.0G H
    ans = transitive (GroupHom.wellDefined fHom (transitiveG (Group.+WellDefined G (Group.identRight G) (Equivalence.reflexive (Setoid.eq S))) (Group.invRight G))) (imageOfIdentityIsIdentity fHom)
Group.identLeft (quotientGroupByHom) {a} = ans
  where
    open Group G
    open Setoid T
    open Equivalence eq
    transitiveG = Equivalence.transitive (Setoid.eq S)
    ans : f ((0G ·A a) ·A (inverse a)) ∼ Group.0G H
    ans = transitive (GroupHom.wellDefined fHom (transitiveG (Group.+WellDefined G (Group.identLeft G) (Equivalence.reflexive (Setoid.eq S))) (Group.invRight G))) (imageOfIdentityIsIdentity fHom)
Group.invLeft (quotientGroupByHom) {x} = ans
  where
    open Group G
    open Setoid T
    open Equivalence eq
    ans : f ((inverse x ·A x) ·A (inverse 0G)) ∼ (Group.0G H)
    ans = transitive (GroupHom.wellDefined fHom (Equivalence.transitive (Setoid.eq S) (replaceGroupOp G (Equivalence.symmetric (Setoid.eq S) (Group.invLeft G)) (Equivalence.symmetric (Setoid.eq S) (invIdent G)) (Equivalence.reflexive (Setoid.eq S)) ((Equivalence.reflexive (Setoid.eq S))) ((Equivalence.reflexive (Setoid.eq S)))) (identRight {0G}))) (imageOfIdentityIsIdentity fHom)
Group.invRight (quotientGroupByHom) {x} = ans
  where
    open Group G
    open Setoid T
    open Equivalence eq
    ans : f ((x ·A inverse x) ·A (inverse 0G)) ∼ (Group.0G H)
    ans = transitive (GroupHom.wellDefined fHom (Equivalence.transitive (Setoid.eq S) (replaceGroupOp G (Equivalence.symmetric (Setoid.eq S) (Group.invRight G)) (Equivalence.symmetric (Setoid.eq S) (invIdent G)) (Equivalence.reflexive (Setoid.eq S)) (Equivalence.reflexive (Setoid.eq S)) (Equivalence.reflexive (Setoid.eq S))) (identRight {0G}))) (imageOfIdentityIsIdentity fHom)


open import Data.Product using ( ∃ ; _×_ ; _,_ ; proj₁ ; proj₂ )
open import Relation.Unary using ( _∈_ )
open import Web.Semantic.DL.TBox.Interp using ( Δ ; _⊨_≈_ ) renaming
  ( Interp to Interp′ ; emp to emp′ )
open import Web.Semantic.DL.Signature using ( Signature )
open import Web.Semantic.Util using ( False ; id )

module Web.Semantic.DL.ABox.Interp where

infixr 4 _,_
infixr 5 _*_

data Interp (Σ : Signature) (X : Set) : Set₁ where
  _,_ : ∀ I → (X → Δ {Σ} I) → (Interp Σ X)

⌊_⌋ : ∀ {Σ X} → Interp Σ X → Interp′ Σ
⌊ I , i ⌋ = I

ind : ∀ {Σ X} → (I : Interp Σ X) → X → Δ ⌊ I ⌋
ind (I , i) = i

ind² : ∀ {Σ X} → (I : Interp Σ X) → (X × X) → (Δ ⌊ I ⌋ × Δ ⌊ I ⌋)
ind² I (x , y) = (ind I x , ind I y)

_*_ : ∀ {Σ X Y} → (Y → X) → Interp Σ X → Interp Σ Y
f * I = (⌊ I ⌋ , λ x → ind I (f x))

emp : ∀ {Σ} → Interp Σ False
emp = (emp′ , id)

data Surjective {Σ X} (I : Interp Σ X) : Set where
  surj : (∀ x → ∃ λ y → ⌊ I ⌋ ⊨ x ≈ ind I y) → (I ∈ Surjective)

ind⁻¹ : ∀ {Σ X} {I : Interp Σ X} → (I ∈ Surjective) → (Δ ⌊ I ⌋ → X)
ind⁻¹ (surj i) x = proj₁ (i x)

surj✓ : ∀ {Σ X} {I : Interp Σ X} (I∈Surj : I ∈ Surjective) → ∀ x → (⌊ I ⌋ ⊨ x ≈ ind I (ind⁻¹ I∈Surj x))
surj✓ (surj i) x = proj₂ (i x)


open import Agda.Builtin.Bool
open import Agda.Builtin.Equality
open import Agda.Builtin.Float

data ⊥ : Set where

defNegZero : -0.0 ≡ 0.0 → ⊥
defNegZero ()

primEqNegZero : primFloatEquality -0.0 0.0 ≡ false
primEqNegZero = refl

primLtNegZero₁ : primFloatNumericalLess 0.0 -0.0 ≡ false
primLtNegZero₁ = refl

primLtNegZero₂ : primFloatNumericalLess -0.0 0.0 ≡ false
primLtNegZero₂ = refl

primShowNegZero : primShowFloat -0.0 ≡ "-0.0"
primShowNegZero = refl


------------------------------------------------------------------------
-- The Agda standard library
--
-- Properties of constructions over unary relations
------------------------------------------------------------------------

{-# OPTIONS --without-K --safe #-}

module Relation.Unary.Properties where

open import Data.Product using (_×_; _,_; swap; proj₁)
open import Data.Sum.Base using (inj₁; inj₂)
open import Data.Unit using (tt)
open import Relation.Binary.Core hiding (Decidable)
open import Relation.Unary
open import Relation.Nullary using (yes; no)
open import Relation.Nullary.Product using (_×-dec_)
open import Relation.Nullary.Sum using (_⊎-dec_)
open import Relation.Nullary.Negation using (¬?)
open import Function using (_$_; _∘_)

----------------------------------------------------------------------
-- The empty set

module _ {a} {A : Set a} where

  ∅? : Decidable {A = A} ∅
  ∅? _ = no λ()

  ∅-Empty : Empty {A = A} ∅
  ∅-Empty x ()

  ∁∅-Universal : Universal {A = A} (∁ ∅)
  ∁∅-Universal = λ x x∈∅ → x∈∅

----------------------------------------------------------------------
-- The universe

module _ {a} {A : Set a} where

  U? : Decidable {A = A} U
  U? _ = yes tt

  U-Universal : Universal {A = A} U
  U-Universal = λ _ → _

  ∁U-Empty : Empty {A = A} (∁ U)
  ∁U-Empty = λ x x∈∁U → x∈∁U _

----------------------------------------------------------------------
-- Subset properties

module _ {a ℓ} {A : Set a} where

  ∅-⊆ : (P : Pred A ℓ) → ∅ ⊆ P
  ∅-⊆ P ()

  ⊆-U : (P : Pred A ℓ) → P ⊆ U
  ⊆-U P _ = _

  ⊆-refl : Reflexive (_⊆_ {A = A} {ℓ})
  ⊆-refl x∈P = x∈P

  ⊆-trans : Transitive (_⊆_ {A = A} {ℓ})
  ⊆-trans P⊆Q Q⊆R x∈P = Q⊆R (P⊆Q x∈P)

  ⊂-asym : Asymmetric (_⊂_ {A = A} {ℓ})
  ⊂-asym (_ , Q⊈P) = Q⊈P ∘ proj₁

----------------------------------------------------------------------
-- Decidability properties

module _ {a} {A : Set a} where

  ∁? : ∀ {ℓ} {P : Pred A ℓ} → Decidable P → Decidable (∁ P)
  ∁? P? x = ¬? (P? x)

  _∪?_ : ∀ {ℓ₁ ℓ₂} {P : Pred A ℓ₁} {Q : Pred A ℓ₂} →
         Decidable P → Decidable Q → Decidable (P ∪ Q)
  _∪?_ P? Q? x = (P? x) ⊎-dec (Q? x)

  _∩?_ : ∀ {ℓ₁ ℓ₂} {P : Pred A ℓ₁} {Q : Pred A ℓ₂} →
         Decidable P → Decidable Q → Decidable (P ∩ Q)
  _∩?_ P? Q? x = (P? x) ×-dec (Q? x)

module _ {a b} {A : Set a} {B : Set b} where

  _×?_ : ∀ {ℓ₁ ℓ₂} {P : Pred A ℓ₁} {Q : Pred B ℓ₂} →
         Decidable P → Decidable Q → Decidable (P ⟨×⟩ Q)
  _×?_ P? Q? (a , b) = (P? a) ×-dec (Q? b)

  _⊙?_ : ∀ {ℓ₁ ℓ₂} {P : Pred A ℓ₁} {Q : Pred B ℓ₂} →
         Decidable P → Decidable Q → Decidable (P ⟨⊙⟩ Q)
  _⊙?_ P? Q? (a , b) = (P? a) ⊎-dec (Q? b)

  _⊎?_ : ∀ {ℓ} {P : Pred A ℓ} {Q : Pred B ℓ} →
         Decidable P → Decidable Q → Decidable (P ⟨⊎⟩ Q)
  _⊎?_ P? Q? (inj₁ a) = P? a
  _⊎?_ P? Q? (inj₂ b) = Q? b

  _~? : ∀ {ℓ} {P : Pred (A × B) ℓ} →
        Decidable P → Decidable (P ~)
  _~? P? = P? ∘ swap


module Data.Boolean.Proofs where

import      Lvl
open import Data
open import Data.Boolean
import      Data.Boolean.Operators
open        Data.Boolean.Operators.Logic using (_⊼_ ; _⊽_ ; _⊕_)
open        Data.Boolean.Operators.Programming
open import Data.Either as Either using (_‖_ ; Left ; Right)
open import Functional
open import Logic.IntroInstances
open import Logic.Propositional as Logic using (_∨_ ; _∧_ ; ¬_ ; _↔_ ; [⊤]-intro ; [↔]-intro ; [⊥]-elim ; [↔]-to-[←] ; [↔]-to-[→])
open import Logic.Predicate
open import Relator.Equals
open import Relator.Equals.Proofs.Equiv
open import Structure.Operator.Monoid
import      Structure.Operator.Names as Names
open import Structure.Operator.Properties
open import Type

private variable ℓ : Lvl.Level
private variable T : Type{ℓ}
private variable P : Bool → Type{ℓ}
private variable a b c t f : Bool
private variable x y : Bool
private variable pt pf : ∀{b} → P(b)

---------------------------------------------
-- Eliminator

module _ {pt pf : T} where
  elim-nested : (elim pt pf (elim t f b) ≡ elim{T = const T} (elim pt pf t) (elim pt pf f) b)
  elim-nested{t = t}{f = f}{b = b} = elim{T = b ↦ (elim pt pf (elim t f b) ≡ elim(elim pt pf t) (elim pt pf f) b)} [≡]-intro [≡]-intro b

module _ {x : T} where
  elim-redundant : (elim{T = const T} x x b ≡ x)
  elim-redundant{b = b} = elim{T = b ↦ elim x x b ≡ x} [≡]-intro [≡]-intro b

elim-inverse : (elim 𝑇 𝐹 b ≡ b)
elim-inverse{b = b} = elim{T = b ↦ elim 𝑇 𝐹 b ≡ b} [≡]-intro [≡]-intro b

elim-anti-inverse : (elim 𝐹 𝑇 b ≡ ! b)
elim-anti-inverse {𝑇} = [≡]-intro
elim-anti-inverse {𝐹} = [≡]-intro

---------------------------------------------
-- Negation

[!]-no-fixpoints : ∀{b} → (! b ≢ b)
[!]-no-fixpoints {𝑇} ()
[!]-no-fixpoints {𝐹} ()

[!!]-elim : ∀{a} → (!(! a) ≡ a)
[!!]-elim {𝑇} = [≡]-intro
[!!]-elim {𝐹} = [≡]-intro
{-# REWRITE [!!]-elim #-}

---------------------------------------------
-- Idempotence

[&&]-idempotence-raw : ∀{a} → (a && a ≡ a)
[&&]-idempotence-raw {𝑇} = [≡]-intro
[&&]-idempotence-raw {𝐹} = [≡]-intro
{-# REWRITE [&&]-idempotence-raw #-}
instance
  [&&]-idempotence : Idempotence(_&&_)
  Idempotence.proof([&&]-idempotence) = [&&]-idempotence-raw

[||]-idempotence-raw : ∀{a} → (a || a ≡ a)
[||]-idempotence-raw {𝑇} = [≡]-intro
[||]-idempotence-raw {𝐹} = [≡]-intro
{-# REWRITE [||]-idempotence-raw #-}
instance
  [||]-idempotence : Idempotence(_||_)
  Idempotence.proof([||]-idempotence) = [||]-idempotence-raw
---------------------------------------------
-- Left anti-identities

[==]-anti-identityₗ : ∀{a} → (𝐹 == a ≡ ! a)
[==]-anti-identityₗ {𝑇} = [≡]-intro
[==]-anti-identityₗ {𝐹} = [≡]-intro
{-# REWRITE [==]-anti-identityₗ #-}

[!=]-anti-identityₗ : ∀{a} → (𝑇 != a ≡ ! a)
[!=]-anti-identityₗ {𝑇} = [≡]-intro
[!=]-anti-identityₗ {𝐹} = [≡]-intro
{-# REWRITE [!=]-anti-identityₗ #-}

---------------------------------------------
-- Right anti-identities

[==]-anti-identityᵣ : ∀{a} → (a == 𝐹 ≡ ! a)
[==]-anti-identityᵣ {𝑇} = [≡]-intro
[==]-anti-identityᵣ {𝐹} = [≡]-intro
{-# REWRITE [==]-anti-identityᵣ #-}

[!=]-anti-identityᵣ : ∀{a} → (a != 𝑇 ≡ ! a)
[!=]-anti-identityᵣ {𝑇} = [≡]-intro
[!=]-anti-identityᵣ {𝐹} = [≡]-intro
{-# REWRITE [!=]-anti-identityᵣ #-}

---------------------------------------------
-- Left identities

[||]-identityₗ-raw : ∀{a} → (𝐹 || a ≡ a)
[||]-identityₗ-raw {𝑇} = [≡]-intro
[||]-identityₗ-raw {𝐹} = [≡]-intro
{-# REWRITE [||]-identityₗ-raw #-}
instance
  [||]-identityₗ : Identityₗ(_||_)(𝐹)
  Identityₗ.proof([||]-identityₗ) = [||]-identityₗ-raw

[&&]-identityₗ-raw : ∀{a} → (𝑇 && a ≡ a)
[&&]-identityₗ-raw {𝑇} = [≡]-intro
[&&]-identityₗ-raw {𝐹} = [≡]-intro
{-# REWRITE [&&]-identityₗ-raw #-}
instance
  [&&]-identityₗ : Identityₗ(_&&_)(𝑇)
  Identityₗ.proof([&&]-identityₗ) = [&&]-identityₗ-raw

[==]-identityₗ-raw : ∀{a} → (𝑇 == a ≡ a)
[==]-identityₗ-raw {𝑇} = [≡]-intro
[==]-identityₗ-raw {𝐹} = [≡]-intro
{-# REWRITE [==]-identityₗ-raw #-}
instance
  [==]-identityₗ : Identityₗ(_==_)(𝑇)
  Identityₗ.proof([==]-identityₗ) = [==]-identityₗ-raw

[!=]-identityₗ-raw : ∀{a} → (𝐹 != a ≡ a)
[!=]-identityₗ-raw {𝑇} = [≡]-intro
[!=]-identityₗ-raw {𝐹} = [≡]-intro
{-# REWRITE [!=]-identityₗ-raw #-}
instance
  [!=]-identityₗ : Identityₗ(_!=_)(𝐹)
  Identityₗ.proof([!=]-identityₗ) = [!=]-identityₗ-raw

---------------------------------------------
-- Left absorbers

[||]-absorberₗ-raw : ∀{a} → (𝑇 || a ≡ 𝑇)
[||]-absorberₗ-raw {𝑇} = [≡]-intro
[||]-absorberₗ-raw {𝐹} = [≡]-intro
{-# REWRITE [||]-absorberₗ-raw #-}
instance
  [||]-absorberₗ : Absorberₗ(_||_)(𝑇)
  Absorberₗ.proof([||]-absorberₗ) {a} = [||]-absorberₗ-raw {a}

[&&]-absorberₗ-raw : ∀{a} → (𝐹 && a ≡ 𝐹)
[&&]-absorberₗ-raw {𝑇} = [≡]-intro
[&&]-absorberₗ-raw {𝐹} = [≡]-intro
{-# REWRITE [&&]-absorberₗ-raw #-}
instance
  [&&]-absorberₗ : Absorberₗ(_&&_)(𝐹)
  Absorberₗ.proof([&&]-absorberₗ) {a} = [&&]-absorberₗ-raw {a}

---------------------------------------------
-- Right identities

[||]-identityᵣ-raw : ∀{a} → (a || 𝐹 ≡ a)
[||]-identityᵣ-raw {𝑇} = [≡]-intro
[||]-identityᵣ-raw {𝐹} = [≡]-intro
{-# REWRITE [||]-identityᵣ-raw #-}
instance
  [||]-identityᵣ : Identityᵣ(_||_)(𝐹)
  Identityᵣ.proof([||]-identityᵣ) = [||]-identityᵣ-raw

[&&]-identityᵣ-raw : ∀{a} → (a && 𝑇 ≡ a)
[&&]-identityᵣ-raw {𝑇} = [≡]-intro
[&&]-identityᵣ-raw {𝐹} = [≡]-intro
{-# REWRITE [&&]-identityᵣ-raw #-}
instance
  [&&]-identityᵣ : Identityᵣ(_&&_)(𝑇)
  Identityᵣ.proof([&&]-identityᵣ) = [&&]-identityᵣ-raw

[==]-identityᵣ-raw : ∀{a} → (a == 𝑇 ≡ a)
[==]-identityᵣ-raw {𝑇} = [≡]-intro
[==]-identityᵣ-raw {𝐹} = [≡]-intro
{-# REWRITE [==]-identityᵣ-raw #-}
instance
  [==]-identityᵣ : Identityᵣ(_==_)(𝑇)
  Identityᵣ.proof([==]-identityᵣ) = [==]-identityᵣ-raw

[!=]-identityᵣ-raw : ∀{a} → (a != 𝐹 ≡ a)
[!=]-identityᵣ-raw {𝑇} = [≡]-intro
[!=]-identityᵣ-raw {𝐹} = [≡]-intro
{-# REWRITE [!=]-identityᵣ-raw #-}
instance
  [!=]-identityᵣ : Identityᵣ(_!=_)(𝐹)
  Identityᵣ.proof([!=]-identityᵣ) = [!=]-identityᵣ-raw

---------------------------------------------
-- Identities

instance
  [||]-identity : Identity(_||_)(𝐹)
  [||]-identity = record{}

instance
  [&&]-identity : Identity(_&&_)(𝑇)
  [&&]-identity = record{}

instance
  [==]-identity : Identity(_==_)(𝑇)
  [==]-identity = record{}

instance
  [!=]-identity : Identity(_!=_)(𝐹)
  [!=]-identity = record{}

---------------------------------------------
-- Right absorbers

[||]-absorberᵣ-raw : ∀{a} → (a || 𝑇 ≡ 𝑇)
[||]-absorberᵣ-raw {𝑇} = [≡]-intro
[||]-absorberᵣ-raw {𝐹} = [≡]-intro
{-# REWRITE [||]-absorberᵣ-raw #-}
instance
  [||]-absorberᵣ : Absorberᵣ(_||_)(𝑇)
  Absorberᵣ.proof([||]-absorberᵣ) {a} = [||]-absorberᵣ-raw {a}

[&&]-absorberᵣ-raw : ∀{a} → (a && 𝐹 ≡ 𝐹)
[&&]-absorberᵣ-raw {𝑇} = [≡]-intro
[&&]-absorberᵣ-raw {𝐹} = [≡]-intro
{-# REWRITE [&&]-absorberᵣ-raw #-}
instance
  [&&]-absorberᵣ : Absorberᵣ(_&&_)(𝐹)
  Absorberᵣ.proof([&&]-absorberᵣ) {a} = [&&]-absorberᵣ-raw {a}

---------------------------------------------
-- Associativity

instance
  [||]-associativity : Associativity(_||_)
  Associativity.proof([||]-associativity) = proof where
    proof : Names.Associativity(_||_)
    proof {𝑇}{𝑇}{𝑇} = [≡]-intro
    proof {𝑇}{𝑇}{𝐹} = [≡]-intro
    proof {𝑇}{𝐹}{𝑇} = [≡]-intro
    proof {𝑇}{𝐹}{𝐹} = [≡]-intro
    proof {𝐹}{𝑇}{𝑇} = [≡]-intro
    proof {𝐹}{𝑇}{𝐹} = [≡]-intro
    proof {𝐹}{𝐹}{𝑇} = [≡]-intro
    proof {𝐹}{𝐹}{𝐹} = [≡]-intro

instance
  [&&]-associativity : Associativity(_&&_)
  Associativity.proof([&&]-associativity) = proof where
    proof : Names.Associativity(_&&_)
    proof {𝑇}{𝑇}{𝑇} = [≡]-intro
    proof {𝑇}{𝑇}{𝐹} = [≡]-intro
    proof {𝑇}{𝐹}{𝑇} = [≡]-intro
    proof {𝑇}{𝐹}{𝐹} = [≡]-intro
    proof {𝐹}{𝑇}{𝑇} = [≡]-intro
    proof {𝐹}{𝑇}{𝐹} = [≡]-intro
    proof {𝐹}{𝐹}{𝑇} = [≡]-intro
    proof {𝐹}{𝐹}{𝐹} = [≡]-intro

instance
  [==]-associativity : Associativity(_==_)
  Associativity.proof([==]-associativity) = proof where
    proof : Names.Associativity(_==_)
    proof {𝑇}{𝑇}{𝑇} = [≡]-intro
    proof {𝑇}{𝑇}{𝐹} = [≡]-intro
    proof {𝑇}{𝐹}{𝑇} = [≡]-intro
    proof {𝑇}{𝐹}{𝐹} = [≡]-intro
    proof {𝐹}{𝑇}{𝑇} = [≡]-intro
    proof {𝐹}{𝑇}{𝐹} = [≡]-intro
    proof {𝐹}{𝐹}{𝑇} = [≡]-intro
    proof {𝐹}{𝐹}{𝐹} = [≡]-intro

instance
  [!=]-associativity : Associativity(_!=_)
  Associativity.proof([!=]-associativity) = proof where
    proof : Names.Associativity(_!=_)
    proof {𝑇}{𝑇}{𝑇} = [≡]-intro
    proof {𝑇}{𝑇}{𝐹} = [≡]-intro
    proof {𝑇}{𝐹}{𝑇} = [≡]-intro
    proof {𝑇}{𝐹}{𝐹} = [≡]-intro
    proof {𝐹}{𝑇}{𝑇} = [≡]-intro
    proof {𝐹}{𝑇}{𝐹} = [≡]-intro
    proof {𝐹}{𝐹}{𝑇} = [≡]-intro
    proof {𝐹}{𝐹}{𝐹} = [≡]-intro

---------------------------------------------
-- Other

[⊼]-pseudo-associativity : (!(a ⊼ b) ⊼ c ≡ a ⊼ !(b ⊼ c))
[⊼]-pseudo-associativity {𝑇} {𝑇} {𝑇} = [≡]-intro
[⊼]-pseudo-associativity {𝑇} {𝑇} {𝐹} = [≡]-intro
[⊼]-pseudo-associativity {𝑇} {𝐹} {𝑇} = [≡]-intro
[⊼]-pseudo-associativity {𝑇} {𝐹} {𝐹} = [≡]-intro
[⊼]-pseudo-associativity {𝐹} {𝑇} {𝑇} = [≡]-intro
[⊼]-pseudo-associativity {𝐹} {𝑇} {𝐹} = [≡]-intro
[⊼]-pseudo-associativity {𝐹} {𝐹} {𝑇} = [≡]-intro
[⊼]-pseudo-associativity {𝐹} {𝐹} {𝐹} = [≡]-intro

[⊼]-to-conjunction : (!(a ⊼ b) ≡ a && b)
[⊼]-to-conjunction {𝑇} {𝑇} = [≡]-intro
[⊼]-to-conjunction {𝑇} {𝐹} = [≡]-intro
[⊼]-to-conjunction {𝐹} {𝑇} = [≡]-intro
[⊼]-to-conjunction {𝐹} {𝐹} = [≡]-intro

[⊼]-to-negation : (b ⊼ b ≡ ! b)
[⊼]-to-negation {𝑇} = [≡]-intro
[⊼]-to-negation {𝐹} = [≡]-intro

[⊼]-pseudo-absorptionₗ : (a ⊼ (a ⊼ b) ≡ a ⊼ (! b))
[⊼]-pseudo-absorptionₗ {𝑇} {𝑇} = [≡]-intro
[⊼]-pseudo-absorptionₗ {𝑇} {𝐹} = [≡]-intro
[⊼]-pseudo-absorptionₗ {𝐹} {𝑇} = [≡]-intro
[⊼]-pseudo-absorptionₗ {𝐹} {𝐹} = [≡]-intro

[⊼]-pseudo-absorptionᵣ : ((a ⊼ b) ⊼ b ≡ (! a) ⊼ b)
[⊼]-pseudo-absorptionᵣ {𝑇} {𝑇} = [≡]-intro
[⊼]-pseudo-absorptionᵣ {𝑇} {𝐹} = [≡]-intro
[⊼]-pseudo-absorptionᵣ {𝐹} {𝑇} = [≡]-intro
[⊼]-pseudo-absorptionᵣ {𝐹} {𝐹} = [≡]-intro

[⊼]-pseudo-pseudo-absorptionₗ : (((! a) ⊼ b) ⊼ (a ⊼ b) ≡ b)
[⊼]-pseudo-pseudo-absorptionₗ {𝑇} {𝑇} = [≡]-intro
[⊼]-pseudo-pseudo-absorptionₗ {𝑇} {𝐹} = [≡]-intro
[⊼]-pseudo-pseudo-absorptionₗ {𝐹} {𝑇} = [≡]-intro
[⊼]-pseudo-pseudo-absorptionₗ {𝐹} {𝐹} = [≡]-intro

[⊼]-pseudo-pseudo-absorptionᵣ : ((a ⊼ (! b)) ⊼ (a ⊼ b) ≡ a)
[⊼]-pseudo-pseudo-absorptionᵣ {𝑇} {𝑇} = [≡]-intro
[⊼]-pseudo-pseudo-absorptionᵣ {𝑇} {𝐹} = [≡]-intro
[⊼]-pseudo-pseudo-absorptionᵣ {𝐹} {𝑇} = [≡]-intro
[⊼]-pseudo-pseudo-absorptionᵣ {𝐹} {𝐹} = [≡]-intro

[⊽]-pseudo-associativity : (!(a ⊽ b) ⊽ c ≡ a ⊽ !(b ⊽ c))
[⊽]-pseudo-associativity {𝑇} {𝑇} {𝑇} = [≡]-intro
[⊽]-pseudo-associativity {𝑇} {𝑇} {𝐹} = [≡]-intro
[⊽]-pseudo-associativity {𝑇} {𝐹} {𝑇} = [≡]-intro
[⊽]-pseudo-associativity {𝑇} {𝐹} {𝐹} = [≡]-intro
[⊽]-pseudo-associativity {𝐹} {𝑇} {𝑇} = [≡]-intro
[⊽]-pseudo-associativity {𝐹} {𝑇} {𝐹} = [≡]-intro
[⊽]-pseudo-associativity {𝐹} {𝐹} {𝑇} = [≡]-intro
[⊽]-pseudo-associativity {𝐹} {𝐹} {𝐹} = [≡]-intro

[⊽]-to-disjunction : (!(a ⊽ b) ≡ a || b)
[⊽]-to-disjunction {𝑇} {𝑇} = [≡]-intro
[⊽]-to-disjunction {𝑇} {𝐹} = [≡]-intro
[⊽]-to-disjunction {𝐹} {𝑇} = [≡]-intro
[⊽]-to-disjunction {𝐹} {𝐹} = [≡]-intro

[⊽]-to-negation : (b ⊽ b ≡ ! b)
[⊽]-to-negation {𝑇} = [≡]-intro
[⊽]-to-negation {𝐹} = [≡]-intro

[⊽]-pseudo-absorptionₗ : (a ⊽ (a ⊽ b) ≡ a ⊽ (! b))
[⊽]-pseudo-absorptionₗ {𝑇} {𝑇} = [≡]-intro
[⊽]-pseudo-absorptionₗ {𝑇} {𝐹} = [≡]-intro
[⊽]-pseudo-absorptionₗ {𝐹} {𝑇} = [≡]-intro
[⊽]-pseudo-absorptionₗ {𝐹} {𝐹} = [≡]-intro

[⊽]-pseudo-absorptionᵣ : ((a ⊽ b) ⊽ b ≡ (! a) ⊽ b)
[⊽]-pseudo-absorptionᵣ {𝑇} {𝑇} = [≡]-intro
[⊽]-pseudo-absorptionᵣ {𝑇} {𝐹} = [≡]-intro
[⊽]-pseudo-absorptionᵣ {𝐹} {𝑇} = [≡]-intro
[⊽]-pseudo-absorptionᵣ {𝐹} {𝐹} = [≡]-intro

[⊽]-pseudo-pseudo-absorptionₗ : (((! a) ⊽ b) ⊽ (a ⊽ b) ≡ b)
[⊽]-pseudo-pseudo-absorptionₗ {𝑇} {𝑇} = [≡]-intro
[⊽]-pseudo-pseudo-absorptionₗ {𝑇} {𝐹} = [≡]-intro
[⊽]-pseudo-pseudo-absorptionₗ {𝐹} {𝑇} = [≡]-intro
[⊽]-pseudo-pseudo-absorptionₗ {𝐹} {𝐹} = [≡]-intro

[⊽]-pseudo-pseudo-absorptionᵣ : ((a ⊽ (! b)) ⊽ (a ⊽ b) ≡ a)
[⊽]-pseudo-pseudo-absorptionᵣ {𝑇} {𝑇} = [≡]-intro
[⊽]-pseudo-pseudo-absorptionᵣ {𝑇} {𝐹} = [≡]-intro
[⊽]-pseudo-pseudo-absorptionᵣ {𝐹} {𝑇} = [≡]-intro
[⊽]-pseudo-pseudo-absorptionᵣ {𝐹} {𝐹} = [≡]-intro

---------------------------------------------
-- Commutativity

instance
  [||]-commutativity : Commutativity(_||_)
  Commutativity.proof([||]-commutativity) = proof where
    proof : Names.Commutativity(_||_)
    proof {𝑇}{𝑇} = [≡]-intro
    proof {𝑇}{𝐹} = [≡]-intro
    proof {𝐹}{𝑇} = [≡]-intro
    proof {𝐹}{𝐹} = [≡]-intro

instance
  [&&]-commutativity : Commutativity(_&&_)
  Commutativity.proof([&&]-commutativity) = proof where
    proof : Names.Commutativity(_&&_)
    proof {𝑇}{𝑇} = [≡]-intro
    proof {𝑇}{𝐹} = [≡]-intro
    proof {𝐹}{𝑇} = [≡]-intro
    proof {𝐹}{𝐹} = [≡]-intro

instance
  [==]-commutativity : Commutativity(_==_)
  Commutativity.proof([==]-commutativity) = proof where
    proof : Names.Commutativity(_==_)
    proof {𝑇}{𝑇} = [≡]-intro
    proof {𝑇}{𝐹} = [≡]-intro
    proof {𝐹}{𝑇} = [≡]-intro
    proof {𝐹}{𝐹} = [≡]-intro

instance
  [!=]-commutativity : Commutativity(_!=_)
  Commutativity.proof([!=]-commutativity) = proof where
    proof : Names.Commutativity(_!=_)
    proof {𝑇}{𝑇} = [≡]-intro
    proof {𝑇}{𝐹} = [≡]-intro
    proof {𝐹}{𝑇} = [≡]-intro
    proof {𝐹}{𝐹} = [≡]-intro

instance
  [⊼]-commutativity : Commutativity(_⊼_)
  Commutativity.proof([⊼]-commutativity) = proof where
    proof : Names.Commutativity(_⊼_)
    proof {𝑇}{𝑇} = [≡]-intro
    proof {𝑇}{𝐹} = [≡]-intro
    proof {𝐹}{𝑇} = [≡]-intro
    proof {𝐹}{𝐹} = [≡]-intro

instance
  [⊽]-commutativity : Commutativity(_⊽_)
  Commutativity.proof([⊽]-commutativity) = proof where
    proof : Names.Commutativity(_⊽_)
    proof {𝑇}{𝑇} = [≡]-intro
    proof {𝑇}{𝐹} = [≡]-intro
    proof {𝐹}{𝑇} = [≡]-intro
    proof {𝐹}{𝐹} = [≡]-intro

instance
  [⊕]-commutativity : Commutativity(_⊕_)
  Commutativity.proof([⊕]-commutativity) = proof where
    proof : Names.Commutativity(_⊕_)
    proof {𝑇}{𝑇} = [≡]-intro
    proof {𝑇}{𝐹} = [≡]-intro
    proof {𝐹}{𝑇} = [≡]-intro
    proof {𝐹}{𝐹} = [≡]-intro

instance
  [&&][||]-distributivityₗ : Distributivityₗ(_&&_)(_||_)
  Distributivityₗ.proof([&&][||]-distributivityₗ) = proof where
    proof : Names.Distributivityₗ(_&&_)(_||_)
    proof {𝑇}{𝑇}{𝑇} = [≡]-intro
    proof {𝑇}{𝑇}{𝐹} = [≡]-intro
    proof {𝑇}{𝐹}{𝑇} = [≡]-intro
    proof {𝑇}{𝐹}{𝐹} = [≡]-intro
    proof {𝐹}{𝑇}{𝑇} = [≡]-intro
    proof {𝐹}{𝑇}{𝐹} = [≡]-intro
    proof {𝐹}{𝐹}{𝑇} = [≡]-intro
    proof {𝐹}{𝐹}{𝐹} = [≡]-intro

instance
  [||][&&]-distributivityₗ : Distributivityₗ(_||_)(_&&_)
  Distributivityₗ.proof([||][&&]-distributivityₗ) = proof where
    proof : Names.Distributivityₗ(_||_)(_&&_)
    proof {𝑇}{𝑇}{𝑇} = [≡]-intro
    proof {𝑇}{𝑇}{𝐹} = [≡]-intro
    proof {𝑇}{𝐹}{𝑇} = [≡]-intro
    proof {𝑇}{𝐹}{𝐹} = [≡]-intro
    proof {𝐹}{𝑇}{𝑇} = [≡]-intro
    proof {𝐹}{𝑇}{𝐹} = [≡]-intro
    proof {𝐹}{𝐹}{𝑇} = [≡]-intro
    proof {𝐹}{𝐹}{𝐹} = [≡]-intro

instance
  [||]-oppositeFunctionₗ : ComplementFunctionₗ(_||_)(!)
  ComplementFunctionₗ.proof([||]-oppositeFunctionₗ) = proof where
    proof : Names.InverseFunctionₗ(_||_)(𝑇)(!)
    proof {𝑇} = [≡]-intro
    proof {𝐹} = [≡]-intro

instance
  [||]-oppositeFunctionᵣ : ComplementFunctionᵣ(_||_)(!)
  ComplementFunctionᵣ.proof([||]-oppositeFunctionᵣ) = proof where
    proof : Names.InverseFunctionᵣ(_||_)(𝑇)(!)
    proof {𝑇} = [≡]-intro
    proof {𝐹} = [≡]-intro

instance
  [&&]-oppositeFunctionₗ : ComplementFunctionₗ(_&&_)(!)
  ComplementFunctionₗ.proof([&&]-oppositeFunctionₗ) = proof where
    proof : Names.InverseFunctionₗ(_&&_)(𝐹)(!)
    proof {𝑇} = [≡]-intro
    proof {𝐹} = [≡]-intro

instance
  [&&]-oppositeFunctionᵣ : ComplementFunctionᵣ(_&&_)(!)
  ComplementFunctionᵣ.proof([&&]-oppositeFunctionᵣ) = proof where
    proof : Names.InverseFunctionᵣ(_&&_)(𝐹)(!)
    proof {𝑇} = [≡]-intro
    proof {𝐹} = [≡]-intro

---------------------------------------------
-- Algebraic structures

instance
  [&&]-monoid : Monoid(_&&_)
  [&&]-monoid = record{}

instance
  [||]-monoid : Monoid(_||_)
  [||]-monoid = record{}

instance
  [==]-monoid : Monoid(_==_)
  [==]-monoid = record{}

instance
  [!=]-monoid : Monoid(_!=_)
  [!=]-monoid = record{}

-- TODO: Set algebra

---------------------------------------------
-- Inverting

inverted-[==][!=] : ∀{a b} → (!(a == b) ≡ (a != b))
inverted-[==][!=] {𝑇}{𝑇} = [≡]-intro
inverted-[==][!=] {𝑇}{𝐹} = [≡]-intro
inverted-[==][!=] {𝐹}{𝑇} = [≡]-intro
inverted-[==][!=] {𝐹}{𝐹} = [≡]-intro

inverted-[!=][==] : ∀{a b} → (!(a != b) ≡ (a == b))
inverted-[!=][==] {𝑇}{𝑇} = [≡]-intro
inverted-[!=][==] {𝑇}{𝐹} = [≡]-intro
inverted-[!=][==] {𝐹}{𝑇} = [≡]-intro
inverted-[!=][==] {𝐹}{𝐹} = [≡]-intro

---------------------------------------------
-- Rules of classical logic

-- A boolean operation is either true or false
bivalence : ∀{a} → ((a ≡ 𝑇) ∨ (a ≡ 𝐹))
bivalence {𝑇} = Logic.[∨]-introₗ [≡]-intro
bivalence {𝐹} = Logic.[∨]-introᵣ [≡]-intro

-- A boolean operation is not both true and false at the same time
disjointness : ∀{a} → ¬((a ≡ 𝑇) ∧ (a ≡ 𝐹))
disjointness {𝑇} (Logic.[∧]-intro [≡]-intro ())
disjointness {𝐹} (Logic.[∧]-intro () [≡]-intro)

[→?]-disjunctive-form : ∀{a b} → ((a →? b) ≡ ((! a) || b))
[→?]-disjunctive-form {𝑇} {𝑇} = [≡]-intro
[→?]-disjunctive-form {𝑇} {𝐹} = [≡]-intro
[→?]-disjunctive-form {𝐹} {𝑇} = [≡]-intro
[→?]-disjunctive-form {𝐹} {𝐹} = [≡]-intro

[==]-disjunctive-form : ∀{a b} → ((a == b) ≡ ((a && b) || ((! a) && (! b))))
[==]-disjunctive-form {𝑇} {𝑇} = [≡]-intro
[==]-disjunctive-form {𝑇} {𝐹} = [≡]-intro
[==]-disjunctive-form {𝐹} {𝑇} = [≡]-intro
[==]-disjunctive-form {𝐹} {𝐹} = [≡]-intro

module 𝑇 where
  [∧]-intro : ∀{a b} → (a ≡ 𝑇) → (b ≡ 𝑇) → ((a && b) ≡ 𝑇)
  [∧]-intro [≡]-intro [≡]-intro = [≡]-intro

  [∨]-introₗ : ∀{a b} → (a ≡ 𝑇) → ((a || b) ≡ 𝑇)
  [∨]-introₗ {_}{𝑇} [≡]-intro = [≡]-intro
  [∨]-introₗ {_}{𝐹} [≡]-intro = [≡]-intro

  [∨]-introᵣ : ∀{a b} → (b ≡ 𝑇) → ((a || b) ≡ 𝑇)
  [∨]-introᵣ {𝑇}{_} [≡]-intro = [≡]-intro
  [∨]-introᵣ {𝐹}{_} [≡]-intro = [≡]-intro

  [∧]-elimₗ : ∀{a b} → ((a && b) ≡ 𝑇) → (a ≡ 𝑇)
  [∧]-elimₗ {𝑇}{𝑇} [≡]-intro = [≡]-intro
  [∧]-elimₗ {𝑇}{𝐹} ()
  [∧]-elimₗ {𝐹}{𝑇} ()
  [∧]-elimₗ {𝐹}{𝐹} ()

  [∧]-elimᵣ : ∀{a b} → ((a && b) ≡ 𝑇) → (b ≡ 𝑇)
  [∧]-elimᵣ {𝑇}{𝑇} [≡]-intro = [≡]-intro
  [∧]-elimᵣ {𝑇}{𝐹} ()
  [∧]-elimᵣ {𝐹}{𝑇} ()
  [∧]-elimᵣ {𝐹}{𝐹} ()

  [∨]-elim : ∀{a b}{ℓ₂}{φ : Type{ℓ₂}} → ((a ≡ 𝑇) → φ) → ((b ≡ 𝑇) → φ) → ((a || b) ≡ 𝑇) → φ
  [∨]-elim {𝑇}{𝑇}{_} f _ [≡]-intro = f [≡]-intro
  [∨]-elim {𝑇}{𝐹}{_} f _ [≡]-intro = f [≡]-intro
  [∨]-elim {𝐹}{𝑇}{_} _ f [≡]-intro = f [≡]-intro
  [∨]-elim {𝐹}{𝐹}{_} _ f ()

  [¬]-intro : ∀{a} → (a ≡ 𝐹) → (! a ≡ 𝑇)
  [¬]-intro [≡]-intro = [≡]-intro

  [¬]-elim : ∀{a} → (! a ≡ 𝑇) → (a ≡ 𝐹)
  [¬]-elim {𝑇} ()
  [¬]-elim {𝐹} [≡]-intro = [≡]-intro

  [¬¬]-elim : ∀{a} → (!(! a) ≡ 𝑇) → (a ≡ 𝑇)
  [¬¬]-elim {𝑇} [≡]-intro = [≡]-intro
  [¬¬]-elim {𝐹} ()

  preserves-[&&][∧] : ∀{a b} → ((a && b) ≡ 𝑇) ↔ (a ≡ 𝑇)∧(b ≡ 𝑇)
  preserves-[&&][∧] = [↔]-intro
    (\{(Logic.[∧]-intro l r) → [∧]-intro l r})
    (proof ↦ Logic.[∧]-intro ([∧]-elimₗ proof) ([∧]-elimᵣ proof))

  preserves-[||][∨] : ∀{a b} → ((a || b) ≡ 𝑇) ↔ (a ≡ 𝑇)∨(b ≡ 𝑇)
  preserves-[||][∨] = [↔]-intro
    (Logic.[∨]-elim [∨]-introₗ [∨]-introᵣ)
    ([∨]-elim Logic.[∨]-introₗ Logic.[∨]-introᵣ)

  preserves-[!][¬] : ∀{a} → (! a ≡ 𝑇) ↔ ¬(a ≡ 𝑇)
  preserves-[!][¬] {a} = [↔]-intro (l{a}) (r{a}) where
    l : ∀{a} → (! a ≡ 𝑇) ← ¬(a ≡ 𝑇)
    l {𝐹} _ = [≡]-intro
    l {𝑇} f = [⊥]-elim (f [≡]-intro)

    r : ∀{a} → (! a ≡ 𝑇) → ¬(a ≡ 𝑇)
    r {𝑇} () _
    r {𝐹} _ ()

  [≡]-transfer : ∀{a b} → ((a == b) ≡ 𝑇) ↔ (a ≡ b)
  [≡]-transfer {𝑇}{𝑇} = [↔]-intro (_ ↦ [≡]-intro) (_ ↦ [≡]-intro)
  [≡]-transfer {𝐹}{𝑇} = [↔]-intro (\()) (\())
  [≡]-transfer {𝑇}{𝐹} = [↔]-intro (\()) (\())
  [≡]-transfer {𝐹}{𝐹} = [↔]-intro (_ ↦ [≡]-intro) (_ ↦ [≡]-intro)

  [≢]-transfer : ∀{a b} → ((a != b) ≡ 𝑇) ↔ (a ≢ b)
  [≢]-transfer {𝑇}{𝑇} = [↔]-intro (ab ↦ [⊥]-elim(ab [≡]-intro)) (\())
  [≢]-transfer {𝐹}{𝑇} = [↔]-intro (_ ↦ [≡]-intro) (_ ↦ \())
  [≢]-transfer {𝑇}{𝐹} = [↔]-intro (_ ↦ [≡]-intro) (_ ↦ \())
  [≢]-transfer {𝐹}{𝐹} = [↔]-intro (ab ↦ [⊥]-elim(ab [≡]-intro)) (\())

module 𝐹 where
  [∧]-introₗ : ∀{a b} → (a ≡ 𝐹) → ((a && b) ≡ 𝐹)
  [∧]-introₗ {_}{𝑇} [≡]-intro = [≡]-intro
  [∧]-introₗ {_}{𝐹} [≡]-intro = [≡]-intro

  [∧]-introᵣ : ∀{a b} → (b ≡ 𝐹) → ((a && b) ≡ 𝐹)
  [∧]-introᵣ {𝑇}{_} [≡]-intro = [≡]-intro
  [∧]-introᵣ {𝐹}{_} [≡]-intro = [≡]-intro

  [∨]-intro : ∀{a b} → (a ≡ 𝐹) → (b ≡ 𝐹) → ((a || b) ≡ 𝐹)
  [∨]-intro [≡]-intro [≡]-intro = [≡]-intro

  [¬]-intro : ∀{a} → (! a ≡ 𝑇) → (a ≡ 𝐹)
  [¬]-intro = 𝑇.[¬]-elim

  [¬]-elim : ∀{a} → (a ≡ 𝐹) → (! a ≡ 𝑇)
  [¬]-elim = 𝑇.[¬]-intro

[≢][𝑇]-is-[𝐹] : ∀{a} → (a ≢ 𝑇) ↔ (a ≡ 𝐹)
[≢][𝑇]-is-[𝐹] {a} = [↔]-intro (l{a}) (r{a}) where
  r : ∀{a} → (a ≢ 𝑇) → (a ≡ 𝐹)
  r {𝑇} (a≢𝑇) = [⊥]-elim ((a≢𝑇) ([≡]-intro))
  r {𝐹} (a≢𝑇) = [≡]-intro

  l : ∀{a} → (a ≢ 𝑇) ← (a ≡ 𝐹)
  l {𝑇} ()
  l {𝐹} (a≡𝐹) ()

[≢][𝐹]-is-[𝑇] : ∀{a} → (a ≢ 𝐹) ↔ (a ≡ 𝑇)
[≢][𝐹]-is-[𝑇] {a} = [↔]-intro (l{a}) (r{a}) where
  r : ∀{a} → (a ≢ 𝐹) → (a ≡ 𝑇)
  r {𝑇} (a≢𝐹) = [≡]-intro
  r {𝐹} (a≢𝐹) = [⊥]-elim ((a≢𝐹) ([≡]-intro))

  l : ∀{a} → (a ≢ 𝐹) ← (a ≡ 𝑇)
  l {𝑇} (a≡𝑇) ()
  l {𝐹} ()

---------------------------------------------
-- If-statements

module _ {ℓ₁ ℓ₂} {T : Type{ℓ₁}} {x y : T} {P : T → Type{ℓ₂}} where
  if-intro : ∀{B} → ((B ≡ 𝑇) → P(x)) → ((B ≡ 𝐹) → P(y)) → P(if B then x else y)
  if-intro {𝑇} px py = px [≡]-intro
  if-intro {𝐹} px py = py [≡]-intro

module _ {ℓ₁ ℓ₂ ℓ₃} {T : Type{ℓ₁}} {x y : T} {P : T → Type{ℓ₂}} {Q : Bool → Type{ℓ₃}} where
  if-elim : ∀{B} → P(if B then x else y) → (P(x) → Q(𝑇)) → (P(y) → Q(𝐹)) → Q(B)
  if-elim{𝑇} p pxq pyq = pxq p
  if-elim{𝐹} p pxq pyq = pyq p

module _ {ℓ₁ ℓ₂ ℓ₃} {T : Type{ℓ₁}} {x y : T} {P : T → Type{ℓ₂}} {Q : Type{ℓ₃}} where
  if-bool-elim : ∀{B} → P(if B then x else y) → (P(x) → (B ≡ 𝑇) → Q) → (P(y) → (B ≡ 𝐹) → Q) → Q
  if-bool-elim{𝑇} p pxq pyq = pxq p [≡]-intro
  if-bool-elim{𝐹} p pxq pyq = pyq p [≡]-intro

module _ {ℓ₁ ℓ₂ ℓ₃ ℓ₄} {T : Type{ℓ₁}} {P : T → Type{ℓ₂}} {X : Type{ℓ₃}} {Y : Type{ℓ₄}} (nxy : X → Y → Logic.⊥) where
  either-bool-left : (xy : (X ∨ Y)) → (X ↔ (Either.isRight(xy) ≡ 𝐹))
  either-bool-left xy with bivalence{Either.isRight(xy)}
  either-bool-left (Left  x) | Right f = [↔]-intro (const x) (const f)
  either-bool-left (Right y) | Left  t = [↔]-intro (\()) (x ↦ empty(nxy x y))

  either-bool-right : (xy : (X ∨ Y)) → (Y ↔ (Either.isRight(xy) ≡ 𝑇))
  either-bool-right xy with bivalence{Either.isRight(xy)}
  either-bool-right (Left  x) | Right f = [↔]-intro (\()) (y ↦ empty(nxy x y))
  either-bool-right (Right y) | Left  t = [↔]-intro (const y) (const t)

module _ {ℓ₁ ℓ₂ ℓ₃ ℓ₄} {T : Type{ℓ₁}} {P : T → Type{ℓ₂}} {X : Type{ℓ₃}} {Y : Type{ℓ₄}} where
  either-bool-leftₗ : (xy : (X ∨ Y)) → (X ← (Either.isRight(xy) ≡ 𝐹))
  either-bool-leftₗ xy with bivalence{Either.isRight(xy)}
  either-bool-leftₗ (Left  x) | Right f = const x
  either-bool-leftₗ (Right y) | Left  t = \()

  either-bool-rightₗ : (xy : (X ∨ Y)) → (Y ← (Either.isRight(xy) ≡ 𝑇))
  either-bool-rightₗ xy with bivalence{Either.isRight(xy)}
  either-bool-rightₗ (Left  x) | Right f = \()
  either-bool-rightₗ (Right y) | Left  t = const y

  if-not-either-bool-intro : ∀{x y : T} → (X → P(x)) → (Y → P(y)) → (xy : (X ∨ Y)) → P(if not(Either.isRight(xy)) then x else y)
  if-not-either-bool-intro {x}{y} xp yp xy = if-intro {x = x}{y = y} (xp ∘ either-bool-leftₗ xy ∘ 𝑇.[¬]-elim) (yp ∘ either-bool-rightₗ xy ∘ 𝑇.[¬¬]-elim ∘ 𝐹.[¬]-elim)

module _ {ℓ₁ ℓ₂ ℓ₃ ℓ₄} {T : Type{ℓ₁}} {P : T → Type{ℓ₂}} {X : Type{ℓ₃}} {Y : Type{ℓ₄}} where
  if-either-bool-intro : ∀{x y : T} → (X → P(x)) → (Y → P(y)) → (xy : (X ∨ Y)) → P(if Either.isRight(xy) then y else x)
  if-either-bool-intro {x}{y} xp yp xy = if-intro {x = y}{y = x} (yp ∘ either-bool-rightₗ {P = P} xy) (xp ∘ either-bool-leftₗ {P = P} xy)

---------------------------------------------
-- The predicate of if-statements

module _ {ℓ} {T : Type{ℓ}} {x y : T} where
  if-and : ∀{B₁ B₂} → (if (B₁ && B₂) then x else y ≡ if B₁ then (if B₂ then x else y) else y)
  if-and {𝐹}{𝐹} = [≡]-intro
  if-and {𝐹}{𝑇} = [≡]-intro
  if-and {𝑇}{𝐹} = [≡]-intro
  if-and {𝑇}{𝑇} = [≡]-intro

  if-or : ∀{B₁ B₂} → (if (B₁ || B₂) then x else y ≡ if B₁ then x else if B₂ then x else y)
  if-or {𝐹}{𝐹} = [≡]-intro
  if-or {𝐹}{𝑇} = [≡]-intro
  if-or {𝑇}{𝐹} = [≡]-intro
  if-or {𝑇}{𝑇} = [≡]-intro

  if-not : ∀{B} → (if (! B) then x else y ≡ if B then y else x)
  if-not {𝐹} = [≡]-intro
  if-not {𝑇} = [≡]-intro

---------------------------------------------
-- The results of if-statements

module _ {ℓ} {T : Type{ℓ}} {x : T} {B} where
  if-then-redundant : (if B then x else x ≡ x)
  if-then-redundant = elim-redundant{b = B}

module _ {ℓ} {T : Type{ℓ}} {B} where
  if-then-bool-inverse : (if B then 𝑇 else 𝐹 ≡ B)
  if-then-bool-inverse = elim-inverse{b = B}

  if-then-bool-anti-inverse : (if B then 𝐹 else 𝑇 ≡ ! B)
  if-then-bool-anti-inverse = elim-anti-inverse{b = B}


open import Type

module Structure.Operator.SetAlgebra {ℓ} (S : Type{ℓ}) where

import      Lvl
open import Functional
open import Logic
open import Logic.Propositional
open import Relator.Equals
open import Relator.Equals.Proofs
open import Structure.Operator.Properties
open import Structure.Operator.Proofs
open import Structure.Relator.Properties
open import Syntax.Transitivity

record Fundamentals : Stmt{ℓ} where
  infixl 1001 _∩_
  infixl 1000 _∪_

  field
    _∪_ : S → S → S -- Union
    _∩_ : S → S → S -- Intersection
    ∅   : S         -- Empty set
    𝐔   : S         -- Universal set

  field -- TODO: This is two commutative monoids with distributivity over each other
    ⦃ [∪]-commutativity ⦄ : Commutativity(_∪_)
    ⦃ [∩]-commutativity ⦄ : Commutativity(_∩_)

    ⦃ [∪]-associativity ⦄ : Associativity(_∪_)
    ⦃ [∩]-associativity ⦄ : Associativity(_∩_)

    ⦃ [∪][∩]-distributivityₗ ⦄ : Distributivityₗ(_∪_)(_∩_)
    ⦃ [∩][∪]-distributivityₗ ⦄ : Distributivityₗ(_∩_)(_∪_)

    ⦃ [∪]-identityₗ ⦄ : Identityₗ(_∪_)(∅)
    ⦃ [∩]-identityₗ ⦄ : Identityₗ(_∩_)(𝐔)

  instance
    [∪][∩]-distributivityᵣ : Distributivityᵣ(_∪_)(_∩_)
    [∪][∩]-distributivityᵣ = [↔]-to-[→] OneTypeTwoOp.distributivity-equivalence-by-commutativity [∪][∩]-distributivityₗ

  instance
    [∩][∪]-distributivityᵣ : Distributivityᵣ(_∩_)(_∪_)
    [∩][∪]-distributivityᵣ = [↔]-to-[→] OneTypeTwoOp.distributivity-equivalence-by-commutativity [∩][∪]-distributivityₗ

  instance
    [∪]-identityᵣ : Identityᵣ(_∪_)(∅)
    [∪]-identityᵣ = [↔]-to-[→] One.identity-equivalence-by-commutativity [∪]-identityₗ

  instance
    [∩]-identityᵣ : Identityᵣ(_∩_)(𝐔)
    [∩]-identityᵣ = [↔]-to-[→] One.identity-equivalence-by-commutativity [∩]-identityₗ

record Complement : Stmt{ℓ} where
  infixl 1002 ∁_
  infixl 1000 _∖_

  field
    ∁_   : S → S -- Complement

  field
    ⦃ fundamentals ⦄ : Fundamentals
  open Fundamentals(fundamentals)

  field -- TODO: Not really inverses. These are using the absorbers
    [∪]-inverseᵣ : ∀{s : S} → (s ∪ ∁(s) ≡ 𝐔)
    [∩]-inverseᵣ : ∀{s : S} → (s ∩ ∁(s) ≡ ∅)

  _∖_ : S → S → S -- Difference
  _∖_ (s₁)(s₂) = s₁ ∩ ∁(s₂)

  [∪]-inverseₗ : ∀{s : S} → (∁(s) ∪ s ≡ 𝐔)
  [∪]-inverseₗ = transitivity(_≡_) (commutativity(_∪_)) [∪]-inverseᵣ

  [∩]-inverseₗ : ∀{s : S} → (∁(s) ∩ s ≡ ∅)
  [∩]-inverseₗ = transitivity(_≡_) (commutativity(_∩_)) [∩]-inverseᵣ

  [∁]-of-[∅] : (∁(∅) ≡ 𝐔)
  [∁]-of-[∅] =
    symmetry(_≡_) (identityₗ(_∪_)(∅))
    🝖 ([∪]-inverseᵣ)

  [∁]-of-[𝐔] : (∁(𝐔) ≡ ∅)
  [∁]-of-[𝐔] =
    symmetry(_≡_) (identityₗ(_∩_)(𝐔))
    🝖 ([∩]-inverseᵣ)

  [∪]-idempotence : ∀{s : S} → (s ∪ s) ≡ s
  [∪]-idempotence{s} =
    ([≡]-intro)
    🝖 (symmetry(_≡_) (identityᵣ(_∩_)(𝐔)))
    🝖 ([≡]-with(expr ↦ ((s ∪ s) ∩ expr)) (symmetry(_≡_) [∪]-inverseᵣ))
    🝖 (symmetry(_≡_) (distributivityₗ(_∪_)(_∩_)))
    🝖 ([≡]-with(expr ↦ (s ∪ expr)) [∩]-inverseᵣ)
    🝖 ((identityᵣ(_∪_)(∅)))

  [∩]-idempotence : ∀{s : S} → (s ∩ s) ≡ s
  [∩]-idempotence{s} =
    ([≡]-intro)
    🝖 (symmetry(_≡_) (identityᵣ(_∪_)(∅)))
    🝖 ([≡]-with(expr ↦ ((s ∩ s) ∪ expr)) (symmetry(_≡_) [∩]-inverseᵣ))
    🝖 (symmetry(_≡_) (distributivityₗ(_∩_)(_∪_)))
    🝖 ([≡]-with(expr ↦ (s ∩ expr)) [∪]-inverseᵣ)
    🝖 ((identityᵣ(_∩_)(𝐔)))

  [∪]-absorber : ∀{s : S} → (s ∪ 𝐔) ≡ 𝐔
  [∪]-absorber{s} =
    ([≡]-with(expr ↦ s ∪ expr) (symmetry(_≡_) [∪]-inverseᵣ))
    🝖 (symmetry(_≡_) (associativity(_∪_)))
    🝖 ([≡]-with(expr ↦ expr ∪ ∁(s)) [∪]-idempotence)
    🝖 ([∪]-inverseᵣ)
    -- s∪𝐔 = s∪(s ∪ ∁(s)) = (s∪s) ∪ ∁(s) = s ∪ ∁(s) = 𝐔

  [∩]-absorber : ∀{s : S} → (s ∩ ∅) ≡ ∅
  [∩]-absorber{s} =
    ([≡]-with(expr ↦ s ∩ expr) (symmetry(_≡_) [∩]-inverseᵣ))
    🝖 (symmetry(_≡_) (associativity(_∩_)))
    🝖 ([≡]-with(expr ↦ expr ∩ ∁(s)) [∩]-idempotence)
    🝖 ([∩]-inverseᵣ)
    -- s∩∅ = s∩(s ∩ ∁(s)) = (s∩s) ∩ ∁(s) = s ∩ ∁(s) = ∅

  -- postulate [∪]-absorptionₗ : ∀{s₁ s₂ : S} → (s₁ ∪ (s₁ ∩ s₂)) ≡ s₁
    -- s₁∪(s₁∩s₂)
    -- = (s₁∪s₁)∩(s₁∪s₂)
    -- = s₁∩(s₁∪s₂)
    -- = (s₁∩s₁) ∪ (s₁∩s₂)
    -- = s₁ ∪ (s₁∩s₂)
    -- = ?
  -- postulate [∩]-absorptionₗ : ∀{s₁ s₂ : S} → (s₁ ∩ (s₁ ∪ s₂)) ≡ s₁

  -- ∁(s₁) ∪ ∁(s₁ ∪ s₂) = ∁(s₁)
  -- ∁(s₁) ∩ ∁(s₁ ∪ s₂) = ∁(s₁ ∪ s₂)
  -- ∁(s₁) ∪ ∁(s₁ ∩ s₂) = ∁(s₁ ∩ s₂)
  -- ∁(s₁) ∩ ∁(s₁ ∩ s₂) = ∁(s₁)

--    postulate a : ∀{a} → a

  -- postulate [∁]-of-[∪] : ∀{s₁ s₂ : S} → ∁(s₁ ∪ s₂) ≡ ∁(s₁) ∩ ∁(s₂)
  -- [∁]-of-[∪] =
    -- ((∁(s₁) ∩ ∁(s₂)) ∪ (s₁ ∪ s₂)) = 𝐔 ?

    -- (s₁ ∪ s₂) ∪ ∁(s₁ ∪ s₂) = 𝐔
    -- ∁(s₂) ∩ ((s₁ ∪ s₂) ∪ ∁(s₁ ∪ s₂)) = ∁(s₁) ∩ 𝐔
    -- (∁(s₂) ∩ (s₁ ∪ s₂)) ∪ (∁(s₁) ∩ ∁(s₁ ∪ s₂)) = ∁(s₂) ∩ 𝐔
    -- (∁(s₂) ∩ (s₁ ∪ s₂)) ∪ (∁(s₁) ∩ ∁(s₁ ∪ s₂)) = ∁(s₂) ∩ 𝐔
    -- (∁(s₂) ∩ (s₁ ∪ s₂)) ∪ (∁(s₁) ∩ ∁(s₁ ∪ s₂)) = ∁(s₂)
    -- ∁(s₂) = (∁(s₂) ∩ (s₁ ∪ s₂)) ∪ (∁(s₁) ∩ ∁(s₁ ∪ s₂))
    -- ∁(s₂) = ((∁(s₂) ∩ s₁) ∪ (∁(s₂) ∩ s₂)) ∪ ∁(s₁ ∪ s₂)
    -- ∁(s₂) = ((∁(s₂) ∩ s₁) ∪ ∅) ∪ ∁(s₁ ∪ s₂)
    -- ∁(s₂) = (∁(s₂) ∩ s₁) ∪ ∁(s₁ ∪ s₂)
    -- ∁(s₁) ∩ ∁(s₂) = ∁(s₁) ∩ ((∁(s₂) ∩ s₁) ∪ ∁(s₁ ∪ s₂))
    -- ∁(s₁) ∩ ∁(s₂) = (∁(s₁) ∩ (∁(s₂) ∩ s₁)) ∪ (∁(s₁) ∩ ∁(s₁ ∪ s₂))
    -- ∁(s₁) ∩ ∁(s₂) = (∁(s₁) ∩ (∁(s₂) ∩ s₁)) ∪ ∁(s₁ ∪ s₂)
    -- ∁(s₁) ∩ ∁(s₂) = (∁(s₁) ∩ (s₁ ∩ ∁(s₂))) ∪ ∁(s₁ ∪ s₂)
    -- ∁(s₁) ∩ ∁(s₂) = ((∁(s₁) ∩ s₁) ∩ ∁(s₂)) ∪ ∁(s₁ ∪ s₂)
    -- ∁(s₁) ∩ ∁(s₂) = (∅ ∩ ∁(s₂)) ∪ ∁(s₁ ∪ s₂)
    -- ∁(s₁) ∩ ∁(s₂) = ∅ ∪ ∁(s₁ ∪ s₂)
    -- ∁(s₁) ∩ ∁(s₂) = ∁(s₁ ∪ s₂)

    -- postulate proof1 : ∀{a b c d} → (a ∩ b) ∩ (c ∪ d) ≡ (a ∩ (b ∩ d)) ∪ (b ∩ (a ∩ c))
      -- (a ∩ b) ∩ (c ∪ d)
      -- = ((a ∩ b) ∩ c) ∪ ((a ∩ b) ∩ d)
      -- = ((b ∩ a) ∩ c) ∪ ((a ∩ b) ∩ d)
      -- = (b ∩ (a ∩ c)) ∪ ((a ∩ b) ∩ d)
      -- = (b ∩ (a ∩ c)) ∪ (a ∩ (b ∩ d))
      -- = (a ∩ (b ∩ d)) ∪ (b ∩ (a ∩ c))

    -- postulate proof2 : ∀{a b} → (a ∩ b) ∩ (∁(a) ∪ ∁(b)) ≡ ∅
      -- (a ∩ b) ∩ (∁(a) ∪ ∁(b))
      -- = (a ∩ (b ∩ ∁(b))) ∪ (b ∩ (a ∩ ∁(a)))
      -- = (a ∩ ∅) ∪ (b ∩ (a ∩ ∁(a)))
      -- = ∅ ∪ (b ∩ (a ∩ ∁(a)))
      -- = b ∩ (a ∩ ∁(a))
      -- = b ∩ ∅
      -- = ∅

    -- ∁(s₁ ∪ s₂) ∪ (s₁ ∪ s₂) = 𝐔
    -- (∁(s₁ ∪ s₂) ∪ (s₁ ∪ s₂)) ∩ (∁(a) ∪ ∁(b)) = 𝐔 ∩ (∁(a) ∪ ∁(b))
    -- (∁(s₁ ∪ s₂) ∪ (s₁ ∪ s₂)) ∩ (∁(a) ∪ ∁(b)) = ∁(a) ∪ ∁(b)
    -- ∁(a) ∪ ∁(b) = (∁(s₁ ∪ s₂) ∪ (s₁ ∪ s₂)) ∩ (∁(a) ∪ ∁(b))
    -- ∁(a) ∪ ∁(b) = (∁(s₁ ∪ s₂) ∩ (∁(a) ∪ ∁(b))) ∪ ((s₁ ∪ s₂) ∩ (∁(a) ∪ ∁(b)))
    -- ∁(a) ∪ ∁(b) = (∁(s₁ ∪ s₂) ∩ (∁(a) ∪ ∁(b))) ∪ ∅
    -- ∁(a) ∪ ∁(b) = ∁(s₁ ∪ s₂) ∩ (∁(a) ∪ ∁(b))

  postulate [∁]-of-[∩] : ∀{s₁ s₂ : S} → ∁(s₁ ∩ s₂) ≡ ∁(s₁) ∪ ∁(s₂)

  [∁∁]-elim : ∀{s : S} → ∁(∁(s)) ≡ s
  [∁∁]-elim {s} = transitivity(_≡_) proof2 (symmetry(_≡_) proof1) where
    proof1 : s ≡ s ∪ ∁(∁(s))
    proof1 =
      [∩]-inverseᵣ {∁(s)}
      ⩺ [≡]-with(s ∪_)
      ⩺ (eq ↦ transitivity(_≡_) eq ((identityᵣ(_∪_)(∅)) {s}))
      ⩺ symmetry(_≡_)
      ⩺ (eq ↦ transitivity(_≡_) eq ((distributivityₗ(_∪_)(_∩_))))
      ⩺ (eq ↦ transitivity(_≡_) eq ([≡]-with(_∩ (s ∪ ∁(∁(s)))) ([∪]-inverseᵣ)))
      ⩺ (eq ↦ transitivity(_≡_) eq (identityₗ(_∩_)(𝐔)))
      -- ∁(s) ∩ ∁(∁(s)) ≡ ∅
      -- s ∪ (∁(s) ∩ ∁(∁(s))) ≡ s ∪ ∅
      -- s ∪ (∁(s) ∩ ∁(∁(s))) ≡ s
      -- s ≡ s ∪ (∁(s) ∩ ∁(∁(s)))
      -- s ≡ (s ∪ ∁(s)) ∩ (s ∪ ∁(∁(s)))
      -- s ≡ 𝐔 ∩ (s ∪ ∁(∁(s)))
      -- s ≡ s ∪ ∁(∁(s))

    proof2 : ∁(∁(s)) ≡ s ∪ ∁(∁(s))
    proof2 =
      [∩]-inverseᵣ {s}
      ⩺ [≡]-with(_∪ ∁(∁(s)))
      ⩺ (eq ↦ transitivity(_≡_) eq (identityₗ(_∪_)(∅)))
      ⩺ symmetry(_≡_)
      ⩺ (eq ↦ transitivity(_≡_) eq ((distributivityᵣ(_∪_)(_∩_))))
      ⩺ (eq ↦ transitivity(_≡_) eq ([≡]-with((s ∪ ∁(∁(s))) ∩_) ([∪]-inverseᵣ)))
      ⩺ (eq ↦ transitivity(_≡_) eq ((identityᵣ(_∩_)(𝐔))))
      -- (s ∩ ∁(s)) ∪ ∁(∁(s)) ≡ ∅ ∪ ∁(∁(s))
      -- (s ∩ ∁(s)) ∪ ∁(∁(s)) ≡ ∁(∁(s))
      -- ∁(∁(s)) ≡ (s ∩ ∁(s)) ∪ ∁(∁(s))
      -- ∁(∁(s)) ≡ (s ∪ ∁(∁(s))) ∩ (∁(s) ∪ ∁(∁(s)))
      -- ∁(∁(s)) ≡ (s ∪ ∁(∁(s))) ∩ 𝐔
      -- ∁(∁(s)) ≡ s ∪ ∁(∁(s))

  postulate [∁]-uniqueness : ∀{s₁ s₂ : S} → (s₁ ∪ s₂ ≡ 𝐔) → (s₁ ∩ s₂ ≡ ∅) → (s₁ ≡ ∁(s₂))

  postulate [∁]-of-[∖] : ∀{s₁ s₂ : S} → ∁(s₁ ∖ s₂) ≡ ∁(s₁) ∪ s₂
  postulate [∖]-of-[∁] : ∀{s₁ s₂ : S} → ∁(s₁) ∖ ∁(s₂) ≡ s₂ ∖ s₁

  postulate [∖]-of-[∪]ᵣ : ∀{s₁ s₂ s₃ : S} → (s₁ ∖ (s₂ ∪ s₃)) ≡ (s₁ ∖ s₂)∩(s₁ ∖ s₃)
  postulate [∖]-of-[∩]ᵣ : ∀{s₁ s₂ s₃ : S} → (s₁ ∖ (s₂ ∩ s₃)) ≡ (s₁ ∖ s₂)∪(s₁ ∖ s₃)

  postulate [∖]-of-[∖]ᵣ : ∀{s₁ s₂ s₃ : S} → (s₁ ∖ (s₂ ∖ s₃)) ≡ (s₁ ∩ s₃)∪(s₁ ∖ s₂)
  postulate [∩]-from-[∖] : ∀{s₁ s₂ : S} → (s₁ ∖ (s₁ ∖ s₂)) ≡ (s₁ ∩ s₂) -- TODO: from [∖]-of-[∖]ᵣ

  postulate [∖]-self : ∀{s : S} → s ∖ s ≡ ∅
  postulate [∖]-of-[∅]ₗ : ∀{s : S} → ∅ ∖ s ≡ ∅
  postulate [∖]-of-[∅]ᵣ : ∀{s : S} → s ∖ ∅ ≡ s
  postulate [∖]-of-[𝐔]ₗ : ∀{s : S} → 𝐔 ∖ s ≡ ∁(s)
  postulate [∖]-of-[𝐔]ᵣ : ∀{s : S} → s ∖ 𝐔 ≡ ∅

record Subset : Type{Lvl.𝐒(ℓ)} where
  field
    _⊆_ : S → S → Stmt{ℓ} -- Subset
    ⦃ fundamentals ⦄ : Fundamentals
  open Fundamentals(fundamentals)

  field
    ⦃ [⊆]-antisymmetry ⦄ : Antisymmetry(_⊆_)(_≡_)
    ⦃ [⊆]-transitivity ⦄ : Transitivity(_⊆_)
    ⦃ [⊆]-reflexivity  ⦄ : Reflexivity(_⊆_)

    [≡]-to-[⊆] : ∀{a b} → (a ≡ b) → (a ⊆ b)

    [⊆]ₗ-of-[∪] : ∀{a b c} → (a ⊆ c) → (b ⊆ c) → ((a ∪ b) ⊆ c)
    [⊆]ᵣ-of-[∪]ₗ : ∀{a b} → (a ⊆ (a ∪ b))

    [⊆]ₗ-of-[∩]ₗ : ∀{a b} → ((a ∩ b) ⊆ a)
    [⊆]ᵣ-of-[∩] : ∀{a b c} → (c ⊆ a) → (c ⊆ b) → (c ⊆ (a ∩ b))

  [⊆]ᵣ-of-[∪]ᵣ : ∀{a b} → (b ⊆ (a ∪ b))
  [⊆]ᵣ-of-[∪]ᵣ {a}{b} =
    [⊆]ᵣ-of-[∪]ₗ {b}{a}
    🝖 [≡]-to-[⊆] (commutativity(_∪_))

  [⊆]ₗ-of-[∩]ᵣ : ∀{a b} → ((a ∩ b) ⊆ b)
  [⊆]ₗ-of-[∩]ᵣ {a}{b} =
    [≡]-to-[⊆] (commutativity(_∩_))
    🝖 [⊆]ₗ-of-[∩]ₗ {b}{a}

  [⊆]-min : ∀{s} → (∅ ⊆ s)
  [⊆]-min {s} =
    [⊆]ᵣ-of-[∪]ₗ {∅}{s}
    🝖 [≡]-to-[⊆] (identityₗ(_∪_)(∅))

  [⊆]-max : ∀{s} → (s ⊆ 𝐔)
  [⊆]-max {s} =
    [≡]-to-[⊆] (symmetry(_≡_) (identityₗ(_∩_)(𝐔)))
    🝖 [⊆]ₗ-of-[∩]ₗ {𝐔}{s}

  [⊆][∩]-equiv : ∀{a b} → (a ⊆ b) ↔ (a ∩ b ≡ a)
  [⊆][∩]-equiv {a}{b} = [↔]-intro l r where
    l : (a ⊆ b) ← (a ∩ b ≡ a)
    l aba =
      [≡]-to-[⊆] (symmetry(_≡_) aba)
      🝖 [⊆]ₗ-of-[∩]ᵣ

    r : (a ⊆ b) → (a ∩ b ≡ a)
    r ab =
      (antisymmetry(_⊆_)(_≡_)
        ([⊆]ₗ-of-[∩]ₗ)
        ([⊆]ᵣ-of-[∩] {a}{b}{a} (reflexivity(_⊆_)) ab)
      )
{-
  [⊆][∪]-equiv : ∀{a b} → (a ⊆ b) ↔ (a ∪ b ≡ b)
  [⊆][∪]-equiv {a}{b} = [↔]-intro l r where
    l : (a ⊆ b) ← (a ∪ b ≡ b)
    l aba =
      [≡]-to-[⊆] (symmetry(_≡_) aba)
      🝖 [⊆]ᵣ-of-[∪]ᵣ

    r : (a ⊆ b) → (a ∪ b ≡ b)
    r ab =
      (antisymmetry
        ([⊆]ᵣ-of-[∪]ᵣ)
        ([⊆]ₗ-of-[∪] {a}{b}{a} reflexivity ab)
      )
-}
  -- [⊆][∖]-equiv : (a ⊆ b) ↔ (a ∖ b ≡ ∅)

  -- [⊆][∁]-equiv : (a ⊆ b) ↔ (∁(b) ⊆ ∁(a))

  -- [∩][∪]-sub : (a ∩ b) ⊆ (a ∪ b)


{-# OPTIONS --universe-polymorphism #-}

module NiceGoals where

------------------------------------------------------------------------

postulate
  Level : Set
  zero : Level
  suc  : (i : Level) → Level
  _⊔_ : Level → Level → Level

{-# BUILTIN LEVEL     Level #-}
{-# BUILTIN LEVELZERO zero  #-}
{-# BUILTIN LEVELSUC  suc   #-}
{-# BUILTIN LEVELMAX _⊔_ #-}

------------------------------------------------------------------------

record RawMonoid c ℓ : Set (suc (c ⊔ ℓ)) where
  infixl 7 _∙_
  infix  4 _≈_
  field
    Carrier : Set c
    _≈_     : Carrier → Carrier → Set ℓ
    _∙_     : Carrier → Carrier → Carrier
    ε       : Carrier

module M (rm : RawMonoid zero zero) where

  open RawMonoid rm

  thm : ∀ x → x ∙ ε ≈ x
  thm = {!!}

  -- agda2-goal-and-context:

  -- rm : RawMonoid zero zero
  -- ------------------------
  -- Goal: (x : RawMonoid.Carrier rm) →
  --       RawMonoid._≈_ rm (RawMonoid._∙_ rm x (RawMonoid.ε rm)) x

------------------------------------------------------------------------

record RawMonoid′ : Set₁ where
  infixl 7 _∙_
  infix  4 _≈_
  field
    Carrier : Set
    _≈_     : Carrier → Carrier → Set
    _∙_     : Carrier → Carrier → Carrier
    ε       : Carrier

module M′ (rm : RawMonoid′) where

  open RawMonoid′ rm

  thm′ : ∀ x → x ∙ ε ≈ x
  thm′ = {!!}

  -- agda2-goal-and-context:

  -- rm : RawMonoid′
  -- ---------------
  -- Goal: (x : Carrier) → x ∙ ε ≈ x

------------------------------------------------------------------------

-- UP isn't relevant.

record RawMonoid″ (Carrier : Set) : Set₁ where
  infixl 7 _∙_
  infix  4 _≈_
  field
    _≈_     : Carrier → Carrier → Set
    _∙_     : Carrier → Carrier → Carrier
    ε       : Carrier

data Bool : Set where
  true false : Bool

data List (A : Set) : Set where
  []  :                        List A
  _∷_ : (x : A)(xs : List A) → List A

module M″ (rm : RawMonoid″ (List Bool)) where

  open RawMonoid″ rm

  thm″ : ∀ x → x ∙ ε ≈ x
  thm″ = {!!}

  -- agda2-goal-and-context:

  -- rm : RawMonoid″ (List Bool)
  -- ---------------------------
  -- Goal: (x : List Bool) →
  --       RawMonoid″._≈_ rm (RawMonoid″._∙_ rm x (RawMonoid″.ε rm)) x

module M₁ (Z : Set₁) where

  postulate
    P : Set
    Q : Set → Set

module M₂ (X Y : Set) where

  module M₁′ = M₁ Set
  open M₁′

  p : P
  p = {!!}

  q : Q X
  q = {!!}

postulate X : Set

pp : M₂.M₁′.P X X
pp = {!!}


-- TODO: Generalize and move to Structure.Categorical.Proofs
module Structure.Category.Proofs where

import      Lvl
open import Data
open import Data.Tuple as Tuple using (_,_)
open import Functional using (const ; swap ; _$_)
open import Lang.Instance
open import Logic
open import Logic.Propositional
open import Logic.Predicate
open import Structure.Category
open import Structure.Categorical.Names
open import Structure.Categorical.Properties
import      Structure.Operator.Properties as Properties
open import Structure.Operator
open import Structure.Relator.Equivalence
open import Structure.Relator.Properties
open import Structure.Setoid
open import Syntax.Function
open import Syntax.Transitivity
open import Type

module _
  {ℓₒ ℓₘ ℓₑ : Lvl.Level}
  {Obj : Type{ℓₒ}}
  {Morphism : Obj → Obj → Type{ℓₘ}}
  ⦃ morphism-equiv : ∀{x y} → Equiv{ℓₑ}(Morphism x y) ⦄
  (cat : Category(Morphism))
  where

  open Category(cat)
  open Category.ArrowNotation(cat)
  open Morphism.OperModule(\{x} → _∘_ {x})
  open Morphism.IdModule(\{x} → _∘_ {x})(id)
  private open module [≡]-Equivalence {x}{y} = Equivalence (Equiv-equivalence ⦃ morphism-equiv{x}{y} ⦄) using ()

  private variable x y z : Obj
  private variable f g h i : x ⟶ y

  associate4-123-321 : (((f ∘ g) ∘ h) ∘ i ≡ f ∘ (g ∘ (h ∘ i)))
  associate4-123-321 = Morphism.associativity(_∘_) 🝖 Morphism.associativity(_∘_)

  associate4-123-213 : (((f ∘ g) ∘ h) ∘ i ≡ (f ∘ (g ∘ h)) ∘ i)
  associate4-123-213 = congruence₂ₗ(_∘_)(_) (Morphism.associativity(_∘_))

  associate4-321-231 : (f ∘ (g ∘ (h ∘ i)) ≡ f ∘ ((g ∘ h) ∘ i))
  associate4-321-231 = congruence₂ᵣ(_∘_)(_) (symmetry(_≡_) (Morphism.associativity(_∘_)))

  associate4-213-121 : ((f ∘ (g ∘ h)) ∘ i ≡ (f ∘ g) ∘ (h ∘ i))
  associate4-213-121 = symmetry(_≡_) (congruence₂ₗ(_∘_)(_) (Morphism.associativity(_∘_))) 🝖 Morphism.associativity(_∘_)

  associate4-231-213 : f ∘ ((g ∘ h) ∘ i) ≡ (f ∘ (g ∘ h)) ∘ i
  associate4-231-213 = symmetry(_≡_) (Morphism.associativity(_∘_))

  associate4-231-123 : f ∘ ((g ∘ h) ∘ i) ≡ ((f ∘ g) ∘ h) ∘ i
  associate4-231-123 = associate4-231-213 🝖 symmetry(_≡_) associate4-123-213

  associate4-231-121 : (f ∘ ((g ∘ h) ∘ i) ≡ (f ∘ g) ∘ (h ∘ i))
  associate4-231-121 = congruence₂ᵣ(_∘_)(_) (Morphism.associativity(_∘_)) 🝖 symmetry(_≡_) (Morphism.associativity(_∘_))

  id-automorphism : Automorphism(id{x})
  ∃.witness id-automorphism = id
  ∃.proof   id-automorphism = intro(Morphism.identityₗ(_∘_)(id)) , intro(Morphism.identityᵣ(_∘_)(id))

  inverse-isomorphism : (f : x ⟶ y) → ⦃ _ : Isomorphism(f) ⦄ → Isomorphism(inv f)
  ∃.witness (inverse-isomorphism f) = f
  ∃.proof   (inverse-isomorphism f) = intro (inverseᵣ(f)(inv f)) , intro (inverseₗ(f)(inv f)) where
    open Isomorphism(f)

  module _ ⦃ op : ∀{x y z} → BinaryOperator(_∘_ {x}{y}{z}) ⦄ where
    op-closed-under-isomorphism : ∀{A B C : Obj} → (f : B ⟶ C) → (g : A ⟶ B) → ⦃ _ : Isomorphism(f) ⦄ → ⦃ _ : Isomorphism(g) ⦄ → Isomorphism(f ∘ g)
    ∃.witness (op-closed-under-isomorphism f g) = inv g ∘ inv f
    Tuple.left (∃.proof (op-closed-under-isomorphism f g)) = intro $
      (inv g ∘ inv f) ∘ (f ∘ g) 🝖-[ associate4-213-121 ]-sym
      (inv g ∘ (inv f ∘ f)) ∘ g 🝖-[ congruence₂ₗ(_∘_) ⦃ op ⦄ (g) (congruence₂ᵣ(_∘_) ⦃ op ⦄ (inv g) (Morphism.inverseₗ(_∘_)(id) (f)(inv f))) ]
      (inv g ∘ id) ∘ g          🝖-[ congruence₂ₗ(_∘_) ⦃ op ⦄ (g) (Morphism.identityᵣ(_∘_)(id)) ]
      inv g ∘ g                 🝖-[ Morphism.inverseₗ(_∘_)(id) (g)(inv g) ]
      id                        🝖-end
      where
        open Isomorphism(f)
        open Isomorphism(g)
    Tuple.right (∃.proof (op-closed-under-isomorphism f g)) = intro $
      (f ∘ g) ∘ (inv g ∘ inv f) 🝖-[ associate4-213-121 ]-sym
      (f ∘ (g ∘ inv g)) ∘ inv f 🝖-[ congruence₂ₗ(_∘_) ⦃ op ⦄ (_) (congruence₂ᵣ(_∘_) ⦃ op ⦄ (_) (Morphism.inverseᵣ(_∘_)(id) (_)(_))) ]
      (f ∘ id) ∘ inv f          🝖-[ congruence₂ₗ(_∘_) ⦃ op ⦄ (_) (Morphism.identityᵣ(_∘_)(id)) ]
      f ∘ inv f                 🝖-[ Morphism.inverseᵣ(_∘_)(id) (_)(_) ]
      id                        🝖-end
      where
        open Isomorphism(f)
        open Isomorphism(g)

  instance
    Isomorphic-reflexivity : Reflexivity(Isomorphic)
    ∃.witness (Reflexivity.proof Isomorphic-reflexivity) = id
    ∃.proof   (Reflexivity.proof Isomorphic-reflexivity) = id-automorphism

  instance
    Isomorphic-symmetry : Symmetry(Isomorphic)
    ∃.witness (Symmetry.proof Isomorphic-symmetry iso-xy) = inv(∃.witness iso-xy)
    ∃.proof   (Symmetry.proof Isomorphic-symmetry iso-xy) = inverse-isomorphism(∃.witness iso-xy)

  module _ ⦃ op : ∀{x y z} → BinaryOperator(_∘_ {x}{y}{z}) ⦄ where
    instance
      Isomorphic-transitivity : Transitivity(Isomorphic)
      ∃.witness (Transitivity.proof Isomorphic-transitivity ([∃]-intro xy) ([∃]-intro yz)) = yz ∘ xy
      ∃.proof   (Transitivity.proof Isomorphic-transitivity ([∃]-intro xy) ([∃]-intro yz)) = op-closed-under-isomorphism ⦃ op ⦄ yz xy

    instance
      Isomorphic-equivalence : Equivalence(Isomorphic)
      Isomorphic-equivalence = record{}


module Direction where

open import Relation.Binary.PropositionalEquality hiding (Extensionality)

----------------------------------------------------------------------
-- direction

data Dir : Set where
  SND RCV : Dir

variable
  d d₁ d₂ d₃ : Dir

-- dual
dual-dir : Dir → Dir
dual-dir SND = RCV
dual-dir RCV = SND

dual-dir-inv : (d : Dir) → dual-dir (dual-dir d) ≡ d
dual-dir-inv SND = refl
dual-dir-inv RCV = refl


------------------------------------------------------------------------
-- The Agda standard library
--
-- Sums of nullary relations
------------------------------------------------------------------------

{-# OPTIONS --without-K --safe #-}

module Relation.Nullary.Sum where

open import Data.Sum
open import Data.Empty
open import Level
open import Relation.Nullary

-- Some properties which are preserved by _⊎_.

infixr 1 _¬-⊎_ _⊎-dec_

_¬-⊎_ : ∀ {p q} {P : Set p} {Q : Set q} →
        ¬ P → ¬ Q → ¬ (P ⊎ Q)
(¬p ¬-⊎ ¬q) (inj₁ p) = ¬p p
(¬p ¬-⊎ ¬q) (inj₂ q) = ¬q q

_⊎-dec_ : ∀ {p q} {P : Set p} {Q : Set q} →
          Dec P → Dec Q → Dec (P ⊎ Q)
yes p ⊎-dec _     = yes (inj₁ p)
_     ⊎-dec yes q = yes (inj₂ q)
no ¬p ⊎-dec no ¬q = no helper
  where
  helper : _ ⊎ _ → ⊥
  helper (inj₁ p) = ¬p p
  helper (inj₂ q) = ¬q q


{-# OPTIONS --universe-polymorphism #-}
-- {-# OPTIONS --verbose tc.records.ifs:15 #-}
-- {-# OPTIONS --verbose tc.constr.findInScope:15 #-}
-- {-# OPTIONS --verbose tc.term.args.ifs:15 #-}
-- {-# OPTIONS --verbose cta.record.ifs:15 #-}
-- {-# OPTIONS --verbose tc.section.apply:25 #-}
-- {-# OPTIONS --verbose tc.mod.apply:100 #-}
-- {-# OPTIONS --verbose scope.rec:15 #-}
-- {-# OPTIONS --verbose tc.rec.def:15 #-}

module 07-subclasses where

open import Data.Bool hiding (_≟_)
open import Data.Nat hiding (_<_)
open import Relation.Nullary.Decidable
open import Function using (_$_; id)

record Eq (A : Set) : Set where
  field eq : A → A → Bool

primEqBool : Bool → Bool → Bool
primEqBool true = id
primEqBool false = not

eqBool : Eq Bool
eqBool = record { eq = primEqBool }

primEqNat : ℕ → ℕ → Bool
primEqNat a b = ⌊ a ≟ b ⌋

primLtNat : ℕ → ℕ → Bool
primLtNat 0 _ = true
primLtNat (suc a) (suc b) = primLtNat a b
primLtNat _ _ = false

neq : {t : Set} → {{eqT : Eq t}} → t → t → Bool
neq a b = not $ eq a b
  where open Eq {{...}}

record Ord₁ (A : Set) : Set where
  field _<_ : A → A → Bool
        eqA : Eq A

ord₁Nat : Ord₁ ℕ
ord₁Nat = record { _<_ = primLtNat; eqA = eqNat }
  where eqNat : Eq ℕ
        eqNat = record { eq = primEqNat }

record Ord₂ {A : Set} (eqA : Eq A) : Set where
  field _<_ : A → A → Bool

ord₂Nat : Ord₂ (record { eq = primEqNat })
ord₂Nat = record { _<_ = primLtNat }

record Ord₃ (A : Set) : Set where
  field _<_ : A → A → Bool
        eqA : Eq A
  open Eq eqA public

ord₃Nat : Ord₃ ℕ
ord₃Nat = record { _<_ = primLtNat; eqA = eqNat }
  where eqNat : Eq ℕ
        eqNat = record { eq = primEqNat }

record Ord₄ {A : Set} (eqA : Eq A) : Set where
  field _<_ : A → A → Bool
  open Eq eqA public

ord₄Nat : Ord₄ (record { eq = primEqNat })
ord₄Nat = record { _<_ = primLtNat }

module test₁ where
  open Ord₁ {{...}}
  open Eq {{...}}

  eqNat = eqA

  test₁ = 5 < 3
  test₂ = eq 5 3
  test₃ = eq true false
  test₄ : {A : Set} → {{ ordA : Ord₁ A }} → A → A → Bool
  test₄ a b = a < b ∨ eq a b
    where eqA' = eqA

module test₂ where
  open Ord₂ {{...}}
  open Eq {{...}}

  eqNat : Eq ℕ
  eqNat = record { eq = primEqNat }

  test₁ = 5 < 3
  test₂ = eq 5 3
  test₃ = eq true false
  test₄ : {A : Set} → {eqA : Eq A} → {{ ordA : Ord₂ eqA }} → A → A → Bool
  test₄ a b = a < b ∨ eq a b

module test₃ where
  open Ord₃ {{...}}
  open Eq {{...}} renaming (eq to eq')

  test₁ = 5 < 3
  test₂ = eq 5 3
  test₃ = eq' true false
  test₄ : {A : Set} → {{ ordA : Ord₃ A }} → A → A → Bool
  test₄ a b = a < b ∨ eq a b

module test₄ where
  open Ord₄ {{...}}
  open Eq {{...}} renaming (eq to eq')

  test₁ = 5 < 3
  test₂ = eq 5 3
  test₃ = eq' true false
  test₄ : {A : Set} → {eqA : Eq A} → {{ ordA : Ord₄ eqA }} → A → A → Bool
  test₄ a b = a < b ∨ eq a b

module test₄′ where
  open Ord₄ {{...}} hiding (eq)
  open Eq {{...}}

  eqNat : Eq ℕ
  eqNat = record { eq = primEqNat }

  test₁ = 5 < 3
  test₂ = eq 5 3
  test₃ = eq true false
  test₄ : {A : Set} → {eqA : Eq A} → {{ ordA : Ord₄ eqA }} → A → A → Bool
  test₄ a b = a < b ∨ eq a b


{-# OPTIONS --without-K  #-}
module algebra.semigroup.morphism where

open import level
open import algebra.semigroup.core
open import equality.core
open import hott.level

module _ {i}{j}
         {X : Set i}⦃ sX : IsSemigroup X ⦄
         {Y : Set j}⦃ sY : IsSemigroup Y ⦄ where
  open IsSemigroup ⦃ ... ⦄

  IsSemigroupMorphism : (X → Y) → Set (i ⊔ j)
  IsSemigroupMorphism f = (x₁ x₂ : X) → f (x₁ * x₂) ≡ f x₁ * f x₂

  is-semigroup-morphism-level : (f : X → Y) → h 1 (IsSemigroupMorphism f)
  is-semigroup-morphism-level f = Π-level λ x₁ → Π-level λ x₂ → is-set _ _