/-
Project: Euclidean Geometry.
Contributors: Adarsh, Anurag, Nikhesh, Sai Niranjan.
-/

import Lake
import Init
import Mathlib.Data.Real.Basic

set_option autoImplicit false

/-!
We define basic geometric objects and state Euclid's 5 postulates.
Convention:
 * Begin variable/theorem names with capital letters.
 * Use underscore between "words" in the variable names.
 * Small letters for Points, Lines, etc.

TODO:
  * Prove equivalence of 5th Postulate and Playfair's axiom.
  * Prove transitivity of `IsParallel` using Playfair's axiom.
  * Show that interior of `Symm_angles` is the same.
-/

structure IncidenceGeometry where
  Point : Type
  Line : Type

  /-- The point lies on the given line.-/
  Lies_on : Point → Line → Prop

  /-- ```In_between a b c``` means "`b` is in between `a` & `c`"-/
  In_between : Point → Point → Point → Prop -- is this supposed to be here?

  -- properties of In_between
  /--`Between_refl_left a b` means "`a` is in between `a` & `b`"-/
  Between_refl_left (a b : Point) : In_between a a b

  /--`Between_refl_right a b` means "`b` is in between `a` & `b`"-/
  Between_refl_right (a b : Point) : In_between a b b

-- Enter some description here.
structure EuclidGeometry extends IncidenceGeometry where

  -- defining Distance
  Distance (a b : Point) : ℝ

  -- Distance axioms
  /--Distance between two points is non-negative.-/
  Dist_is_not_neg (a b : Point): Distance a b ≥ 0
  /--Distance from a point to itself is 0.-/
  Dist_same_point (a : Point) : Distance a a = 0
  /--Distance between two distinct points is strictly positive.-/
  Dist_geq_0 (a b : Point) : a ≠ b ↔ Distance a b > 0
  /--Distance from `a` to `b` = Distance from `b` to `a`-/
  Dist_is_symm (a b : Point) : Distance a b = Distance b a
  /--Triangle Inequality: `Distance a b + Distance b c ≥ Distance a c`-/
  Dist_tri_ineq (a b c : Point) : Distance a b + Distance b c ≥ Distance a c
  /--Distance between collinear points `a`, `b`, `c`: `Distance a b + Distance b c = Distance a c`.-/
  Dist_in_between (a b c : Point) (h : In_between a b c) :
    Distance a b + Distance b c = Distance a c

  -- Postulate 1
  -- Between two points there is an unique line passing through them

  /--Function that takes two distinct points `a` & `b` and gives a line.-/
  Line_of_two_points (a b : Point) (h : a ≠ b): Line
  /--The line `Line_of_two_points a b` contains the points `a` & `b`.-/
  Point_contain (a b : Point) (h : a ≠ b) :
    have l : Line := Line_of_two_points a b h
    Lies_on a l ∧ Lies_on b l

  /--A unique line passes through two distinct points `a` & `b`.-/
  Line_unique (A B: Point) (h : A ≠ B) (l1 l2 : Line):
    (Lies_on A l1 ∧ Lies_on B l1) ∧ (Lies_on A l2 ∧ Lies_on B l2) → l1 = l2

  /--Definition of Collinear points `a`, `b`, `c`.-/
  Collinear_point (A B C : Point) (h : A ≠ B):
    In_between A B C ∨ In_between A C B ∨ In_between C A B
    → Lies_on C (Line_of_two_points A B h)

/--A line segment is determined by its endpoints.
Segment is a structure that consisting of two endpoints: `p1` & `p2`.-/
structure Segment (geom : IncidenceGeometry) where
  p1 : geom.Point
  p2 : geom.Point

instance : Coe EuclidGeometry IncidenceGeometry where
  coe geom := { Point := geom.Point, Line := geom.Line, Lies_on := geom.Lies_on, In_between := geom.In_between, Between_refl_left := geom.Between_refl_left, Between_refl_right := geom.Between_refl_right}

/--Function gives the length of the segment `seg`.-/
def EuclidGeometry.length (geom : EuclidGeometry) (seg : Segment geom) : ℝ :=
  geom.Distance seg.p1 seg.p2

variable (geom : EuclidGeometry)

/--`Lies_on_segment p seg` means that point `p` lies on segment `seg`.-/
def EuclidGeometry.Lies_on_segment (p : geom.Point) (seg : Segment geom) : Prop :=
  geom.In_between seg.p1 p seg.p2

-- this defines when a segment lies on a line (use with CAUTION)
/--`Segment_in_line seg l` says that all the points of the segment `seg` lie on the line `l`.-/
def EuclidGeometry.Segment_in_line (geom: EuclidGeometry)
  (seg: Segment geom)(l: geom.Line) : Prop :=
  ∀ p: geom.Point, geom.In_between seg.p1 p seg.p2 → geom.Lies_on p l

