import macros
import tactic
using Nat

-- In this example, we prove two simple theorems about even/odd numbers.
-- First, we define the predicates even and odd.
definition even (a : Nat) := ∃ b, a = 2*b
definition odd  (a : Nat) := ∃ b, a = 2*b + 1

-- Prove that the sum of two even numbers is even.
--
--   Notes: we use the macro
--      obtain [bindings] ',' 'from' [expr]_1 ',' [expr]_2
--
--   It is syntax sugar for existential elimination.
--   It expands to
--
--     exists_elim [expr]_1 (fun [binding], [expr]_2)
--
--   It is defined in the file macros.lua.
--
--   We also use the calculational proof style.
--   See doc/lean/calc.md for more information.
--
--  We use the first two obtain-expressions to extract the
--  witnesses w1 and w2 s.t. a = 2*w1 and b = 2*w2.
--  We can do that because H1 and H2 are evidence/proof for the
--  fact that 'a' and 'b' are even.
--
--  We use a calculational proof 'calc' expression to derive
--  the witness w1+w2 for the fact that a+b is also even.
--  That is, we provide a derivation showing that a+b = 2*(w1 + w2)
theorem EvenPlusEven {a b : Nat} (H1 : even a) (H2 : even b) : even (a + b)
:= obtain (w1 : Nat) (Hw1 : a = 2*w1), from H1,
   obtain (w2 : Nat) (Hw2 : b = 2*w2), from H2,
     exists_intro (w1 + w2)
        (calc a + b  =  2*w1 + b      : { Hw1 }
                ...  =  2*w1 + 2*w2   : { Hw2 }
                ...  =  2*(w1 + w2)   : symm (distributer 2 w1 w2))

exit

rewrite_set basic
add_rewrite distributer eq_id : basic

theorem EvenPlusEven2 {a b : Nat} (H1 : even a) (H2 : even b) : even (a + b)
:= obtain (w1 : Nat) (Hw1 : a = 2*w1), from H1,
   obtain (w2 : Nat) (Hw2 : b = 2*w2), from H2,
     exists_intro (w1 + w2)
       (calc a + b = 2*(w1 + w2) : by simp basic)

-- In the following example, we omit the arguments for add_assoc, refl and distribute.
-- Lean can infer them automatically.
--
-- refl is the reflexivity proof. (refl a) is a proof that two
-- definitionally equal terms are indeed equal.
-- "definitionally equal" means that they have the same normal form.
-- We can also view it as "Proof by computation".
-- The normal form of (1+1), and 2*1 is 2.
--
-- Another remark: '2*w + 1 + 1' is not definitionally equal to '2*w + 2*1'.
-- The gotcha is that '2*w + 1 + 1' is actually '(2*w + 1) + 1' since +
-- is left associative. Moreover, Lean normalizer does not use
-- any theorems such as + associativity.
theorem OddPlusOne {a : Nat} (H : odd a) : even (a + 1)
:= obtain (w : Nat) (Hw : a = 2*w + 1), from H,
   exists_intro (w + 1)
        (calc a + 1 = 2*w + 1 + 1   : { Hw }
                ... = 2*w + (1 + 1) : add_assoc _ _ _
                ... = 2*w + 2*1     : refl _
                ... = 2*(w + 1)     : symm (distributer _ _ _))

-- The following command displays the proof object produced by Lean after
-- expanding macros, and infering implicit/missing arguments.
print environment 2

-- By default, Lean does not display implicit arguments.
-- The following command will force it to display them.
set_option pp::implicit true

print environment 2

-- As an exercise, prove that the sum of two odd numbers is even,
-- and other similar theorems.