l3lab/ntp-mathlib huggingface dataset:
/-- The Grundy value of the sum of two nim games with natural numbers of piles equals their bitwise
xor. -/
@[simp]
theorem grundyValue_nim_add_nim (n m : ℕ) :
    grundyValue (nim.{u} n + nim.{u} m) = n ^^^ m := by
  -- We do strong induction on both variables.
  induction' n using Nat.strong_induction_on with n hn generalizing m
  induction' m using Nat.strong_induction_on with m hm
  rw [grundyValue_eq_mex_left]
  refine (Ordinal.mex_le_of_ne.{u, u} fun i => ?_).antisymm
    (Ordinal.le_mex_of_forall fun ou hu => ?_)
  -- The Grundy value `n ^^^ m` can't be reached by left moves.
  · apply leftMoves_add_cases i <;>
      · -- A left move leaves us with a Grundy value of `k ^^^ m` for `k < n`, or
        -- `n ^^^ k` for `k < m`.
        refine' fun a => leftMovesNimRecOn a fun ok hk => _
        obtain ⟨k, rfl⟩ := Ordinal.lt_omega.1 (hk.trans (Ordinal.nat_lt_omega _))
        simp only [add_moveLeft_inl, add_moveLeft_inr, moveLeft_nim', Equiv.symm_apply_apply]
        -- The inequality follows from injectivity.
        rw [nat_cast_lt] at hk
        first
        | rw [hn _ hk]
        | rw [hm _ hk]
        refine' fun h => hk.ne _
        rw [Ordinal.nat_cast_inj] at h
        first
        | 

Proof state:
case h.h.refine_1.hl.intro
n : ℕ
hn : ∀ m < n, ∀ (m_1 : ℕ), grundyValue (nim ↑m + nim ↑m_1) = ↑(m ^^^ m_1)
m : ℕ
hm : ∀ m_1 < m, grundyValue (nim ↑n + nim ↑m_1) = ↑(n ^^^ m_1)
i : LeftMoves (nim ↑n + nim ↑m)
a : LeftMoves (nim ↑n)
k : ℕ
hk : k < n
h : k ^^^ m = n ^^^ m
⊢ k = n

Next tactic:
rwa [Nat.xor_left_inj] at h
/-- The Grundy value of the sum of two nim games with natural numbers of piles equals their bitwise
xor. -/
@[simp]
theorem grundyValue_nim_add_nim (n m : ℕ) :
    grundyValue (nim.{u} n + nim.{u} m) = n ^^^ m := by
  -- We do strong induction on both variables.
  induction' n using Nat.strong_induction_on with n hn generalizing m
  induction' m using Nat.strong_induction_on with m hm
  rw [grundyValue_eq_mex_left]
  refine (Ordinal.mex_le_of_ne.{u, u} fun i => ?_).antisymm
    (Ordinal.le_mex_of_forall fun ou hu => ?_)
  -- The Grundy value `n ^^^ m` can't be reached by left moves.
  · apply leftMoves_add_cases i <;>
      · -- A left move leaves us with a Grundy value of `k ^^^ m` for `k < n`, or
        -- `n ^^^ k` for `k < m`.
        

Proof state:
case h.h.refine_1.hr
n : ℕ
hn : ∀ m < n, ∀ (m_1 : ℕ), grundyValue (nim ↑m + nim ↑m_1) = ↑(m ^^^ m_1)
m : ℕ
hm : ∀ m_1 < m, grundyValue (nim ↑n + nim ↑m_1) = ↑(n ^^^ m_1)
i : LeftMoves (nim ↑n + nim ↑m)
⊢ ∀ (i : LeftMoves (nim ↑m)), grundyValue (moveLeft (nim ↑n + nim ↑m) (toLeftMovesAdd (Sum.inr i))) ≠ ↑(n ^^^ m)

Next tactic:
refine' fun a => leftMovesNimRecOn a fun ok hk => _
/-- The Grundy value of the sum of two nim games with natural numbers of piles equals their bitwise
xor. -/
@[simp]
theorem grundyValue_nim_add_nim (n m : ℕ) :
    grundyValue (nim.{u} n + nim.{u} m) = n ^^^ m := by
  -- We do strong induction on both variables.
  induction' n using Nat.strong_induction_on with n hn generalizing m
  induction' m using Nat.strong_induction_on with m hm
  rw [grundyValue_eq_mex_left]
  refine (Ordinal.mex_le_of_ne.{u, u} fun i => ?_).antisymm
    (Ordinal.le_mex_of_forall fun ou hu => ?_)
  -- The Grundy value `n ^^^ m` can't be reached by left moves.
  · apply leftMoves_add_cases i <;>
      · -- A left move leaves us with a Grundy value of `k ^^^ m` for `k < n`, or
        -- `n ^^^ k` for `k < m`.
        refine' fun a => leftMovesNimRecOn a fun ok hk => _
        

Proof state:
case h.h.refine_1.hr
n : ℕ
hn : ∀ m < n, ∀ (m_1 : ℕ), grundyValue (nim ↑m + nim ↑m_1) = ↑(m ^^^ m_1)
m : ℕ
hm : ∀ m_1 < m, grundyValue (nim ↑n + nim ↑m_1) = ↑(n ^^^ m_1)
i : LeftMoves (nim ↑n + nim ↑m)
a : LeftMoves (nim ↑m)
ok : Ordinal.{u}
hk : ok < ↑m
⊢ grundyValue (moveLeft (nim ↑n + nim ↑m) (toLeftMovesAdd (Sum.inr (toLeftMovesNim { val := ok, property := hk })))) ≠
    ↑(n ^^^ m)

Next tactic:
obtain ⟨k, rfl⟩ := Ordinal.lt_omega.1 (hk.trans (Ordinal.nat_lt_omega _))
/-- The Grundy value of the sum of two nim games with natural numbers of piles equals their bitwise
xor. -/
@[simp]
theorem grundyValue_nim_add_nim (n m : ℕ) :
    grundyValue (nim.{u} n + nim.{u} m) = n ^^^ m := by
  -- We do strong induction on both variables.
  induction' n using Nat.strong_induction_on with n hn generalizing m
  induction' m using Nat.strong_induction_on with m hm
  rw [grundyValue_eq_mex_left]
  refine (Ordinal.mex_le_of_ne.{u, u} fun i => ?_).antisymm
    (Ordinal.le_mex_of_forall fun ou hu => ?_)
  -- The Grundy value `n ^^^ m` can't be reached by left moves.
  · apply leftMoves_add_cases i <;>
      · -- A left move leaves us with a Grundy value of `k ^^^ m` for `k < n`, or
        -- `n ^^^ k` for `k < m`.
        refine' fun a => leftMovesNimRecOn a fun ok hk => _
        obtain ⟨k, rfl⟩ := Ordinal.lt_omega.1 (hk.trans (Ordinal.nat_lt_omega _))
        

Proof state:
case h.h.refine_1.hr.intro
n : ℕ
hn : ∀ m < n, ∀ (m_1 : ℕ), grundyValue (nim ↑m + nim ↑m_1) = ↑(m ^^^ m_1)
m : ℕ
hm : ∀ m_1 < m, grundyValue (nim ↑n + nim ↑m_1) = ↑(n ^^^ m_1)
i : LeftMoves (nim ↑n + nim ↑m)
a : LeftMoves (nim ↑m)
k : ℕ
hk : ↑k < ↑m
⊢ grundyValue (moveLeft (nim ↑n + nim ↑m) (toLeftMovesAdd (Sum.inr (toLeftMovesNim { val := ↑k, property := hk })))) ≠
    ↑(n ^^^ m)

Next tactic:
simp only [add_moveLeft_inl, add_moveLeft_inr, moveLeft_nim', Equiv.symm_apply_apply]
/-- The Grundy value of the sum of two nim games with natural numbers of piles equals their bitwise
xor. -/
@[simp]
theorem grundyValue_nim_add_nim (n m : ℕ) :
    grundyValue (nim.{u} n + nim.{u} m) = n ^^^ m := by
  -- We do strong induction on both variables.
  induction' n using Nat.strong_induction_on with n hn generalizing m
  induction' m using Nat.strong_induction_on with m hm
  rw [grundyValue_eq_mex_left]
  refine (Ordinal.mex_le_of_ne.{u, u} fun i => ?_).antisymm
    (Ordinal.le_mex_of_forall fun ou hu => ?_)
  -- The Grundy value `n ^^^ m` can't be reached by left moves.
  · apply leftMoves_add_cases i <;>
      · -- A left move leaves us with a Grundy value of `k ^^^ m` for `k < n`, or
        -- `n ^^^ k` for `k < m`.
        refine' fun a => leftMovesNimRecOn a fun ok hk => _
        obtain ⟨k, rfl⟩ := Ordinal.lt_omega.1 (hk.trans (Ordinal.nat_lt_omega _))
        simp only [add_moveLeft_inl, add_moveLeft_inr, moveLeft_nim', Equiv.symm_apply_apply]
        -- The inequality follows from injectivity.
        

Proof state:
case h.h.refine_1.hr.intro
n : ℕ
hn : ∀ m < n, ∀ (m_1 : ℕ), grundyValue (nim ↑m + nim ↑m_1) = ↑(m ^^^ m_1)
m : ℕ
hm : ∀ m_1 < m, grundyValue (nim ↑n + nim ↑m_1) = ↑(n ^^^ m_1)
i : LeftMoves (nim ↑n + nim ↑m)
a : LeftMoves (nim ↑m)
k : ℕ
hk : ↑k < ↑m
⊢ grundyValue (nim ↑n + nim ↑k) ≠ ↑(n ^^^ m)

Next tactic:
rw [nat_cast_lt] at hk
/-- The Grundy value of the sum of two nim games with natural numbers of piles equals their bitwise
xor. -/
@[simp]
theorem grundyValue_nim_add_nim (n m : ℕ) :
    grundyValue (nim.{u} n + nim.{u} m) = n ^^^ m := by
  -- We do strong induction on both variables.
  induction' n using Nat.strong_induction_on with n hn generalizing m
  induction' m using Nat.strong_induction_on with m hm
  rw [grundyValue_eq_mex_left]
  refine (Ordinal.mex_le_of_ne.{u, u} fun i => ?_).antisymm
    (Ordinal.le_mex_of_forall fun ou hu => ?_)
  -- The Grundy value `n ^^^ m` can't be reached by left moves.
  · apply leftMoves_add_cases i <;>
      · -- A left move leaves us with a Grundy value of `k ^^^ m` for `k < n`, or
        -- `n ^^^ k` for `k < m`.
        refine' fun a => leftMovesNimRecOn a fun ok hk => _
        obtain ⟨k, rfl⟩ := Ordinal.lt_omega.1 (hk.trans (Ordinal.nat_lt_omega _))
        simp only [add_moveLeft_inl, add_moveLeft_inr, moveLeft_nim', Equiv.symm_apply_apply]
        -- The inequality follows from injectivity.
        rw [nat_cast_lt] at hk
        

Proof state:
case h.h.refine_1.hr.intro
n : ℕ
hn : ∀ m < n, ∀ (m_1 : ℕ), grundyValue (nim ↑m + nim ↑m_1) = ↑(m ^^^ m_1)
m : ℕ
hm : ∀ m_1 < m, grundyValue (nim ↑n + nim ↑m_1) = ↑(n ^^^ m_1)
i : LeftMoves (nim ↑n + nim ↑m)
a : LeftMoves (nim ↑m)
k : ℕ
hk : k < m
⊢ grundyValue (nim ↑n + nim ↑k) ≠ ↑(n ^^^ m)

Next tactic:
first
        | rw [hn _ hk]
        | rw [hm _ hk]
/-- The Grundy value of the sum of two nim games with natural numbers of piles equals their bitwise
xor. -/
@[simp]
theorem grundyValue_nim_add_nim (n m : ℕ) :
    grundyValue (nim.{u} n + nim.{u} m) = n ^^^ m := by
  -- We do strong induction on both variables.
  induction' n using Nat.strong_induction_on with n hn generalizing m
  induction' m using Nat.strong_induction_on with m hm
  rw [grundyValue_eq_mex_left]
  refine (Ordinal.mex_le_of_ne.{u, u} fun i => ?_).antisymm
    (Ordinal.le_mex_of_forall fun ou hu => ?_)
  -- The Grundy value `n ^^^ m` can't be reached by left moves.
  · apply leftMoves_add_cases i <;>
      · -- A left move leaves us with a Grundy value of `k ^^^ m` for `k < n`, or
        -- `n ^^^ k` for `k < m`.
        refine' fun a => leftMovesNimRecOn a fun ok hk => _
        obtain ⟨k, rfl⟩ := Ordinal.lt_omega.1 (hk.trans (Ordinal.nat_lt_omega _))
        simp only [add_moveLeft_inl, add_moveLeft_inr, moveLeft_nim', Equiv.symm_apply_apply]
        -- The inequality follows from injectivity.
        rw [nat_cast_lt] at hk
        first
        | 

Proof state:
case h.h.refine_1.hr.intro
n : ℕ
hn : ∀ m < n, ∀ (m_1 : ℕ), grundyValue (nim ↑m + nim ↑m_1) = ↑(m ^^^ m_1)
m : ℕ
hm : ∀ m_1 < m, grundyValue (nim ↑n + nim ↑m_1) = ↑(n ^^^ m_1)
i : LeftMoves (nim ↑n + nim ↑m)
a : LeftMoves (nim ↑m)
k : ℕ
hk : k < m
⊢ grundyValue (nim ↑n + nim ↑k) ≠ ↑(n ^^^ m)

Next tactic:
rw [hn _ hk]
/-- The Grundy value of the sum of two nim games with natural numbers of piles equals their bitwise
xor. -/
@[simp]
theorem grundyValue_nim_add_nim (n m : ℕ) :
    grundyValue (nim.{u} n + nim.{u} m) = n ^^^ m := by
  -- We do strong induction on both variables.
  induction' n using Nat.strong_induction_on with n hn generalizing m
  induction' m using Nat.strong_induction_on with m hm
  rw [grundyValue_eq_mex_left]
  refine (Ordinal.mex_le_of_ne.{u, u} fun i => ?_).antisymm
    (Ordinal.le_mex_of_forall fun ou hu => ?_)
  -- The Grundy value `n ^^^ m` can't be reached by left moves.
  · apply leftMoves_add_cases i <;>
      · -- A left move leaves us with a Grundy value of `k ^^^ m` for `k < n`, or
        -- `n ^^^ k` for `k < m`.
        refine' fun a => leftMovesNimRecOn a fun ok hk => _
        obtain ⟨k, rfl⟩ := Ordinal.lt_omega.1 (hk.trans (Ordinal.nat_lt_omega _))
        simp only [add_moveLeft_inl, add_moveLeft_inr, moveLeft_nim', Equiv.symm_apply_apply]
        -- The inequality follows from injectivity.
        rw [nat_cast_lt] at hk
        first
        | rw [hn _ hk]
        | 

Proof state:
case h.h.refine_1.hr.intro
n : ℕ
hn : ∀ m < n, ∀ (m_1 : ℕ), grundyValue (nim ↑m + nim ↑m_1) = ↑(m ^^^ m_1)
m : ℕ
hm : ∀ m_1 < m, grundyValue (nim ↑n + nim ↑m_1) = ↑(n ^^^ m_1)
i : LeftMoves (nim ↑n + nim ↑m)
a : LeftMoves (nim ↑m)
k : ℕ
hk : k < m
⊢ grundyValue (nim ↑n + nim ↑k) ≠ ↑(n ^^^ m)

Next tactic:
rw [hm _ hk]
/-- The Grundy value of the sum of two nim games with natural numbers of piles equals their bitwise
xor. -/
@[simp]
theorem grundyValue_nim_add_nim (n m : ℕ) :
    grundyValue (nim.{u} n + nim.{u} m) = n ^^^ m := by
  -- We do strong induction on both variables.
  induction' n using Nat.strong_induction_on with n hn generalizing m
  induction' m using Nat.strong_induction_on with m hm
  rw [grundyValue_eq_mex_left]
  refine (Ordinal.mex_le_of_ne.{u, u} fun i => ?_).antisymm
    (Ordinal.le_mex_of_forall fun ou hu => ?_)
  -- The Grundy value `n ^^^ m` can't be reached by left moves.
  · apply leftMoves_add_cases i <;>
      · -- A left move leaves us with a Grundy value of `k ^^^ m` for `k < n`, or
        -- `n ^^^ k` for `k < m`.
        refine' fun a => leftMovesNimRecOn a fun ok hk => _
        obtain ⟨k, rfl⟩ := Ordinal.lt_omega.1 (hk.trans (Ordinal.nat_lt_omega _))
        simp only [add_moveLeft_inl, add_moveLeft_inr, moveLeft_nim', Equiv.symm_apply_apply]
        -- The inequality follows from injectivity.
        rw [nat_cast_lt] at hk
        first
        | rw [hn _ hk]
        | rw [hm _ hk]
        

Proof state:
case h.h.refine_1.hr.intro
n : ℕ
hn : ∀ m < n, ∀ (m_1 : ℕ), grundyValue (nim ↑m + nim ↑m_1) = ↑(m ^^^ m_1)
m : ℕ
hm : ∀ m_1 < m, grundyValue (nim ↑n + nim ↑m_1) = ↑(n ^^^ m_1)
i : LeftMoves (nim ↑n + nim ↑m)
a : LeftMoves (nim ↑m)
k : ℕ
hk : k < m
⊢ ↑(n ^^^ k) ≠ ↑(n ^^^ m)

Next tactic:
refine' fun h => hk.ne _
/-- The Grundy value of the sum of two nim games with natural numbers of piles equals their bitwise
xor. -/
@[simp]
theorem grundyValue_nim_add_nim (n m : ℕ) :
    grundyValue (nim.{u} n + nim.{u} m) = n ^^^ m := by
  -- We do strong induction on both variables.
  induction' n using Nat.strong_induction_on with n hn generalizing m
  induction' m using Nat.strong_induction_on with m hm
  rw [grundyValue_eq_mex_left]
  refine (Ordinal.mex_le_of_ne.{u, u} fun i => ?_).antisymm
    (Ordinal.le_mex_of_forall fun ou hu => ?_)
  -- The Grundy value `n ^^^ m` can't be reached by left moves.
  · apply leftMoves_add_cases i <;>
      · -- A left move leaves us with a Grundy value of `k ^^^ m` for `k < n`, or
        -- `n ^^^ k` for `k < m`.
        refine' fun a => leftMovesNimRecOn a fun ok hk => _
        obtain ⟨k, rfl⟩ := Ordinal.lt_omega.1 (hk.trans (Ordinal.nat_lt_omega _))
        simp only [add_moveLeft_inl, add_moveLeft_inr, moveLeft_nim', Equiv.symm_apply_apply]
        -- The inequality follows from injectivity.
        rw [nat_cast_lt] at hk
        first
        | rw [hn _ hk]
        | rw [hm _ hk]
        refine' fun h => hk.ne _
        

Proof state:
case h.h.refine_1.hr.intro
n : ℕ
hn : ∀ m < n, ∀ (m_1 : ℕ), grundyValue (nim ↑m + nim ↑m_1) = ↑(m ^^^ m_1)
m : ℕ
hm : ∀ m_1 < m, grundyValue (nim ↑n + nim ↑m_1) = ↑(n ^^^ m_1)
i : LeftMoves (nim ↑n + nim ↑m)
a : LeftMoves (nim ↑m)
k : ℕ
hk : k < m
h : ↑(n ^^^ k) = ↑(n ^^^ m)
⊢ k = m

Next tactic:
rw [Ordinal.nat_cast_inj] at h
/-- The Grundy value of the sum of two nim games with natural numbers of piles equals their bitwise
xor. -/
@[simp]
theorem grundyValue_nim_add_nim (n m : ℕ) :
    grundyValue (nim.{u} n + nim.{u} m) = n ^^^ m := by
  -- We do strong induction on both variables.
  induction' n using Nat.strong_induction_on with n hn generalizing m
  induction' m using Nat.strong_induction_on with m hm
  rw [grundyValue_eq_mex_left]
  refine (Ordinal.mex_le_of_ne.{u, u} fun i => ?_).antisymm
    (Ordinal.le_mex_of_forall fun ou hu => ?_)
  -- The Grundy value `n ^^^ m` can't be reached by left moves.
  · apply leftMoves_add_cases i <;>
      · -- A left move leaves us with a Grundy value of `k ^^^ m` for `k < n`, or
        -- `n ^^^ k` for `k < m`.
        refine' fun a => leftMovesNimRecOn a fun ok hk => _
        obtain ⟨k, rfl⟩ := Ordinal.lt_omega.1 (hk.trans (Ordinal.nat_lt_omega _))
        simp only [add_moveLeft_inl, add_moveLeft_inr, moveLeft_nim', Equiv.symm_apply_apply]
        -- The inequality follows from injectivity.
        rw [nat_cast_lt] at hk
        first
        | rw [hn _ hk]
        | rw [hm _ hk]
        refine' fun h => hk.ne _
        rw [Ordinal.nat_cast_inj] at h
        

Proof state:
case h.h.refine_1.hr.intro
n : ℕ
hn : ∀ m < n, ∀ (m_1 : ℕ), grundyValue (nim ↑m + nim ↑m_1) = ↑(m ^^^ m_1)
m : ℕ
hm : ∀ m_1 < m, grundyValue (nim ↑n + nim ↑m_1) = ↑(n ^^^ m_1)
i : LeftMoves (nim ↑n + nim ↑m)
a : LeftMoves (nim ↑m)
k : ℕ
hk : k < m
h : n ^^^ k = n ^^^ m
⊢ k = m

Next tactic:
first
        | rwa [Nat.xor_left_inj] at h
        | rwa [Nat.xor_right_inj] at h
/-- The Grundy value of the sum of two nim games with natural numbers of piles equals their bitwise
xor. -/
@[simp]
theorem grundyValue_nim_add_nim (n m : ℕ) :
    grundyValue (nim.{u} n + nim.{u} m) = n ^^^ m := by
  -- We do strong induction on both variables.
  induction' n using Nat.strong_induction_on with n hn generalizing m
  induction' m using Nat.strong_induction_on with m hm
  rw [grundyValue_eq_mex_left]
  refine (Ordinal.mex_le_of_ne.{u, u} fun i => ?_).antisymm
    (Ordinal.le_mex_of_forall fun ou hu => ?_)
  -- The Grundy value `n ^^^ m` can't be reached by left moves.
  · apply leftMoves_add_cases i <;>
      · -- A left move leaves us with a Grundy value of `k ^^^ m` for `k < n`, or
        -- `n ^^^ k` for `k < m`.
        refine' fun a => leftMovesNimRecOn a fun ok hk => _
        obtain ⟨k, rfl⟩ := Ordinal.lt_omega.1 (hk.trans (Ordinal.nat_lt_omega _))
        simp only [add_moveLeft_inl, add_moveLeft_inr, moveLeft_nim', Equiv.symm_apply_apply]
        -- The inequality follows from injectivity.
        rw [nat_cast_lt] at hk
        first
        | rw [hn _ hk]
        | rw [hm _ hk]
        refine' fun h => hk.ne _
        rw [Ordinal.nat_cast_inj] at h
        first
        | 

Proof state:
case h.h.refine_1.hr.intro
n : ℕ
hn : ∀ m < n, ∀ (m_1 : ℕ), grundyValue (nim ↑m + nim ↑m_1) = ↑(m ^^^ m_1)
m : ℕ
hm : ∀ m_1 < m, grundyValue (nim ↑n + nim ↑m_1) = ↑(n ^^^ m_1)
i : LeftMoves (nim ↑n + nim ↑m)
a : LeftMoves (nim ↑m)
k : ℕ
hk : k < m
h : n ^^^ k = n ^^^ m
⊢ k = m

Next tactic:
rwa [Nat.xor_left_inj] at h
/-- The Grundy value of the sum of two nim games with natural numbers of piles equals their bitwise
xor. -/
@[simp]
theorem grundyValue_nim_add_nim (n m : ℕ) :
    grundyValue (nim.{u} n + nim.{u} m) = n ^^^ m := by
  -- We do strong induction on both variables.
  induction' n using Nat.strong_induction_on with n hn generalizing m
  induction' m using Nat.strong_induction_on with m hm
  rw [grundyValue_eq_mex_left]
  refine (Ordinal.mex_le_of_ne.{u, u} fun i => ?_).antisymm
    (Ordinal.le_mex_of_forall fun ou hu => ?_)
  -- The Grundy value `n ^^^ m` can't be reached by left moves.
  · apply leftMoves_add_cases i <;>
      · -- A left move leaves us with a Grundy value of `k ^^^ m` for `k < n`, or
        -- `n ^^^ k` for `k < m`.
        refine' fun a => leftMovesNimRecOn a fun ok hk => _
        obtain ⟨k, rfl⟩ := Ordinal.lt_omega.1 (hk.trans (Ordinal.nat_lt_omega _))
        simp only [add_moveLeft_inl, add_moveLeft_inr, moveLeft_nim', Equiv.symm_apply_apply]
        -- The inequality follows from injectivity.
        rw [nat_cast_lt] at hk
        first
        | rw [hn _ hk]
        | rw [hm _ hk]
        refine' fun h => hk.ne _
        rw [Ordinal.nat_cast_inj] at h
        first
        | rwa [Nat.xor_left_inj] at h
        | 

Proof state:
case h.h.refine_1.hr.intro
n : ℕ
hn : ∀ m < n, ∀ (m_1 : ℕ), grundyValue (nim ↑m + nim ↑m_1) = ↑(m ^^^ m_1)
m : ℕ
hm : ∀ m_1 < m, grundyValue (nim ↑n + nim ↑m_1) = ↑(n ^^^ m_1)
i : LeftMoves (nim ↑n + nim ↑m)
a : LeftMoves (nim ↑m)
k : ℕ
hk : k < m
h : n ^^^ k = n ^^^ m
⊢ k = m

Next tactic:
rwa [Nat.xor_right_inj] at h
/-- The Grundy value of the sum of two nim games with natural numbers of piles equals their bitwise
xor. -/
@[simp]
theorem grundyValue_nim_add_nim (n m : ℕ) :
    grundyValue (nim.{u} n + nim.{u} m) = n ^^^ m := by
  -- We do strong induction on both variables.
  induction' n using Nat.strong_induction_on with n hn generalizing m
  induction' m using Nat.strong_induction_on with m hm
  rw [grundyValue_eq_mex_left]
  refine (Ordinal.mex_le_of_ne.{u, u} fun i => ?_).antisymm
    (Ordinal.le_mex_of_forall fun ou hu => ?_)
  -- The Grundy value `n ^^^ m` can't be reached by left moves.
  · apply leftMoves_add_cases i <;>
      · -- A left move leaves us with a Grundy value of `k ^^^ m` for `k < n`, or
        -- `n ^^^ k` for `k < m`.
        refine' fun a => leftMovesNimRecOn a fun ok hk => _
        obtain ⟨k, rfl⟩ := Ordinal.lt_omega.1 (hk.trans (Ordinal.nat_lt_omega _))
        simp only [add_moveLeft_inl, add_moveLeft_inr, moveLeft_nim', Equiv.symm_apply_apply]
        -- The inequality follows from injectivity.
        rw [nat_cast_lt] at hk
        first
        | rw [hn _ hk]
        | rw [hm _ hk]
        refine' fun h => hk.ne _
        rw [Ordinal.nat_cast_inj] at h
        first
        | rwa [Nat.xor_left_inj] at h
        | rwa [Nat.xor_right_inj] at h
  -- Every other smaller Grundy value can be reached by left moves.
  · -- If `u < m ^^^ n`, then either `u ^^^ n < m` or `u ^^^ m < n`.
    

Proof state:
case h.h.refine_2
n : ℕ
hn : ∀ m < n, ∀ (m_1 : ℕ), grundyValue (nim ↑m + nim ↑m_1) = ↑(m ^^^ m_1)
m : ℕ
hm : ∀ m_1 < m, grundyValue (nim ↑n + nim ↑m_1) = ↑(n ^^^ m_1)
ou : Ordinal.{u}
hu : ou < ↑(n ^^^ m)
⊢ ∃ i, grundyValue (moveLeft (nim ↑n + nim ↑m) i) = ou

Next tactic:
obtain ⟨u, rfl⟩ := Ordinal.lt_omega.1 (hu.trans (Ordinal.nat_lt_omega _))
/-- The Grundy value of the sum of two nim games with natural numbers of piles equals their bitwise
xor. -/
@[simp]
theorem grundyValue_nim_add_nim (n m : ℕ) :
    grundyValue (nim.{u} n + nim.{u} m) = n ^^^ m := by
  -- We do strong induction on both variables.
  induction' n using Nat.strong_induction_on with n hn generalizing m
  induction' m using Nat.strong_induction_on with m hm
  rw [grundyValue_eq_mex_left]
  refine (Ordinal.mex_le_of_ne.{u, u} fun i => ?_).antisymm
    (Ordinal.le_mex_of_forall fun ou hu => ?_)
  -- The Grundy value `n ^^^ m` can't be reached by left moves.
  · apply leftMoves_add_cases i <;>
      · -- A left move leaves us with a Grundy value of `k ^^^ m` for `k < n`, or
        -- `n ^^^ k` for `k < m`.
        refine' fun a => leftMovesNimRecOn a fun ok hk => _
        obtain ⟨k, rfl⟩ := Ordinal.lt_omega.1 (hk.trans (Ordinal.nat_lt_omega _))
        simp only [add_moveLeft_inl, add_moveLeft_inr, moveLeft_nim', Equiv.symm_apply_apply]
        -- The inequality follows from injectivity.
        rw [nat_cast_lt] at hk
        first
        | rw [hn _ hk]
        | rw [hm _ hk]
        refine' fun h => hk.ne _
        rw [Ordinal.nat_cast_inj] at h
        first
        | rwa [Nat.xor_left_inj] at h
        | rwa [Nat.xor_right_inj] at h
  -- Every other smaller Grundy value can be reached by left moves.
  · -- If `u < m ^^^ n`, then either `u ^^^ n < m` or `u ^^^ m < n`.
    obtain ⟨u, rfl⟩ := Ordinal.lt_omega.1 (hu.trans (Ordinal.nat_lt_omega _))
    

Proof state:
case h.h.refine_2.intro
n : ℕ
hn : ∀ m < n, ∀ (m_1 : ℕ), grundyValue (nim ↑m + nim ↑m_1) = ↑(m ^^^ m_1)
m : ℕ
hm : ∀ m_1 < m, grundyValue (nim ↑n + nim ↑m_1) = ↑(n ^^^ m_1)
u : ℕ
hu : ↑u < ↑(n ^^^ m)
⊢ ∃ i, grundyValue (moveLeft (nim ↑n + nim ↑m) i) = ↑u

Next tactic:
replace hu := Ordinal.nat_cast_lt.1 hu
/-- The Grundy value of the sum of two nim games with natural numbers of piles equals their bitwise
xor. -/
@[simp]
theorem grundyValue_nim_add_nim (n m : ℕ) :
    grundyValue (nim.{u} n + nim.{u} m) = n ^^^ m := by
  -- We do strong induction on both variables.
  induction' n using Nat.strong_induction_on with n hn generalizing m
  induction' m using Nat.strong_induction_on with m hm
  rw [grundyValue_eq_mex_left]
  refine (Ordinal.mex_le_of_ne.{u, u} fun i => ?_).antisymm
    (Ordinal.le_mex_of_forall fun ou hu => ?_)
  -- The Grundy value `n ^^^ m` can't be reached by left moves.
  · apply leftMoves_add_cases i <;>
      · -- A left move leaves us with a Grundy value of `k ^^^ m` for `k < n`, or
        -- `n ^^^ k` for `k < m`.
        refine' fun a => leftMovesNimRecOn a fun ok hk => _
        obtain ⟨k, rfl⟩ := Ordinal.lt_omega.1 (hk.trans (Ordinal.nat_lt_omega _))
        simp only [add_moveLeft_inl, add_moveLeft_inr, moveLeft_nim', Equiv.symm_apply_apply]
        -- The inequality follows from injectivity.
        rw [nat_cast_lt] at hk
        first
        | rw [hn _ hk]
        | rw [hm _ hk]
        refine' fun h => hk.ne _
        rw [Ordinal.nat_cast_inj] at h
        first
        | rwa [Nat.xor_left_inj] at h
        | rwa [Nat.xor_right_inj] at h
  -- Every other smaller Grundy value can be reached by left moves.
  · -- If `u < m ^^^ n`, then either `u ^^^ n < m` or `u ^^^ m < n`.
    obtain ⟨u, rfl⟩ := Ordinal.lt_omega.1 (hu.trans (Ordinal.nat_lt_omega _))
    replace hu := Ordinal.nat_cast_lt.1 hu
    

Proof state:
case h.h.refine_2.intro
n : ℕ
hn : ∀ m < n, ∀ (m_1 : ℕ), grundyValue (nim ↑m + nim ↑m_1) = ↑(m ^^^ m_1)
m : ℕ
hm : ∀ m_1 < m, grundyValue (nim ↑n + nim ↑m_1) = ↑(n ^^^ m_1)
u : ℕ
hu : u < n ^^^ m
⊢ ∃ i, grundyValue (moveLeft (nim ↑n + nim ↑m) i) = ↑u

Next tactic:
cases' Nat.lt_xor_cases hu with h h