diff --git a/Mathlib/SetTheory/Game/Basic.lean b/Mathlib/SetTheory/Game/Basic.lean index f6b0a8b89caf6..2b0f180c57c65 100644 --- a/Mathlib/SetTheory/Game/Basic.lean +++ b/Mathlib/SetTheory/Game/Basic.lean @@ -1,7 +1,7 @@ /- Copyright (c) 2019 Mario Carneiro. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. -Authors: Reid Barton, Mario Carneiro, Isabel Longbottom, Kim Morrison, Apurva Nakade +Authors: Reid Barton, Mario Carneiro, Isabel Longbottom, Kim Morrison, Apurva Nakade, Yuyang Zhao -/ import Mathlib.Algebra.Order.Group.Defs import Mathlib.Algebra.Ring.Int @@ -28,8 +28,6 @@ namespace SetTheory open Function PGame -open PGame - universe u -- Porting note: moved the setoid instance to PGame.lean @@ -238,7 +236,7 @@ theorem quot_natCast : ∀ n : ℕ, ⟦(n : PGame)⟧ = (n : Game) theorem quot_eq_of_mk'_quot_eq {x y : PGame} (L : x.LeftMoves ≃ y.LeftMoves) (R : x.RightMoves ≃ y.RightMoves) (hl : ∀ i, (⟦x.moveLeft i⟧ : Game) = ⟦y.moveLeft (L i)⟧) (hr : ∀ j, (⟦x.moveRight j⟧ : Game) = ⟦y.moveRight (R j)⟧) : (⟦x⟧ : Game) = ⟦y⟧ := - game_eq (equiv_of_mk_equiv L R (fun _ => equiv_iff_game_eq.2 (hl _)) + game_eq (.of_equiv L R (fun _ => equiv_iff_game_eq.2 (hl _)) (fun _ => equiv_iff_game_eq.2 (hr _))) /-! Multiplicative operations can be defined at the level of pre-games, @@ -341,25 +339,25 @@ theorem mul_moveRight_inr {x y : PGame} {i j} : cases y rfl --- @[simp] -- Porting note: simpNF linter complains +@[simp] theorem neg_mk_mul_moveLeft_inl {xl xr yl yr} {xL xR yL yR} {i j} : (-(mk xl xr xL xR * mk yl yr yL yR)).moveLeft (Sum.inl (i, j)) = -(xL i * mk yl yr yL yR + mk xl xr xL xR * yR j - xL i * yR j) := rfl --- @[simp] -- Porting note: simpNF linter complains +@[simp] theorem neg_mk_mul_moveLeft_inr {xl xr yl yr} {xL xR yL yR} {i j} : (-(mk xl xr xL xR * mk yl yr yL yR)).moveLeft (Sum.inr (i, j)) = -(xR i * mk yl yr yL yR + mk xl xr xL xR * yL j - xR i * yL j) := rfl --- @[simp] -- Porting note: simpNF linter complains +@[simp] theorem neg_mk_mul_moveRight_inl {xl xr yl yr} {xL xR yL yR} {i j} : (-(mk xl xr xL xR * mk yl yr yL yR)).moveRight (Sum.inl (i, j)) = -(xL i * mk yl yr yL yR + mk xl xr xL xR * yL j - xL i * yL j) := rfl --- @[simp] -- Porting note: simpNF linter complains +@[simp] theorem neg_mk_mul_moveRight_inr {xl xr yl yr} {xL xR yL yR} {i j} : (-(mk xl xr xL xR * mk yl yr yL yR)).moveRight (Sum.inr (i, j)) = -(xR i * mk yl yr yL yR + mk xl xr xL xR * yR j - xR i * yR j) := @@ -381,6 +379,17 @@ theorem rightMoves_mul_cases {x y : PGame} (k) {P : (x * y).RightMoves → Prop} · apply hl · apply hr +/-- `x * y` and `y * x` have the same moves. -/ +protected lemma mul_comm (x y : PGame) : x * y ≡ y * x := + match x, y with + | ⟨xl, xr, xL, xR⟩, ⟨yl, yr, yL, yR⟩ => by + refine Identical.of_equiv ((Equiv.prodComm _ _).sumCongr (Equiv.prodComm _ _)) + ((Equiv.sumComm _ _).trans ((Equiv.prodComm _ _).sumCongr (Equiv.prodComm _ _))) ?_ ?_ <;> + · rintro (⟨_, _⟩ | ⟨_, _⟩) <;> + exact ((((PGame.mul_comm _ (mk _ _ _ _)).add (PGame.mul_comm (mk _ _ _ _) _)).trans + (PGame.add_comm _ _)).sub (PGame.mul_comm _ _)) + termination_by (x, y) + /-- `x * y` and `y * x` have the same moves. -/ def mulCommRelabelling (x y : PGame.{u}) : x * y ≡r y * x := match x, y with @@ -416,6 +425,9 @@ instance isEmpty_rightMoves_mul (x y : PGame.{u}) cases y assumption +/-- `x * 0` has exactly the same moves as `0`. -/ +protected lemma mul_zero (x : PGame) : x * 0 ≡ 0 := identical_zero _ + /-- `x * 0` has exactly the same moves as `0`. -/ def mulZeroRelabelling (x : PGame) : x * 0 ≡r 0 := Relabelling.isEmpty _ @@ -428,6 +440,9 @@ theorem mul_zero_equiv (x : PGame) : x * 0 ≈ 0 := theorem quot_mul_zero (x : PGame) : (⟦x * 0⟧ : Game) = 0 := game_eq x.mul_zero_equiv +/-- `0 * x` has exactly the same moves as `0`. -/ +protected lemma zero_mul (x : PGame) : 0 * x ≡ 0 := identical_zero _ + /-- `0 * x` has exactly the same moves as `0`. -/ def zeroMulRelabelling (x : PGame) : 0 * x ≡r 0 := Relabelling.isEmpty _ @@ -459,6 +474,40 @@ def negMulRelabelling (x y : PGame.{u}) : -x * y ≡r -(x * y) := exact (negMulRelabelling _ _).symm termination_by (x, y) +/-- `x * -y` and `-(x * y)` have the same moves. -/ +@[simp] +lemma mul_neg (x y : PGame) : x * -y = -(x * y) := + match x, y with + | mk xl xr xL xR, mk yl yr yL yR => by + refine ext rfl rfl ?_ ?_ + · rintro (⟨i, j⟩ | ⟨i, j⟩) _ ⟨rfl⟩ + · refine (@mul_moveLeft_inl (mk xl xr xL xR) (-mk yl yr yL yR) i j).trans ?_ + dsimp + rw [PGame.neg_sub', PGame.neg_add] + congr + exacts [mul_neg _ (mk _ _ _ _), mul_neg _ _, mul_neg _ _] + · refine (@mul_moveLeft_inr (mk xl xr xL xR) (-mk yl yr yL yR) i j).trans ?_ + dsimp + rw [PGame.neg_sub', PGame.neg_add] + congr + exacts [mul_neg _ (mk _ _ _ _), mul_neg _ _, mul_neg _ _] + · rintro (⟨i, j⟩ | ⟨i, j⟩) _ ⟨rfl⟩ + · refine (@mul_moveRight_inl (mk xl xr xL xR) (-mk yl yr yL yR) i j).trans ?_ + dsimp + rw [PGame.neg_sub', PGame.neg_add] + congr + exacts [mul_neg _ (mk _ _ _ _), mul_neg _ _, mul_neg _ _] + · refine (@mul_moveRight_inr (mk xl xr xL xR) (-mk yl yr yL yR) i j).trans ?_ + dsimp + rw [PGame.neg_sub', PGame.neg_add] + congr + exacts [mul_neg _ (mk _ _ _ _), mul_neg _ _, mul_neg _ _] + termination_by (x, y) + +/-- `-x * y` and `-(x * y)` have the same moves. -/ +lemma neg_mul (x y : PGame) : -x * y ≡ -(x * y) := + ((PGame.mul_comm _ _).trans (of_eq (mul_neg _ _))).trans (PGame.mul_comm _ _).neg + @[simp] theorem quot_neg_mul (x y : PGame) : (⟦-x * y⟧ : Game) = -⟦x * y⟧ := game_eq (negMulRelabelling x y).equiv @@ -467,7 +516,6 @@ theorem quot_neg_mul (x y : PGame) : (⟦-x * y⟧ : Game) = -⟦x * y⟧ := def mulNegRelabelling (x y : PGame) : x * -y ≡r -(x * y) := (mulCommRelabelling x _).trans <| (negMulRelabelling _ x).trans (mulCommRelabelling y x).negCongr -@[simp] theorem quot_mul_neg (x y : PGame) : ⟦x * -y⟧ = (-⟦x * y⟧ : Game) := game_eq (mulNegRelabelling x y).equiv @@ -605,10 +653,22 @@ def mulOneRelabelling : ∀ x : PGame.{u}, x * 1 ≡r x (try rintro (⟨i, ⟨⟩⟩ | ⟨i, ⟨⟩⟩)) <;> { dsimp apply (Relabelling.subCongr (Relabelling.refl _) (mulZeroRelabelling _)).trans - rw [sub_zero] + rw [sub_zero_eq_add_zero] exact (addZeroRelabelling _).trans <| (((mulOneRelabelling _).addCongr (mulZeroRelabelling _)).trans <| addZeroRelabelling _) } +/-- `1 * x` has the same moves as `x`. -/ +protected lemma one_mul : ∀ (x : PGame), 1 * x ≡ x + | ⟨xl, xr, xL, xR⟩ => by + refine Identical.of_equiv ((Equiv.sumEmpty _ _).trans (Equiv.punitProd _)) + ((Equiv.sumEmpty _ _).trans (Equiv.punitProd _)) ?_ ?_ <;> + · rintro (⟨⟨⟩, _⟩ | ⟨⟨⟩, _⟩) + exact ((((PGame.zero_mul (mk _ _ _ _)).add (PGame.one_mul _)).trans (PGame.zero_add _)).sub + (PGame.zero_mul _)).trans (PGame.sub_zero _) + +/-- `x * 1` has the same moves as `x`. -/ +protected lemma mul_one (x : PGame) : x * 1 ≡ x := (x.mul_comm _).trans x.one_mul + @[simp] theorem quot_mul_one (x : PGame) : (⟦x * 1⟧ : Game) = ⟦x⟧ := game_eq <| PGame.Relabelling.equiv <| mulOneRelabelling x @@ -791,10 +851,7 @@ def mulOption (x y : PGame) (i : LeftMoves x) (j : LeftMoves y) : PGame := the first kind. -/ lemma mulOption_neg_neg {x} (y) {i j} : mulOption x y i j = mulOption x (-(-y)) i (toLeftMovesNeg <| toRightMovesNeg j) := by - dsimp only [mulOption] - congr 2 - · rw [neg_neg] - iterate 2 rw [moveLeft_neg, moveRight_neg, neg_neg] + simp [mulOption] /-- The left options of `x * y` agree with that of `y * x` up to equivalence. -/ lemma mulOption_symm (x y) {i j} : ⟦mulOption x y i j⟧ = (⟦mulOption y x j i⟧ : Game) := by @@ -924,6 +981,13 @@ def inv'Zero : inv' 0 ≡r 1 := by theorem inv'_zero_equiv : inv' 0 ≈ 1 := inv'Zero.equiv +/-- `inv' 1` has exactly the same moves as `1`. -/ +lemma inv'_one : inv' 1 ≡ 1 := by + rw [Identical.ext_iff] + constructor + · simp [memₗ_def, inv'] + · simp [memᵣ_def, inv'] + /-- `inv' 1` has exactly the same moves as `1`. -/ def inv'One : inv' 1 ≡r (1 : PGame.{u}) := by change Relabelling (mk _ _ _ _) 1 @@ -959,6 +1023,11 @@ theorem inv_eq_of_pos {x : PGame} (h : 0 < x) : x⁻¹ = inv' x := by theorem inv_eq_of_lf_zero {x : PGame} (h : x ⧏ 0) : x⁻¹ = -inv' (-x) := by classical exact (if_neg h.not_equiv).trans (if_neg h.not_gt) +/-- `1⁻¹` has exactly the same moves as `1`. -/ +lemma inv_one : 1⁻¹ ≡ 1 := by + rw [inv_eq_of_pos PGame.zero_lt_one] + exact inv'_one + /-- `1⁻¹` has exactly the same moves as `1`. -/ def invOne : 1⁻¹ ≡r 1 := by rw [inv_eq_of_pos PGame.zero_lt_one] diff --git a/Mathlib/SetTheory/Game/PGame.lean b/Mathlib/SetTheory/Game/PGame.lean index 94f9ebf8cd866..13572cc2afa35 100644 --- a/Mathlib/SetTheory/Game/PGame.lean +++ b/Mathlib/SetTheory/Game/PGame.lean @@ -1,7 +1,7 @@ /- Copyright (c) 2019 Mario Carneiro. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. -Authors: Reid Barton, Mario Carneiro, Isabel Longbottom, Kim Morrison +Authors: Reid Barton, Mario Carneiro, Isabel Longbottom, Kim Morrison, Yuyang Zhao -/ import Mathlib.Algebra.Order.ZeroLEOne import Mathlib.Data.List.InsertNth @@ -23,7 +23,8 @@ takes two types (thought of as indexing the possible moves for the players Left pair of functions out of these types to `SetTheory.PGame` (thought of as describing the resulting game after making a move). -Combinatorial games themselves, as a quotient of pregames, are constructed in `Game.lean`. +Combinatorial games themselves, as a quotient of pregames, are constructed in +`SetTheory.Game.Basic`. ## Conway induction @@ -148,6 +149,17 @@ theorem rightMoves_mk {xl xr xL xR} : (⟨xl, xr, xL, xR⟩ : PGame).RightMoves theorem moveRight_mk {xl xr xL xR} : (⟨xl, xr, xL, xR⟩ : PGame).moveRight = xR := rfl +lemma ext' {x y : PGame} (hl : x.LeftMoves = y.LeftMoves) (hr : x.RightMoves = y.RightMoves) + (hL : HEq x.moveLeft y.moveLeft) (hR : HEq x.moveRight y.moveRight) : + x = y := by + cases x; cases y; cases hl; cases hr; cases hL; cases hR; rfl + +lemma ext {x y : PGame} (hl : x.LeftMoves = y.LeftMoves) (hr : x.RightMoves = y.RightMoves) + (hL : ∀ i j, HEq i j → x.moveLeft i = y.moveLeft j) + (hR : ∀ i j, HEq i j → x.moveRight i = y.moveRight j) : + x = y := + ext' hl hr (hfunext hl (heq_of_eq <| hL · · ·)) (hfunext hr (heq_of_eq <| hR · · ·)) + -- TODO define this at the level of games, as well, and perhaps also for finsets of games. /-- Construct a pre-game from list of pre-games describing the available moves for Left and Right. -/ @@ -344,6 +356,152 @@ instance uniqueOneLeftMoves : Unique (LeftMoves 1) := instance isEmpty_one_rightMoves : IsEmpty (RightMoves 1) := instIsEmptyPEmpty +/-! ### Identity -/ + +/-- Two pre-games are identical if their left and right sets are identical. +That is, `Identical x y` if every left move of `x` is identical to some left move of `y`, +every right move of `x` is identical to some right move of `y`, and vice versa. -/ +def Identical : ∀ (_ _ : PGame.{u}), Prop + | mk _ _ xL xR, mk _ _ yL yR => + Relator.BiTotal (fun i j ↦ Identical (xL i) (yL j)) ∧ + Relator.BiTotal (fun i j ↦ Identical (xR i) (yR j)) + +@[inherit_doc] scoped infix:50 " ≡ " => PGame.Identical + +/-- `x ∈ₗ y` if `x` is identical to some left move of `y`. -/ +def memₗ (x y : PGame.{u}) : Prop := ∃ b, x ≡ y.moveLeft b + +/-- `x ∈ᵣ y` if `x` is identical to some right move of `y`. -/ +def memᵣ (x y : PGame.{u}) : Prop := ∃ b, x ≡ y.moveRight b + +@[inherit_doc] scoped infix:50 " ∈ₗ " => PGame.memₗ +@[inherit_doc] scoped infix:50 " ∈ᵣ " => PGame.memᵣ +@[inherit_doc PGame.memₗ] binder_predicate x " ∈ₗ " y:term => `($x ∈ₗ $y) +@[inherit_doc PGame.memᵣ] binder_predicate x " ∈ᵣ " y:term => `($x ∈ᵣ $y) + +theorem identical_iff : ∀ {x y : PGame}, x ≡ y ↔ + Relator.BiTotal (x.moveLeft · ≡ y.moveLeft ·) ∧ Relator.BiTotal (x.moveRight · ≡ y.moveRight ·) + | mk _ _ _ _, mk _ _ _ _ => Iff.rfl + +@[refl, simp] protected theorem Identical.refl (x) : x ≡ x := + PGame.recOn x fun _ _ _ _ IHL IHR ↦ ⟨Relator.BiTotal.refl IHL, Relator.BiTotal.refl IHR⟩ + +protected theorem Identical.rfl {x} : x ≡ x := Identical.refl x + +@[symm] protected theorem Identical.symm : ∀ {x y}, x ≡ y → y ≡ x + | mk _ _ _ _, mk _ _ _ _, ⟨hL, hR⟩ => ⟨hL.symm fun _ _ h ↦ h.symm, hR.symm fun _ _ h ↦ h.symm⟩ + +theorem identical_comm {x y} : x ≡ y ↔ y ≡ x := + ⟨Identical.symm, Identical.symm⟩ + +@[trans] protected theorem Identical.trans : ∀ {x y z}, x ≡ y → y ≡ z → x ≡ z + | mk _ _ _ _, mk _ _ _ _, mk _ _ _ _, ⟨hL₁, hR₁⟩, ⟨hL₂, hR₂⟩ => + ⟨hL₁.trans (fun _ _ _ h₁ h₂ ↦ h₁.trans h₂) hL₂, hR₁.trans (fun _ _ _ h₁ h₂ ↦ h₁.trans h₂) hR₂⟩ + +theorem identical_of_is_empty (x y : PGame) + [IsEmpty x.LeftMoves] [IsEmpty x.RightMoves] + [IsEmpty y.LeftMoves] [IsEmpty y.RightMoves] : x ≡ y := + identical_iff.2 <| by simp [Relator.BiTotal, Relator.LeftTotal, Relator.RightTotal] + +/-- `Identical` as a `Setoid`. -/ +def identicalSetoid : Setoid PGame := + ⟨Identical, Identical.refl, Identical.symm, Identical.trans⟩ + +instance : IsRefl PGame (· ≡ ·) := ⟨Identical.refl⟩ +instance : IsSymm PGame (· ≡ ·) := ⟨fun _ _ ↦ Identical.symm⟩ +instance : IsTrans PGame (· ≡ ·) := ⟨fun _ _ _ ↦ Identical.trans⟩ +instance : IsEquiv PGame (· ≡ ·) := { } + +/-- If `x` and `y` are identical, then a left move of `x` is identical to some left move of `y`. -/ +lemma Identical.moveLeft : ∀ {x y}, x ≡ y → + ∀ i, ∃ j, x.moveLeft i ≡ y.moveLeft j + | mk _ _ _ _, mk _ _ _ _, ⟨hl, _⟩, i => hl.1 i + +/-- If `x` and `y` are identical, then a left move of `y` is identical to some left move of `x`. -/ +lemma Identical.moveLeft_symm : ∀ {x y}, x ≡ y → + ∀ i, ∃ j, x.moveLeft j ≡ y.moveLeft i + | mk _ _ _ _, mk _ _ _ _, ⟨hl, _⟩, i => hl.2 i + +/-- If `x` and `y` are identical, then a right move of `x` is identical to some right move of `y`. +-/ +lemma Identical.moveRight : ∀ {x y}, x ≡ y → + ∀ i, ∃ j, x.moveRight i ≡ y.moveRight j + | mk _ _ _ _, mk _ _ _ _, ⟨_, hr⟩, i => hr.1 i + +/-- If `x` and `y` are identical, then a right move of `y` is identical to some right move of `x`. +-/ +lemma Identical.moveRight_symm : ∀ {x y}, x ≡ y → + ∀ i, ∃ j, x.moveRight j ≡ y.moveRight i + | mk _ _ _ _, mk _ _ _ _, ⟨_, hr⟩, i => hr.2 i + +theorem identical_iff' : ∀ {x y : PGame}, x ≡ y ↔ + ((∀ i, x.moveLeft i ∈ₗ y) ∧ (∀ j, y.moveLeft j ∈ₗ x)) ∧ + ((∀ i, x.moveRight i ∈ᵣ y) ∧ (∀ j, y.moveRight j ∈ᵣ x)) + | mk xl xr xL xR, mk yl yr yL yR => by + convert identical_iff <;> + dsimp [Relator.BiTotal, Relator.LeftTotal, Relator.RightTotal] <;> + congr! <;> + exact exists_congr <| fun _ ↦ identical_comm + +theorem memₗ_def {x y : PGame} : x ∈ₗ y ↔ ∃ b, x ≡ y.moveLeft b := .rfl + +theorem memᵣ_def {x y : PGame} : x ∈ᵣ y ↔ ∃ b, x ≡ y.moveRight b := .rfl + +theorem moveLeft_memₗ (x : PGame) (b) : x.moveLeft b ∈ₗ x := ⟨_, .rfl⟩ + +theorem moveRight_memᵣ (x : PGame) (b) : x.moveRight b ∈ᵣ x := ⟨_, .rfl⟩ + +theorem memₗ.congr_right : ∀ {x y : PGame}, + x ≡ y → (∀ {w : PGame}, w ∈ₗ x ↔ w ∈ₗ y) + | mk _ _ _ _, mk _ _ _ _, ⟨⟨h₁, h₂⟩, _⟩, _w => + ⟨fun ⟨i, hi⟩ ↦ (h₁ i).imp (fun _ ↦ hi.trans), + fun ⟨j, hj⟩ ↦ (h₂ j).imp (fun _ hi ↦ hj.trans hi.symm)⟩ + +theorem memᵣ.congr_right : ∀ {x y : PGame}, + x ≡ y → (∀ {w : PGame}, w ∈ᵣ x ↔ w ∈ᵣ y) + | mk _ _ _ _, mk _ _ _ _, ⟨_, ⟨h₁, h₂⟩⟩, _w => + ⟨fun ⟨i, hi⟩ ↦ (h₁ i).imp (fun _ ↦ hi.trans), + fun ⟨j, hj⟩ ↦ (h₂ j).imp (fun _ hi ↦ hj.trans hi.symm)⟩ + +theorem memₗ.congr_left : ∀ {x y : PGame}, + x ≡ y → (∀ {w : PGame}, x ∈ₗ w ↔ y ∈ₗ w) + | _, _, h, mk _ _ _ _ => ⟨fun ⟨i, hi⟩ ↦ ⟨i, h.symm.trans hi⟩, fun ⟨i, hi⟩ ↦ ⟨i, h.trans hi⟩⟩ + +theorem memᵣ.congr_left : ∀ {x y : PGame}, + x ≡ y → (∀ {w : PGame}, x ∈ᵣ w ↔ y ∈ᵣ w) + | _, _, h, mk _ _ _ _ => ⟨fun ⟨i, hi⟩ ↦ ⟨i, h.symm.trans hi⟩, fun ⟨i, hi⟩ ↦ ⟨i, h.trans hi⟩⟩ + +lemma Identical.ext : ∀ {x y}, (∀ z, z ∈ₗ x ↔ z ∈ₗ y) → (∀ z, z ∈ᵣ x ↔ z ∈ᵣ y) → x ≡ y + | mk _ _ _ _, mk _ _ _ _, hl, hr => identical_iff'.mpr + ⟨⟨fun i ↦ (hl _).mp ⟨i, refl _⟩, fun j ↦ (hl _).mpr ⟨j, refl _⟩⟩, + ⟨fun i ↦ (hr _).mp ⟨i, refl _⟩, fun j ↦ (hr _).mpr ⟨j, refl _⟩⟩⟩ + +lemma Identical.ext_iff {x y} : x ≡ y ↔ (∀ z, z ∈ₗ x ↔ z ∈ₗ y) ∧ (∀ z, z ∈ᵣ x ↔ z ∈ᵣ y) := + ⟨fun h ↦ ⟨@memₗ.congr_right _ _ h, @memᵣ.congr_right _ _ h⟩, fun h ↦ h.elim Identical.ext⟩ + +lemma Identical.congr_right {x y z} (h : x ≡ y) : z ≡ x ↔ z ≡ y := + ⟨fun hz ↦ hz.trans h, fun hz ↦ hz.trans h.symm⟩ + +lemma Identical.congr_left {x y z} (h : x ≡ y) : x ≡ z ↔ y ≡ z := + ⟨fun hz ↦ h.symm.trans hz, fun hz ↦ h.trans hz⟩ + +/-- Show `x ≡ y` by giving an explicit correspondence between the moves of `x` and `y`. -/ +lemma Identical.of_fn {x y : PGame} + (l : x.LeftMoves → y.LeftMoves) (il : y.LeftMoves → x.LeftMoves) + (r : x.RightMoves → y.RightMoves) (ir : y.RightMoves → x.RightMoves) + (hl : ∀ i, x.moveLeft i ≡ y.moveLeft (l i)) + (hil : ∀ i, x.moveLeft (il i) ≡ y.moveLeft i) + (hr : ∀ i, x.moveRight i ≡ y.moveRight (r i)) + (hir : ∀ i, x.moveRight (ir i) ≡ y.moveRight i) : x ≡ y := + identical_iff.mpr + ⟨⟨fun i ↦ ⟨l i, hl i⟩, fun i ↦ ⟨il i, hil i⟩⟩, ⟨fun i ↦ ⟨r i, hr i⟩, fun i ↦ ⟨ir i, hir i⟩⟩⟩ + +lemma Identical.of_equiv {x y : PGame} + (l : x.LeftMoves ≃ y.LeftMoves) (r : x.RightMoves ≃ y.RightMoves) + (hl : ∀ i, x.moveLeft i ≡ y.moveLeft (l i)) (hr : ∀ i, x.moveRight i ≡ y.moveRight (r i)) : + x ≡ y := + .of_fn l l.symm r r.symm hl (by simpa using hl <| l.symm ·) hr (by simpa using hr <| r.symm ·) + /-! ### Pre-game order relations -/ @@ -476,6 +634,13 @@ instance : Preorder PGame := le_trans_aux (fun {i} => (IHzl i).1) fun {j} => (IHyr j).2.1⟩ lt := fun x y => x ≤ y ∧ x ⧏ y } +lemma Identical.le : ∀ {x y}, x ≡ y → x ≤ y + | mk _ _ _ _, mk _ _ _ _, ⟨hL, hR⟩ => le_of_forall_lf + (fun i ↦ let ⟨_, hj⟩ := hL.1 i; lf_of_le_moveLeft hj.le) + (fun i ↦ let ⟨_, hj⟩ := hR.2 i; lf_of_moveRight_le hj.le) + +lemma Identical.ge {x y} (h : x ≡ y) : y ≤ x := h.symm.le + theorem lt_iff_le_and_lf {x y : PGame} : x < y ↔ x ≤ y ∧ x ⧏ y := Iff.rfl @@ -709,6 +874,8 @@ protected theorem equiv_comm {x y : PGame} : (x ≈ y) ↔ (y ≈ x) := theorem equiv_of_eq {x y : PGame} (h : x = y) : x ≈ y := by subst h; rfl +lemma Identical.equiv {x y} (h : x ≡ y) : x ≈ y := ⟨h.le, h.ge⟩ + @[trans] theorem le_of_le_of_equiv {x y z : PGame} (h₁ : x ≤ y) (h₂ : y ≈ z) : x ≤ z := h₁.trans h₂.1 @@ -822,12 +989,41 @@ theorem equiv_congr_right {x₁ x₂ : PGame} : (x₁ ≈ x₂) ↔ ∀ y₁, (x ⟨fun h _ => ⟨fun h' => Equiv.trans (Equiv.symm h) h', fun h' => Equiv.trans h h'⟩, fun h => (h x₂).2 <| equiv_rfl⟩ -theorem equiv_of_mk_equiv {x y : PGame} (L : x.LeftMoves ≃ y.LeftMoves) - (R : x.RightMoves ≃ y.RightMoves) (hl : ∀ i, x.moveLeft i ≈ y.moveLeft (L i)) - (hr : ∀ j, x.moveRight j ≈ y.moveRight (R j)) : x ≈ y := by +theorem Equiv.ext {x y : PGame} + (hl : Relator.BiTotal (fun i j ↦ x.moveLeft i ≈ y.moveLeft j)) + (hr : Relator.BiTotal (fun i j ↦ x.moveRight i ≈ y.moveRight j)) : x ≈ y := by constructor <;> rw [le_def] - · exact ⟨fun i => Or.inl ⟨_, (hl i).1⟩, fun j => Or.inr ⟨_, by simpa using (hr (R.symm j)).1⟩⟩ - · exact ⟨fun i => Or.inl ⟨_, by simpa using (hl (L.symm i)).2⟩, fun j => Or.inr ⟨_, (hr j).2⟩⟩ + · exact ⟨fun i ↦ .inl <| (hl.1 i).imp (fun _ ↦ (·.1)), + fun j ↦ .inr <| (hr.2 j).imp (fun _ ↦ (·.1))⟩ + · exact ⟨fun i ↦ .inl <| (hl.2 i).imp (fun _ ↦ (·.2)), + fun j ↦ .inr <| (hr.1 j).imp (fun _ ↦ (·.2))⟩ + +/-- Show `x ≈ y` by giving an explicit correspondence between the moves of `x` and `y`. -/ +lemma Equiv.of_fn {x y : PGame} + (l : x.LeftMoves → y.LeftMoves) (il : y.LeftMoves → x.LeftMoves) + (r : x.RightMoves → y.RightMoves) (ir : y.RightMoves → x.RightMoves) + (hl : ∀ i, x.moveLeft i ≈ y.moveLeft (l i)) + (hil : ∀ i, x.moveLeft (il i) ≈ y.moveLeft i) + (hr : ∀ i, x.moveRight i ≈ y.moveRight (r i)) + (hir : ∀ i, x.moveRight (ir i) ≈ y.moveRight i) : x ≈ y := + .ext ⟨fun i ↦ ⟨l i, hl i⟩, fun i ↦ ⟨il i, hil i⟩⟩ ⟨fun i ↦ ⟨r i, hr i⟩, fun i ↦ ⟨ir i, hir i⟩⟩ + +lemma Equiv.of_equiv {x y : PGame} + (l : x.LeftMoves ≃ y.LeftMoves) (r : x.RightMoves ≃ y.RightMoves) + (hl : ∀ i, x.moveLeft i ≈ y.moveLeft (l i)) (hr : ∀ i, x.moveRight i ≈ y.moveRight (r i)) : + x ≈ y := + .of_fn l l.symm r r.symm hl (by simpa using hl <| l.symm ·) hr (by simpa using hr <| r.symm ·) + +theorem Equiv.ext' {x y : PGame} + (hl : (∀ a ∈ₗ x, ∃ b ∈ₗ y, a ≈ b) ∧ (∀ b ∈ₗ y, ∃ a ∈ₗ x, a ≈ b)) + (hr : (∀ a ∈ᵣ x, ∃ b ∈ᵣ y, a ≈ b) ∧ (∀ b ∈ᵣ y, ∃ a ∈ᵣ x, a ≈ b)) : + x ≈ y := by + refine Equiv.ext (hl.imp (fun h i ↦ ?_) (fun h i ↦ ?_)) (hr.imp (fun h i ↦ ?_) (fun h i ↦ ?_)) <;> + obtain ⟨_, ⟨i, hi⟩, h⟩ := h _ ⟨i, refl _⟩ + · exact ⟨i, Equiv.trans h hi.equiv⟩ + · exact ⟨i, Equiv.trans hi.symm.equiv h⟩ + · exact ⟨i, Equiv.trans h hi.equiv⟩ + · exact ⟨i, Equiv.trans hi.symm.equiv h⟩ /-- The fuzzy, confused, or incomparable relation on pre-games. @@ -1074,7 +1270,7 @@ instance : Neg PGame := ⟨neg⟩ @[simp] -theorem neg_def {xl xr xL xR} : -mk xl xr xL xR = mk xr xl (fun j => -xR j) fun i => -xL i := +theorem neg_def {xl xr xL xR} : -mk xl xr xL xR = mk xr xl (-xR ·) (-xL ·) := rfl instance : InvolutiveNeg PGame := @@ -1173,6 +1369,39 @@ theorem moveRight_neg_symm {x : PGame} (i) : theorem moveRight_neg_symm' {x : PGame} (i) : x.moveRight i = -(-x).moveLeft (toLeftMovesNeg i) := by simp +theorem leftMoves_neg_cases {x : PGame} (k) {P : (-x).LeftMoves → Prop} + (h : ∀ i, P <| toLeftMovesNeg i) : + P k := by + rw [← toLeftMovesNeg.apply_symm_apply k] + exact h _ + +theorem rightMoves_neg_cases {x : PGame} (k) {P : (-x).RightMoves → Prop} + (h : ∀ i, P <| toRightMovesNeg i) : + P k := by + rw [← toRightMovesNeg.apply_symm_apply k] + exact h _ + +/-- If `x` has the same moves as `y`, then `-x` has the sames moves as `-y`. -/ +lemma Identical.neg : ∀ {x₁ x₂ : PGame}, x₁ ≡ x₂ → -x₁ ≡ -x₂ + | mk _ _ _ _, mk _ _ _ _, ⟨⟨hL₁, hL₂⟩, ⟨hR₁, hR₂⟩⟩ => + ⟨⟨fun i ↦ (hR₁ i).imp (fun _ ↦ Identical.neg), fun j ↦ (hR₂ j).imp (fun _ ↦ Identical.neg)⟩, + ⟨fun i ↦ (hL₁ i).imp (fun _ ↦ Identical.neg), fun j ↦ (hL₂ j).imp (fun _ ↦ Identical.neg)⟩⟩ + +/-- If `-x` has the same moves as `-y`, then `x` has the sames moves as `y`. -/ +lemma Identical.of_neg : ∀ {x₁ x₂ : PGame}, -x₁ ≡ -x₂ → x₁ ≡ x₂ + | mk x₁l x₁r x₁L x₁R, mk x₂l x₂r x₂L x₂R => by + simpa using Identical.neg (x₁ := mk _ _ (-x₁R ·) (-x₁L ·)) (x₂ := mk _ _ (-x₂R ·) (-x₂L ·)) + +lemma memₗ_neg_iff : ∀ {x y : PGame}, + x ∈ₗ -y ↔ ∃ z ∈ᵣ y, x ≡ -z + | mk _ _ _ _, mk _ _ _ _ => + ⟨fun ⟨_i, hi⟩ ↦ ⟨_, ⟨_, refl _⟩, hi⟩, fun ⟨_, ⟨i, hi⟩, h⟩ ↦ ⟨i, h.trans hi.neg⟩⟩ + +lemma memᵣ_neg_iff : ∀ {x y : PGame}, + x ∈ᵣ -y ↔ ∃ z ∈ₗ y, x ≡ -z + | mk _ _ _ _, mk _ _ _ _ => + ⟨fun ⟨_i, hi⟩ ↦ ⟨_, ⟨_, refl _⟩, hi⟩, fun ⟨_, ⟨i, hi⟩, h⟩ ↦ ⟨i, h.trans hi.neg⟩⟩ + /-- If `x` has the same moves as `y`, then `-x` has the same moves as `-y`. -/ def Relabelling.negCongr : ∀ {x y : PGame}, x ≡r y → -x ≡r -y | ⟨_, _, _, _⟩, ⟨_, _, _, _⟩, ⟨L, R, hL, hR⟩ => @@ -1200,6 +1429,10 @@ theorem neg_lf_neg_iff {x y : PGame} : -y ⧏ -x ↔ x ⧏ y := theorem neg_lt_neg_iff {x y : PGame} : -y < -x ↔ x < y := by rw [lt_iff_le_and_lf, lt_iff_le_and_lf, neg_le_neg_iff, neg_lf_neg_iff] +@[simp] +theorem neg_identical_neg_iff {x y : PGame} : (-x ≡ -y) ↔ (x ≡ y) := + ⟨Identical.of_neg, Identical.neg⟩ + @[simp] theorem neg_equiv_neg_iff {x y : PGame} : (-x ≈ -y) ↔ (x ≈ y) := by show Equiv (-x) (-y) ↔ Equiv x y @@ -1271,6 +1504,18 @@ instance : Add PGame.{u} := · exact fun i => IHxr i y · exact IHyr⟩ +@[simp] +theorem mk_add_moveLeft {xl xr yl yr} {xL xR yL yR} {i} : + (mk xl xr xL xR + mk yl yr yL yR).moveLeft i = + i.rec (xL · + mk yl yr yL yR) (mk xl xr xL xR + yL ·) := + rfl + +@[simp] +theorem mk_add_moveRight {xl xr yl yr} {xL xR yL yR} {i} : + (mk xl xr xL xR + mk yl yr yL yR).moveRight i = + i.rec (xR · + mk yl yr yL yR) (mk xl xr xL xR + yR ·) := + rfl + /-- The pre-game `((0 + 1) + ⋯) + 1`. Note that this is **not** the usual recursive definition `n = {0, 1, … | }`. For instance, @@ -1414,6 +1659,110 @@ instance isEmpty_nat_rightMoves : ∀ n : ℕ, IsEmpty (RightMoves n) rw [PGame.nat_succ, rightMoves_add] infer_instance +/-- `x + y` has exactly the same moves as `y + x`. -/ +protected lemma add_comm (x y : PGame) : x + y ≡ y + x := + match x, y with + | mk xl xr xL xR, mk yl yr yL yR => by + refine Identical.of_equiv (Equiv.sumComm _ _) (Equiv.sumComm _ _) ?_ ?_ <;> + · rintro (_ | _) <;> + · dsimp; exact PGame.add_comm _ _ + termination_by (x, y) + +/-- `(x + y) + z` has exactly the same moves as `x + (y + z)`. -/ +protected lemma add_assoc (x y z : PGame) : x + y + z ≡ x + (y + z) := + match x, y, z with + | mk xl xr xL xR, mk yl yr yL yR, mk zl zr zL zR => by + refine Identical.of_equiv (Equiv.sumAssoc _ _ _) (Equiv.sumAssoc _ _ _) ?_ ?_ <;> + · rintro ((_ | _) | _) + · exact PGame.add_assoc _ _ _ + · exact PGame.add_assoc (mk _ _ _ _) _ _ + · exact PGame.add_assoc (mk _ _ _ _) (mk _ _ _ _) _ + termination_by (x, y, z) + +/-- `x + 0` has exactly the same moves as `x`. -/ +protected lemma add_zero : ∀ (x : PGame), x + 0 ≡ x + | mk xl xr xL xR => by + refine Identical.of_equiv (Equiv.sumEmpty _ _) (Equiv.sumEmpty _ _) ?_ ?_ <;> + · rintro (_ | ⟨⟨⟩⟩) + exact PGame.add_zero _ + +/-- `0 + x` has exactly the same moves as `x`. -/ +protected lemma zero_add (x : PGame) : 0 + x ≡ x := + (PGame.add_comm _ _).trans x.add_zero + +/-- `-(x + y)` has exactly the same moves as `-x + -y`. -/ +protected lemma neg_add (x y : PGame) : -(x + y) = -x + -y := + match x, y with + | mk xl xr xL xR, mk yl yr yL yR => by + refine ext rfl rfl ?_ ?_ <;> + · rintro (i | i) _ ⟨rfl⟩ + · exact PGame.neg_add _ _ + · simpa [Equiv.refl] using PGame.neg_add _ _ + termination_by (x, y) + +/-- `-(x + y)` has exactly the same moves as `-y + -x`. -/ +protected lemma neg_add_rev (x y : PGame) : -(x + y) ≡ -y + -x := + Identical.trans (of_eq (x.neg_add y)) (PGame.add_comm _ _) + +lemma identical_zero_iff : ∀ (x : PGame), + x ≡ 0 ↔ IsEmpty x.LeftMoves ∧ IsEmpty x.RightMoves + | mk xl xr xL xR => by + constructor + · rintro ⟨h₁, h₂⟩ + dsimp [Relator.BiTotal, Relator.LeftTotal, Relator.RightTotal] at h₁ h₂ + simp_rw [IsEmpty.forall_iff, and_true, IsEmpty.exists_iff] at h₁ h₂ + exact ⟨⟨h₁⟩, ⟨h₂⟩⟩ + · rintro ⟨h₁, h₂⟩ + exact identical_of_is_empty _ _ + +/-- Any game without left or right moves is identival to 0. -/ +lemma identical_zero (x : PGame) [IsEmpty x.LeftMoves] [IsEmpty x.RightMoves] : x ≡ 0 := + x.identical_zero_iff.mpr ⟨by infer_instance, by infer_instance⟩ + +lemma add_eq_zero_iff : ∀ (x y : PGame), x + y ≡ 0 ↔ x ≡ 0 ∧ y ≡ 0 + | mk xl xr xL xR, mk yl yr yL yR => by + simp_rw [identical_zero_iff, leftMoves_add, rightMoves_add, isEmpty_sum] + tauto + +lemma Identical.add_right {x₁ x₂ y} : x₁ ≡ x₂ → x₁ + y ≡ x₂ + y := + match x₁, x₂, y with + | mk x₁l x₁r x₁L x₁R, mk x₂l x₂r x₂L x₂R, mk yl yr yL yR => by + intro h + refine ⟨⟨?_, ?_⟩, ⟨?_, ?_⟩⟩ <;> rintro (_ | _) <;> try exact ⟨.inr _, h.add_right⟩ + · exact (h.1.1 _).elim (⟨.inl ·, ·.add_right⟩) + · exact (h.1.2 _).elim (⟨.inl ·, ·.add_right⟩) + · exact (h.2.1 _).elim (⟨.inl ·, ·.add_right⟩) + · exact (h.2.2 _).elim (⟨.inl ·, ·.add_right⟩) + termination_by (x₁, x₂, y) + +lemma Identical.add_left {x y₁ y₂} (hy : y₁ ≡ y₂) : x + y₁ ≡ x + y₂ := + (x.add_comm y₁).trans (hy.add_right.trans (y₂.add_comm x)) + +/-- If `w` has the same moves as `x` and `y` has the same moves as `z`, +then `w + y` has the same moves as `x + z`. -/ +lemma Identical.add {x₁ x₂ y₁ y₂ : PGame.{u}} (hx : x₁ ≡ x₂) (hy : y₁ ≡ y₂) : x₁ + y₁ ≡ x₂ + y₂ := + hx.add_right.trans hy.add_left + +lemma memₗ_add_iff {x y₁ y₂ : PGame} : + x ∈ₗ y₁ + y₂ ↔ (∃ z ∈ₗ y₁, x ≡ z + y₂) ∨ (∃ z ∈ₗ y₂, x ≡ y₁ + z) := by + cases' y₁ with y₁l y₁r y₁L y₁R + cases' y₂ with y₂l y₂r y₂L y₂R + constructor + · rintro ⟨(i | i), hi⟩ + exacts [.inl ⟨y₁L i, moveLeft_memₗ _ _, hi⟩, .inr ⟨y₂L i, moveLeft_memₗ _ _, hi⟩] + · rintro (⟨_, ⟨i, hi⟩, h⟩ | ⟨_, ⟨i, hi⟩, h⟩) + exacts [⟨.inl i, h.trans hi.add_right⟩, ⟨.inr i, h.trans hi.add_left⟩] + +lemma memᵣ_add_iff {x y₁ y₂ : PGame} : + x ∈ᵣ y₁ + y₂ ↔ (∃ z ∈ᵣ y₁, x ≡ z + y₂) ∨ (∃ z ∈ᵣ y₂, x ≡ y₁ + z) := by + cases' y₁ with y₁l y₁r y₁L y₁R + cases' y₂ with y₂l y₂r y₂L y₂R + constructor + · rintro ⟨(i | i), hi⟩ + exacts [.inl ⟨y₁R i, moveRight_memᵣ _ _, hi⟩, .inr ⟨y₂R i, moveRight_memᵣ _ _, hi⟩] + · rintro (⟨_, ⟨i, hi⟩, h⟩ | ⟨_, ⟨i, hi⟩, h⟩) + exacts [⟨.inl i, h.trans hi.add_right⟩, ⟨.inr i, h.trans hi.add_left⟩] + /-- If `w` has the same moves as `x` and `y` has the same moves as `z`, then `w + y` has the same moves as `x + z`. -/ def Relabelling.addCongr : ∀ {w x y z : PGame.{u}}, w ≡r x → y ≡r z → w + y ≡r x + z @@ -1432,9 +1781,19 @@ instance : Sub PGame := ⟨fun x y => x + -y⟩ @[simp] -theorem sub_zero (x : PGame) : x - 0 = x + 0 := +theorem sub_zero_eq_add_zero (x : PGame) : x - 0 = x + 0 := show x + -0 = x + 0 by rw [neg_zero] +protected lemma sub_zero (x : PGame) : x - 0 ≡ x := + _root_.trans (of_eq x.sub_zero_eq_add_zero) x.add_zero + +protected lemma neg_sub' (x y : PGame) : -(x - y) = -x - -y := PGame.neg_add _ _ + +/-- If `w` has the same moves as `x` and `y` has the same moves as `z`, +then `w - y` has the same moves as `x - z`. -/ +lemma Identical.sub {x₁ x₂ y₁ y₂ : PGame.{u}} (hx : x₁ ≡ x₂) (hy : y₁ ≡ y₂) : x₁ - y₁ ≡ x₂ - y₂ := + hx.add hy.neg + /-- If `w` has the same moves as `x` and `y` has the same moves as `z`, then `w - y` has the same moves as `x - z`. -/ def Relabelling.subCongr {w x y z : PGame} (h₁ : w ≡r x) (h₂ : y ≡r z) : w - y ≡r x - z := @@ -1764,4 +2123,4 @@ end PGame end SetTheory -set_option linter.style.longFile 1900 +set_option linter.style.longFile 2300