rename testgame to adam

This commit is contained in:
Alexander Bentkamp
2023-03-23 17:10:13 +01:00
parent a262cff63b
commit 5c73d3bddb
166 changed files with 0 additions and 0 deletions
@@ -0,0 +1,63 @@
import TestGame.Metadata
import Mathlib
Game "TestGame"
World "Function"
Level 1
Title "Anonyme Funktionen"
Introduction
"
Auf die Frage hin, ob sie von einem Bibliothek wisse, erzählt euch das kleine Mädchen,
dass es auf der Insel nur einen gäbe, aber sie bedrängt euch so mit einer Frage,
dass sie euch gar nicht sagt, wo dieser zu finden sei.
"
Statement "" : ∃ f : ℤ → ℤ, ∀ x, f x < x := by
use (fun x ↦ x - 1)
simp
Hint : ∃ f : ℤ → ℤ, ∀ x, f x < x =>
"
**Robo**: `f : ℤ → ℤ` ist die Notation für eine Funktion und `f x` ist diese Funktion
angewendet auf ein Element `(x : ℤ)`.
**Du**: War `→` nicht eben noch eine Implikation?
**Robo**: Doch, die brauchen das gleiche Zeichen für beides.
**Du**: Dann ist `f : ℤ → ℤ` also einfach abstrakt irgendeine Funktion,
wie definier ich aber jetzt konkret eine Abbildungsvorschrift?
**Robo**: Man kennt hier eine Notation für eine anonyme Funktion:
`fun (x : ℤ) ↦ x ^ 2` ist
$$
\\begin\{aligned}
f : \\mathbb\{ℤ} &\\to \\mathbb\{ℤ} \\\\
x &\\mapsto x ^ 2
\\end\{aligned}
$$
**Robo**: PS, `↦` ist `\\mapsto`. Aber man kann auch stattdessen `=>` benützen.
"
HiddenHint : ∃ f : ℕ → ℤ, ∀ x, f x < x =>
"
**Du**: Ja aber was mach ich damit?
**Robo**: Wie immer gehst du ein `∃` mit `use …` an.
"
-- TODO: Why does this not show?
HiddenHint : ∀ (x : ℤ), (fun (y : ℤ) => y - 1) x < x =>
"**Du**: Zu was sich das wohl vereinfacht?"
HiddenHint (x : ℤ) : (fun y => y - 1) x < x =>
"**Du**: Zu was sich das wohl vereinfacht?"
Conclusion "Das Mädchen wird kurz ruhig, dann beginnt es zu lächeln und zeigt strahlend
in eine Richtung. Ihr folgt ihrem Finger und euch fällt in weiter ferne eine pompöse Struktur
auf einem flachen Hügel auf.
"
@@ -0,0 +1,82 @@
import TestGame.Metadata
import Mathlib
Game "TestGame"
World "Function"
Level 2
Title "let"
Introduction
"
Ihr macht euch auf Richtung Bibliothek entlang kleiner Pfade zwischen verschiedenster Behausungen.
**Du**: Sag mal, ich weiss jetzt dass ich eine Funktion als `fun x ↦ x - 1` definieren kann,
aber wie kann ich der einen Namen geben?
**Robo**: Wenn jemand hier eine Funktion definiert, werden die dir
`def f (x : ℤ) : ℤ := x - 1` oder `def f : ℤ → ℤ := (fun x ↦ x - 1)` geben.
**Du**: Und das bedeutet beides das gleiche?
**Robo**: Praktisch, ja. Aber! Wenn du eine Funktion in einer Aufgabe benennen willst,
schreibst du `let f := fun (x : ℤ) ↦ x - 1`!
**Du**: Und was ist der Unterschied?
**Robo**: Deines mit `let` ist für innerhalb von einem Beweis, das andere mit `def`
ist für ausserhalb von einem Beweis. Hier, ich geb dir mal eine Aufgabe:
```
def f (x : ℤ) : ℤ := (x + 4)
```
und:
"
def f (x : ℤ) : ℤ := (x + 4)
Statement "" (x : ℤ) : ∃ (g : ℤ → ℤ), (g ∘ f) x = x + 1 := by
let g := fun (x : ℤ) ↦ x - 3
use g
simp
unfold f
ring
NewTactic «let»
NewLemma Function.comp_apply
Hint (x : ℤ) : ∃ g, (g ∘ f) x = x + 1 =>
"**Du**: Ist `g ∘ f` Komposition von Funktionen?
**Robo**: Richtig! Das schreibt man mit `\\comp`.
**Du** Und hier könnte ich also zuerst
`let g := fun (x : ℤ) ↦ _` definieren, anstatt direkt
`use fun (x : ℤ) ↦ _`?
**Robo**: Genau! Das ist zwar praktisch das gleiche, aber kann manchmal nützlich sein.
"
-- TODO: Make some hints work here
Hint (x : ℤ) : ((fun (x : ℤ) ↦ x - 3) ∘ f) x = x + 1 =>
"**Robo**: Manchmal must du nachhelfen und Funktionen mit `unfold f` öffnen, manchmal nicht.
Um erlich zu sein, sagt mein Programm nicht genau wann man das machen muss…"
-- TODO : Make this work
Hint (x : ℤ) (g := (fun (x : ℤ) ↦ x - 3)) : (g ∘ f) x = x + 1 =>
"**Robo**: `(g ∘ f) x` ist per Definition `g (f x)`, aber mit
`rw [Function.comp_apply]` kann man das explizit umschreiben, aber `simp` kennt das
Lemma auch."
Hint (x : ℤ) : f x - 3 = x + 1 =>
"**Robo**: Manchmal must du nachhelfen und Definitionen mit `unfold f` öffnen, mamchmal klappts
ohne.
Um erlich zu sein, sagt mein Programm nicht genau wann man das machen muss…"
-- TODO: Block simp-Lemma
Conclusion "**Du**: Dann verstehst du etwas Mathe?
**Robo**: Ich hatte ja keine Ahnung ob die generierte Aufgabe beweisbar ist…
Und damit erreicht ihr den Hügel mit der Bibliothek."
@@ -0,0 +1,177 @@
import TestGame.Metadata
import Mathlib
Game "TestGame"
World "Function"
Level 3
Title "Komposition"
Introduction
"
Endlich kommt ihr zur Bibliothek. Komischerweise stehen an der Tür
zwei Wächtern. Der eine zeigt euch ein Blatt mit
```
def f : ℚ → ℚ := fun x ↦ 5 * x
```
der andere eines mit
```
def g : ℚ → ℚ := fun x ↦ if 0 ≤ x then 2*x else 0
```
und gibt Dir ein Blatt mit einer einzelnen Zeile am oberen Ende.
"
open Set
namespace LevelFunction4
def f : ℚ → ℚ := fun x ↦ 5 * x
def g : ℚ → ℚ := fun x ↦ if 0 ≤ x then 2*x else 0
Statement ""
: f ∘ g = g ∘ f := by
funext x
simp_rw [Function.comp_apply]
unfold g
by_cases 0 ≤ x
have h' : 0 ≤ f x
unfold f
linarith
rw [if_pos h', if_pos h]
unfold f
ring
have h' : ¬ (0 ≤ f x)
unfold f
linarith
rw [if_neg h, if_neg h']
unfold f
ring
NewTactic funext by_cases simp_rw linarith
NewLemma not_le if_pos if_neg
Hint : f ∘ g = g ∘ f =>
"
**Robo**: Schau mal, die beiden haben zwei Funktionen, eine davon mit stückweiser Definition.
**Du**: Und ich soll zeigen, dass die beiden vertauschbar sind?
**Robo**: Genau, am besten wählst du mit `funext x` ein beliebiges Element aus, und zeigst das
dann für dieses.
"
-- TODO : This does not trigger.
-- TODO: These 5 hints should be mutually exclusive. i.e. they should not trigger
-- if a assumption is missing.
Hint (x : ℚ) : (f ∘ g) x = (g ∘ f) x =>
"
**Du**: Ah und jetzt kann ich erst einmal `(g ∘ f) {x}` zu `g (f {x})` umschreiben?
**Robo**: Mit `simp_rw` geht das übrigens schneller.
**Robo**: Und danach willst du eien Fallunterscheidung
machen, `by_cases h : 0 ≤ {x}`.
**Du**: Damit krieg ich die Fälle `0 ≤ {x}` und `{x} < 0`?
**Robo**: Genau! Oder präziser `0 ≤ {x}` und `¬(0 ≤ {x})`. Das ist nicht ganz das gleiche, und man
könnte mit dem Lemma `not_le` zwischen `¬(0 ≤ {x})` und `0 < {x}` wechseln.
"
Hint (x : ℚ) (h : 0 ≤ x) : f (g x) = g (f x) =>
"
**Robo**: Um das ausrechnen zu können, brauchst du nicht nur `0 ≤ x` sondern auch noch
eine neue Annahme `0 ≤ f x`.
**Du**: Also `have h₂ : _`?
"
Hint (x : ℚ) (h : 0 ≤ x) (h₂ : 0 ≤ f x) : f (g x) = g (f x) =>
"
**Du**: Mit den beiden Annahmen sagt die Definition von `g` ja z.B.
`(if 0 ≤ x then _)` wobei ich weiss dass `0 ≤ x` wahr ist,
kann ich das dann einfach vereinfachen?
**Robo**: Dafür must du zuerst die Definition von `g` öffnen, also `unfold`. Und dann mit
dem Lemma `if_pos {h}` das umschreiben.
"
Hint (x : ℚ) (h : ¬ 0 ≤ x) : f (g x) = g (f x) =>
"
**Du**: Ich sehe, das ist jetzt der zweite Fall, da brauch ich sicher wieder eine zweite Annahme
`¬(0 ≤ f x)`…
"
Hint (x : ℚ) (h : ¬ 0 ≤ x) (h₂ : ¬ 0 ≤ f x) : f (g x) = g (f x) =>
"
**Robo**: Jetzt ist der Zeitpunkt wo die Definition von `g` geöffnet sein sollte.
**Robo**: Wenn man ein If-Statement mit wahrer Prämisse mit `if_pos` vereinfacht, dann
braucht man für eine falsche Prämisse…
**Du**: `if_neg`?
"
-- END TODO
HiddenHint (x : ℚ) (h : 0 ≤ x) (h₂ : 0 ≤ f x) : f (2 * x) = g (f x) =>
"
**Robo**: Jetzt das Gleiche noch mit `if_pos {h₂}`.
"
HiddenHint (x : ℚ) (h : 0 ≤ x) (h₂ : 0 ≤ f x) : f (g x) = 2 * f x =>
"
**Robo**: Jetzt das Gleiche noch mit `if_pos {h}`.
"
Hint (x : ℚ) (h : 0 ≤ x) (h₂ : 0 ≤ f x) : f (2 * x) = 2 * f x =>
"
**Robo**: Wenn du jetzt noch die Definition von `f` öffnest, dann kann `ring` den
Rest ausrechnen.
"
-- TODO: This are also showing in the case of the Hint below
-- Proof of `0 ≤ f x`.
Hint (x : ℚ) (h : 0 ≤ x) : 0 ≤ f x =>
" **Robo**: Wenn du die Definition von `f` öffnest, dann hast du schon das wissen,
um das zu beweisen."
HiddenHint (x : ℚ) (h : 0 ≤ x) : 0 ≤ f x =>
"**Du** *(in Gedanken)*: Was war das nochmals... Ungleichungen... `linarith`!"
HiddenHint (x : ℚ) (h : ¬0 ≤ x) : ¬ 0 ≤ f x =>
" **Robo**: Das ist das selbe wie vorhin…"
-- Hint for modifying `h` wrongly.
Hint (x : ℚ) (h : x < 0) : f (g x) = g (f x) =>
"
**Robo**: Das war nicht so ideal, hier brauchst du die Annahme in der Form `({h} : ¬ 0 ≤ {x})`!
"
-- TODO: In this case we get to see the Hints above
Hint (x : ℚ) (h : 0 ≤ x) : 0 ≤ x =>
"**Robo**: Dieses Goal ist entstanden, als du `rw [if_pos]` anstatt `rw [if_pos {h}]`
geschrieben hast."
Hint (x : ℚ) (h : 0 ≤ f x) : 0 ≤ f x =>
"**Robo**: Dieses Goal ist entstanden, als du `rw [if_pos]` anstatt `rw [if_pos {h}]`
geschrieben hast."
Hint (x : ℚ) (h : ¬ 0 ≤ x) : ¬ 0 ≤ x =>
"**Robo**: Dieses Goal ist entstanden, als du `rw [if_neg]` anstatt `rw [if_neg {h}]`
geschrieben hast."
Hint (x : ℚ) (h : ¬ 0 ≤ f x) : ¬ 0 ≤ f x =>
"**Robo**: Dieses Goal ist entstanden, als du `rw [if_neg]` anstatt `rw [if_neg {h}]`
geschrieben hast."
Conclusion
"Zufrieden tauschen die beiden Wächter ihren Platz und geben so dabei den
Durchgang frei."
end LevelFunction4
@@ -0,0 +1,38 @@
import TestGame.Metadata
import Mathlib
Game "TestGame"
World "Function"
Level 4
Title ""
Introduction
"
Ihr läuft durch verschiedenste Gänge der Bibliothek, allesamt mit Büchern entlang der Wände.
**Du**: Wenn wir wüssten, dass nur ein möglicher Weg hierhin führt, könnten wir
ausschliessen, dass wir im Kreis laufen.
Plötzlich begegnet ihr einem älteren Wesen mit Fakel. Auf die Frage antwortet es mit
"
open Set Function
Statement "" : Injective (fun (n : ℤ) ↦ n + 3) := by
intro a b
simp
NewDefinition Injective
Hint : Injective (fun (n : ℤ) ↦ n + 3) =>
"**Robo**: `Injective` ist als `∀ \{a b : U}, f a = f b → a = b`
definiert, also kannst du mit `intro` anfangen.
"
Hint (a b : ℤ) : (fun n => n + 3) a = (fun n => n + 3) b → a = b =>
"**Du**: Kann man das wohl vereinfachen?"
Hint (a b : ℤ) (hab : (fun n => n + 3) a = (fun n => n + 3) b) : a = b =>
"**Robot**: Jetzt musst du wohl `{hab}` vereinfachen."
Conclusion "**Du** Woa das war ja einfach!"
@@ -0,0 +1,79 @@
import TestGame.Metadata
import Mathlib
set_option tactic.hygienic false
Game "TestGame"
World "Function"
Level 5
Title ""
Introduction
"
Sofort hackt die ältere Gestalt nach:
"
open Set Function
example (f : ℤ → ℤ) (h : StrictMono f) : Injective f := by
apply StrictMono.injective
assumption
-- Odd.strictMono_pow
Statement "" : Injective (fun (n : ℤ) ↦ n^3 + (n + 3)) := by
apply StrictMono.injective
apply StrictMono.add
apply Odd.strictMono_pow
trivial
intro a b
simp
NewDefinition Injective
NewLemma StrictMono.injective StrictMono.add Odd.strictMono_pow
Hint : Injective fun (n : ℤ) => n ^ 3 + (n + 3) =>
"**Du**: Hmm, das ist etwas schwieriger…
**Robo**: Aber ich hab einen Trick auf Lager:
Das Lemma `StrictMono.injective` sagt, dass jede strikt monotone Funktion injektive ist,
und ich habe das Gefühl Monotonie ist hier einfacher zu zeigen."
HiddenHint : Injective fun (n : ℤ) => n ^ 3 + (n + 3) =>
"**Robo**: `apply` ist wonach du suchst."
Hint : StrictMono fun (n : ℤ) => n ^ 3 + (n + 3) =>
"**Du**: Jetzt möchte ich strikte Monotonie von `n ^ 3` und `n + 3` separat zeigen,
schliesslich scheint es mir als wär das zweite wieder einfach.
**Robo**: Dafür hab ich `StrictMono.add` bereit!"
Hint : StrictMono fun (x : ℤ) => x ^ 3 =>
"**Du**: Hmm, darauf hab ich jetzt wenig Lust. Gibt's dafür auch was? Das gilt ja nur
wenn der Exponent ungerade ist.
**Robo**: Du könntest mal `Odd.strictMono_pow` versuchen…"
HiddenHint : Odd 3 =>
"**Du**: Ist das nicht ne Trivialität? Warte mal!"
Hint : StrictMono fun (x : ℤ) => x + 3 =>
"**Du**: Ha! Und dieser Teil funktioniert sicher gleich wie Injektivität vorhin!"
Hint (a b : ℤ) : a ^ 3 + (a + 3) = b ^ 3 + (b + 3) → a = b =>
"**Robo**: Ich glaube, dieser Weg ist zu steinig. Fang doch nochmals von vorne an!
"
Hint (a b : ℤ) (h : a ^ 3 + (a + 3) = b ^ 3 + (b + 3)) : a = b =>
"**Robo**: Ich glaube, dieser Weg ist zu steinig. Fang doch nochmals von vorne an!
"
Conclusion "**Du**: Danke vielmals!
Und damit lässt das Wesen mitten im Gang stehen, wo es weiter über Injektivität nachdenkt."
@@ -0,0 +1,76 @@
import TestGame.Metadata
import Mathlib
Game "TestGame"
World "Function"
Level 6
Title "Injektive"
Introduction
"
Weiterirrend kommt ihr an eine Verzweigung.
**Robo**: Sieht beides gleich aus.
Ein paar Schritte in den linken Korridor hinein seht ihr auf dem Boden ein Blatt mit Gekritzel:
```
def f : ℕ → ℕ := fun n ↦ if Even n then n^2 else n+1
```
**Du**: Hier haben wir wieder eine stückweise Funktion
$$
f(n) = \\begin{cases}
n^2 & \\text{falls } n \\text{ gerade} \\\\
n+1 & \\text{andernfalls.}
\\end{cases}
$$
Darunter steht in leicht leuchtender Schrift:
"
namespace FunctionLvl7
open Function
def f : ℕ → ℕ := fun n ↦ if Even n then n^2 else n+1
Statement "" : ¬ (f + f).Injective := by
unfold Injective
push_neg
use 2
use 3
simp
Hint : ¬ (Injective (f + f)) =>
"
**Robo**: Das ist sicher ein Hinweis.
**Du**: Aber `¬ Injective` sagt mir nichts…
**Robo**: Könntest du etwas mit `¬ ∀` anfangen? Dann könntest du ja `Injektive` zuerst öffnen.
**Du**: Darüber haben wir doch mal was gelernt…
"
HiddenHint : ¬ (Injective (f + f)) =>
"
**Robo**: Das war `push_neg`.
"
Hint : ∃ a b, (f + f) a = (f + f) b ∧ a ≠ b =>
"**Du** Jetzt muss ich einfach ein Gegenbeispiel nennen, oder?
**Robo** Genau! Welche beiden Zahlen möchtest du denn verwenden?"
Conclusion
"
Als ihr das Problem gelöst habt, erschleicht euch ein starkes
Gefühl, dass dies der falsche Weg ist.
Also geht ihr zurück und nehmt die rechte Gabelung.
"
end FunctionLvl7
@@ -0,0 +1,37 @@
import TestGame.Metadata
import Mathlib
Game "TestGame"
World "Function"
Level 7
Title "Surjektive"
Introduction
"
Endlich kommt ihr in einen große, beleuchteten zentralen Raum.
Alle Wände sind voll mit Büchern und
in der Mitte sitzt an einem einsamen
Tisch ein Gelehrter, der tatsächlich das gesuchte Buch zeigen kann.
Bevor er dieses aushändigt, will er aber folgendes wissen:
"
open Function
Statement "" : Surjective (fun (n : ℤ) ↦ n + 1) := by
intro y
use y-1
simp
NewDefinition Surjective
Hint : Surjective (fun (n : ℤ) ↦ n + 1) =>
"**Robo**: Die Definition von `Surjective f` ist `∀ y, (∃ x, f x = y)`.
**Du**: Dann kann ich das auch einfach wie Quantifier behandeln?
**Robo**: Schieß drauf los!"
Conclusion
"Der Gelehrte händigt euch schmunzelnd das Buch aus."
@@ -0,0 +1,38 @@
import TestGame.Metadata
import Mathlib
Game "TestGame"
World "Function"
Level 8
Title ""
Introduction
"
**Du**: Ehm, und wie kommen wir da wieder raus?
**Gelehrter**: Gerne zeige ich euch den Weg, nachdem ihr mir auch noch folgendes erklärt:
"
open Function
Statement "" : Bijective (fun (n : ℤ) ↦ n + 1) := by
unfold Bijective
constructor
intro a b
simp
intro y
use y-1
simp
Hint : Bijective (fun (n : ℤ) ↦ n + 1) =>
"**Robo** *(flüsternd)*: `Bijectve f` ist als `Injective f ∧ Surjective f` definiert.
**Du**: Dann ist das ja ganz simpel!
"
Conclusion
"Zufrieden drückt euch der Gelehrte eine neue Fackel in die Hand und
zeigt euch den Weg nach draußen."
NewDefinition Bijective
@@ -0,0 +1,61 @@
import TestGame.Metadata
import Mathlib
Game "TestGame"
World "Function"
Level 9
Title "Inverse"
Introduction
"
Eigentlich hast du nur beiläufig Robo gefragt, ob bijektiv nicht auch bedeute, dass
eine Inverse Funktion bestehe. Jetzt steht ihr aber schon seit einer halben Stunde rum
und der Gelehrte möchte wissen, wie das den genau ginge.
Offensichtlich kennt er diese Aussage als `Function.bijective_iff_has_inverse` aus seinen Büchern,
aber er möchte, dass Du ihm das hier und jetzt nochmals von Grund auf zeigst.
"
open Function
--TODO: This is a really hard proof
Statement bijective_iff_has_inverse "" {A B : Type} (f : A → B) :
Bijective f ↔ ∃ g, LeftInverse g f ∧ RightInverse g f := by
constructor
intro h
use fun x => (h.2 x).choose
constructor
· intro x
simp
apply h.1
apply Exists.choose_spec (h.2 (f x))
· intro x
simp
apply Exists.choose_spec (h.2 x)
intro ⟨g, h₁, h₂⟩
constructor
· intro a b hab
have h : g (f a) = g (f b)
· apply congrArg
assumption
rw [h₁, h₁] at h
assumption
· intro x
use g x
rw [h₂]
NewDefinition LeftInverse RightInverse
NewLemma Exists.choose Exists.choose_spec congrArg congrFun
DisabledLemma Function.bijective_iff_has_inverse
Hint {A B : Type} (f : A → B) : Bijective f ↔ ∃ g, LeftInverse g f ∧ RightInverse g f =>
"**Du**: Nah da sagt mir so manches nichts, aber ich kann ja mal mit dem `↔` anfangen, das kenn
ich ja schon."
Conclusion
"Endlich entkommt ihr der Bibliothek.
**Robo**: Da würden mich keine zehn Pferde nochmals hineinbringen!
**Du**: Von wegen Pferden, wie viele PS hat eigentlich unser Raumschiff?"
@@ -0,0 +1,93 @@
import TestGame.Metadata
import Mathlib
Game "TestGame"
World "Function"
Level 10
Title "Inverse"
Introduction
"
Eigentlich hast du nur beiläufig Robo gefragt, ob bijektiv nicht auch bedeute, dass
eine Inverse Funktion bestehe. Jetzt steht ihr aber schon seit einer halben Stunde rum
und der Gelehrte möchte wissen, wie das den genau ginge.
Offensichtlich kennt er diese Aussage als `Function.bijective_iff_has_inverse` aus seinen Büchern,
aber er möchte, dass Du ihm das hier und jetzt nochmals von Grund auf zeigst.
"
open Function
set_option pp.rawOnError true
Statement bijective_iff_has_inverse "" {A B : Type} (f : A → B) :
Bijective f ↔ ∃ g, LeftInverse g f ∧ RightInverse g f := by
Branch
exfalso
Hint "Das war eine blöde Idee
dd
ddds
"
Hint (hidden := true) "constructor"
constructor
Hint "intro h" -- does not show
intro h
Hint "rcases h with ⟨h₁, h₂⟩" -- shows too late: 1, 2 after
rcases h with ⟨h₁, h₂⟩
Hint (strict := true) "let g := fun x => (h₂ x).choose" -- shows correct + 1 after
let g := fun x => (h₂ x).choose
Hint "use g"
use g
Hint "constructor" -- does not show
constructor
Hint "intro x" -- does not show
intro x
Hint "simp" -- Error updating: Error fetching goals: Rpc error: InternalError: unknown universe metavariable '?_uniq.286465'. Try again.
simp
Hint "apply h₁"
apply h₁
Hint "apply Exists.choose_spec (h₂ (f x))"
apply Exists.choose_spec (h₂ (f x))
Hint "intro y" -- Error updating: Error fetching goals: Rpc error: InternalError: unknown universe metavariable '?_uniq.286465'. Try again.
intro y
Hint "simp"
simp
Hint "apply Exists.choose_spec (h₂ y)"
apply Exists.choose_spec (h₂ y)
Hint "intro ⟨g, h₁, h₂⟩"
intro ⟨g, h₁, h₂⟩
Hint "constructor"
constructor
Hint "intro a b hab"
intro a b hab
Hint (strict := true) "have h : g (f a) = g (f b)"
have h : g (f a) = g (f b)
Hint "apply congrArg"
apply congrArg
Hint "assumption"
assumption
Hint "rw [h₁, h₁] at h"
rw [h₁, h₁] at h
Hint "assumption"
assumption
Hint "intro x"
intro x
Hint "use g x"
use g x
Hint "rw [h₂]"
rw [h₂]
-- NewDefinition LeftInverse RightInverse
-- NewLemma Exists.choose Exists.choose_spec congrArg congrFun
-- DisabledLemma Function.bijective_iff_has_inverse
Hint (A B: Type) (f : A → B) (h : Bijective f) :
(∃ g, LeftInverse g f ∧ RightInverse g f) → Bijective f =>
"Test"
Conclusion
"Endlich entkommt ihr dem Tempel.
**Robo**: Da würden mich keine zehn Pferde nochmals hineinbringen!
**Du**: Von wegen Pferden, wie viele PS hat eigentlich unser Raumschiff?"