/-
Copyright (c) 2022 Julian Kuelshammer. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author : Julian Kuelshammer
-/

import data.int.basic
import tactic.linear_combination
import tactic.linarith

/-
# Quotients in Lean

Upon request, let's try to see how to construct number systems like the integers or the
rational numbers in Lean. Note that this is again some mathematical way to do this, not the
actual way, e.g. integers are defined as the disjoint union of ℕ with itself, where the first
copy is interpreted as the usual natural numbers while the second copy is interpreted as the
numbers `1-n` where `n : ℕ`. Similarly, ℚ is contructed as pairs of coprime integers (p,q).
This makes them computationally a bit better behaved than our quotient way.

## Equivalence relations in Lean

Lean knows what an equivalence relation is. It is a reflexive, symmetric and transitive relation.
A relation on a set `X` is a function `X → X → Prop`, i.e. a function that takes two elements
of a set `X` and outputs a truth value depending whether they are related or not.

```
def reflexive := ∀ x, x ∼ x

def symmetric := ∀ ⦃x y⦄, x ∼ y → y ∼ x

def transitive := ∀ ⦃x y z⦄, x ∼ y → y ∼ m z → x ∼ z

def equivalence := reflexive r ∧ symmetric r ∧ transitive r
```

-/

def R (r s : ℕ × ℕ) : Prop :=
r.1+s.2=s.1+r.2

lemma R_def (r s : ℕ × ℕ) :
R r s ↔ r.1 + s.2 = s.1 + r.2 :=
begin
  sorry
end

lemma R_refl : reflexive R :=
begin
  sorry
end

lemma R_symm : symmetric R :=
begin
  sorry
end

lemma R_trans : transitive R :=
begin
  /- The lemma add_right_inj could be helpful at some point. -/
  sorry
end

lemma R_equiv : equivalence R :=
begin
  sorry
end

/- A setoid on a Type is a relation together with the fact that
  this relation is an equivalence relation. -/
instance s : setoid (ℕ × ℕ) :=
_

structure int_plane_non_zero :=
(fst : ℤ) (snd : ℤ) (non_zero : snd ≠ 0)

def S (r s : int_plane_non_zero) : Prop :=
r.1 * s.2 = s.1 * r.2

lemma S_def (r s : int_plane_non_zero) :
S r s ↔ r.1 * s.2 = s.1 * r.2 :=
begin
  sorry
end

lemma S_refl : reflexive S :=
begin
  sorry
end

lemma S_symm : symmetric S :=
begin
  sorry
end

lemma S_trans : transitive S :=
begin
  /- The following lemma would be helpful at some point: mul_right_inj' -/
  sorry
end

lemma S_equiv : equivalence S :=
begin
  sorry
end

instance t : setoid (int_plane_non_zero) :=
_


universe u

open classical

theorem not_or_and_not : ∀ { p q : Prop}, ¬ (p ∨ q) → ¬ p ∧ ¬ q :=
begin
  intros p q h,
  split,
  intro hp,
  apply h,
  left,
  exact hp,
  intro hq,
  apply h,
  right,
  exact hq,
end

theorem not_not : ∀ {p : Prop} , ¬¬p → p :=
begin
  intros p nnp,
  by_contradiction,
  exact nnp h,
end

theorem exists_not_of_not_exists {α :Sort u} {p : α → Prop} : (¬(∀ x : α , p x)) → ∃ x : α , ¬(p x) :=
begin
  intro h₁,
  by_contradiction,
  have h₂ := forall_not_of_not_exists h,
  simp at h₂,
  apply h₁,
  intro x,
  apply not_not,
  apply h₂,
end

theorem not_or_and_not_eqv (p q : Prop) : ¬ (p ∨ q) ↔ (¬p) ∧ (¬q) :=
begin
  split,
  intro h,
  split,
  intro hp,
  apply h,
  left,
  exact hp,
  intro hq,
  apply h,
  right,
  exact hq,
  intros h h₁,
  cases h with np nq,
  cases h₁,
  exact np h₁,
  exact nq h₁,