-- this theorem says that the line is unique when the length of the segment is non-zero
/--There is a unique line that contains a line segment of non-zero length.-/
theorem EuclidGeometry.Unique_line_from_segment
  (seg : Segment geom) (h : ¬ seg.p1 = seg.p2) (l1 l2 : geom.Line) :
  geom.Segment_in_line seg l1 ∧ geom.Segment_in_line seg l2
  → l1 = l2
  := by
    intro h1
    let ⟨h2, h3⟩ := h1
    apply geom.Line_unique seg.p1 seg.p2 h l1 l2
    apply And.intro
    case left =>
      apply And.intro
      case left =>
        have lem : geom.In_between seg.p1 seg.p1 seg.p2 :=
          geom.Between_refl_left seg.p1 seg.p2
        simp [Segment_in_line] at h2
        have lem' : geom.In_between seg.p1 seg.p1 seg.p2 →
          geom.Lies_on seg.p1 l1 := by
            simp [h2, lem]
        apply lem'
        assumption
      case right =>
        have lem : geom.In_between seg.p1 seg.p2 seg.p2 :=
          geom.Between_refl_right seg.p1 seg.p2
        simp [Segment_in_line] at h2
        have lem' : geom.In_between seg.p1 seg.p2 seg.p2 →
          geom.Lies_on seg.p2 l1 := by
            simp [h2, lem]
        apply lem'
        assumption
    case right =>
      apply And.intro
      case left =>
        have lem : geom.In_between seg.p1 seg.p1 seg.p2 :=
          geom.Between_refl_left seg.p1 seg.p2
        simp [Segment_in_line] at h3
        have lem' : geom.In_between seg.p1 seg.p1 seg.p2 →
          geom.Lies_on seg.p1 l2 := by
            simp [h3, lem]
        apply lem'
        assumption
      case right =>
        have lem : geom.In_between seg.p1 seg.p2 seg.p2 :=
          geom.Between_refl_right seg.p1 seg.p2
        simp [Segment_in_line] at h3
        have lem' : geom.In_between seg.p1 seg.p2 seg.p2 →
          geom.Lies_on seg.p2 l2 := by
            simp [h3, lem]
        apply lem'
        assumption

/--Definition of Parallel Lines-/
def EuclidGeometry.IsParallel (l1 l2 : geom.Line) : Prop :=
  (l1 = l2) ∨ (∀ a : geom.Point, geom.Lies_on a l1 → ¬ geom.Lies_on a l2)

-- IsParallel is an equivalence relation on Lines
/--`IsParallel` is reflexive.-/
theorem EuclidGeometry.Parallel_refl (l : geom.Line) :
  IsParallel geom l l
  := by
    simp [IsParallel]

/-- `IsParallel` is symmetric.-/
theorem EuclidGeometry.Parallel_symm (l1 l2 : geom.Line) :
  IsParallel geom l1 l2 → IsParallel geom l2 l1
  := by
    intro h
    simp [IsParallel]
    simp [IsParallel] at h
    apply Or.elim h
    case left =>
      intro h1
      apply Or.inl
      rw [h1]
    case right =>
      intro h2
      apply Or.inr
      intro p hp
      have lem : geom.Lies_on p l1 → ¬ geom.Lies_on p l2 := by
        apply of_eq_true
        apply eq_true (h2 p)
      by_contra h3
      simp [h3] at lem
      contradiction

-- transitivity : for this we need Playfair's theorem.
-- Transitivity has not been used in any definition/theorem,
-- so we shall prove it later.
/--`IsParallel` is transitive.-/
theorem EuclidGeometry.Parallel_trans (l1 l2 l3: geom.Line) :
  IsParallel geom l1 l2 → IsParallel geom l2 l3 → IsParallel geom l1 l3
  := by
    intro h1 h2
    have h1' : IsParallel geom l1 l2 := h1
    have h2' : IsParallel geom l2 l3 := h2

    simp [IsParallel]
    simp [IsParallel] at h1
    simp [IsParallel] at h2
    apply Or.elim h1
    case left =>
      intro h12eq
      apply Or.elim h2
      case left =>
        intro h23eq
        apply Or.inl
        rw [<- h23eq, h12eq]
      case right =>
        intro hk
        rw [<-h12eq] at h2'
        simp [IsParallel] at h2'
        assumption
    case right =>
      intro hk
      apply Or.elim h2
      case left =>
        intro h23eq
        rw [h23eq] at h1'
        simp [IsParallel] at h1'
        assumption
      case right =>
        intro hk1
        by_cases l1 = l3
        case pos =>
          apply Or.inl
          assumption
        case neg =>
          apply Or.inr
          intro p hp
          by_contra hk'
          sorry               -- need to use Playfair's axiom here for this case.

/--Definition of intersection of lines. Here, we just say: `¬ IsParallel`. -/
def EuclidGeometry.Lines_intersect (l1 l2 : geom.Line) : Prop
  := ¬ IsParallel geom l1 l2

