storytime

pull/54/head
Jon Eugster 2 years ago
parent b0d9816ce6
commit 32a17ed9e8

@ -11,7 +11,7 @@ import TestGame.Levels.Sum
import TestGame.Levels.Numbers
import TestGame.Levels.Inequality
import TestGame.Levels.LeanStuff
import TestGame.Levels.Lean
import TestGame.Levels.SetTheory
import TestGame.Levels.Function
import TestGame.Levels.SetFunction
@ -23,23 +23,22 @@ Game "TestGame"
Title "Lean 4 game"
Introduction
"
TODO
"
Conclusion
"Fertig!"
Path Proposition → Implication → Predicate
Path Predicate → Contradiction → Sum → LeanStuff
Path LeanStuff → SetTheory → SetTheory2 → SetFunction
Path Proposition → Implication → Predicate → Predicate → Contradiction → Sum → Lean
Path Predicate → Inequality → Sum
-- Path Predicate → Prime -- → Induction
-- Path Sum → Inequality -- → Induction
Path Lean → SetTheory → SetTheory2 → SetFunction
Path Lean → Function → SetFunction
Path Predicate → Prime -- → Induction
Path Sum → Inequality -- → Induction
Path Inequality → Function
Path SetTheory2 → Numbers
Path Module → Basis → Module2
Path LeanStuff → Function → SetFunction
MakeGame

@ -33,6 +33,6 @@ Hint : Bijective (fun (n : ) ↦ n + 1) =>
Conclusion
"Zufrieden drückt euch der Gelehrte eine neue Fackel in die Hand und
zeigt euch den Weg nach draussen."
zeigt euch den Weg nach draußen."
NewDefinition Bijective

@ -28,7 +28,7 @@ aber niemand von den Einwohnern wusste was davon...
erzählen…
Und damit leitet Robo den Landeanflug ein. Implis scheint ein riesiger Tagbau zu sein auf
dem nach allem möglichen gegraben wird. Überall seht ihr Förderbänder kreuz und queer.
dem nach allem möglichen gegraben wird. Überall seht ihr Förderbänder kreuz und quer.
Das Operationsteam begrüsst euch freundlich und lädt zum Essen im Kommandoturm.
"

@ -18,7 +18,8 @@ Statement (A B : Prop) (hb : B) : A → (A ∧ B) := by
Hint "**Du**: Einen Moment, das ist eine Implikation (`\\to`),
also `A` impliziert `A und B`, soweit so gut, also eine Tautologie.
**Robo**: Die scheinen hier `tauto` auch nicht zu verstehen.
**Robo**: Du hast recht, eigentlich könnte man `tauto` sagen,
aber das scheinen die hier tauto nicht zu verstehen.
Implikationen kannst du aber mit `intro h` angehen."
intro hA
Hint "**Du**: Jetzt habe ich also angenommen, dass `A` wahr ist und muss `A ∧ B` zeigen,

@ -8,7 +8,7 @@ Title "Implikation"
Introduction
"
Selbstsicher folgt ihr den Anweisungen und geht nach draussen zum
Selbstsicher folgt ihr den Anweisungen und geht nach draußen zum
defekten Kontrollelement. Dieses zeigt ein kompliziertes Diagram:
$$
\\begin{CD}

@ -37,7 +37,7 @@ hier bei `(h : A ↔ B)` heissen sie `h.mp` und `h.mpr`.
**Operationsleiter**: \"Modulo Ponens\" ist ein lokaler Begriff hier,
aber das ist doch nicht wichtig.
**Robo**: Und das \"r\" in `mpr` stünde für \"reverse\" weils die Rückrichtung ist.
**Robo**: Und das \"r\" in `mpr` stünde für \"reverse\" weil's die Rückrichtung ist.
"
NewTactic constructor

@ -3,6 +3,8 @@ import TestGame.Metadata
import Init.Data.ToString
-- #check List UInt8
set_option tactic.hygienic false
Game "TestGame"
World "Implication"
Level 7

@ -6,3 +6,11 @@ import TestGame.Levels.Inequality.L04_Linarith
Game "TestGame"
World "Inequality"
Title "Ungleichung"
Introduction "
Später erinnerst du dich gar nicht mehr wo und wann du diese Unterhaltung hattest, geschweige
denn mit wem. Vielleicht war es ein Traum, oder eine Erscheinung. Vielleicht war es
auch nur eines Abends über einer Runde Getränke.
Aber auf jedenfall hast du irgendwo gelernt, was du nun weisst.
"