end

theorem not_and_or_not : ∀ {p q : Prop}, ¬ (p ∧ q) → ¬ p ∨ ¬q :=
begin
  intros p q h₁,
  by_contradiction h₂,
  rw not_or_and_not_eqv at h₂,
  apply h₁,
  cases h₂ with nnp nnq,
  split,
  apply not_not,
  assumption,
  apply not_not,
  assumption,
end

theorem implies_or_not (p q : Prop) : p → q ↔ (¬p) ∨ q :=
begin
  split,
  intro hpq,
  by_cases p,
  right,
  exact hpq h,
  left,
  exact h,
  intro h,
  intro hp,
  cases h,
  apply false.elim,
  apply h,
  apply hp,
  exact h,
end

theorem not_not_eqv (p : Prop) : ¬¬ p ↔ p :=
begin
  split,
  apply not_not,
  apply not_not_intro,
end

theorem not_implies (p q : Prop) : ¬ (p → q) ↔ p ∧ ¬q :=
begin
  rw implies_or_not,
  rw not_or_and_not_eqv,
  rw not_not_eqv,
end

theorem contrapostive (p q : Prop) : (¬q → ¬p) → (p → q) :=
begin
  intros hnqnp hp,
  cases (em q),
  exact h,
  apply false.elim,
  apply hnqnp h,
  exact hp,
end


variables p q r s: Prop

-- commutativity of ∧ and ∨

theorem and_switch (h: p ∧ q): q ∧ p :=
  and.intro h.right h.left

example: p ∧ q ↔ q ∧ p :=
  iff.intro
    (and_switch p q)
    (and_switch q p)

theorem or_switch (h: p ∨ q): q ∨ p :=
  or.elim h
    (assume p, or.inr p)
    (assume q, or.inl q)

example: p ∨ q ↔ q ∨ p :=
  iff.intro
    (or_switch p q)
    (or_switch q p)

-- associativity of ∧ and ∨

example: p ∧ (q ∨ r) ↔ (p ∧ q) ∨ (p ∧ r) :=
  iff.intro
    (assume h: p ∧ (q ∨ r),
      have hp: p, from h.left,
      or.elim h.right
        (assume hq: q,
          have hpq: p ∧ q, from and.intro hp hq,
            or.inl hpq)
        (assume hr: r,
          have hpr: p ∧ r, from and.intro hp hr,
            or.inr hpr))
    (assume h: (p ∧ q) ∨ (p ∧ r),
      or.elim h
        (assume hpq: p ∧ q,
          have hp: p, from hpq.left,
          have hqr: q ∨ r, from or.inl hpq.right,
          and.intro hp hqr)
        (assume hpr: p ∧ r,
          have hp: p, from hpr.left,
          have hqr: q ∨ r, from or.inr hpr.right,
          and.intro hp hqr))

example: (p ∧ q) ∧ r ↔ p ∧ (q ∧ r) :=
  iff.intro
    (assume h: (p ∧ q) ∧ r,
      have hpq: p ∧ q, from h.left,
      have hp: p, from hpq.left,
      have hq: q, from hpq.right,
      have hr: r, from h.right,
      ⟨hp, ⟨hq, hr⟩⟩)
    (assume h: p ∧ (q ∧ r),
      have hp: p, from h.left,
      have hqr: q ∧ r, from h.right,
      have hq: q, from hqr.left,
      have hr: r, from hqr.right,
      ⟨⟨hp, hq⟩, hr⟩)


import algebra.group_power
import data.real.basic
import tactic.norm_num

-- The integers, rationals and reals are all different types in Lean
-- and the obvious inclusions between them are all denoted by ↑ .
lemma rational_half_not_an_integer : ¬ ∃ n : ℤ, (1/2 : ℚ) = ↑n :=
begin
  -- proof by contradiction
  rintros ⟨n,Hn⟩, -- n is an integer, Hn the proof that 1/2 = n
  -- goal is "false"
  have H := rat.coe_int_denom n, -- H says denominator of n is 1
  rw ←Hn at H, -- H now says denominator of 1/2 is 1...
  exact absurd H dec_trivial -- ...but denominator of 1/2 isn't 1.