/-- Existence of intersection point for two intersecting lines.-/
theorem EuclidGeometry.Lines_intersect_Point_Exist
  (l1 l2 : geom.Line) (h : Lines_intersect geom l1 l2) :
  ∃ c : geom.Point, geom.Lies_on c l1 ∧ geom.Lies_on c l2
  := by
    simp [Lines_intersect, IsParallel] at h
    rw [not_or] at h
    let ⟨_, h2⟩ := h
    simp at h2
    assumption

/--`Lines_intersect2 l1 l2 p` states that the lines `l1, l2`
intersect at the point `p`.-/
def EuclidGeometry.Lines_intersect2
  (l1 l2 : geom.Line) (p : geom.Point) :
  Prop
  := geom.Lies_on p l1 ∧ geom.Lies_on p l2

/--Uniqueness of intersection point for two intersecting lines.-/
theorem EuclidGeometry.Lines_intersect_point_unique
  (l1 l2 : geom.Line) (h : Lines_intersect geom l1 l2) (A B : geom.Point) :
  Lines_intersect2 geom l1 l2 A →
  Lines_intersect2 geom l1 l2 B →
  A = B
  := by
    intro h1 h2
    by_contra hab
    simp [Lines_intersect2] at h1
    let ⟨h1l, h1r⟩ := h1
    simp [Lines_intersect2] at h2
    let ⟨h2l, h2r⟩ := h2
    have lem1 : l1 = l2 := by
      apply geom.Line_unique A B hab
      simp [h1l, h1r, h2l, h2r]
    simp [Lines_intersect, IsParallel] at h
    rw [not_or] at h
    let ⟨hk, _⟩ := h
    contradiction

/--Existence of intersection point of a line and a segment -/
def EuclidGeometry.Line_intersect_segment
  (seg : Segment geom) (l : geom.Line) : Prop :=
  ∃ p : geom.Point, geom.Lies_on p l ∧ geom.Lies_on_segment p seg

/--`Line_intersect_segment2 seg l p` states that
line `l` intersects the segment `seg` at point `p`-/
def EuclidGeometry.Line_intersect_segment2
  (seg : Segment geom) (l : geom.Line) (p : geom.Point): Prop :=
  geom.Lies_on p l ∧ geom.Lies_on_segment p seg

-- postulate 3
-- A circle can be constructed with any centre and any radius.
-- this is just the definition of a circle.
/-- Circle is a structure with a point `centre` and positive real `radius`.-/
structure Circle (geom : EuclidGeometry) where
  centre : geom.Point
  radius : ℝ

/--`On_circle p circ` states that the point `p` lies on the circle `circ`.-/
def EuclidGeometry.On_circle (p : geom.Point) (circ : Circle geom) : Prop
  := geom.Distance p circ.centre = circ.radius

-- postulate 4

/--Angle is a structure with three points.
`p1 = a`, `Pivot = o`, `p2 = b` denotes the angle (a o b).-/
structure Angle (geom : EuclidGeometry) where
  p1 : geom.Point
  Pivot : geom.Point
  p2 : geom.Point
-- p1 and p2 need to be different from the Pivot

variable (reflexAngle : Angle geom → Angle geom)

--measure of an angle
variable (mAngle : Angle geom → ℝ)

/--`Int_point_angle a A` states that point `a` is inside angle `A`.-/
def EuclidGeometry.Int_point_angle (a : geom.Point) (A : Angle geom) : Prop :=
  let A1 : Angle geom := Angle.mk a A.Pivot A.p1
  let A2 : Angle geom := Angle.mk a A.Pivot A.p2
  (mAngle (A1) < mAngle  A) ∧ (mAngle (A2) < mAngle A) -- strict inequality because of 120

-- we need to write that Int_point_angle for angles AOB and BOA are the same

-- Postulate 4 says all right angles are equal.
-- We are assigning it a value of 90
/--Define a property called `Is_right_angle A` which states that
`A` is a right angle and its measure is 90.-/
def EuclidGeometry.Is_right_angle (A : Angle geom): Prop := mAngle A = 90

-- Postulate 5

/--`Opp_sided_points p1 p2 l` states that the two points `p1, p2`
are on opposite sides of line `l`.-/
def EuclidGeometry.Opp_sided_points (p1 p2 : geom.Point) (l : geom.Line) : Prop :=
  geom.Line_intersect_segment (Segment.mk p1 p2) l

/--`Same_sided_points p1 p2 l` states that the two points `p1, p2`
are on the same side of line `l`. Here, it is: `¬ Opp_sided_points p1 p2 l`-/
def EuclidGeometry.Same_sided_points (p1 p2 : geom.Point) (l : geom.Line) : Prop :=
  ¬ geom.Opp_sided_points p1 p2 l

