/- Clara Löh 2021 -/

import tactic  -- standard proof tactics

open classical -- we want to work in classical logic

/-
# Mini Geometry

This is a basic implementation of Mini Geometry, 
as described in 
http://www.mathematik.uni-r.de/loeh/teaching/geometrie_ss21/lecture_notes.pdf
-/

/- Mini Geometry models -/

/-
MG 1: Zu je zwei verschiedenen Punkten gibt es höchstens eine Gerade, so
dass beide Punkte auf dieser Geraden liegen.
-/

def satisfies_MG1
    (P : Type*)
    (G : Type*)
    (lies_on: P → G → Prop)
:= ∀ x y : P, ∀ g h : G,
   ( x ≠ y 
   ∧ lies_on x g ∧ lies_on y g
   ∧ lies_on x h ∧ lies_on y h)
   → g = h     

/-
MG 2: Auf jeder Geraden liegen mindestens zwei verschiedene Punkte.
-/

def satisfies_MG2
    (P : Type*)
    (G : Type*)
    (lies_on: P → G → Prop)
:= ∀ g : G, ∃ x y : P,
     x ≠ y ∧ lies_on x g ∧ lies_on y g  

/-
Lean version of Mini Geometry models:
-/

class MG
      (P : Type*) -- points
      (G : Type*) -- lines
:= (lies_on : P → G → Prop)
   (MG1 : satisfies_MG1 P G lies_on)
   (MG2 : satisfies_MG2 P G lies_on)

/- Some basic notions -/

/- Tests whether x is an intersection of g and h -/
def is_intersection_of
    (P : Type*) (G : Type*) [MG P G]
    (x : P)
    (g : G)
    (h : G)
:= MG.lies_on x g ∧ MG.lies_on x h

/- Tests whether g and h are parallel -/
def are_parallel
    (P : Type*) (G : Type*) [MG P G]
    (g : G)
    (h : G)
:= ¬ (∃ x : P, is_intersection_of P G x g h)
    
/- Tests whether x and y lie on a common line -/    
def lie_on_a_common_line
    (P : Type*) (G : Type*) [MG P G]
    (x : P)
    (y : P)
:= ∃ g : G, ( MG.lies_on x g
            ∧ MG.lies_on y g )    

/- Tests whether (x,y,z) is a triangle -/
def is_triangle
    (P : Type*) (G : Type*) [MG P G]
    (x : P)
    (y : P)
    (z : P)
:= lie_on_a_common_line P G x y
 ∧ lie_on_a_common_line P G y z
 ∧ lie_on_a_common_line P G z x     

-- exercise: 
/- Tests whether (x,y,z) is an anti-triangle -/
def is_anti_triangle
    (P : Type*) (G : Type*) [MG P G]
    (x : P)
    (y : P)
    (z : P)
:= ¬ lie_on_a_common_line P G x y
 ∧ ¬ lie_on_a_common_line P G y z
 ∧ ¬ lie_on_a_common_line P G z x     

/- Some very simple properties -/

/- If x and y lie on a common line, 
   then y and x lie on a common line. -/
lemma lie_on_a_common_line_sym 
    (P : Type*) (G : Type*) [MG P G]
    (x : P)
    (y : P)
    (line_xy: lie_on_a_common_line P G x y) -- hypotheses
  : lie_on_a_common_line P G y x -- claim
:= 
begin
  -- extracts a g out of "∃ g : G, ..."
  rcases line_xy with ⟨ g : G, xy_on_g ⟩, 
  -- xy_on_g = MG.lies_on x g ∧ MG.lies_on y g
  have x_on_g: MG.lies_on x g, 
       by {apply xy_on_g.1},
  have y_on_g: MG.lies_on y g,
       by {apply xy_on_g.2},

  show lie_on_a_common_line P G y x,
       by {unfold lie_on_a_common_line,
           use g, -- constructs ∃-formula
           exact and.intro y_on_g x_on_g}, -- constructs ∧-formula
end

/- If (x,y,z) is a triangle, then also (y,x,z) is a triangle. -/
lemma triangle_sym_12
      (P : Type*) (G : Type*) [MG P G]
      (x : P) (y : P) (z : P)
      (triangle_xyz: is_triangle P G x y z)