end

lemma real_half_not_an_integer : ¬ (∃ n : ℤ, (1/2 : ℝ) = (n : ℝ) ) :=
begin
  rintro ⟨n,Hn⟩, -- n is an integer, Hn the proof that it's 1/2
  apply rational_half_not_an_integer,
  existsi n,
  -- now our hypothesis is that 1/2 = n as reals, and we want to
  -- deduce 1/2 = n as rationals!
  -- This is possible by some messing around with coercions
  -- from integers to rationals to reals. I wish this were easier
  -- for beginners in Lean...
  rw ←@rat.cast_inj ℝ _ _,
  rw rat.cast_coe_int,
  rw ←Hn, --goal now is to prove that real 1/2 = rational 1/2
  simp -- simplifier is good at that sort of thing
end

-- proof that the real numbers which are integers and whose squares are less than three
-- are precisely -1, 0 and 1

lemma square_lt_three_of_ge_two (n : ℕ) : ¬ (n + 2) * (n + 2) < 3 :=
begin
  intro H,
  suffices Hab : 4 < 3,
    exact absurd Hab dec_trivial,
  exact calc
    4 = 2 * 2             : rfl
...   ≤ (n + 2) * 2       : nat.mul_le_mul_right 2 (show 2 ≤ n+2, from dec_trivial)
...   ≤ (n + 2) * (n + 2) : nat.mul_le_mul_left (n+2) (show 2 ≤ n+2, from dec_trivial)
...   < 3                 : H
end

lemma int_squared_lt_three {z : ℤ} : z ^ 2 < 3 → z = -1 ∨ z = 0 ∨ z = 1 :=
begin
  cases z with n n,
  { rw pow_two,
    show ↑n * ↑n < ↑3 → _,
    rw [←int.coe_nat_mul,int.coe_nat_lt],
    intro Hn,
    cases n,
      right,left,refl,
    cases n,
      right,right,refl,
    cases square_lt_three_of_ge_two n Hn,
  },
  { rw [pow_two,←int.nat_abs_mul_self],
    show ↑((n+1)*(n+1)) < ↑3 → _,
    rw int.coe_nat_lt,
    intro Hn,
    cases n,
      left,trivial,
    cases square_lt_three_of_ge_two n Hn,
  }
end

theorem B_is_minus_one_zero_one (x : ℝ) : x ∈ { x : ℝ | x^2 < 3 ∧ ∃ y : ℤ, x = ↑y} ↔ x = -1 ∨ x = 0 ∨ x = 1 :=
begin
  split,
  { intro H,
    cases H.right with y Hy,
    have Hleft := H.left,
    rw [Hy,pow_two,←int.cast_mul] at Hleft,
    have Htemp : (3 : ℝ) = (3 : ℤ),
      refl,
    rw Htemp at Hleft,
    rw [int.cast_lt,←pow_two] at Hleft,
    rw Hy,
    cases int_squared_lt_three Hleft with h h,
      left,rw h,refl,
    cases h with h h,
      right,left,rw h,refl,
      right,right,rw h,refl
  },
  { intro H,
    cases H,
      rw H,
      split,norm_num,existsi (-1 : ℤ),refl,
    cases H,
      rw H,
      split,norm_num,existsi (0 : ℤ),refl,
    rw H,
    split,norm_num,existsi (1 : ℤ),refl
  }
end


-- ---------------------------------------------------------------------
-- Ejercicio. Demostrar que si c es un número real no nulo, entonces la
-- función
--    f(x) = c * x
-- es suprayectiva.
-- ----------------------------------------------------------------------

import data.real.basic

open function

example
  {c : ℝ}
  (h : c ≠ 0)
  : surjective (λ x, c * x) :=
begin
  intro x,
  use (x / c),
  change c * (x / c) = x,
  rw mul_comm,
  apply div_mul_cancel,
  exact h,
end