-- List of Axioms and Euclid's Postulates
structure Axioms where

  /--Euclid Postulate #2: every segment lies on a line.-/
  Post2 (geom : EuclidGeometry) :
    ∀ s : Segment geom, ∃ l: geom.Line, geom.Segment_in_line s l

  /--Line can be obtained from a segment.-/
  Line_from_segment : Segment geom → geom.Line

  -- property of Line_from_segment
  /---/
  Line_from_segment_contains_segment (seg : Segment geom) :
    geom.Segment_in_line seg (Line_from_segment seg)

  -- properties of mAngle
  /--Measure of any angle is non-negative.-/
  mAngle_non_neg (a b c : geom.Point) :
    mAngle (Angle.mk a b c) ≥ 0

  /--Definition of Zero Angle.-/
  ZeroAngle (a b c : geom.Point) (_ : geom.In_between a c b):
    mAngle (Angle.mk a b c) = 0

  /--If not a zero angle, then the measure of the angle is positive.-/
  mAngle_postive (a b c : geom.Point) :
    ¬ geom.In_between a c b →
    ¬ geom.In_between c a b →
    mAngle (Angle.mk a b c) > 0

  /--`mReflexAngle A` returns the measure of the reflex angle of angle `A`.-/
  mReflexAngle (A : Angle geom) :
    mAngle (reflexAngle A) = 360 - mAngle A

  /-- Angle `A` as sum of its constituents.
  For `mAngle_add a A h`, we have:

  `have A1 : Angle geom := Angle.mk a A.Pivot A.p1`

  `have A2 : Angle geom := Angle.mk a A.Pivot A.p2`

  then
  `mAngle A = mAngle A1 + mAngle A2`-/
  mAngle_add (a : geom.Point) (A : Angle geom)
    (_ : EuclidGeometry.Int_point_angle geom mAngle a A) :
    have A1 : Angle geom := Angle.mk a A.Pivot A.p1
    have A2 : Angle geom := Angle.mk a A.Pivot A.p2
    mAngle A = mAngle A1 + mAngle A2

  /--The measure of a straight angle is 180.-/
  StraightAngle (a b c : geom.Point) (_ : geom.In_between a b c) :
    mAngle (Angle.mk a b c) = 180

  -- equality of measure of "symmetric" angles: e.g. Angle AOB = Angle BOA
  -- can't write directly that they are equal as they are different structures "entrywise".
  -- we need to also mention that their interior points are equal.
  /--`Symm_angles (a o b)` states that:

  `mAngle (Angle (a o b)) = mAngle (Angle (b o a))`.-/
  Symm_angles (a o b : geom.Point):
    mAngle (Angle.mk a o b) = mAngle (Angle.mk b o a)

  -- Statement of Postulate 5:
  -- Let line segment AB and CD be intersected by
  -- line l at points p1 and p2 respectively.
  -- Let A and C be same-sided wrt l.
  -- Let mAngle(A p1 p2) + mAngle(C p2 p1) < 180.
  -- Then there exists a point p such that
  -- line from AB and CD intersect at p
  -- and p is same-sided as A wrt to line l.
  /--Euclid's Postulate #5.-/
  Post5 (a b c d p1 p2 : geom.Point) (l : geom.Line)
    (hab : ¬ a = b) (hcd : ¬ c = d) :
    geom.Line_intersect_segment2 (Segment.mk a b) l p1
    →
    geom.Line_intersect_segment2 (Segment.mk c d) l p2
    →
    geom.Same_sided_points a c l
    →
    mAngle (Angle.mk a p1 p2) + mAngle (Angle.mk c p2 p1) < 180
    →
    ∃ p : geom.Point,
    geom.Lines_intersect2
    (geom.Line_of_two_points a b hab) (geom.Line_of_two_points c d hcd) p
    ∧
    geom.Same_sided_points p a l

/--Vertically opposite angles are equal.-/
theorem VOAequal (a b c d o : geom.Point) (SELF : Axioms geom reflexAngle mAngle)
(h1 : ¬a = b)
(h2 : ¬c = d)
(h3 : geom.In_between a o b ∧ geom.In_between c o d)
(_ : ¬geom.IsParallel
  (geom.Line_of_two_points a b h1)
  (geom.Line_of_two_points c d h2))