: is_triangle P G y x z
:= 
begin
  -- (x,y,z): x--y, y--z, z--x
  -- (y,x,z): y--x, x--z, z--y

  /- extracting that x and y lie on a common line,
          that y and z lie on a common line, and 
          that z and x lie on a common line. -/
  rcases triangle_xyz with ⟨ line_xy, ⟨ line_yz, line_zx⟩  ⟩,
  /- thus, by the previous lemma, also: 
           y and x lie on a common line, and
           x and z lie on a common line, and 
           z and y lie on a common line. -/
  have line_yx: lie_on_a_common_line P G y x,
       by {exact lie_on_a_common_line_sym P G x y line_xy},   
  have line_xz: lie_on_a_common_line P G x z, 
       by {exact lie_on_a_common_line_sym P G z x line_zx},
  have line_zy: lie_on_a_common_line P G z y,
       by {exact lie_on_a_common_line_sym P G y z line_yz},     
  /- we now only recombine the statements in a different order -/
  show is_triangle P G y x z,
       by {unfold is_triangle,
           exact and.intro line_yx (and.intro line_xz line_zy)},
end      

-- exercise
/- If (x,y,z) is a triangle, then also (z,x,y) is a triangle. -/
lemma triangle_cyc
      (P : Type*) (G : Type*) [MG P G]
      (x : P) (y : P) (z : P)
      (triangle_xyz: is_triangle P G x y z)
: is_triangle P G z x y
:= 
begin
  -- geg: (x,y,z): x--y, y--z, z--x
  -- beh: (z,x,y): z--x, x--y, y--z

  /- extracting that x and y lie on a common line,
          that y and z lie on a common line, and 
          that z and x lie on a common line. -/
  rcases triangle_xyz with ⟨ line_xy, ⟨ line_yz, line_zx⟩  ⟩,
  /- we now only recombine the statements in a different order -/
  show is_triangle P G z x y,
       by {unfold is_triangle,
           exact and.intro line_zx (and.intro line_xy line_yz)},
end


/- Some examples of Mini Geometry models -/

/- The empty model: 
   It has no points, no lines and the trivial lies_on relation; 
   in addition to the definition of these components, we have 
   to supply proofs for the axioms MG1 and MG2.
   -/
def empty_lies_on 
    (x : empty)
    (g : empty)
:= false    

lemma empty_satisfies_MG1 
: satisfies_MG1 empty empty empty_lies_on
:= 
begin -- this is so simple that it can be checked automatically
  by tauto, 
end 

lemma empty_satisfies_MG2
: satisfies_MG2 empty empty empty_lies_on
:=
begin -- this is also almost automatic; found via hint
  by {unfold satisfies_MG2,  
      simp at *, 
      exact dec_trivial},
end

-- Thus: The empty model indeed is a Mini Geometry: 
instance empty_MG : MG empty empty 
:= MG.mk empty_lies_on
         empty_satisfies_MG1 
         empty_satisfies_MG2


/- The I model:
   We consider the Mini Geometry associated with the graph 
    0 --- 1    2 
-/

inductive I_P : Type 
| I_0 
| I_1
| I_2

inductive I_G : Type
| I_01

def I_lies_on
    (x : I_P)
    (y : I_G) 
  : Prop  
:= 
begin
   -- thanks to the special structure of the I model, 
   -- we only need to check  whether x is I_0 or I_1;
   -- we use a case distinction:
   cases x,
     case I_P.I_0 : {exact true},
     case I_P.I_1 : {exact true},
     case I_P.I_2 : {exact false}, 
end

-- in the I model, all lines are equal (there is only one line)
lemma I_G_all_equal 
      (g : I_G)
      (h : I_G)
    : (g = h)
:= 
begin
  -- we use a case distinction:   
  cases g,       -- only one possible case for g: g = I_01
    cases h,     -- only one possible case for h: h = I_01
      exact rfl, -- thus, g = h, by reflexivity
end         