-- Su prueba es
--
-- c : ℝ,
-- h : c ≠ 0
-- ⊢ surjective (λ (x : ℝ), c * x)
--    >> intro x,
-- x : ℝ
-- ⊢ ∃ (a : ℝ), (λ (x : ℝ), c * x) a = x
--    >> use (x / c),
-- ⊢ (λ (x : ℝ), c * x) (x / c) = x
--    >> change c * (x / c) = x,
-- ⊢ c * (x / c) = x
--    >> rw mul_comm,
-- ⊢ x / c * c = x
--    >> apply div_mul_cancel,
-- ⊢ c ≠ 0
--    >> exact h,
-- no goals


import .love01_definitions_and_statements_demo

/-! # LoVe Exercise 1: Definitions and Statements

Replace the placeholders (e.g., `:= sorry`) with your solutions. -/

set_option pp.beta true
set_option pp.generalized_field_notation false

namespace LoVe

/-! ## Question 1: Truncated Subtraction

1.1. Define the function `sub` that implements truncated subtraction on natural
numbers by recursion. "Truncated" means that results that mathematically would
be negative are represented by 0. For example:

    `sub 7 2 = 5`
    `sub 2 7 = 0` -/

def sub : ℕ → ℕ → ℕ
| m            0            := m
| 0            _            := 0
| (nat.succ m) (nat.succ n) := sub m n

/-! 1.2. Check that your function works as expected. -/

#eval sub 0 0   -- expected: 0
#eval sub 0 1   -- expected: 0
#eval sub 0 7   -- expected: 0
#eval sub 1 0   -- expected: 1
#eval sub 1 1   -- expected: 0
#eval sub 3 0   -- expected: 3
#eval sub 2 7   -- expected: 0
#eval sub 3 1   -- expected: 2
#eval sub 3 3   -- expected: 0
#eval sub 3 7   -- expected: 0
#eval sub 7 2   -- expected: 5

/-! ## Question 2: Arithmetic Expressions

Consider the type `aexp` from the lecture and the function `eval` that
computes the value of an expression. You will find the definitions in the file
`love01_definitions_and_statements_demo.lean`. One way to find them quickly is
to

1. hold the Control (on Linux and Windows) or Command (on macOS) key pressed;
2. move the cursor to the identifier `aexp` or `eval`;
3. click the identifier. -/

#check aexp
#check eval

/-! 2.1. Test that `eval` behaves as expected. Make sure to exercise each
constructor at least once. You can use the following environment in your tests.
What happens if you divide by zero?

Make sure to use `#eval`. For technical reasons, `#reduce` does not work well
here. Note that `#eval` (Lean's evaluation command) and `eval` (our evaluation
function on `aexp`) are unrelated. -/

def some_env : string → ℤ
| "x" := 3
| "y" := 17
| _   := 201

#eval eval some_env (aexp.var "x")   -- expected: 3
#eval eval some_env (aexp.num 12)                              -- expected: 12
#eval eval some_env (aexp.add (aexp.var "x") (aexp.var "y"))   -- expected: 20
#eval eval some_env (aexp.sub (aexp.num 5) (aexp.var "y"))     -- expected: -12
#eval eval some_env (aexp.mul (aexp.num 11) (aexp.var "z"))    -- expected: 2211
#eval eval some_env (aexp.div (aexp.num 2) (aexp.num 0))       -- expected: 0

/-! 2.2. The following function simplifies arithmetic expressions involving
addition. It simplifies `0 + e` and `e + 0` to `e`. Complete the definition so
that it also simplifies expressions involving the other three binary
operators. -/