@ -8,17 +8,26 @@ Title "Kleinergleich"
Introduction
"
Ungleichheiten werden in Lean generell immer als Kleinergleich `≤` (`\\le`) oder `<`
geschrieben.
*(Gesrpäch)*
Die Symbole `≥` und `>` gibt es zwar auch, sind aber nur Notation für die gleiche
Aussage mit `≤` und `<`.
**Robo** (*lallend*, oder war's fröhlich proklamierend?):
…und deshalb sind `≥` und `>` eigentlich nur Notationen für `≤`,
welches man übrigens `\\le` schreibt, was für Less-Equal (also Kleinergleich) steht…
Zudem sind `<` und `≤` auf `` so definiert, dass `0 < n` und `1 ≤ n` per Definition
äquivalent sind. Die folgende Aussage ist also mit `rfl` beweisbar.
**Du**: Wir haben's verstanden, man benützt also Standartmässig lieber `≤` und `<`,
aber damit weiß ich eh nichts anzufangen.
**dritte Person**: Komm schon, das kannst du ja sicher:
"
Statement
"$0 < n$ und $1 ≤ n$ sind äquivalente Aussagen."
(n m : ) : m < n ↔ m.succ ≤ n := by
(n m : ) : m < n ↔ m.succ ≤ n := by
Hint "**Robo**: Du Narr! Das ist doch eine Kuriosität, dass `m < n` auf `` per Definition
als `m + 1 ≤ n` definiert ist!
**dritte Person**: Du verdirbst den Witz! Ich wollte ihn doch nur testen."
rfl
OnlyTactic rfl
Conclusion "**Du**: Ha. ha… Na aber jetzt mal ehrlich, wie funktioniert das eigentlich?"

@ -12,67 +12,42 @@ Title "Kleinergleich"
Introduction
"
Es gibt zwei intrinsische Möglichkeiten, zu sagen dass `(n : )` nicht Null ist:
`n ≠ 0` oder `0 < n`.
*weitere Person*: …ich sag dir, eine positive Zahl kann man sowohl mit `0 < n`
als auch `n ≠ 0` darstellen.
Das folgende Lemma kannst du immer brauchen um zwischen den beiden zu wechseln.
*Robo*: Und da gibts leider keinen Standard dazu.
(*Note:* `0 < n` wird in Lemma-Namen oft mit `_pos` beschrieben anstatt `zero_lt`, siehe z.B.
`Nat.succ_pos`.)
**weitere Person*: Ja und, da kann man ja einfach mit `Nat.pos_iff_ne_zero`
wechseln. Wart mal, wieso galt das nochmals…
"
Statement Nat.pos_iff_ne_zero (n : ) : 0 < n ↔ n ≠ 0 := by
Hint "**Robo** (*flüsternd*): Wenn du ein bisschen schwere Maschinerie auffahren willst,
um in zu beeindrucken, hab ich was. Mach doch eine Fallunterscheidung ob `n` Null ist
oder nicht!
"
**Du** (*flüsternd*): Und wie geht das?
Statement Nat.pos_iff_ne_zero
"Benutze Induktion um zu zeigen, dass $0 < n$ und $n \\ne 0$ äquivalent sind."
(n : ) : 0 < n ↔ n ≠ 0 := by
induction n
**Robo** (*laut und selbstsicher*): Wir fangen mit `rcases n` an!"
rcases n
Hint "**Du**: Hmm, das muss man doch vereinfachen können.
**Robo** (*flüsternd*): Zweiter pompöser Auftritt: sag einfach `simp` und lass das alles
automatisch geschehen."
simp
Hint "**Du**: Und hier fang ich wohl am besten an wie ich das schon kenne."
constructor
intro
simp
intro
Hint "**Robo**: Warte! Den Rest geb ich dir als Lemma: `Nat.suc_pos`."
apply Nat.succ_pos
NewTactic simp
NewLemma Nat.succ_pos
DisabledLemma Nat.pos_iff_ne_zero
Hint : 0 < Nat.zero ↔ Nat.zero ≠ 0 =>
"Den Induktionsanfang kannst du oft mit `simp` lösen."
Hint (n : ) (h : 0 < n ↔ n ≠ 0) : 0 < Nat.succ n ↔ Nat.succ n ≠ 0 =>
"Jetzt der Induktionsschritt. Fang mal mit `constructor` an."
HiddenHint (n : ) : 0 < Nat.succ n → Nat.succ n ≠ 0 =>
"Auch das kann `simp`."
Hint (n : ) : n.succ ≠ 0 =>
"Auch das kann `simp`."
Hint (n : ) : 0 < Nat.succ n =>
"Hier kannst du das Lemma `Nat.succ_pos` mit `apply` anwenden."
/- Second, less ideal path -/
Hint (n : ) (h : 0 < n) : n ≠ 0 =>
"An dieser Stelle fürst du am besten einen Beweis durch Widerspruch."
HiddenHint (n : ) (h : 0 < n) : n ≠ 0 =>
"Das macht man mit `by_contra`."
Hint (n : ) (h : 0 < n) (g : n = 0) : False =>
"Brauche `rw [_] at _` um eine Annahme `0 < 0` zu erzeugen."
HiddenHint (h : 0 < 0) : False =>
"Mit `contradiction` schliesst du den Widerspruchsbeweis."
Hint (n : ) (h : n ≠ 0) : 0 < n =>
"Diese Richtung beweist du am besten per Induktion."
HiddenHint (n : ) (h : n ≠ 0) : 0 < n =>
"Starte mit `induction n`."
Conclusion "**Du**: Oh `simp` ist ja echt nicht schlecht…
HiddenHint : 0 < Nat.zero =>
"Mit `contradiction` kannst du den Induktionsanfang schliessen."
Die andere Person scheint beeindruckt, hat aber gleichzeitig auch das Bedürfnis, Dich aus
der Reserve zu locken."

@ -9,15 +9,18 @@ Title "Linarith"
Introduction
"
Die Taktik `linarith` kann alle Systeme von linearen (Un-)gleichungen über ``, ``, etc. lösen.
Über `` ist sie etwas schwächer, aber einfache Aussagen kann sie trotzdem beweisen.
**dritte Person**: Nah wenn wir so spielen:
"
Statement
"Wenn $n \\ge 2$, zeige, dass $n$ nich Null sein kann."
(n : ) (h : 2 ≤ n) : n ≠ 0 := by
Statement (n : ) (h : 2 ≤ n) : n ≠ 0 := by
Hint "**Du**: `simp` geht hier nicht, was mir ja auch einläuchtet.
**Robo**: Ist auch keine Vereinfachung, die du machen willst. Stattdessen,
`linarith` kann lineare Gleichungen und Ungleichungen lösen. Das ist das Powertool
in der hinsicht."
linarith
NewTactic linarith
NewLemma Nat.pos_iff_ne_zero
Conclusion "**Du**: Naja so beeindruckend war das jetzt auch noch nicht."

@ -9,20 +9,21 @@ Title "Linarith"
Introduction
"
Sobald man mit einem Ring arbeitet, der eine lineare Order hat (also z.B. `` oder ``),
ist `linarith` stärker und kann Systeme von Gleichungen und Ungleichungen angehen.
**Robo**: Die Taktik kann aber noch viel mehr.
`linarith` kann aber nur mit linearen Ungleichungen umgehen, mit Termen der Form `x ^ 2`
kann es nicht umgehen.
"
**weitere Person**: Hier, probier mal!
Statement
"
Angenommen man hat für zwei Ganzzahlen $x, y$ folgende Ungleichungen.
$$
\\begin{aligned} 5 * y &\\le 35 - 2 * x \\\\ 2 * y &\\le x + 3 \\end{aligned}
\\begin{aligned}
5 * y &\\le 35 - 2 * x \\\\
2 * y &\\le x + 3
\\end{aligned}
$$
Zeige, dass $y \\le 5$.
"
(x y : ) (h₂ : 5 * y ≤ 35 - 2 * x) (h₃ : 2 * y ≤ x + 3) : y ≤ 5 := by
Statement (x y : ) (h₂ : 5 * y ≤ 35 - 2 * x) (h₃ : 2 * y ≤ x + 3) : y ≤ 5 := by
linarith
Conclusion "**Du**: Boah, das ist schon gar nicht schlecht.
Und damit endet auch Deine Erinnerung und wer weiss was du anschließend gemacht hast…"

@ -0,0 +1,26 @@
import TestGame.Levels.Lean.L01_Type
import TestGame.Levels.Lean.L02_Universe
import TestGame.Levels.Lean.L03_ImplicitArguments
import TestGame.Levels.Lean.L04_InstanceArguments
Game "TestGame"
World "Lean"
Title "Lean"
Introduction
"Während ihr weiter durch Täler, über Geröllhalden und zwischen monumentalen Steintürmen
umherzieht, fragst Du eines Tages Robo.
**Du**: Sag mal, hast du dir je Gedanken dazu gemacht, wie du eigentlich funktionierts?
**Robo**: Was meinst du, wie ich funktioniere? Ich bin halt… ich…
**Du**: Ja schon, aber was woher weisst du denn alles was du weisst?
**Robo**: Das kann ich dir sagen. Früher habe ich viele Datenträger verschlungen,
und dadurch gelernt.
**Du**: Ob so eine Diskette wohl lecker schmeckt? Egal, ich hab ein paar Fragen zu deinem
Lean-Modul.
**Robo**: Na dann nur zu!"

@ -0,0 +1,41 @@
import TestGame.Metadata
import Mathlib
set_option tactic.hygienic false
Game "TestGame"
World "Lean"
Level 1
Title "Typen"
Introduction
"
**Du**: Also, wieso schreib ich denn sowohl `(n : )` für eine natürliche Zahl wie
auch `(h : A ∧ ¬ B)` für eine Aussage?
**Robo**: Alles in Lean sind Objekte von einem *Typen*, man nennt das auch
\"dependent type theory\". Rechts vom `:` steht immer der Typ der dieses Objekt hat.
**Du**: Verstehe, dann war `` der Typ der natürlichen Zahlen, `Prop` der Typ
aller logischen Aussagen, und so weiter. Un wenn `R` einfach irgendein Typ ist, dann…
**Robot: …würdest du das als `(R : Type)` schreiben.
**Du**: Also sind Typen ein bisschen jene Grundlage, die in meinem Studium die
Mengen eingenommen haben?
**Robo**: Genau. Ein Ring ist dann zum Beispiel als `(R : Type) [Ring R]` definiert,
also als Typen, auf dem eine Ringstruktur besteht.
**Robo**: Hier ein Beispiel. Die Taktik `ring` funktioniert in jedem Typen, der
genügend Struktur definiert hat, zum Beispiel in einem kommutativen Ring:
"
Statement (R : Type) [CommRing R] (a b : R) : a + b = b + a := by
ring
Conclusion "**Robo**: `[CommRing R]` nennt man übrigens eine Instanz und die
eckigen Klammern sagen Lean, dass es automatisch suchen soll, ob es so eine Instanz
findet, wenn man ein Lemma anwenden will."

@ -0,0 +1,44 @@
import TestGame.Metadata
import Mathlib
set_option tactic.hygienic false
Game "TestGame"
World "Lean"
Level 2
Title "Universen"
Introduction
"**Du**: Aber wenn alles Typen sind, welcher Typ hat dann `Type`?
**Robo**: `Type 1` und dieser hat Typ `Type 2`, etc.
**Robo**: Die Zahl nennt man *Universum*. Manchmal führt man Universen explizit
mit `universum u` ein, öfter siehst du `(R : Type _)`, was einfach ein Platzhalter
für irgend ein Universum ist.
**Du**: Das klingt ein bisschen nach Mengentheoretische Probleme, die man normalerweise
ignoriert.
**Robo**: Genau! Deshalb schreibt man eigentlich immer einfach `Type _` und ist glücklich.
Spezifischer muss man erst werden wenn man sowas wie Kategorientheorie anschaut, wo
man die Universen tatsächlich kontrollieren muss.
**Du**: Oke, hier rein, da raus. Aber hast du mir noch eine Aufgabe?
"
universe u
Statement
(R : Type u) [CommRing R] (a b : R) : a + b = b + a := by
Hint "**Robo**: Naja, Aufgaben zu Universen sind nicht so natürlich,
aber vorige Aufgabe würde man eigentlich besser so schreiben, da
kannst du mindestens das Uniersum beobachten."
ring
Conclusion "**Du**: Na dann. Aber gut dass ich's mal gesehen hab."
-- Hint (R : Type) (h : CommRing R) (a : R) (b : R) : a + b = b + a =>
-- ""

@ -0,0 +1,71 @@
import TestGame.Metadata
import Mathlib
import TestGame.Options.BigOperators
set_option tactic.hygienic false
Game "TestGame"
World "Lean"
Level 3
Title "Implizite Argumente"
Introduction
"
**Du**: Was mich aber mehr beschäftigt, ist, dass Lemmas manchmal viel mehr Argumente
haben als ich hinschreiben muss.
**Robo**: Lean kann manche Argumente aus dem Kontext erschliessen. Hast du zum Beispiel
ein Lemma von vorhin
```
lemma Fin.sum_univ_castSucc {β : Type _} [AddCommMonoid β] {n : } (f : Fin (n + 1) → β) :
∑ i : Fin (n + 1), f i = ∑ i : Fin n, f (↑Fin.castSucc.toEmbedding i) + f (Fin.last n) := by
sorry
```
dann reicht es ja Lean `f` zu geben und daraus kann es herausfinden, was die anderen
(`β`, `n`) sein müssen.
**Robo**: Solche *implizite Argumente* markiert man dann mit `{_ : _}` während
*explizite Arumente* mit `(_ : _)` markiert werden.
**Du**: Dann könnte ich also einfach `Fin.sum_univ_castSucc f` schreiben?
**Robo**: Genau!
**Du**: Und was war dann das `(n := m + 1)` vorhin genau?
**Robo**: Damit kann man im Aussnahmefall die impliziten Argumente doch angeben. Hier haben wir
gesagt, es soll für das Argument `n` den Term `m + 1` einsetzen. Hier mach das doch noch einmal
unter weniger Stress:
"
open BigOperators
Statement (m : ) : ∑ i : Fin (m + 1), (i : ) + (m + 1) = ∑ i : Fin (Nat.succ m + 1), ↑i := by
Branch
rw [Fin.sum_univ_castSucc]
Hint "**Robo**: Siehst du, ohne die Hilfe macht es das Falsche. Deshalb muss man hier
explizit mit `Fin.sum_univ_castSucc (n := m + 1)` nachhelfen."
rw [Fin.sum_univ_castSucc]
Hint "**Robo**: Na klar, in dem Beispiel kannst du einfach weiter umschreiben bis es
nicht mehr geht, aber das war nicht der Punkt…"
rw [Fin.sum_univ_castSucc]
Hint "**Robo**: Na klar, in dem Beispiel kannst du einfach weiter umschreiben bis es
nicht mehr geht, aber das war nicht der Punkt…"
rfl
rw [Fin.sum_univ_castSucc (n := m + 1)]
rfl
OnlyTactic rw rfl
Conclusion "**Du**: Gibt es auch noch ander Methoden implizite Argumente anzugeben.
**Robo** `@Fin.sum_univ_castSucc` würde *alle* Argumente explizit machen,
aber das ist unparktischer, weil man dann irgendwie
`@Fin.sum_univ_castSucc _ _ (m + 1)` schreiben müsste.
**Du**: Ah und ich sehe der `_` ist überall in Lean ein Platzhalter, der automatisch
gefüllt wird."

@ -0,0 +1,57 @@
import TestGame.Metadata
import Mathlib
import TestGame.Options.BigOperators
set_option tactic.hygienic false
Game "TestGame"
World "Lean"
Level 4
Title "Instanz-Argumente"
Introduction
"**Du**: Also nochmals als Zusammenfassung, dann gibt es 3 Arten von Argumenten,
explizite mit `()`, implizite mit `{}` und Instanzen mit `[]`?
**Robo**: Korrekt. Instanzen sind damit auch Implizite Argumente. Der Unterschied
zwischen `{}` und `[]` ist also *wie* Lean diese füllt.
**Du**: Verstehe, bei den ersten sucht es logisch nach einer richtigen Möglichkeit,
beim zweiten geht's durch alle Instanzen, die es kennt.
**Robo**: Funktioniert hier bei mir nicht, aber wenn du ausserhalb eines Beweises
`#synth Ring ` in ein Dokument schreibt, zeigt dir Lean, ob es eine Instanz finden kann.
**Du**: Ich glaube das macht alles Sinn.
**Robo**: Hier, mach nochmals das Gleiche wie vorhin aber mit @-Syntax um das zu
verinnerlichen:
"
open BigOperators
Statement (m : ) :
∑ i : Fin (m + 1), (i : ) + (m + 1) = ∑ i : Fin (Nat.succ m + 1), ↑i := by
Hint "*Robo*: Schreibe `rw [@Fin.sum_univ_castSucc _ _ (m + 1)]`
anstatt `rw [Fin.sum_univ_castSucc (n := m + 1)]`!"
rw [@Fin.sum_univ_castSucc _ _ (m + 1)]
rfl
OnlyTactic rw rfl
Conclusion "
**Du**: Danke Robo!
Um zwei weitere Ecken und plötzlich steht ihr wieder vor dem Golem, dem ihr schon begegnet seit.
Dieser lädt euch zum Abendmahl ein. Ihr erfährt, dass er ganz gerne liest und er zeigt euch
sein neustes Buch, das er leider nicht lesen kann. Nich tnur ist es der zweite Band einer Serie
(der Erste hat offensichtlich was mit \"Urbildern\" zu tun), sondern ist es auch in einem
Dialekt geschrieben, der anscheinend nur auf einem Nachbarsmond gesprochen wird.
Ihr beschliesst dem herzlichen Golem zu helfen und sowohl dem Mond einen Besuch abzustatten,
um den Dialekt zu lernen, wie auch auf dem zweiten Mond in der Bibliothek nach dem
ersten Band zu suchen.
"

@ -1,8 +0,0 @@
import TestGame.Levels.LeanStuff.L01_Type
import TestGame.Levels.LeanStuff.L02_Universe
import TestGame.Levels.LeanStuff.L03_ImplicitArguments
import TestGame.Levels.LeanStuff.L04_InstanceArguments
Game "TestGame"
World "LeanStuff"
Title "Lean"

@ -1,49 +0,0 @@
import TestGame.Metadata
import Mathlib
set_option tactic.hygienic false
Game "TestGame"
World "LeanStuff"
Level 1
Title "Typen"
Introduction
"
Dieses Kapitel führt ein paar Lean-spezifische Sachen ein, die du wissen solltest.
Mathematisch haben diese Sachen keinen Inhalt, aber es ist wichtig, dass du etwas
verstehst wie Lean manche Sachen macht.
Als erstes geht es um Typen.
Oft sieht man Argumente von der Form `(U : Type)` was heisst \"sei $U$ ein Typ.\"
Als Mathematiker kann man sich Typen ein bisschen wie Mengen vorstellen, in dem Sinn
dass sie die Grundlage der Mathematik bilden: Alles sind Typen.
Zum Beispiel ist `` der Typ der natürlichen Zahlen, `Prop` der Typ der logischen
Aussagen, und ein Ring ist ein Typ `(R : Type)` zusammen mit einer Instanz `[Ring R]`,
die sagt, dass auf diesem Typ eine Ringstruktur besteht.
**Achtung**: Wie du aber gleich sehen wirst sind Typen und Mengen in Lean unterschiedliche
Sachen.
Hier ein kleines Beispiel zu Typen und Instanzen:
"
Statement
""
(R : Type) [CommRing R] (a b : R) : a + b = b + a := by
ring
Hint (R : Type) (h : CommRing R) (a : R) (b : R) : a + b = b + a =>
"
Die Taktik `ring` funktioniert in jedem Typen,
ist aber stärker, je nach Instanz auf dem Typen.
In Mathlib sind Instanzen `[CommSemiring ]`, [CommRing ]`, `[Field ]`, etc. definiert.
Die Taktik `ring` muss eine dieser Instanzen finden, die sagen, dass die Addition kommutative ist,
damit das Lemma `add_comm` angewendet und die Aussage bewiesen werden kann.
"

@ -1,51 +0,0 @@
import TestGame.Metadata
import Mathlib
set_option tactic.hygienic false
Game "TestGame"
World "LeanStuff"
Level 2
Title "Universen"
Introduction
"
Ein weitere Syntax, den man in Lean abundzu sieht sind Universen.
Diese sind für Mathematiker erst einmal nicht so wichtig, und es reicht zu wissen,
dass diese existieren.
Da alle Objekte in Lean einen Typ haben, kann man sich fragen, welchen Typ hat eigentlich `Type`
selber? Die Anwort darauf ist dass `Type` vom Typ `Type 1` ist und dieses wiederum vom Typ `Type 2`
usw.
Da Lemmas in Lean gerne so algemein wie möglich formuliert werden, sieht man oft `(R : Type _)`
anstatt `(R : Type)`, wobei `_` einfach ein Platzhalter für eine Zahl ist.
Alternativ kann man auch explizit Universum-Levels definieren, so sind die folgenden beiden
Aussdrücke äquivalent:
```
variable (R : Type _)
universe u
variable (R : Type u)
```
In der Praxis kann man immer ohne bedenken `Type _` verwendenen und wenn man auf
(mengentheoretische)
Probleme stösst, muss man dan eventuell die Universen spezifizieren.
*Die Normalform ist eigentlich `(R : Type _)` zu schreiben solange man kein Grund hat
das Universum einzuschränken.*
"
Statement
""
(R : Type _) [CommRing R] (a b : R) : a + b = b + a := by
ring
-- Hint (R : Type) (h : CommRing R) (a : R) (b : R) : a + b = b + a =>
-- ""

@ -1,63 +0,0 @@
import TestGame.Metadata
import Mathlib
import TestGame.Options.BigOperators
set_option tactic.hygienic false
Game "TestGame"
World "LeanStuff"
Level 3
Title "Implizite Argumente"
Introduction
"
Auch wichtiger Syntax ist der Unterschied zwischen
impliziten und expliziten Argumenten von Lemmas. **Explizite Argumente**
schreibt man mit runden Klammern `()`, **impliziete Argumente** mit geschweiften `{}`.
Als implizit werden alle Argumente markiert, die Lean selbständig aus dem Kontext
erschliessen und einfüllen kann.
Als Beispiel schauen wir uns ein bekanntes Lemma an:
```
lemma Fin.sum_univ_castSucc {β : Type _} [AddCommMonoid β] {n : } (f : Fin (n + 1) → β) :
∑ i : Fin (n + 1), f i = ∑ i : Fin n, f (↑Fin.castSucc.toEmbedding i) + f (Fin.last n) := by
sorry
```
Hier ist unter anderem `n` als implizites Argument angegeben, da Lean aus `f` herauslesen kann,
was `n` sein muss. Falls man trotzdem einmal das implizites Argument angeben muss
(z.B. um `rw` zu helfen, wenn es mehrere Möglichkeiten gibt),
kann man dies mit `Fin.sum_univ_castSucc (n := m + 1)` machen.
"
open BigOperators
Statement
"Zeige $(\\sum_{i=0}^{m} i) + (m + 1) = \\sum_{i=0}^{m + 1} i$."
(m : ) :
∑ i : Fin (m + 1), (i : ) + (m + 1) = ∑ i : Fin (Nat.succ m + 1), ↑i := by
rw [Fin.sum_univ_castSucc (n := m + 1)]
rfl
OnlyTactic rw rfl
NewLemma Fin.sum_univ_castSucc
HiddenHint (m : ) :
∑ i : Fin (m + 1), (i : ) + (m + 1) = ∑ i : Fin (Nat.succ m + 1), ↑i =>
"Das Lemma `Fin.sum_univ_castSucc` hilft."
Hint (m : ) :
∑ i : Fin m, (Fin.castSucc.toEmbedding i : ) + ↑(Fin.last m) + (m + 1) =
∑ i : Fin (Nat.succ m + 1), ↑i =>
"Hier hat `rw` die falsche der beiden Summen umgeschrieben. Hilf ihm mit
`rw [Fin.sum_univ_castSucc (n := m + 1)]`."
Hint (m : ) :
∑ i : Fin (m + 1), (i : ) + (m + 1) =
∑ i : Fin (m + 1), ↑i + (m + 1) =>
"Jetzt sind beide Seiten gleich und das Goal kann mit `rfl` geschlossen werden."

@ -1,78 +0,0 @@
import TestGame.Metadata
import Mathlib
import TestGame.Options.BigOperators
set_option tactic.hygienic false
Game "TestGame"
World "LeanStuff"
Level 4
Title "Instanz-Argumente"
Introduction
"
Bezüglich impliziten Argumente gibt es noch einige weitere Punkte oder Tricks,
die man wissen sollte.
* Instanz-Argumente wie `[Ring R]` sind auch impilzite Argumente. Der Unterschied ist, dass
Lean einen anderen Mechanismus braucht, um diese zu füllen: Es sucht nach einer entsprechenden
*Instanz* und, setzt die erste solche Instanz ein.
Ausserhalb eines Beweises könnte man auch mit
```
#synth Ring
```
testen, ob Lean eine ensprechende Instanz findet. Instanzen werden dafür gebraucht, Typen
mit (algebraischer) Stukturen zu versehen.
* Ein `_` irgendwo im Lean-Code ist immer ein Platzhalter, den Lean versucht aus dem Kontext zu
füllen. Das kann praktisch sein, wenn man etwas nicht ausschreiben will, das offensichtlich ist.
* Mit `@` kann man forcieren, dass alle Argumente explizit sind.
Für ein Lemma
```
lemma not_or_of_imp {A B : Prop} (h : A → B) :
¬A B := sorry
```
heisst das zum Beispiel dass `not_or_of_imp g` das gleiche ist wie
`@not_or_of_imp _ _ g`.
Und `Fin.sum_univ_castSucc (n := m + 1)` könnte man auch als
`@Fin.sum_univ_castSucc _ _ (m + 1)` schreiben.
"
open BigOperators
Statement
"Zeige $(\\sum_{i=0}^{m} i) + (m + 1) = \\sum_{i=0}^{m + 1} i$."
(m : ) :
∑ i : Fin (m + 1), (i : ) + (m + 1) = ∑ i : Fin (Nat.succ m + 1), ↑i := by
rw [Fin.sum_univ_castSucc (n := m + 1)]
rfl
OnlyTactic rw rfl
NewLemma Fin.sum_univ_castSucc
Hint (m : ) :
∑ i : Fin (m + 1), (i : ) + (m + 1) = ∑ i : Fin (Nat.succ m + 1), ↑i =>
"
Probier nochmals das gleiche, diesmal mit
```
rw [@Fin.sum_univ_castSucc _ _ (m + 1)]
```
anstatt
```
rw [Fin.sum_univ_castSucc (n := m + 1)]
```
"
Hint (m : ) :
∑ i : Fin m, (Fin.castSucc.toEmbedding i : ) + ↑(Fin.last m) + (m + 1) =
∑ i : Fin (Nat.succ m + 1), ↑i =>
"Sackgasse!"
Hint (m : ) :
∑ i : Fin (m + 1), (i : ) + (m + 1) =
∑ i : Fin (m + 1), ↑i + (m + 1) =>
"Jetzt sind beide Seiten gleich und das Goal kann mit `rfl` geschlossen werden."

@ -1,8 +1,11 @@
import TestGame.Levels.Prime.L01_Dvd
import TestGame.Levels.Prime.L04_Prime
import TestGame.Levels.Prime.L05_Prime
import TestGame.Levels.Prime.L06_ExistsUnique
-- import TestGame.Levels.Prime.L04_Prime
-- import TestGame.Levels.Prime.L05_Prime
-- import TestGame.Levels.Prime.L06_ExistsUnique
Game "TestGame"
World "Prime"
Title "Teilbarkeit"
Introduction "Ihr schlendert durch die Befestigung ohne direktes Ziel. Und sprecht mit
verschiedenen Einwohnern."

@ -1,6 +1,7 @@
import TestGame.Metadata
import Mathlib.Tactic.Ring
import Mathlib
Game "TestGame"
World "Prime"
@ -10,43 +11,39 @@ Title "Teilbarkeit"
Introduction
"
Die Aussage \"$m$ teilt $n$.\" wird in Lean als `m | n` (`\\|`) geschrieben.
Ihr begenet einer Frau, die mit Vorschlaghammer und Schaufel anscheinend an einer Erweiterung
ihres Hauses baut. Im gespräch erzählt sie euch wie die Dornenwände gezüchtet wurden vor ihrer
Zeit, und über's Wetter und so.
**Wichtig:** `` (Teilbarkeit) ist ein spezielles Unicode Symbol, das nicht dem
senkrechten Strich auf der Tastatur (`|`) entspricht. Man erhält es mit `\\|`.
`m n` bedeutet `∃ c, n = m * c`, das heisst, man kann damit genau gleich umgehen
wie mit einem `∃`-Quantifier.
**Handwerkerin**: (*langer Monolog*) …, und dann gestern habe ich zwei Herren überhört,
wie sie an folgender Aufgabe gesessen sind, könnt ihr mir das erklären?
"
Statement dvd_add
"Wenn $m$ ein Teiler von $n$ und $k$ ist, dann teilt es die Summe."
(n m k : ) (h : m n) (g : m k) : m n + k := by
-- Die Aussage \"$m$ teilt $n$.\" wird in Lean als `m | n` (`\\|`) geschrieben.
-- **Wichtig:** `` (Teilbarkeit) ist ein spezielles Unicode Symbol, das nicht dem
-- senkrechten Strich auf der Tastatur (`|`) entspricht. Man erhält es mit `\\|`.
-- `m n` bedeutet `∃ c, n = m * c`, das heisst, man kann damit genau gleich umgehen
-- wie mit einem `∃`-Quantifier.
Statement dvd_add (n m k : ) (h : m n) (g : m k) : m n + k := by
Hint "**Robo**: `n m` bedeutet \"$n$ teilt $m$\", der senkrechte Strich ist allerdings
ein spezieller, den man mit `\\|` schreibt.
Definiert ist dieses Symbol als `∃ c, n = m * c`.
**Du**: Dann kann ich direkt `rcases` und `use` verwenden, wie wenns ein `∃` wäre?
**Robo**: Genau!"
Hint (hidden := true) "**Robo**: Fang doch damit an, mit `rcases _ with ⟨x ,hx⟩`
alle Hyptothesen aufzuteilen."
rcases h with ⟨x, h⟩
rcases g with ⟨y, g⟩
Hint (hidden := true) "**Robo**: Jetzt musst du mit `use _` eine Zahl angeben so dass
`{n} + {k} = {m} * _` gilt."
use x + y
Hint (hidden := true) "**Du**: Mit ein bisschen umschreiben kann man sicer `ring` verwenden."
rw [h, g]
ring
HiddenHint (n : ) (m : ) (k : ) (h : m n) (g : m k) : m n + k =>
"
Wenn man explizit mit der Definition von Teilbarkeit arbeiten will,
sollte man als erstes alle Annahmen der Form `x y` mit `rcases` aufteilen.
"
HiddenHint (n : ) (m : ) (k : ) (x : ) (h : n = m * x) (g : m k) : m n + k =>
"
Wenn man explizit mit der Definition von Teilbarkeit arbeiten will,
sollte man als erstes alle Annahmen der Form `x y` mit `rcases` aufteilen.
"
HiddenHint (n : ) (m : ) (k : ) (y : ) (h : m n) (g : k = m * y) : m n + k =>
"
Wenn man explizit mit der Definition von Teilbarkeit arbeiten will,
sollte man als erstes alle Annahmen der Form `x y` mit `rcases` aufteilen.
"
HiddenHint (n : ) (m : ) (k : ) (x : ) (y : ) (h : n = m * x) (g : k = m * y) : m n + k =>
"
Jetzt kannst du mit `use` eine Zahl angeben, so dass $m * X = n + k$.
"
DisabledLemma dvd_add

@ -22,9 +22,9 @@ Statement ""
**Robo** Ja. Da kommst Du jetzt selbst drauf, wie das geht, oder?
"
Hint (hidden := true) "Ist doch genau wie eben:
Hint (hidden := true) "**Robo**: Ist doch genau wie eben:
die Aussage, die zu beweisen ist, gehört selbst zu den Annahmen.
Also wird `asumption` auch wieder funktionieren."
Also wird `assumption` auch wieder funktionieren."
assumption
Conclusion

@ -12,12 +12,12 @@ Title "Und"
Introduction
"
Der nächste Formalosoph in der Reihe hat seine Frage bereìts mitgebracht.
Er legt sie uns vor, setzt sich hin, und häkelt.
Er legt sie uns vor, setzt sich hin und häkelt.
"
Statement "" (A B : Prop) (hA : A) (hB : B) : A ∧ B := by
Hint
"
**Du**: Also, wir haben zwei Annahmen: `{A}` gilt, und `{B}` gilt. Auch. Und beweisen sollen wir
**Du**: Also, wir haben zwei Annahmen: `{A}` gilt, und `{B}` gilt auch. Und beweisen sollen wir
dass `{A} und {B}` gilt. Ich glaube, diese Formalospinner treiben mich noch zur Verzweiflung.
Kann ich nicht wieder `trivial` sagen?

@ -8,3 +8,13 @@ import TestGame.Levels.Sum.L06_Summary
Game "TestGame"
World "Sum"
Title "Endliche Summe"
Introduction "Mit dem Gefühl, dass sich *Evenine* und *Oddeus* in Zukunft wieder
besser verstehen werden, steigt ihr in eurer Raumschiff und setzt eure Reise fort.
Bald erreicht ihr einen neuen Planet. Die oberfläche scheint steinig zu sein, aber nicht etwa
geröll oder Chaos. Stattdessen, scheinen unzählige Steinplatten zu bizzaren hohen Türme
gestapelt und die ganze Landschaft wirkt wie ein grosses Puzzle in dem jede Platte
feinsäuberlich auf den darunterliegenden Platten aufbaut.
Bald trefft ihr auch die Bewohner dieses Planeten an."

@ -12,36 +12,55 @@ Title "Simp"
Introduction
"
**Unbekannte**: Willkommen auf *Indu*, unserem Planeten! Bevor ich euch herumzeigen will,
sagt mir, ob ihr unsere Lebensweise zu verstehen und schätzen wisst:
In diesem Kapitel lernen wir endliche Summen und mehr Übungen zur Induktion.
Eine endliche Summe läuft erstmal immer über einen endlichen Index
`Fin n`, welcher $n$ Elemente
$\\{0, 1, \\ldots, n-1\\}$ beinhaltet.
"
Der Syntax für $\\sum_{i=0}^n a_i$ ist `∑ i : Fin n, _` (\\sum)
-- Eine endliche Summe läuft erstmal immer über einen endlichen Index
-- `Fin n`, welcher $n$ Elemente
-- $\\{0, 1, \\ldots, n-1\\}$ beinhaltet.
Als erstes kann die Taktik `simp` (für \"simplification\") ganz viel Triviales vereinfachen.
`simp` ist eine der stärksten Taktiken in Lean und verwendet
ganz viele markierte Lemmas um das Goal zu vereinfachen.
-- Der Syntax für $\\sum_{i=0}^n a_i$ ist `∑ i : Fin n, _` (\\sum)
Zum Beispiel kennt es ein Lemma das ungefähr so aussieht:
-- Als erstes kann die Taktik `simp` (für \"simplification\") ganz viel Triviales vereinfachen.
-- `simp` ist eine der stärksten Taktiken in Lean und verwendet
-- ganz viele markierte Lemmas um das Goal zu vereinfachen.
```
@[simp]
lemma sum_const_add (n : ) : (∑ i in Fin n, 0) = 0 := by
sorry
```
-- Zum Beispiel kennt es ein Lemma das ungefähr so aussieht:
Die Taktik `simp` benützt alle Lemmas, die mit `@[simp]` markiert sind.
-- ```
-- @[simp]
-- lemma sum_const_add (n : ) : (∑ i in Fin n, 0) = 0 := by
-- sorry
-- ```
(Tipp: `simp?` zeigt an, welche Lemmas `simp` benutzen würde.)
"
-- Die Taktik `simp` benützt alle Lemmas, die mit `@[simp]` markiert sind.
-- (Tipp: `simp?` zeigt an, welche Lemmas `simp` benutzen würde.)
open BigOperators
Statement
"Zeige $\\sum_{i = 0} ^ {n-1} (0 + 0) = 0$."
(n : ) : (∑ i : Fin n, (0 + 0)) = 0 := by
Statement (n : ) : (∑ i : Fin n, (0 + 0)) = 0 := by
Hint "BUG"
-- TODO (Bug): Invalid escape sequence:
-- "**Du**: Oh das ist ganz schön viel neues… Mal sehen, das sagt wohl
-- $( \\sum_i 0 + 0 ) = 0$. Dann ist das vielleicht doch nicht so komplex.
-- **Robo**: Genau! Man schreibt `\\sum`. Beachte den Index:
-- $( \\sum_\{i=0}^\{n-1} 0 + 0 ) = 0$, also `Fin n` ist ein Typ mit den Elementen
-- $\\{0, \\ldots, n-1\\}$.
-- **Du**: Oke, also `Fin n` hat `n` Elemente. Und was mach ich jetzt?
-- **Robo**: `simp` ist eine ganz starke Taktik, die viele Terme vereinfacht, wir
-- fangen besser an, diese zu benützen.
-- Irgendwie hast du das Gefühl ein Déjà-vue zu haben…"
simp
NewTactic simp
OnlyTactic simp
Conclusion "**Unbekannte**: Sehr gut, folgt mir!"

@ -13,29 +13,42 @@ Title "endliche Summe"
Introduction
"
Generell sind aber nur solche Lemmas `@[simp]` markiert, klar eine Vereinfachung darstellen.
So ist ein Lemma wie `Finset.sum_add_distrib` kein `simp`-Lemma, da beide Seiten je
nach Situation bevorzugt sein könnte:
$$
\\sum_{i = 0}^n a_i + b_i = \\sum_{i = 0}^n a_i + \\sum_{j = 0}^n b_j
$$
Dieses Lemma kann aber mit `rw` angewendet werden.
Während euch die Person zu einem besonders herausragenden Steinturm führt, löchert
sie euch noch weiter mit Fragen.
"
open BigOperators
Statement
"Zeige dass $\\sum_{i=0}^{n-1} (i + 1) = n + \\sum_{i=0}^{n-1} i$."
"$\\sum_{i=0}^{n-1} (i + 1) = n + \\sum_{i=0}^{n-1} i$."
(n : ) : ∑ i : Fin n, ((i : ) + 1) = n + (∑ i : Fin n, (i : )) := by
-- Hint "**Du**: Hmm, wieder `simp`?
-- **Robo**: Nicht ganz. `simp` benützt nur Lemmas, die klar eine Vereinfachung darstellen,
-- und in deiner Bibliothek mit `@[simp]` markiert wird. Hier brauchen wir eine andere
-- Umformung:
-- $$
-- \\sum_\{i = 0}^n a_i + b_i = \\sum_\{i = 0}^n a_i + \\sum_\{j = 0}^n b_j
-- $$
-- **Robo*: Da unklar ist, welche Seite \"einfacher\" ist, wird so ein Lemma nicht mit
-- `@[simp]` markiert. Das heisst du musst `Finset.sum_add_distrib` mit `rw`
-- explizit anwenden.
-- "
rw [Finset.sum_add_distrib]
Hint "Die zweite Summe `∑ x : Fin n, 1` kann `simp` zu `n` vereinfacht werden."
Hint "**Robo**: Die zweite Summe `∑ x : Fin n, 1` kann jetzt aber mit
`simp` zu `n` vereinfacht werden."
simp
Hint "Bis auf Umordnung sind jetzt beide Seiten gleich, darum kann `ring` das Goal schließen.
Hint "**Robo**: Bis auf Umordnung sind jetzt beide Seiten gleich!
Alternativ kann man auch mit `rw [add_comm]` dies explizit umordnen."
**Du**: Dann greift jetzt wohl `ring`!
**Robo**: Genau! Und alternativ könntest du mit `rw [add_comm]` die Arbeit von `ring`
auch manuell machen."
ring
NewLemma Finset.sum_add_distrib add_comm
Conclusion "Eure Begleitung scheint mit der Antwort zu frieden zu sein und zeigt
freudig an dem Turm empor, den ihr soeben erreicht habt."

@ -15,78 +15,75 @@ Title "Arithmetische Summe"
Introduction
"
Oft beweist man Aussagen über Summen am besten per Induktion.
**Du**: Wie werden solche Meisterwerke eigentlich gebaut?
Mit `induction n` startet man einen Induktionsbeweis. Dies erstellt 2 neue Goals:
Da zeigt eure Begleitung auf eine kleine Steinplatte neben dem Eingang,
auf der eien Beschreibung gekritzelt ist.
* **Induktionsanfang**: `n = 0`. Dieser kann ganz oft direkt mit `simp` bewiesen werden.
* **Induktionsschritt**: Man kriegt eine Annahme `(n_ih : P n)` und muss `P (n + 1)`
beweisen. Für endliche Summen will man normalerweise danach zuerst
`rw [Fin.sum_univ_castSucc]` brauchen, welches
$$\\sum_{i=0}^{n} a_i = \\sum_{i=0}^{n-1} a_i + a_n$$
umschreibt.
**Bemerkung:**
Eine technische Sonderheit bezüglich endlichen Summen
ist der kleine Pfeil `↑` in `∑ i : Fin (n + 1), ↑i`.
Da `i : Fin n` technisch keine natürliche Zahl ist (sondern vom Typ `Fin n`), muss man
dieses zuerst mit `↑i` oder `(i : )` in eine natürliche Zahl umwandeln. Diese nennt man
*Coersion*.
Gleichermassen, kommen hier im Induktionsschritt die Terme `↑(↑Fin.castSucc.toEmbedding i)`
und `↑(Fin.last (n + 1))` vor. Diese Terme können mit `simp` vereinfacht werden.
**Robo**: Das ist wohl der bekannte arithmetische Turm von Indu, über den hab ich schon
einmal Daten verarbeitet. Und die antwort auf deine Frage: Vermutlich ein Stein nach
dem anderen.
"
open BigOperators
Statement arithmetic_sum
"Zeige $2 \\cdot \\sum_{i = 0}^n i = n \\cdot (n + 1)$."
"$2 \\cdot \\sum_{i = 0}^n i = n \\cdot (n + 1)$."
(n : ) : 2 * (∑ i : Fin (n + 1), ↑i) = n * (n + 1) := by
Hint "**Du**: Klar, die werden ja nicht oben anfangen mit bauen. Sag mal,
wie zeige ich denn die arithmetische Summe, die hier gekritzelt steht?
Ich würde gerne Induktion über $n$ anwenden.
**Robo**: Ja dann ist's einfach `induction n`, ist doch logisch!"
induction n
Hint "**Du**: Zuerst den Induktionsanfang…
**Robo**: Diesen kannst du oft mit `simp` abkürzen!"
simp
Hint "**Robo**: Jetzt im Induktionsschritt: Bei Induktion über endlichen Summen willst du
immer mit `rw [Fin.sum_univ_castSucc]` anfangen" -- :
-- $$\\sum_\{i=0}^n a_i = \\sum_{i=0}^\{n-1} a_i + a_n$$"
rw [Fin.sum_univ_castSucc]
rw [mul_add]
-- TODO: Bug. Dieser Hint wird nicht angezeigt.
Hint "**Du**: Oh das sieht jetz aber kompliziert aus…
**Robo**: Da musst du etwas drüber hinweg lesen. Am besten machst du kurz `simp`,
dann sieht's schon wieder besser aus."
simp
rw [n_ih]
rw [Nat.succ_eq_add_one]
ring
Hint "**Du**: Was bedeutet eigentlich der kleine Pfeil `↑`?
NewTactic induction
NewLemma Fin.sum_univ_castSucc Nat.succ_eq_add_one mul_add add_mul
**Robo**: Das ist eine *Coersion*. Sowas wie wenn man eine natürliche Zahl als Integer anschaut,
also die natürliche Abbildung ``. Oder hier, wenn ein Element `x : Fin n` stattdessen als
Element in `(↑x : )` angeschaut wird.
Hint (n : ) : 2 * (∑ i : Fin (n + 1), ↑i) = n * (n + 1) =>
"Als Erinnerung, einen Induktionsbeweis startet man mit `induction n`."
**Robo**: Übrigens, um die Induktionshypothese anzuwenden brauchst du zuerst das Lemma
`mul_add`."
rw [mul_add]
Hint "**Du**: Und wie wende ich jetzt die Induktionshypothese an?
Hint : 2 * ∑ i : Fin (Nat.zero + 1), ↑i = Nat.zero * (Nat.zero + 1) =>
"Den Induktionsanker $n = 0$ kann `simp` oft beweisen."
**Robo mit `rw` wie jede andere Annahme auch."
rw [n_ih]
Hint "**Robo**: Jetzt musst du noch kurz `rw [Nat.succ_eq_add_one]` anwenden.
Hint (n : ) (hn : 2 * ∑ i : Fin (n + 1), ↑i = n * (n + 1)) :
2 * ∑ i : Fin (Nat.succ n + 1), ↑i = Nat.succ n * (Nat.succ n + 1) =>
"Den Induktionsschritt beginnt man oft mit `rw [Fin.sum_univ_castSucc]`."
**Du**: Aber wieso?
-- Hint (n : ) (hn : 2 * ∑ i : Fin (n + 1), ↑i = n * (n + 1)) :
-- 2 * (∑ i : Fin (n + 1), ↑(Fin.castSucc.toEmbedding i) +
-- ↑(Fin.last (n + 1))) = Nat.succ n * (Nat.succ n + 1) =>
-- "Die Taktik `simp` vereinfacht `↑(↑Fin.castSucc.toEmbedding i)`. "
**Robo**: Naja, `ring` ist jetzt auch noch nicht so stark, und erkennt nicht dass `n.succ`
und `n + 1` das gleiche sind.
Hint (n : ) (hn : 2 * ∑ i : Fin (n + 1), ↑i = n * (n + 1)) :
2 * (∑ x : Fin (n + 1), ↑x + (n + 1)) = Nat.succ n * (Nat.succ n + 1) =>
"Um Die Induktionshypothese anzuwenden muss man noch
$$2 \\cdot ((\\sum_\{x=0}^n x) + (n + 1)) = 2 \\cdot \\sum_\{x=0}^n x + 2 \\cdot (n + 1))$$
umschreiben. Dazu kannst du `mul_add` benützen.
"
**Du**: Aber das könnte man doch ändern, oder?
Hint (n : ) (hn : 2 * ∑ i : Fin (n + 1), ↑i = n * (n + 1)) :
2 * ∑ x : Fin (n + 1), ↑x + 2 * (n + 1) = Nat.succ n * (Nat.succ n + 1) =>
"`simp` vereinfacht `↑(↑Fin.castSucc.toEmbedding i)` zu `↑i`.
Danach kann die Induktionshypothese mit `rw` angewendet werden."
**Robo**: Vielleicht wenn wir einmal einem Techniker begegnen, der mir ein Update
einspielen kann…"
Branch
ring_nf
Hint "**Robo**: Wie gesagt, brauch doch `rw [Nat.succ_eq_add_one]` als Fix für meine
kleinen Maken."
rw [Nat.succ_eq_add_one]
ring
Hint (n : ) (hn : 2 * ∑ i : Fin (n + 1), ↑i = n * (n + 1)) :
n * (n + 1) + 2 * (n + 1) = Nat.succ n * (Nat.succ n + 1) =>
"
Im Moment muss man hier `ring` noch helfen,
indem man mit `rw [Nat.succ_eq_add_one]` zuerst `Nat.succ n = n + 1` umschreibt.
NewTactic induction
NewLemma Fin.sum_univ_castSucc Nat.succ_eq_add_one mul_add add_mul
(Dies wird irgendwann noch gefixt)
"
Conclusion "Du schaust dich um und bewunderst das Tal in dem hunderte, wenn nicht tausende,
Steintürme in allen Formen und Höhen stehen."

@ -12,47 +12,22 @@ Title "Summe aller ungeraden Zahlen"
Introduction
"
Hier nochmals eine Übung zur Induktion.
**Du**: Haben eigentlich alle Türme hier so kryptische Beschreibungen am Eingang?
Du gehst zu einem etwas kleineren Nachbarsturm.
"
set_option tactic.hygienic false
open BigOperators
Statement odd_arithmetic_sum
"Zeige folgende Gleichung zur Summe aller ungeraden Zahlen:
$\\sum_{i = 0}^n (2n + 1) = n ^ 2$."
"$\\sum_{i = 0}^n (2n + 1) = n ^ 2$."
(n : ) : (∑ i : Fin n, (2 * (i : ) + 1)) = n ^ 2 := by
induction' n with n hn
Hint "**Robo**: Das funktioniert genau gleich wie zuvor, viel Glück."
induction n
simp
rw [Fin.sum_univ_castSucc]
simp
rw [hn]
rw [n_ih]
rw [Nat.succ_eq_add_one]
ring
HiddenHint (n : ) : (∑ i : Fin n, (2 * (i : ) + 1)) = n ^ 2 =>
"
Fange wieder mit `induction {n}` an.
"
HiddenHint : ∑ i : Fin Nat.zero, ((2 : ) * i + 1) = Nat.zero ^ 2 =>
"
Den Induktionsanfang kannst du wieder mit `simp` beweisen.
"
HiddenHint (n : ) : ∑ i : Fin (Nat.succ n), ((2 : ) * i + 1) = Nat.succ n ^ 2 =>
"
Den Induktionsschritt startest du mit `rw [Fin.sum_univ_castSucc]`.
"
HiddenHint (n : ) (hn : ∑ i : Fin n, (2 * (i : ) + 1) = n ^ 2) :
∑ x : Fin n, (2 * (x : ) + 1) + (2 * n + 1) = Nat.succ n ^ 2 =>
"
Hier kommt die Induktionshypothese {hn} ins Spiel.
"
HiddenHint (n : ) : n ^ 2 + (2 * n + 1) = Nat.succ n ^ 2 =>
"
Mit `rw [Nat.succ_eq_add_one]` und `ring` kannst du hier abschliessen.
"

@ -18,19 +18,32 @@ Title "Summe vertauschen"
Introduction
"
Verschachtelte endliche Summen kann man beliebig tauschen.
Nun aber zeigt euch eure Begleiterin zwei weitere Türme mit einer kleinen Brücke, die
zwischen den beiden verläuft. Die Tafel am Eingang wurde von einem herunterfallenden Stein
zerstört. Auf der oberen Hälfte steht nur folgendes:
$$\\sum_{i=0}^n\\sum_{j=0}^m a_{ij} = \\sum_{j=0}^m\\sum_{i=0}^n a_{ij}$$
Dieses Lemma heisst `Finset.sum_comm`
**Du**: Ich glaube, ich kann das in eurem Dialekt formulieren und euch damit helfen!
"
Statement
"Zeige dass
$\\sum_{i=0}^n\\sum_{j=0}^m 2^i (1 + j) = \\sum_{j=0}^m\\sum_{i=0}^n 2^i (1 + j)$."
(n m : ) : ∑ i : Fin n, ∑ j : Fin m, ( 2^i * (1 + j) : ) =
(n m : ) : ∑ i : Fin n, ∑ j : Fin m, ( 2^i * (1 + j) : ) =
∑ j : Fin m, ∑ i : Fin n, ( 2^i * (1 + j) : ) := by
Hint "**Robo**: Das sieht gut aus, aber du solltest das kurz beweisen, um sicher zu sein.
**Du**: Hast du nicht ein Lemma dafür?
**Robo**: Doch, probier mal `Finset.sum_comm`."
rw [Finset.sum_comm]
NewLemma Finset.sum_comm
Conclusion "
Euer Begleiter ist ganz begeistert als er dir das Stück Papier aus den Händen nimmt,
auf dem du die Aussage gekritzelt hast. Gleich zückt sie einen Meißel und beginnt eine
neue Platte zu erstellen.
Ihr winkt ihr noch zum Abschied und geht weiter.
"

@ -17,109 +17,76 @@ Title "Zusammenfassung"
Introduction
"
Zusammenfassung aus diesem Kapitel
**Du**: Robo gib mir nochmals eine Übersicht, bitte.
## Notationen / Begriffe
**Robo**: Aber klar:
| | Beschreibung |
|:---------------------|:------------------------------------------|
| `Fin n` | Ist ein Typ mit Zahlen $0, \\ldots, n-1$. |
| `∑ (i : Fin n), a i` | $\\sum_{i=0}^{n-1} a_i$ |
| `↑i` | Eine Coersion, z.B. `Fin n → `. |
## Taktiken
und
| | Taktik | Beispiel |
|:---|:--------------------------|:-------------------------------------|
| 20 | `simp` | Simplifikation. |
| 21 | `induction n` | Induktion über $n$ |
| 21 | `simp` | Simplifikation. |
| 22 | `induction n` | Induktion über $n$ |
Und hier noch eine etwas schwierigere Übung.
Das Resultat aus Level 3 kannst du als `arithmetic_sum` wiederverwenden:
$$
2 \\cdot \\sum_{i = 0}^n i = n \\cdot (n + 1)
$$
Da löst sich aus der Steinlandschaft plötzlich ein grosser Steingolem. Er schaut euch
bedrohlich an und fragt in tiefer Stimme:
"
open BigOperators
Statement
"Zeige $\\sum_{i = 0}^m i^3 = (\\sum_{i = 0}^m i)^2$."
(m : ) : (∑ i : Fin (m + 1), (i : )^3) = (∑ i : Fin (m + 1), (i : ))^2 := by
induction' m with m hm
Statement (m : ) : (∑ i : Fin (m + 1), (i : )^3) = (∑ i : Fin (m + 1), (i : ))^2 := by
Hint "**Du**: Gulp. Naja das wird schon klappen. Also man fängt wieder mit Induktion an…"
induction m
Hint "**Du**: Also den Induktionsanfang kann man einfach zeigen…"
simp
Hint "**Robo**: Und jetzt wieder `rw [Fin.sum_univ_castSucc]` und `simp` um vorwärts zu
kommen!"
rw [Fin.sum_univ_castSucc]
simp
rw [hm]
rw [Fin.sum_univ_castSucc (n := m + 1)]
simp
rw [add_pow_two]
rw [arithmetic_sum]
ring
Hint "**Robo**: Siehst du die Induktionshypothese hier drin?"
rw [n_ih]
Hint "**Du**: Ok, damit habe ich die linke Seite der Gleichung ziemlich gut bearbeitet.
Aber, ehm, mit der Rechten komme ich nicht weiter…
NewLemma arithmetic_sum add_pow_two
Der Golem schaut dich finster an.
HiddenHint (m : ) : ∑ i : Fin (m + 1), (i : ) ^ 3 = (∑ i : Fin (m + 1), ↑i) ^ 2 =>
"Führe auch hier einen Induktionsbeweis."
**Robo**: Du willst `Fin.sum_univ_castSucc` auf der rechten Seite anwenden, aber es
gibt mehrere Orte, wo das Lemma passen würde.
Deshalb musst du mit `rw [Fin.sum_univ_castSucc (n := {n} + 1)]` angeben, wo genau.
HiddenHint : ∑ i : Fin (Nat.zero + 1), (i : ) ^ 3 = (∑ i : Fin (Nat.zero + 1), ↑i) ^ 2 =>
"`simp` kann den Induktionsanfang beweisen."
**Du**: Was bedeutet das?
Hint (m : ) : ∑ i : Fin (Nat.succ m + 1), (i : ) ^ 3 = (∑ i : Fin (Nat.succ m + 1), ↑i) ^ 2 =>
"Im Induktionsschritt willst du das Goal so umformen, dass du folgende Therme
ersetzen kannst:
* `∑ i : Fin (m + 1), ↑i ^ 3` (Induktionshypothese)
* `2 * (∑ i : Fin (m + 1), ↑i)` (arithmetische Summe)
"
HiddenHint (m : ) : ∑ i :
Fin (Nat.succ m + 1), (i : ) ^ 3 = (∑ i : Fin (Nat.succ m + 1), ↑i) ^ 2 =>
"
Als erstes kannst du mal mit dem bekannten `rw [Fin.sum_univ_castSucc]` anfangen.
"
**Robo** Das Lemma hat eine Annahme `n` und du sagst ihm explizit, was es für dieses `n`
einsetzen muss, nämlich `{n} + 1`"
Branch
rw [Fin.sum_univ_castSucc]
Hint "**Robo**: Das hat jetzt einfach `Fin.sum_univ_castSucc` am ersten Ort angewendet,
wo das möglich war. Das ist nicht so ideal, die like Seite war schon okay.
HiddenHint (m : ) : ∑ i : Fin (m + 1), (Fin.castSucc.toEmbedding i : ) ^ 3 +
↑(Fin.last (m + 1)) ^ 3 = (∑ i : Fin (Nat.succ m + 1), ↑i) ^ 2 =>
"Mit `simp` kriegst du das `↑(Fin.castSucc.toEmbedding i)` weg"
Hint (m : ) : ∑ x : Fin (m + 1), (x : ) ^ 3 + (m + 1) ^ 3 =
(∑ i : Fin (Nat.succ m + 1), ↑i) ^ 2 =>
"Jetzt kannst du die Induktionshypothese benützen."
Hint (m : ) : (∑ i : Fin (m + 1), (i : )) ^ 2 + (m + 1) ^ 3 = (∑ i : Fin (Nat.succ m + 1), ↑i) ^ 2 =>
"Die linke Seite ist jetzt erst mal gut. Um auf der rechten Seite `Fin.sum_univ_castSucc`
anzuwenden, haben wir ein Problem: Lean schreibt immer die erste Instanz um, also würde gerne
auf der linken Seite `(∑ i : Fin (m + 1), ↑i) ^ 2` umschreiben.
Wir können Lean hier weiterhelfen, indem wir manche Argemente von `Fin.sum_univ_castSucc`
explizit angeben. Die Funktion hat ein Argument mit dem Namen `n`, welches wir z.B. explizit
angeben können:
```
rw [Fin.sum_univ_castSucc (n := m + 1)]
```
"
**Robo**: Geh doch zurück und bring `rw` dazu am anderen Ort umzuschreiben."
rw [Fin.sum_univ_castSucc (n := n + 1)]
simp
Hint "**Robo**: `add_pow_two` ist auch noch nützlich!"
rw [add_pow_two]
Hint "**Du**: Ich glaube, ich sehe hier ne arithmetische Summe drin!!
HiddenHint (m : ) : (∑ i : Fin (m + 1), ↑i) ^ 2 + (m + 1) ^ 3 =
(∑ i : Fin (m + 1), ↑(Fin.castSucc.toEmbedding i) + ↑(Fin.last (m + 1))) ^ 2 =>
"Wenn du noch einen AUsdruck `↑(Fin.castSucc.toEmbedding i)` hast, solltest du mal
`simp` aufrufen."
**Robo**: Ich habe dir das dies von vorhin temporär als `arithmetic_sum` gespeichert,
damit du diese brauchen kannst."
rw [arithmetic_sum]
Hint "**Du**: Jetzt sollten es eigentlich nur noch arithmetische Operationen sein."
ring
Hint (m : ) : (∑ i : Fin (m + 1), ↑i) ^ 2 + (m + 1) ^ 3 = (∑ i : Fin (m + 1), ↑i + (m + 1)) ^ 2 =>
"Die rechte Seite hat die Form $(a + b)^2$ welche mit `add_pow_two` zu $a^2 + 2ab + b^2$
umgeschrieben werden kann."
NewLemma arithmetic_sum add_pow_two
HiddenHint (m : ) : (∑ i : Fin (m + 1), ↑i) ^ 2 + (m + 1) ^ 3 =
(∑ i : Fin (m + 1), ↑i) ^ 2 + (2 * ∑ i : Fin (m + 1), ↑i) * (m + 1) + (m + 1) ^ 2 =>
"Wenn du noch einen AUsdruck `↑(Fin.castSucc.toEmbedding i)` hast, solltest du mal
`simp` aufrufen."
Conclusion "Der Golem denkt ganz lange nach, und ihr bekommt das Gefühl, dass er gar nie
aggressive war, sondern nur eine sehr tiefe Stimme hat.
Hint (m : ) : (∑ i : Fin (m + 1), ↑i) ^ 2 + (m + 1) ^ 3 =
(∑ i : Fin (m + 1), ↑i) ^ 2 + (2 * ∑ i : Fin (m + 1), ↑i) * (m + 1) + (m + 1) ^ 2 =>
"Jetzt hast du in der Mitte `2 * ∑ i : Fin (m + 1), ↑i)`, welches du mit der
arithmetischen Summe `arithmetic_sum` umschreiben kannst."
Mit einem kleinen Erdbeben setzt er sich hin und winkt euch dankend zu.
Hint (m : ) : (∑ i : Fin (m + 1), ↑i) ^ 2 + (m + 1) ^ 3 =
(∑ i : Fin (m + 1), ↑i) ^ 2 + m * (m + 1) * (m + 1) + (m + 1) ^ 2 =>
"Den Rest sollte `ring` für dich übernehmen."
Damit zieht ihr weiter durch die karge Landschaft auf diesem Planet."

Loading…
Cancel
Save