(h5: geom.Int_point_angle mAngle c (Angle.mk a o b))
(h6: geom.Int_point_angle mAngle a (Angle.mk c o d)):
let COB := Angle.mk c o b
let AOD := Angle.mk a o d
mAngle (COB) = mAngle (AOD)
:= by
  let COA := Angle.mk c o a
  let AOC := Angle.mk a o c
  let AOD := Angle.mk a o d
  let AOB := Angle.mk a o b
  let COD := Angle.mk c o d
  let COB := Angle.mk c o b
  have lem1 : mAngle (AOB) = 180 := Axioms.StraightAngle SELF a o b (And.left h3)
  have lem2 : mAngle (COD) = 180 := Axioms.StraightAngle SELF c o d (And.right h3)
  have lem3 : mAngle (AOB) = mAngle (COA) + mAngle (COB) :=
    Axioms.mAngle_add SELF c AOB h5
  have lem4 : mAngle (COD) = mAngle (AOC) + mAngle (AOD) :=
    Axioms.mAngle_add SELF a COD h6
  have lem5 : mAngle COA = mAngle AOC := by
    apply Axioms.Symm_angles SELF
  have lem6 : mAngle (COA) + mAngle (COB) = mAngle (AOC) + mAngle (AOD) := by
    rw [<-lem3, <-lem4, lem1, lem2]
  rw [lem5] at lem6
  simp [add_left_cancel] at lem6
  assumption


/-
Copyright (c) 2021 Yaël Dillies, Bhavik Mehta. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yaël Dillies, Bhavik Mehta
-/
import algebra.big_operators.order
import data.nat.basic

/-!
# Equitable functions

> THIS FILE IS SYNCHRONIZED WITH MATHLIB4.
> Any changes to this file require a corresponding PR to mathlib4.

This file defines equitable functions.

A function `f` is equitable on a set `s` if `f a₁ ≤ f a₂ + 1` for all `a₁, a₂ ∈ s`. This is mostly
useful when the codomain of `f` is `ℕ` or `ℤ` (or more generally a successor order).

## TODO

`ℕ` can be replaced by any `succ_order` + `conditionally_complete_monoid`, but we don't have the
latter yet.
-/

open_locale big_operators

variables {α β : Type*}

namespace set

/-- A set is equitable if no element value is more than one bigger than another. -/
def equitable_on [has_le β] [has_add β] [has_one β] (s : set α) (f : α → β) : Prop :=
∀ ⦃a₁ a₂⦄, a₁ ∈ s → a₂ ∈ s → f a₁ ≤ f a₂ + 1

@[simp]
lemma equitable_on_empty [has_le β] [has_add β] [has_one β] (f : α → β) : equitable_on ∅ f :=
λ a _ ha, (set.not_mem_empty _ ha).elim

lemma equitable_on_iff_exists_le_le_add_one {s : set α} {f : α → ℕ} :
  s.equitable_on f ↔ ∃ b, ∀ a ∈ s, b ≤ f a ∧ f a ≤ b + 1 :=
begin
  refine ⟨_, λ ⟨b, hb⟩ x y hx hy, (hb x hx).2.trans (add_le_add_right (hb y hy).1 _)⟩,
  obtain rfl | ⟨x, hx⟩ := s.eq_empty_or_nonempty,
  { simp },
  intros hs,
  by_cases h : ∀ y ∈ s, f x ≤ f y,
  { exact ⟨f x, λ y hy, ⟨h _ hy, hs hy hx⟩⟩ },
  push_neg at h,
  obtain ⟨w, hw, hwx⟩ := h,
  refine ⟨f w, λ y hy, ⟨nat.le_of_succ_le_succ _, hs hy hw⟩⟩,
  rw (nat.succ_le_of_lt hwx).antisymm (hs hx hw),
  exact hs hx hy,
end

lemma equitable_on_iff_exists_image_subset_Icc {s : set α} {f : α → ℕ} :
  s.equitable_on f ↔ ∃ b, f '' s ⊆ Icc b (b + 1) :=
by simpa only [image_subset_iff] using equitable_on_iff_exists_le_le_add_one

lemma equitable_on_iff_exists_eq_eq_add_one {s : set α} {f : α → ℕ} :
  s.equitable_on f ↔ ∃ b, ∀ a ∈ s, f a = b ∨ f a = b + 1 :=
by simp_rw [equitable_on_iff_exists_le_le_add_one, nat.le_and_le_add_one_iff]

section ordered_semiring
variables [ordered_semiring β]

lemma subsingleton.equitable_on {s : set α} (hs : s.subsingleton) (f : α → β) : s.equitable_on f :=
λ i j hi hj, by { rw hs hi hj, exact le_add_of_nonneg_right zero_le_one }

lemma equitable_on_singleton (a : α) (f : α → β) : set.equitable_on {a} f :=
set.subsingleton_singleton.equitable_on f

end ordered_semiring
end set

open set

namespace finset
variables {s : finset α} {f : α → ℕ} {a : α}

lemma equitable_on.le (h : equitable_on (s : set α) f) (ha : a ∈ s) :
  (∑ i in s, f i) / s.card ≤ f a :=
(equitable_on_iff_le_le_add_one.1 h a ha).1

lemma equitable_on.le_add_one (h : equitable_on (s : set α) f) (ha : a ∈ s) :
  f a ≤ (∑ i in s, f i) / s.card + 1 :=
(equitable_on_iff_le_le_add_one.1 h a ha).2