def simplify : aexp → aexp
| (aexp.add (aexp.num 0) e₂) := simplify e₂
| (aexp.add e₁ (aexp.num 0)) := simplify e₁
| (aexp.sub e₁ (aexp.num 0)) := simplify e₁
| (aexp.mul (aexp.num 0) e₂) := aexp.num 0
| (aexp.mul e₁ (aexp.num 0)) := aexp.num 0
| (aexp.mul (aexp.num 1) e₂) := simplify e₂
| (aexp.mul e₁ (aexp.num 1)) := simplify e₁
| (aexp.div (aexp.num 0) e₂) := aexp.num 0
| (aexp.div e₁ (aexp.num 0)) := aexp.num 0
| (aexp.div e₁ (aexp.num 1)) := simplify e₁
-- catch-all cases below
| (aexp.num i)               := aexp.num i
| (aexp.var x)               := aexp.var x
| (aexp.add e₁ e₂)           := aexp.add (simplify e₁) (simplify e₂)
| (aexp.sub e₁ e₂)           := aexp.sub (simplify e₁) (simplify e₂)
| (aexp.mul e₁ e₂)           := aexp.mul (simplify e₁) (simplify e₂)
| (aexp.div e₁ e₂)           := aexp.div (simplify e₁) (simplify e₂)

/-! 2.3. Is the `simplify` function correct? In fact, what would it mean for it
to be correct or not? Intuitively, for `simplify` to be correct, it must
return an arithmetic expression that yields the same numeric value when
evaluated as the original expression.

Given an environment `env` and an expression `e`, state (without proving it)
the property that the value of `e` after simplification is the same as the
value of `e` before. -/

lemma simplify_correct (env : string → ℤ) (e : aexp) :
  eval env (simplify e) = eval env e :=
sorry

/-! ## Question 3: λ-Terms

3.1. Complete the following definitions, by replacing the `sorry` markers by
terms of the expected type.

Hint: A procedure for doing so systematically is described in Section 1.1.4 of
the Hitchhiker's Guide. As explained there, you can use `_` as a placeholder
while constructing a term. By hovering over `_`, you will see the current
logical context. -/

def I : α → α :=
λa, a

def K : α → β → α :=
λa b, a

def C : (α → β → γ) → β → α → γ :=
λg b a, g a b

def proj_1st : α → α → α :=
λx y, x

/-! Please give a different answer than for `proj_1st`. -/

def proj_2nd : α → α → α :=
λx y, y

def some_nonsense : (α → β → γ) → α → (α → γ) → β → γ :=
λg a f b, g a b

/-! 3.2. Show the typing derivation for your definition of `C` above, on paper
or using ASCII or Unicode art. You might find the characters `–` (to draw
horizontal bars) and `⊢` useful. -/

/-! Let `D` := `g : α → β → γ, b : β, a : α`. We have

    –––––––––––––––––– Var    –––––––––– Var
    D ⊢ g : α → β → γ         D ⊢ a : α
    –––––––––––––––––––––––––––––––––––– App    –––––––––– Var
    D ⊢ g a : β → γ                             D ⊢ b : β
    –––––––––––––––––––––––––––––––––––––––––––––––––––––– App
    D ⊢ g a b : γ
    –––––––––––––––––––––––––––––––––––––––––––--- Lam
    g : α → β → γ, b : β ⊢ (λa : α, g a b) : α → γ
    ––––––––––––––––––––––––––––––––––––––––––––––------- Lam
    g : α → β → γ ⊢ (λ(b : β) (a : α), g a b) : β → α → γ
    –––––––––––––––––––––––––––––––––––––––––––––––---------------------- Lam
    ⊢ (λ(g : α → β → γ) (b : β) (a : α), g a b) : (α → β → γ) → β → α → γ -/

end LoVe


/-
Copyright (c) 2022 Violeta Hernández Palacios. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Violeta Hernández Palacios
-/

import set_theory.game.basic
import set_theory.ordinal.natural_ops

/-!
# Ordinals as games

We define the canonical map `ordinal → pgame`, where every ordinal is mapped to the game whose left
set consists of all previous ordinals.

The map to surreals is defined in `ordinal.to_surreal`.

# Main declarations

- `ordinal.to_pgame`: The canonical map between ordinals and pre-games.
- `ordinal.to_pgame_embedding`: The order embedding version of the previous map.
-/

universe u

open pgame

open_locale natural_ops pgame

namespace ordinal

