/-- "Do `f`, then `g`": the function that sends `a` to `g (f a)`. It can only be formed when theoutputs of `f` (of type `β`) are inputs that `g` accepts. -/def andThen {α β γ : Type} (f : α → β) (g : β → γ) : α → γ := fun a => g (f a)
and_then_eq_comptheoremProved here from basic factsNo axioms
and_then_eq_compLean 4Proved here from basic factsFormally verified
/-- Our `andThen f g` is the function Lean's library writes `g ∘ f`, read "`g` after `f`".They agree by definition, so `rfl` proves it. -/theorem and_then_eq_comp {α β γ : Type} (f : α → β) (g : β → γ) : andThen f g = g ∘ f := rfl
double_then_addtheoremProved here from basic factsNo axioms
double_then_addLean 4Proved here from basic factsFormally verified
/-- "Double, then add 3" sends `x` to `2x + 3`. -/theorem double_then_add (x : Int) : andThen (fun n => 2 * n) (fun n => n + 3) x = 2 * x + 3 := by show 2 * x + 3 = 2 * x + 3 rfl
order_matterstheoremProved here from basic factspropext, Quot.sound
order_mattersLean 4Proved here from basic factsFormally verified
/-- **Order matters**: "add 3, then double" and "double, then add 3" are different functions.One input is enough to tell them apart: at `0` they give `6` and `3`. -/theorem order_matters : andThen (fun n : Int => n + 3) (fun n => 2 * n) ≠ andThen (fun n : Int => 2 * n) (fun n => n + 3) := by intro h have h0 := congrFun h 0 rw [add_then_double, double_then_add] at h0 omega
add_mul_swap_ifftheoremProved here from basic factspropext, Quot.sound
add_mul_swap_iffLean 4Proved here from basic factsFormally verified
/-- **When can "add `a`" and "multiply by `m`" be swapped?** "Add `a`, then multiply by `m`" gives`m(x + a) = mx + ma`, while "multiply by `m`, then add `a`" gives `mx + a`. They agree for every`x` exactly when `ma = a`, that is, when `a = 0` or `m = 1`. -/theorem add_mul_swap_iff (a m : Int) : (∀ x, andThen (fun n => n + a) (fun n => m * n) x = andThen (fun n => m * n) (fun n => n + a) x) ↔ a = 0 ∨ m = 1 := by constructor · intro h -- At `x = 0` the two chains give `m · a` and `a`. have h0 : m * (0 + a) = m * 0 + a := h 0 have hma : (m - 1) * a = 0 := by rw [Int.sub_mul, Int.one_mul] rw [Int.zero_add, Int.mul_zero, Int.zero_add] at h0 omega cases Int.mul_eq_zero.mp hma with | inl hm => exact Or.inr (by omega) | inr ha => exact Or.inl ha · intro h x show m * (x + a) = m * x + a cases h with | inl ha => rw [ha, Int.add_zero, Int.add_zero] | inr hm => rw [hm, Int.one_mul, Int.one_mul]
/-- `g` **undoes** `f` if doing `f` and then `g` always gives back the input: `g (f a) = a`. -/def Undoes {α β : Type} (g : β → α) (f : α → β) : Prop := ∀ a, g (f a) = a
add_undoes_subtheoremProved here from basic factspropext, Quot.sound
add_undoes_subLean 4Proved here from basic factsFormally verified
/-- … and "add 3" undoes "subtract 3". Each undoes the other: they are **inverse** functions. -/theorem add_undoes_sub : Undoes (fun x : Int => x + 3) (fun x => x - 3) := by intro a show a - 3 + 3 = a omega
halve_undoes_doubletheoremProved here from basic factspropext, Classical.choice, Quot.sound
halve_undoes_doubleLean 4Proved here from basic factsFormally verified
/-- On the rational numbers, "halve" undoes "double" and "double" undoes "halve": `x ↦ 2x` and`x ↦ x / 2` are inverse functions of `ℚ`. -/theorem halve_undoes_double : Undoes (fun x : Rat => x / 2) (fun x => 2 * x) ∧ Undoes (fun x : Rat => 2 * x) (fun x => x / 2) := by constructor · intro a show 2 * a / 2 = a grind · intro a show 2 * (a / 2) = a grind
int_halve_undoes_doubletheoremApplies a library theorempropext
int_halve_undoes_doubleLean 4Applies a library theoremFormally verified
/-- On the integers, halving *with rounding down* (Lean's `y / 2`) still undoes doubling:`(2x) / 2 = x`. This applies core Lean's `Int.mul_ediv_cancel_left`. -/theorem int_halve_undoes_double : Undoes (fun y : Int => y / 2) (fun x => 2 * x) := fun x => Int.mul_ediv_cancel_left x (by decide)
int_double_not_undoes_halvetheoremProved here from basic factsNo axioms
int_double_not_undoes_halveLean 4Proved here from basic factsFormally verified
/-- … but on the integers doubling does **not** undo halving: `1` is halved (rounding down) to`0`, and doubling `0` gives `0`, not `1`. So `x ↦ 2x` has no inverse function `ℤ → ℤ`: oddnumbers are never outputs, so nothing can be sent back to them. -/theorem int_double_not_undoes_halve : ¬ Undoes (fun x : Int => 2 * x) (fun y => y / 2) := by intro h have h1 : 2 * (1 / 2 : Int) = 1 := h 1 exact absurd h1 (by decide)
undo_chaintheoremProved here from basic factsNo axioms
undo_chainLean 4Proved here from basic factsFormally verified
/-- **Socks and shoes.** If `f'` undoes `f` and `g'` undoes `g`, then "`f`, then `g`" is undone by"`g'`, then `f'`": undo the **last** step **first**. The calculation:`f' (g' (g (f a))) = f' (f a) = a`. -/theorem undo_chain {α β γ : Type} {f : α → β} {g : β → γ} {f' : β → α} {g' : γ → β} (hf : Undoes f' f) (hg : Undoes g' g) : Undoes (andThen g' f') (andThen f g) := by intro a calc andThen g' f' (andThen f g a) = f' (g' (g (f a))) := rfl _ = f' (f a) := by rw [hg (f a)] _ = a := hf a
/-- Fahrenheit to Celsius: subtract `32`, then multiply by `5/9` — the undoing steps, in reverseorder. -/def toCelsius : Rat → Rat := andThen (fun f => f - 32) (fun x => 5 / 9 * x)
to_celsius_undoestheoremProved here from basic factspropext, Classical.choice, Quot.sound
to_celsius_undoesLean 4Proved here from basic factsFormally verified
/-- `toCelsius` undoes `toFahrenheit` for **every** temperature. This is the socks-and-shoes rule`undo_chain` applied to the two steps: `× 5/9` undoes `× 9/5`, and `− 32` undoes `+ 32`. -/theorem to_celsius_undoes : Undoes toCelsius toFahrenheit := by apply undo_chain · intro c show 5 / 9 * (9 / 5 * c) = c grind · intro x show x + 32 - 32 = x grind
wrong_order_failstheoremProved here from basic factspropext, Classical.choice, Quot.sound
wrong_order_failsLean 4Proved here from basic factsFormally verified
/-- Undoing the steps in the **wrong** order fails: "multiply by `5/9`, then subtract `32`" doesnot undo `toFahrenheit`. At `0 °C`: `toFahrenheit 0 = 32`, and `5/9 · 32 − 32 = −128/9 ≠ 0`. -/theorem wrong_order_fails : ¬ Undoes (andThen (fun x : Rat => 5 / 9 * x) (fun x => x - 32)) toFahrenheit := by intro h have h0 := h 0 revert h0 decide +kernel
injective_of_undoestheoremProved here from basic factsNo axioms
injective_of_undoesLean 4Proved here from basic factsFormally verified
/-- If `f` can be undone, then different inputs always give different outputs: from`f a = f b`, apply the undoing function `g` to both sides to get `a = g (f a) = g (f b) = b`. -/theorem injective_of_undoes {α β : Type} {f : α → β} {g : β → α} (h : Undoes g f) (a b : α) (hab : f a = f b) : a = b := calc a = g (f a) := (h a).symm _ = g (f b) := by rw [hab] _ = b := h b
injective_of_undoes_librarytheoremApplies a library theoremNo axioms
injective_of_undoes_libraryLean 4Applies a library theoremFormally verified
/-- The same fact from core Lean's library, where "`g` undoes `f`" is called`Function.LeftInverse g f` (it is defined exactly like our `Undoes g f`) and "never sends twoinputs to the same output" is `Function.Injective f`. This applies`Function.LeftInverse.injective`. -/theorem injective_of_undoes_library {α β : Type} {f : α → β} {g : β → α} (h : Undoes g f) : Function.Injective f := Function.LeftInverse.injective h
square_cannot_be_undonetheoremProved here from basic factsNo axioms
square_cannot_be_undoneLean 4Proved here from basic factsFormally verified
/-- **Squaring on ℤ cannot be undone**: `2² = (−2)² = 4`, so an undoing function would have tosend `4` back to both `2` and `−2`. -/theorem square_cannot_be_undone : ¬ ∃ g : Int → Int, Undoes g (fun x => x * x) := by intro ⟨g, hg⟩ have h : (2 : Int) = -2 := injective_of_undoes hg 2 (-2) (by decide) exact absurd h (by decide)
/-- Show that "add 3" undoes "subtract 3".Hint: `intro a`, then `show a - 3 + 3 = a` to unfold the definitions, and finish with `omega`. -/theorem add_undoes_sub : Undoes (fun x : Int => x + 3) (fun x => x - 3) := by sorry
Show the compiled solution
add_undoes_subLean 4Proved here from basic factsFormally verified
/-- … and "add 3" undoes "subtract 3". Each undoes the other: they are **inverse** functions. -/theorem add_undoes_sub : Undoes (fun x : Int => x + 3) (fun x => x - 3) := by intro a show a - 3 + 3 = a omega
/-- Socks and shoes: "`f`, then `g`" is undone by "`g'`, then `f'`".Hint: `intro a`. The goal unfolds to `f' (g' (g (f a))) = a`. Rewrite with `hg (f a)`, which says`g' (g (f a)) = f a`, and finish with `hf a`. You may need `show f' (g' (g (f a))) = a` first. -/theorem undo_chain {α β γ : Type} {f : α → β} {g : β → γ} {f' : β → α} {g' : γ → β} (hf : Undoes f' f) (hg : Undoes g' g) : Undoes (andThen g' f') (andThen f g) := by sorry
Show the compiled solution
undo_chainLean 4Proved here from basic factsFormally verified
/-- **Socks and shoes.** If `f'` undoes `f` and `g'` undoes `g`, then "`f`, then `g`" is undone by"`g'`, then `f'`": undo the **last** step **first**. The calculation:`f' (g' (g (f a))) = f' (f a) = a`. -/theorem undo_chain {α β γ : Type} {f : α → β} {g : β → γ} {f' : β → α} {g' : γ → β} (hf : Undoes f' f) (hg : Undoes g' g) : Undoes (andThen g' f') (andThen f g) := by intro a calc andThen g' f' (andThen f g a) = f' (g' (g (f a))) := rfl _ = f' (f a) := by rw [hg (f a)] _ = a := hf a