lemma equitable_on_iff :
  equitable_on (s : set α) f ↔
    ∀ a ∈ s, f a = (∑ i in s, f i) / s.card ∨ f a = (∑ i in s, f i) / s.card + 1 :=
by simp_rw [equitable_on_iff_le_le_add_one, nat.le_and_le_add_one_iff]

end finset


import mynat.add -- definition of addition
namespace mynat -- hide

/- Axiom : add_zero (a : mynat) :
a + 0 = a
-/

/- Axiom : add_succ (a b : mynat) :
a + succ(b) = succ(a + b)
-/

/-
# Tutorial world

## Level 4: addition

We have a new import -- the definition of addition.

Peano defined addition `a + b` by induction on `b`, or,
more precisely, by *recursion* on `b`. He first explained how to add 0 to a number:
this is the base case.

* `add_zero (a : mynat) : a + 0 = a`

We will call this theorem `add_zero`. More precisely, `add_zero` is the name
of the *proof* of the theorem. **Note the name of this proof**.
Mathematicians sometimes call it "Lemma 2.1" or "Hypothesis P6" or something. But
computer scientists call it `add_zero` because it tells you
what the answer to "$x$ add zero" is. It's a *much* better name than "Lemma 2.1".
Even better, we can use the rewrite tactic with `add_zero`.
If you ever see `x + 0` in your goal, `rw add_zero` should simplify it to `x`.
This is because `add_zero` is a proof that `x + 0 = x` (more precisely,
`add_zero x` is a proof that `x + 0 = x` but Lean can figure out the `x` from the context).

Now here's the inductive step. If you know how to add `d` to `a`, then
Peano tells you how to add `succ(d)` to `a`. It looks like this:

* `add_succ (a d : mynat) : a + succ(d) = succ (a + d)`

What's going on here is that we assume `a + d` is already
defined, and we define `a + succ(d)` to be the number after it.
**Note the name of this proof too** -- `add_succ` tells you
how to add a successor to something. If you ever see `... + succ ...`
in your goal, you should be able to use `rw add_succ,` to make
progress. Here is a simple example where we shall see both. Let's prove
that $x$ add the number after $0$ is the number after $x$.

Delete `sorry` (don't forget you can widen this box if you can't see the sorry).
Observe that the goal mentions `... + succ ...`. So type

`rw add_succ,`

and hit enter; see the goal change. **Don't forget the commma**.
Do you see that the goal now mentions ` ... + 0 ...`? So type

`rw add_zero,`

and then observe that you can close the goal with

`refl,`

and you're done. You have finished tutorial world! There are important things
written below the lemma, including what to do next.

-/

/- Lemma : no-side-bar
For all natural numbers $a$, we have
$$a + \operatorname{succ}(0) = \operatorname{succ}(a).$$
-/
lemma add_succ_zero (a : mynat) : a + succ(0) = succ(a) :=
begin [nat_num_game]
  rw add_succ,
  rw add_zero,
  refl,

end

end mynat -- hide

/-
## Examining proofs.

You might want to review this proof now; at
three lines long it is our current record. Click on a line in the proof
and use the L/R arrow keys to put your cursor as far left as it will go.
Then use the U/D arrow keys to move your cursor
up and down from line to line, and you can see what
Lean is thinking on each line of the proof.

## No problems?

When you're happy, let's move onto Addition World, and
learn about proof by induction. Go back to the main menu and select addition world.

## Problems?

Question: why has the top right hand box gone blank?

Answer: Maybe you tried a tactic which didn't work. Or maybe you're
in the middle of typing a tactic. Try deleting up to the last
comma, *or adding a comma at the end of your code*. Look at the
error message. What line is the first error on? Perhaps
Lean thinks you're in the middle of writing a tactic command that you
think you finished. If Lean is still attempting to process a tactic
command it won't display anything. You can get it to stop processing by
adding a comma.

If the worst comes to the worst, just delete what you wrote. Most people
with problems have written random stuff in the proof box. The only thing
you're supposed to be writing is lines like

`rw add_zero,`
`rw h,`
`refl,`

One line of code with a comma at the end. Nothing else at all goes in the box.

If you cannot see what you have done wrong, you can always
take a look at the solutions (github.com, opens in new window).
-/


/-
Copyright (c) 2015 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.

Author: Leonardo de Moura

Define propositional calculus, valuation, provability, validity, prove soundness.

This file is based on Floris van Doorn Coq files.

Similar to soundness.lean, but defines Nc in Type.
The idea is to be able to prove soundness using recursive equations.
-/
import data.nat data.list
open nat bool list decidable

definition PropVar [reducible] := nat

inductive PropF :=
| Var  : PropVar → PropF
| Bot  : PropF
| Conj : PropF → PropF → PropF
| Disj : PropF → PropF → PropF
| Impl : PropF → PropF → PropF