/-- Converts an ordinal into the corresponding pre-game. -/
noncomputable! def to_pgame : ordinal.{u} → pgame.{u}
| o := ⟨o.out.α, pempty, λ x, let hwf := ordinal.typein_lt_self x in
        (typein (<) x).to_pgame, pempty.elim⟩
using_well_founded { dec_tac := tactic.assumption }

theorem to_pgame_def (o : ordinal) :
  o.to_pgame = ⟨o.out.α, pempty, λ x, (typein (<) x).to_pgame, pempty.elim⟩ :=
by rw to_pgame

@[simp] theorem to_pgame_left_moves (o : ordinal) : o.to_pgame.left_moves = o.out.α :=
by rw [to_pgame, left_moves]

@[simp] theorem to_pgame_right_moves (o : ordinal) : o.to_pgame.right_moves = pempty :=
by rw [to_pgame, right_moves]

instance is_empty_zero_to_pgame_left_moves : is_empty (to_pgame 0).left_moves :=
by { rw to_pgame_left_moves, apply_instance }

instance is_empty_to_pgame_right_moves (o : ordinal) : is_empty o.to_pgame.right_moves :=
by { rw to_pgame_right_moves, apply_instance }

/-- Converts an ordinal less than `o` into a move for the `pgame` corresponding to `o`, and vice
versa. -/
noncomputable def to_left_moves_to_pgame {o : ordinal} : set.Iio o ≃ o.to_pgame.left_moves :=
(enum_iso_out o).to_equiv.trans (equiv.cast (to_pgame_left_moves o).symm)

@[simp] theorem to_left_moves_to_pgame_symm_lt {o : ordinal} (i : o.to_pgame.left_moves) :
  ↑(to_left_moves_to_pgame.symm i) < o :=
(to_left_moves_to_pgame.symm i).prop

theorem to_pgame_move_left_heq {o : ordinal} :
  o.to_pgame.move_left == λ x : o.out.α, (typein (<) x).to_pgame :=
by { rw to_pgame, refl }

@[simp] theorem to_pgame_move_left' {o : ordinal} (i) :
  o.to_pgame.move_left i = (to_left_moves_to_pgame.symm i).val.to_pgame :=
(congr_heq to_pgame_move_left_heq.symm (cast_heq _ i)).symm

theorem to_pgame_move_left {o : ordinal} (i) :
  o.to_pgame.move_left (to_left_moves_to_pgame i) = i.val.to_pgame :=
by simp

/-- `0.to_pgame` has the same moves as `0`. -/
noncomputable def zero_to_pgame_relabelling : to_pgame 0 ≡r 0 :=
relabelling.is_empty _

noncomputable instance unique_one_to_pgame_left_moves : unique (to_pgame 1).left_moves :=
(equiv.cast $ to_pgame_left_moves 1).unique

@[simp] theorem one_to_pgame_left_moves_default_eq :
  (default : (to_pgame 1).left_moves) = @to_left_moves_to_pgame 1 ⟨0, zero_lt_one⟩ :=
rfl

@[simp] theorem to_left_moves_one_to_pgame_symm (i) :
  (@to_left_moves_to_pgame 1).symm i = ⟨0, zero_lt_one⟩ :=
by simp

theorem one_to_pgame_move_left (x) : (to_pgame 1).move_left x = to_pgame 0 :=
by simp

/-- `1.to_pgame` has the same moves as `1`. -/
noncomputable def one_to_pgame_relabelling : to_pgame 1 ≡r 1 :=
⟨equiv.equiv_of_unique _ _, equiv.equiv_of_is_empty _ _,
  λ i, by simpa using zero_to_pgame_relabelling, is_empty_elim⟩

theorem to_pgame_lf {a b : ordinal} (h : a < b) : a.to_pgame ⧏ b.to_pgame :=
by { convert move_left_lf (to_left_moves_to_pgame ⟨a, h⟩), rw to_pgame_move_left }