lemma I_satisfies_MG1
: satisfies_MG1 I_P I_G I_lies_on
:= 
begin
  -- in order to show this forall-statement,
  -- we prove the statement for each member 
  assume x y : I_P,
  assume g h : I_G,
  show ( x ≠ y 
       ∧ I_lies_on x g ∧ I_lies_on y g 
       ∧ I_lies_on x h ∧ I_lies_on y h)
       → g = h, 
  by {intro, -- adding the assumption on the left hand side of the implication 
      exact I_G_all_equal g h}, -- there is only one line!
end

lemma I_satisfies_MG2
: satisfies_MG2 I_P I_G I_lies_on
:= 
begin
  assume g : I_G, -- there is only one possibility! I_G.I_01
  show ∃ x y : I_P, x ≠ y 
                  ∧ I_lies_on x g ∧ I_lies_on y g,
  begin
    use I_P.I_0, -- take x as "0"
    use I_P.I_1, -- take y as "1"
    by tauto,    -- now it can simply be checked automatically
  end
end

-- The I model indeed is a Mini Geometry
instance I_MG : MG I_P I_G
:= MG.mk I_lies_on
         I_satisfies_MG1
         I_satisfies_MG2


/- Parallel postulate -/
def is_parallel_through
    (P : Type*) (G : Type*) [MG P G]
    (x : P)
    (g : G)
    (h : G)
:=  MG.lies_on x h ∧ are_parallel P G g h   

def parallel_postulate
    (P : Type*) (G : Type*) [MG P G]
:= ∀ x : P , ∀ g : G,
     (¬ MG.lies_on x g)
   → (∃ h : G, is_parallel_through P G x g h
             ∧ ∀ h' : G, is_parallel_through P G x g h' → h' = h )

/- The parallel postulate is independent of the Mini Geometry axioms,
   i.e., there exist models of Mini Geometry that satisfy it 
     and there exist models of Mini Geometry that do not satisfy it. -/

-- The empty model satisfies the parallel postulate
lemma pp_empty_MG
: parallel_postulate empty empty
:=
begin 
  -- again, this is so simple 
  -- that this can be basically solved automatically
  assume x,
  show _, 
       by {cases x}
end

-- The I model does not satisfy the parallel postulate
lemma not_pp_I_MG
: ¬ parallel_postulate I_P I_G
:=
begin
  -- these will be our witnesses:   
  let x : I_P := I_P.I_2,
  let g : I_G := I_G.I_01,   

  -- x does not lie on g
  have x_not_on_g: ¬ MG.lies_on x g, 
       by {exact not_of_eq_false rfl},
                    
  -- but there is no parallel through x
  have no_parallel_through_x: 
       ¬ (∃ h : I_G, is_parallel_through I_P I_G x g h),
  begin -- by exhaustive search
    exact (finset.exists_mem_empty_iff (λ (x : I_G), are_parallel I_P I_G g x)).mp,
  end,

  -- we can now combine these properties into establishing that
  -- the parallel postulate is not satisfied, because of x and g
  by {unfold parallel_postulate, 
      refine not_forall.mpr _, 
      use x, 
      refine not_forall.mpr _, 
      use g,
      finish}, 
end

-- Thus, the parallel postulate is independent of MG
theorem pp_is_independent_of_MG
: (∃ P G : Type, ∃ MGPG : MG P G,   @parallel_postulate P G MGPG)
∧ (∃ P G : Type, ∃ MGPG : MG P G, ¬ @parallel_postulate P G MGPG)
:=
begin
  have pp_example: ∃ P G : Type, ∃ MGPG : MG P G, @parallel_postulate P G MGPG,
       begin -- the empty MG is such an example
         use empty,
         use empty,
         use empty_MG,
         exact pp_empty_MG,
       end,     
  
  have pp_nonexample: (∃ P G : Type, ∃ MGPG : MG P G, ¬ @parallel_postulate P G MGPG),
       begin -- the I MG is such a non-example
         use I_P,
         use I_G,
         use I_MG,
         exact not_pp_I_MG,
       end,

  -- it remains to combine both parts into the and-statement
  by {exact and.intro pp_example pp_nonexample},   
end