namespace PropF
  notation `#`:max P:max := Var P
  notation A ∨ B         := Disj A B
  notation A ∧ B         := Conj A B
  infixr `⇒`:27          := Impl
  notation `⊥`           := Bot

  definition Neg A       := A ⇒ ⊥
  notation ~ A           := Neg A
  definition Top         := ~⊥
  notation `⊤`           := Top
  definition BiImpl A B  := A ⇒ B ∧ B ⇒ A
  infixr `⇔`:27          := BiImpl

  definition valuation   := PropVar → bool

  definition TrueQ (v : valuation) : PropF → bool
  | TrueQ (# P)   := v P
  | TrueQ ⊥       := ff
  | TrueQ (A ∨ B) := TrueQ A || TrueQ B
  | TrueQ (A ∧ B) := TrueQ A && TrueQ B
  | TrueQ (A ⇒ B) := bnot (TrueQ A) || TrueQ B

  definition is_true [reducible] (b : bool) := b = tt

  -- the valuation v satisfies a list of PropF, if forall (A : PropF) in Γ,
  -- (TrueQ v A) is tt (the Boolean true)
  definition Satisfies v Γ := ∀ A, A ∈ Γ → is_true (TrueQ v A)
  definition Models Γ A    := ∀ v, Satisfies v Γ → is_true (TrueQ v A)

  infix `⊨`:80 := Models

  definition Valid p := [] ⊨ p
  reserve infix `⊢`:26

  /- Provability -/

  inductive Nc : list PropF → PropF → Type :=
  infix ⊢ := Nc
  | Nax   : ∀ Γ A,   A ∈ Γ →             Γ ⊢ A
  | ImpI  : ∀ Γ A B, A::Γ ⊢ B →          Γ ⊢ A ⇒ B
  | ImpE  : ∀ Γ A B, Γ ⊢ A ⇒ B → Γ ⊢ A → Γ ⊢ B
  | BotC  : ∀ Γ A,   (~A)::Γ ⊢ ⊥ →       Γ ⊢ A
  | AndI  : ∀ Γ A B, Γ ⊢ A → Γ ⊢ B →     Γ ⊢ A ∧ B
  | AndE₁ : ∀ Γ A B, Γ ⊢ A ∧ B →         Γ ⊢ A
  | AndE₂ : ∀ Γ A B, Γ ⊢ A ∧ B →         Γ ⊢ B
  | OrI₁  : ∀ Γ A B, Γ ⊢ A →             Γ ⊢ A ∨ B
  | OrI₂  : ∀ Γ A B, Γ ⊢ B →             Γ ⊢ A ∨ B
  | OrE   : ∀ Γ A B C, Γ ⊢ A ∨ B → A::Γ ⊢ C → B::Γ ⊢ C → Γ ⊢ C

  infix ⊢ := Nc

  definition Provable A := [] ⊢ A

  definition Prop_Soundness := ∀ A, Provable A → Valid A

  definition Prop_Completeness := ∀ A, Valid A → Provable A

  open Nc

  -- Remark ⌞t⌟ indicates we should not pattern match on t.
  -- In the following lemma, we only need to pattern match on Γ ⊢ A,
  -- by pattern matching on A, we would be creating 10*6 cases instead of 10.

  lemma weakening2 : ∀ {Γ A Δ}, Γ ⊢ A → Γ ⊆ Δ → Δ ⊢ A
  | Γ ⌞A⌟     Δ (Nax Γ A Hin)          Hs := !Nax  (Hs A Hin)
  | Γ ⌞A ⇒ B⌟ Δ (ImpI Γ A B H)         Hs := !ImpI (weakening2 H (cons_sub_cons A Hs))
  | Γ ⌞B⌟     Δ (ImpE Γ A B H₁ H₂)     Hs := !ImpE (weakening2 H₁ Hs) (weakening2 H₂ Hs)
  | Γ ⌞A⌟     Δ (BotC Γ A H)           Hs := !BotC (weakening2 H (cons_sub_cons (~A) Hs))
  | Γ ⌞A ∧ B⌟ Δ (AndI Γ A B H₁ H₂)     Hs := !AndI (weakening2 H₁ Hs) (weakening2 H₂ Hs)
  | Γ ⌞A⌟     Δ (AndE₁ Γ A B H)        Hs := !AndE₁ (weakening2 H Hs)
  | Γ ⌞B⌟     Δ (AndE₂ Γ A B H)        Hs := !AndE₂ (weakening2 H Hs)
  | Γ ⌞A ∨ B⌟ Δ (OrI₁ Γ A B H)         Hs := !OrI₁ (weakening2 H Hs)
  | Γ ⌞A ∨ B⌟ Δ (OrI₂ Γ A B H)         Hs := !OrI₂ (weakening2 H Hs)
  | Γ ⌞C⌟     Δ (OrE Γ A B C H₁ H₂ H₃) Hs :=
       !OrE (weakening2 H₁ Hs) (weakening2 H₂ (cons_sub_cons A Hs)) (weakening2 H₃ (cons_sub_cons B Hs))

  lemma weakening : ∀ Γ Δ A, Γ ⊢ A → Γ++Δ ⊢ A :=
  λ Γ Δ A H, weakening2 H (sub_append_left Γ Δ)

  lemma deduction : ∀ Γ A B, Γ ⊢ A ⇒ B → A::Γ ⊢ B :=
  λ Γ A B H, !ImpE (weakening2 H (sub_cons A Γ)) (!Nax (mem_cons A Γ))

  lemma prov_impl : ∀ A B, Provable (A ⇒ B) → ∀ Γ, Γ ⊢ A → Γ ⊢ B :=
  λ A B Hp Γ Ha,
    have wHp : Γ ⊢ (A ⇒ B), from !weakening Hp,
    !ImpE wHp Ha

  lemma Satisfies_cons : ∀ {A Γ v}, Satisfies v Γ → is_true (TrueQ v A) → Satisfies v (A::Γ) :=
  λ A Γ v s t B BinAG,
    or.elim BinAG
      (λ e : B = A, by rewrite e; exact t)
      (λ i : B ∈ Γ, s _ i)

  theorem Soundness_general {v : valuation} : ∀ {A Γ}, Γ ⊢ A → Satisfies v Γ → is_true (TrueQ v A)
  | ⌞A⌟     Γ (Nax Γ A Hin) s  := s _ Hin
  | ⌞A ⇒ B⌟ Γ (ImpI Γ A B H) s :=
    by_cases
      (λ t : is_true (TrueQ v A),
        have aux₁ : Satisfies v (A::Γ), from Satisfies_cons s t,
        have aux₂ : is_true (TrueQ v B), from Soundness_general H aux₁,
        bor_inr aux₂)
      (λ f : ¬ is_true (TrueQ v A),
        have aux : bnot (TrueQ v A) = tt, by rewrite (eq_ff_of_ne_tt f),
        bor_inl aux)
  | ⌞B⌟ Γ (ImpE Γ A B H₁ H₂) s :=
    have aux₁ : bnot (TrueQ v A) || TrueQ v B = tt, from Soundness_general H₁ s,
    have aux₂ : TrueQ v A = tt, from Soundness_general H₂ s,
    by rewrite [aux₂ at aux₁, bnot_true at aux₁, ff_bor at aux₁]; exact aux₁
  | ⌞A⌟ Γ (BotC Γ A H) s := by_contradiction
    (λ n : TrueQ v A ≠ tt,
      have aux₁ : TrueQ v A    = ff, from eq_ff_of_ne_tt n,
      have aux₂ : TrueQ v (~A) = tt, begin change (bnot (TrueQ v A) || ff = tt), rewrite aux₁ end,
      have aux₃ : Satisfies v ((~A)::Γ), from Satisfies_cons s aux₂,
      have aux₄ : TrueQ v ⊥ = tt, from Soundness_general H aux₃,
      absurd aux₄ ff_ne_tt)
  | ⌞A ∧ B⌟ Γ (AndI Γ A B H₁ H₂) s :=
    have aux₁ : TrueQ v A = tt, from Soundness_general H₁ s,
    have aux₂ : TrueQ v B = tt, from Soundness_general H₂ s,
    band_intro aux₁ aux₂
  | ⌞A⌟     Γ (AndE₁ Γ A B H) s :=
    have aux : TrueQ v (A ∧ B) = tt, from Soundness_general H s,
    band_elim_left aux
  | ⌞B⌟     Γ (AndE₂ Γ A B H) s :=
    have aux : TrueQ v (A ∧ B) = tt, from Soundness_general H s,
    band_elim_right aux
  | ⌞A ∨ B⌟ Γ (OrI₁ Γ A B H) s :=
    have aux : TrueQ v A = tt, from Soundness_general H s,
    bor_inl aux
  | ⌞A ∨ B⌟ Γ (OrI₂ Γ A B H) s :=
    have aux : TrueQ v B = tt, from Soundness_general H s,
    bor_inr aux
  | ⌞C⌟     Γ (OrE Γ A B C H₁ H₂ H₃) s :=
    have aux : TrueQ v A || TrueQ v B = tt, from Soundness_general H₁ s,
    or.elim (or_of_bor_eq aux)
      (λ At : TrueQ v A = tt,
        have aux : Satisfies v (A::Γ), from Satisfies_cons s At,
        Soundness_general H₂ aux)
      (λ Bt : TrueQ v B = tt,
        have aux : Satisfies v (B::Γ), from Satisfies_cons s Bt,
        Soundness_general H₃ aux)

  theorem Soundness : Prop_Soundness :=
  λ A H v s, Soundness_general H s

end PropF