theorem to_pgame_le {a b : ordinal} (h : a ≤ b) : a.to_pgame ≤ b.to_pgame :=
begin
  refine le_iff_forall_lf.2 ⟨λ i, _, is_empty_elim⟩,
  rw to_pgame_move_left',
  exact to_pgame_lf ((to_left_moves_to_pgame_symm_lt i).trans_le h)
end

theorem to_pgame_lt {a b : ordinal} (h : a < b) : a.to_pgame < b.to_pgame :=
⟨to_pgame_le h.le, to_pgame_lf h⟩

theorem to_pgame_nonneg (a : ordinal) : 0 ≤ a.to_pgame :=
zero_to_pgame_relabelling.ge.trans $ to_pgame_le $ ordinal.zero_le a

@[simp] theorem to_pgame_lf_iff {a b : ordinal} : a.to_pgame ⧏ b.to_pgame ↔ a < b :=
⟨by { contrapose, rw [not_lt, not_lf], exact to_pgame_le }, to_pgame_lf⟩

@[simp] theorem to_pgame_le_iff {a b : ordinal} : a.to_pgame ≤ b.to_pgame ↔ a ≤ b :=
⟨by { contrapose, rw [not_le, pgame.not_le], exact to_pgame_lf }, to_pgame_le⟩

@[simp] theorem to_pgame_lt_iff {a b : ordinal} : a.to_pgame < b.to_pgame ↔ a < b :=
⟨by { contrapose, rw not_lt, exact λ h, not_lt_of_le (to_pgame_le h) }, to_pgame_lt⟩

@[simp] theorem to_pgame_equiv_iff {a b : ordinal} : a.to_pgame ≈ b.to_pgame ↔ a = b :=
by rw [pgame.equiv, le_antisymm_iff, to_pgame_le_iff, to_pgame_le_iff]

theorem to_pgame_injective : function.injective ordinal.to_pgame :=
λ a b h, to_pgame_equiv_iff.1 $ equiv_of_eq h

@[simp] theorem to_pgame_eq_iff {a b : ordinal} : a.to_pgame = b.to_pgame ↔ a = b :=
to_pgame_injective.eq_iff

/-- The order embedding version of `to_pgame`. -/
@[simps] noncomputable def to_pgame_embedding : ordinal.{u} ↪o pgame.{u} :=
{ to_fun := ordinal.to_pgame,
  inj' := to_pgame_injective,
  map_rel_iff' := @to_pgame_le_iff }

/-- The sum of ordinals as games corresponds to natural addition of ordinals. -/
theorem to_pgame_add : ∀ a b : ordinal.{u}, a.to_pgame + b.to_pgame ≈ (a ♯ b).to_pgame
| a b := begin
  refine ⟨le_of_forall_lf (λ i, _) is_empty_elim, le_of_forall_lf (λ i, _) is_empty_elim⟩,
  { apply left_moves_add_cases i;
    intro i;
    let wf := to_left_moves_to_pgame_symm_lt i;
    try { rw add_move_left_inl }; try { rw add_move_left_inr };
    rw [to_pgame_move_left', lf_congr_left (to_pgame_add _ _), to_pgame_lf_iff],
    { exact nadd_lt_nadd_right wf _ },
    { exact nadd_lt_nadd_left wf _ } },
  { rw to_pgame_move_left',
    rcases lt_nadd_iff.1 (to_left_moves_to_pgame_symm_lt i) with ⟨c, hc, hc'⟩ | ⟨c, hc, hc'⟩;
    rw [←to_pgame_le_iff, ←le_congr_right (to_pgame_add _ _)] at hc';
    apply lf_of_le_of_lf hc',
    { apply add_lf_add_right,
      rwa to_pgame_lf_iff },
    { apply add_lf_add_left,
      rwa to_pgame_lf_iff } }
end
using_well_founded { dec_tac := `[solve_by_elim [psigma.lex.left, psigma.lex.right]] }

@[simp] theorem to_pgame_add_mk (a b : ordinal) :
  ⟦a.to_pgame⟧ + ⟦b.to_pgame⟧ = ⟦(a ♯ b).to_pgame⟧ :=
quot.sound (to_pgame_add a b)

end ordinal