Working…

This commit is contained in:
Stefan Kebekus
2026-08-11 13:59:48 +02:00
parent 609214a230
commit daae893145
95 changed files with 253 additions and 48008 deletions
+18 -13
View File
@@ -49,13 +49,13 @@ example (x y : ) (h : x = y) : x + 1 = y + 1 := by
Definition 2.1.1 des Skripts:
> *Definition (Gruppe).* Eine Gruppe ist eine nicht-leere Menge `G`
> mit einer Abbildung `m : G G G`, sodass folgende Eigenschaften
> gelten. _Assoziativität:_ _Neutrales Element:_ Es gibt genau ein
> Element `e` aus `G`, sodass für alle `a` aus `G` gilt:
> `m(e,a) = m(a,e) = a`. _Inverse Elemente:_ Für alle `a` aus `G`
> gibt es genau ein Element `b` aus `G`, sodass `m(a,b) = m(b,a) = e`
> ist.
> *Definition (Gruppe).* Eine Gruppe ist eine nicht-leere Menge $`G`
mit einer Abbildung $`m : G \times G \to G`, sodass folgende
Eigenschaften gelten. _Assoziativität:_ _Neutrales Element:_ Es
gibt genau ein Element $`e` aus $`G`, sodass für alle $`a` aus $`G`
gilt: $`m(e,a) = m(a,e) = a`. _Inverse Elemente:_ Für alle $`a`
aus $`G` gibt es genau ein Element $`b` aus $`G`, sodass
$`m(a,b) = m(b,a) = e` ist.
In Mathlib ist eine Gruppe eine Typklasse `Group G`. Die Verknüpfung
schreibt sich `a * b`, das neutrale Element `1`, das Inverse `a⁻¹`;
@@ -68,9 +68,10 @@ Widerspruch — die Eindeutigkeit folgt aus den übrigen Axiomen. Genau
das beweisen wir jetzt, mit dem Standardargument aus der linearen
Algebra:
> Es sei `e` ein weiteres Element, das von links neutral wirkt. Weil
> `1` neutral ist, gilt `e·1 = e`. Weil `e` neutral wirkt, gilt
> `e·1 = 1`. Zusammen folgt `e = e·1 = 1`.
> Es sei $`e` ein weiteres Element, das von links neutral wirkt.
Weil $`1` neutral ist, gilt $`e \cdot 1 = e`. Weil $`e` neutral
wirkt, gilt $`e \cdot 1 = 1`. Zusammen folgt
$`e = e \cdot 1 = 1`.
Der Lean-Beweis benutzt die Taktik `calc`, die eine Kette von
Gleichungen Schritt für Schritt abarbeitet das Lean-Gegenstück zur
@@ -87,9 +88,13 @@ theorem neutral_eindeutig {G : Type*} [Group G] (e : G)
# Übungsaufgabe
Zeigen Sie ebenso die Eindeutigkeit des Inversen: Wenn `a·b = 1` ist,
dann ist `b` bereits _das_ Inverse `a⁻¹`. Das Standardargument:
`b = 1·b = (a⁻¹·a)·b = a⁻¹·(a·b) = a⁻¹·1 = a⁻¹`. Öffnen Sie
Zeigen Sie ebenso die Eindeutigkeit des Inversen: Wenn
$`a \cdot b = 1` ist, dann ist $`b` bereits _das_ Inverse $`a^{-1}`.
Das Standardargument:
$$`b = 1 \cdot b = (a^{-1} \cdot a) \cdot b = a^{-1} \cdot (a \cdot b) = a^{-1} \cdot 1 = a^{-1}`
Öffnen Sie
`AlgebraInLean/Basics.lean` und ersetzen Sie das `sorry` durch einen
Beweis `calc` und die Lemmata `one_mul`, `inv_mul_cancel`,
`mul_assoc`, `mul_one` genügen.
+49 -43
View File
@@ -29,26 +29,27 @@ ersten Mal selbst etwas in Lean _definieren_: eine Gruppenwirkung.
# Das zentrale Schlüssellemma
> *Lemma 18.1.1 (Zentrales Schlüssellemma).* Es sei `m ` und es sei `p`
> eine Primzahl. Weiter sei `G` eine Gruppe der Ordnung `p^m`, die auf
> einer endlichen Menge `M` operiert. Weiter sei
> `M₀ = { m M : g G: g·m = m }` die Menge der Fixpunkte. Dann
> ist `|M| |M₀| (mod p)`.
> *Lemma 18.1.1 (Zentrales Schlüssellemma).* Es sei
$`m \in \mathbb{N}` und es sei $`p` eine Primzahl. Weiter sei $`G`
eine Gruppe der Ordnung $`p^m`, die auf einer endlichen Menge $`M`
operiert. Weiter sei
$`M_0 = \{ m \in M : g \cdot m = m \ \forall g \in G \}` die Menge
der Fixpunkte. Dann ist $`|M| \equiv |M_0| \pmod{p}`.
Wie sagt Mathlib das alles?
* eine Gruppe der Ordnung `p^m`: Dafür hat Mathlib ein Prädikat
`IsPGroup p G` (die Ordnung jedes Elements ist eine p-Potenz für
endliche Gruppen ist das äquivalent, nach dem Satz von Cauchy
unten!). Das Lemma `IsPGroup.of_card` übersetzt unsere
Voraussetzung `Nat.card G = p ^ m` dorthin.
* eine Gruppe der Ordnung $`p^m`: Dafür hat Mathlib ein Prädikat
`IsPGroup p G` (die Ordnung jedes Elements ist eine
$`p`-Potenz für endliche Gruppen ist das äquivalent, nach dem
Satz von Cauchy unten!). Das Lemma `IsPGroup.of_card` übersetzt
unsere Voraussetzung `Nat.card G = p ^ m` dorthin.
* die auf einer endlichen Menge `M` operiert: Eine Wirkung von `G`
auf `M` ist eine Typklasse, `[MulAction G M]`; die Endlichkeit von
`M` ist die Typklasse `[Finite M]`.
* Die Fixpunktmenge heißt `MulAction.fixedPoints G M`, und die
Kongruenz `|M| |M₀| (mod p)` schreibt sich
Kongruenz $`|M| \equiv |M_0| \pmod{p}` schreibt sich
`Nat.card M Nat.card (fixedPoints G M) [MOD p]`.
Der Beweis im Skript zerlegt `M` in Bahnen und zitiert die
@@ -75,8 +76,9 @@ gleich auf.)
# Der Satz von Cauchy
> *Satz 18.1.2 (Satz von Cauchy).* Wenn die Ordnung einer endlichen Gruppe
> durch `p` teilbar ist, dann existiert ein Element von Ordnung `p`.
> *Satz 18.1.2 (Satz von Cauchy).* Wenn die Ordnung einer endlichen
Gruppe durch $`p` teilbar ist, dann existiert ein Element von
Ordnung $`p`.
Wir folgen dem Beweis des Skripts Satz für Satz. Der Beweis
konstruiert eine raffinierte Hilfsmenge mit einer raffinierten
@@ -86,22 +88,23 @@ Arbeit: Wir bauen erst die Menge und die Wirkung.
## Die Menge M
Betrachte die Menge
`M = { (a₁, , a_p) G G : a₁·a₂ a_p = e }`.
$`M = \bigl\{ (a_1, \dots, a_p) \in G \times \dots \times G : a_1 \cdot a_2 \cdots a_p = e \bigr\}`.
Mathlib kennt diese Menge. Ein p-Tupel von Gruppenelementen ist ein
`List.Vector G p` eine Liste der Länge `p` , und `M` ist die Menge
`Equiv.Perm.vectorsProdEqOne G p` aller Vektoren, deren Einträge sich
zu 1 multiplizieren.
Mathlib kennt diese Menge. Ein $`p`-Tupel von Gruppenelementen ist
ein `List.Vector G p` eine Liste der Länge $`p` , und $`M` ist die
Menge `Equiv.Perm.vectorsProdEqOne G p` aller Vektoren, deren
Einträge sich zu 1 multiplizieren.
Gegeben ein Tupel `(a₁, , a_p) M`, dann stellen wir erst einmal
fest, dass der letzte Eintrag des Tupels durch die ersten Einträge
eindeutig bestimmt ist, `a_p = (a₁ a_{p-1})⁻¹`. Wir erhalten die
folgende Gleichung: `|M| = |G^{p-1}| = |G|^{p-1}`.
Gegeben ein Tupel $`(a_1, \dots, a_p) \in M`, dann stellen wir erst
einmal fest, dass der letzte Eintrag des Tupels durch die ersten
Einträge eindeutig bestimmt ist,
$`a_p = (a_1 \cdots a_{p-1})^{-1}`. Wir erhalten die folgende
Gleichung: $`|M| = |G^{p-1}| = |G|^{p-1}`.
Auch das steht schon in Mathlib: Die Bijektion
`(a₁, , a_{p-1}) (a₁, , a_{p-1}, (a₁ a_{p-1})⁻¹)` heißt
`VectorsProdEqOne.vectorEquiv`, und die resultierende Zählformel ist
`VectorsProdEqOne.card`:
$`(a_1, \dots, a_{p-1}) \mapsto (a_1, \dots, a_{p-1}, (a_1 \cdots a_{p-1})^{-1})`
heißt `VectorsProdEqOne.vectorEquiv`, und die resultierende
Zählformel ist `VectorsProdEqOne.card`:
```
VectorsProdEqOne.card (G : Type) [Group G] (n : ) [Fintype G] :
@@ -112,22 +115,25 @@ VectorsProdEqOne.card (G : Type) [Group G] (n : ) [Fintype G] :
Als Nächstes brauchen wir eine schicke Gruppenwirkung, denn wir
wollen das zentrale Schlüssellemma anwenden. Dazu lassen wir die
zyklische Gruppe `/(p)` auf `M` durch zyklisches Vertauschen wirken.
zyklische Gruppe $`\mathbb{Z}/(p)` auf $`M` durch zyklisches
Vertauschen wirken.
Der zyklische Shift eines Vektors `v vectorsProdEqOne G p` um `k`
Stellen ist `VectorsProdEqOne.rotate v k`. Die Fußnote des Skripts
der Shift bildet `M` auf sich ab, weil in jeder Gruppe aus `a·b = e`
auch `b·a = e` gilt ist das Mathlib-Lemma
Der zyklische Shift eines Vektors `v vectorsProdEqOne G p` um
$`k` Stellen ist `VectorsProdEqOne.rotate v k`. Die Fußnote des
Skripts der Shift bildet $`M` auf sich ab, weil in jeder Gruppe
aus $`a \cdot b = e` auch $`b \cdot a = e` gilt ist das
Mathlib-Lemma
`List.prod_rotate_eq_one_of_prod_eq_one`, das schon in der Definition
von `rotate` steckt.
Damit `/(p)` _als Gruppe_ wirkt, müssen wir die Wirkungsaxiome
nachprüfen: Der Shift um 0 tut nichts, und der Shift um `j + k` ist
der Shift um `k` gefolgt vom Shift um `j`. Mathlib stellt
`rotate_zero`, `rotate_rotate` und `rotate_length` bereit (der Shift
um die volle Länge `p` tut nichts); daraus leiten wir zuerst ab, dass
der Shift nur von der Verschiebung _modulo p_ abhängt genau deshalb
wirkt `/(p)` und nicht bloß ``.
Damit $`\mathbb{Z}/(p)` _als Gruppe_ wirkt, müssen wir die
Wirkungsaxiome nachprüfen: Der Shift um 0 tut nichts, und der Shift
um $`j + k` ist der Shift um $`k` gefolgt vom Shift um $`j`. Mathlib
stellt `rotate_zero`, `rotate_rotate` und `rotate_length` bereit (der
Shift um die volle Länge $`p` tut nichts); daraus leiten wir zuerst
ab, dass der Shift nur von der Verschiebung _modulo p_ abhängt
genau deshalb wirkt $`\mathbb{Z}/(p)` und nicht bloß
$`\mathbb{N}`.
```lean
theorem rotate_mul {G : Type*} [Group G] {p : }
@@ -151,7 +157,7 @@ theorem rotate_mod {G : Type*} [Group G] {p : }
Nun können wir die Wirkung definieren. Zwei technische Anmerkungen.
Mathlibs `MulAction` erwartet eine multiplikativ geschriebene Gruppe;
`/(p)` = `ZMod p` ist aber additiv geschrieben. Der Wrapper
$`\mathbb{Z}/(p)` = `ZMod p` ist aber additiv geschrieben. Der Wrapper
`Multiplicative` wechselt die Notation (dank
`open Multiplicative` oben schreiben sich seine Übergänge kurz
`toAdd` und `ofAdd`). Die Voraussetzung `[NeZero p]` schließt
@@ -176,13 +182,13 @@ instance rotateAction {G : Type*} [Group G] {p : }
## Die Fixpunkte
Die Fixpunktmenge dieser Wirkung ist
`M = { (a, , a) G^p : a^p = e }`.
$`M_0 = \{ (a, \dots, a) \in G^p : a^p = e \}`.
Mit anderen Worten: Ein Tupel ist genau dann ein Fixpunkt, wenn es
konstant ist, seine Liste also für ein `a` die Liste
`List.replicate p a` das ist `(a, , a)` ist. (Die Bedingung
`a^p = e` gilt dann automatisch, weil sich die Einträge eines Tupels
in `M` zu `e` multiplizieren.) Der Schlüsselschritt ist das Lemma
konstant ist, seine Liste also für ein $`a` die Liste
`List.replicate p a` das ist $`(a, \dots, a)` ist. (Die
Bedingung $`a^p = e` gilt dann automatisch, weil sich die Einträge
eines Tupels in $`M` zu $`e` multiplizieren.) Der Schlüsselschritt ist das Lemma
`List.rotate_one_eq_self_iff_eq_replicate`: Eine Liste, die der
zyklische Shift um _eine_ Stelle nicht ändert, ist konstant.
+30 -26
View File
@@ -27,34 +27,37 @@ und Körpererweiterungen als Vektorräume.
# Das Mathlib-Wörterbuch
* Eine Körpererweiterung `L/K` ist in Mathlib eine
`Algebra K L`-Instanz zwischen zwei Körpern `L` wird damit
insbesondere ein K-Vektorraum, genau wie im Skript.
* Eine Körpererweiterung $`L/K` ist in Mathlib eine
`Algebra K L`-Instanz zwischen zwei Körpern $`L` wird damit
insbesondere ein $`K`-Vektorraum, genau wie im Skript.
* Eine Kette `K L M` besteht aus drei Algebra-Instanzen und der
Verträglichkeitsbedingung `IsScalarTower K L M` (erst nach `L`,
dann nach `M` einbetten ist dasselbe wie direkt nach `M`).
* Eine Kette $`K \subseteq L \subseteq M` besteht aus drei
Algebra-Instanzen und der Verträglichkeitsbedingung
`IsScalarTower K L M` (erst nach $`L`, dann nach $`M` einbetten
ist dasselbe wie direkt nach $`M`).
* Der Grad `[L:K]` heißt `Module.finrank K L`. Achtung, hier weicht
die Konvention vom Skript ab: `finrank` hat Werte in ``, und
unendlichdimensionale Erweiterungen bekommen den Wert 0 wo das
Skript in seiner Fußnote mit `` rechnet, rechnet Mathlib mit 0.
`[L:K]` ist endlich ist die Typklasse `FiniteDimensional K L`.
* Der Grad $`[L:K]` heißt `Module.finrank K L`. Achtung, hier weicht
die Konvention vom Skript ab: `finrank` hat Werte in $`\mathbb{N}`,
und unendlichdimensionale Erweiterungen bekommen den Wert 0 wo
das Skript in seiner Fußnote mit $`\infty` rechnet, rechnet Mathlib
mit 0. $`[L:K]` ist endlich ist die Typklasse
`FiniteDimensional K L`.
# Die Gradformel
Satz 3.6.1 des Skripts, mit Beweis:
> *Satz 3.6.1 (Gradformel).* Es sei `K L M` eine Kette von
> Körpererweiterungen. Dann gilt die Gleichung
> `[M:K] = [M:L]·[L:K]`.
>
> *Satz 3.6.1 (Gradformel).* Es sei $`K \subseteq L \subseteq M` eine
Kette von Körpererweiterungen. Dann gilt die Gleichung
$`[M:K] = [M:L] \cdot [L:K]`.
> _Beweis._ Wir kümmern uns zuerst um die unendlichen Fälle. Es
> seien jetzt also `a := [L:K]` und `b := [M:L]` beide endlich. Wähle
> Basen `ℓ₁, , _a` von `L` als K-Vektorraum und `m₁, , m_b` von `M`
> als L-Vektorraum. Ich behaupte, dass die `a·b` Produkte `(ℓᵢ·m)`
> eine Basis von `M` als K-Vektorraum bilden; damit ist dann sofort
> `[M:K] = a·b` gezeigt.
seien jetzt also $`a := [L:K]` und $`b := [M:L]` beide endlich.
Wähle Basen $`\ell_1, \dots, \ell_a` von $`L` als
$`K`-Vektorraum und $`m_1, \dots, m_b` von $`M` als
$`L`-Vektorraum. Ich behaupte, dass die $`a \cdot b` Produkte
$`(\ell_i \cdot m_j)` eine Basis von $`M` als $`K`-Vektorraum
bilden; damit ist dann sofort $`[M:K] = a \cdot b` gezeigt.
Die unendlichen Fälle entfallen in unserer Fassung, weil wir
Endlichkeit voraussetzen; in Mathlibs 0-Konvention gilt die Formel
@@ -63,8 +66,9 @@ sogar uneingeschränkt (`Module.finrank_mul_finrank`).
Die Behauptung mit den beiden Beweisschritten Die Produkte bilden
ein Erzeugendensystem und Die Produkte sind linear unabhängig
ist in Mathlib die Konstruktion `Basis.smulTower`: Aus einer Basis
`` von `L/K` und einer Basis `m` von `M/L` baut sie die Basis
`(ℓᵢ m)` von `M/K` (das Lemma `Basis.smulTower_apply` bestätigt,
$`\ell` von $`L/K` und einer Basis $`m` von $`M/L` baut sie die
Basis $`(\ell_i \cdot m_j)` von $`M/K` (das Lemma
`Basis.smulTower_apply` bestätigt,
dass die Basiselemente wirklich die Produkte sind). Der Rest ist
Abzählen der Indexmenge.
@@ -101,12 +105,12 @@ Skript.
Das erste Korollar des Skripts zur Gradformel:
> Es sei `K L M` eine Kette von Körpererweiterungen. Wenn
> `[M:K]` endlich ist, dann ist `[L:K]` endlich, und sogar ein Teiler
> von `[M:K]`.
> Es sei $`K \subseteq L \subseteq M` eine Kette von
Körpererweiterungen. Wenn $`[M:K]` endlich ist, dann ist
$`[L:K]` endlich, und sogar ein Teiler von $`[M:K]`.
Der erste Teil ist `Module.Finite.left` (und `Module.Finite.right`
liefert die Endlichkeit von `[M:L]`) beide müssen mit `have := `
liefert die Endlichkeit von $`[M:L]`) beide müssen mit `have := `
in den Kontext geholt werden, damit die Instanzsuche sie sieht. Der
Teiler-Teil ist die Übung: Öffnen Sie `AlgebraInLean/Degrees.lean`
und ersetzen Sie das `sorry` durch einen Beweis mit `gradformel`.
+23 -19
View File
@@ -22,13 +22,13 @@ file := "frobenius"
Dieses Kapitel übersetzt den Satz über den Frobenius-Endomorphismus
aus Kapitel 14 des Skripts Satz für Satz nach Lean den Traum jedes
Studienanfängers: `(a+b)^p = a^p + b^p`.
Studienanfängers: $`(a+b)^p = a^p + b^p`.
# Das Mathlib-Wörterbuch
* `R` hat Charakteristik `p` ist die Typklasse `CharP R p`. Das
entscheidende Lemma ist `CharP.cast_eq_zero`: Das Bild von `p` in
`R` ist Null.
* $`R` hat Charakteristik $`p` ist die Typklasse `CharP R p`.
Das entscheidende Lemma ist `CharP.cast_eq_zero`: Das Bild von
$`p` in $`R` ist Null.
* Binomialkoeffizienten heißen `Nat.choose`; die binomische Formel
ist `add_pow`, eine Summe über `Finset.range`. Endliche Summen und
@@ -38,19 +38,22 @@ Studienanfängers“: `(a+b)^p = a^p + b^p`.
Satz und Definition 14.2.2 des Skripts:
> *Satz und Definition 14.2.2 (Frobenius-Endomorphismus).* Es sei `p` eine
> Primzahl und es sei `R` ein kommutativer Ring mit Eins der
> Charakteristik `p`. Dann ist die Abbildung `F : R R`, `a a^p`
> ein Ringmorphismus.
>
> _Beweis._ Die Verträglichkeit mit der Multiplikation ist klar, weil
> `R` kommutativ ist: `(a·b)^p = a^p·b^p`. Ebenso ist `F(1) = 1`.
> Interessant ist nur die Verträglichkeit mit der Addition. Die
> binomische Formel gilt in jedem kommutativen Ring . Für alle
> Indizes `0 < k < p` ist der Binomialkoeffizient ein Vielfaches von
> `p` . Weil `R` die Charakteristik `p` hat, ist `p = 0` in `R`,
> und alle diese Summanden verschwinden. Übrig bleiben nur die
> Summanden für `k = 0` und `k = p`.
> *Satz und Definition 14.2.2 (Frobenius-Endomorphismus).* Es sei
$`p` eine Primzahl und es sei $`R` ein kommutativer Ring mit Eins
der Charakteristik $`p`. Dann ist die Abbildung
$`F : R \to R`, $`a \mapsto a^p` ein Ringmorphismus.
> _Beweis._ Die Verträglichkeit mit der Multiplikation ist klar,
weil $`R` kommutativ ist: $`(a \cdot b)^p = a^p \cdot b^p`.
Ebenso ist $`F(1) = 1`. Interessant ist nur die Verträglichkeit
mit der Addition. Die binomische Formel gilt in jedem
kommutativen Ring, also ist
$`(a+b)^p = \sum_{k=0}^{p} \binom{p}{k} \cdot a^k \cdot b^{p-k}`.
Für alle Indizes $`0 < k < p` ist der Binomialkoeffizient
$`\binom{p}{k}` ein Vielfaches von $`p` . Weil $`R` die
Charakteristik $`p` hat, ist $`p = 0` in $`R`, und alle diese
Summanden verschwinden. Übrig bleiben nur die Summanden für
$`k = 0` und $`k = p`.
```lean
theorem frobenius_mul {R : Type*} [CommRing R] (p : )
@@ -115,8 +118,9 @@ Elementen. In Mathlib heißt er `GaloisField p n`.
# Übungsaufgabe
Der Frobenius des Körpers `𝔽_p` ist die Identität: Für jedes
`a 𝔽_p` ist `a^p = a`. Das ist ein alter Bekannter der kleine
Der Frobenius des Körpers $`\mathbb{F}_p` ist die Identität: Für
jedes $`a \in \mathbb{F}_p` ist $`a^p = a`. Das ist ein alter
Bekannter der kleine
Satz von Fermat aus unserem dritten Kapitel! In Mathlib heißt er
`ZMod.pow_card`. Öffnen Sie `AlgebraInLean/Frobenius.lean` und
ersetzen Sie das `sorry` durch einen Beweis.
+22 -15
View File
@@ -28,11 +28,11 @@ ist und es auch hier bleibt.
# Das Mathlib-Wörterbuch
* Die Galoisgruppe einer Körpererweiterung `L/K` ist der Typ
* Die Galoisgruppe einer Körpererweiterung $`L/K` ist der Typ
`L [K] L` der K-Algebren-Automorphismen; Mathlib stellt dafür
sogar die Notation `Gal(L/K)` bereit.
* `L/K` ist galoissch ist die Typklasse `IsGalois K L` definiert
* $`L/K` ist galoissch ist die Typklasse `IsGalois K L` definiert
als separabel und normal, genau wie im Skript.
* Ein Zwischenkörper ist ein Term `Z : IntermediateField K L`; der
@@ -43,10 +43,12 @@ ist und es auch hier bleibt.
Satz und Definition 16.1.1 des Skripts:
> *Satz und Definition 16.1.1 (Invariante Elemente, Fixkörper).* Sei `L` ein
> Körper und `G` eine Menge von Automorphismen `L L`. Dann ist die
> Menge `Fix G = { a L : σ(a) = a für alle σ G }` ein Unterkörper
> von `L`. Der Beweis des folgenden Satzes ist eine Hausaufgabe.
> *Satz und Definition 16.1.1 (Invariante Elemente, Fixkörper).* Sei
$`L` ein Körper und $`G` eine Menge von Automorphismen
$`L \to L`. Dann ist die Menge
$`\operatorname{Fix} G = \{ a \in L : \sigma(a) = a \ \forall \sigma \in G \}`
ein Unterkörper von $`L`. Der Beweis des folgenden Satzes ist
eine Hausaufgabe.
Wir definieren die Menge in Lean und beweisen exemplarisch die
Abgeschlossenheit unter der Multiplikation der Rest der Hausaufgabe
@@ -71,23 +73,28 @@ theorem mul_mem_fix {L : Type*} [Field L]
Auch die großen Sätze dieses Teils der Vorlesung stehen in Mathlib;
wir zitieren sie mit ihren Namen.
> *Satz von Emil Artin (Satz 16.1.2).* Es sei `G` eine endliche Untergruppe der
> Automorphismengruppe eines Körpers `L` und es sei `K := Fix G`.
> Dann ist `L/K` eine Galoiserweiterung mit Galoisgruppe
> `Gal(L/K) = G`. Insbesondere ist `[L:K] = |G|`.
> *Satz von Emil Artin (Satz 16.1.2).* Es sei $`G` eine endliche
Untergruppe der Automorphismengruppe eines Körpers $`L` und es sei
$`K := \operatorname{Fix} G`. Dann ist $`L/K` eine
Galoiserweiterung mit Galoisgruppe
$`\operatorname{Gal}(L/K) = G`. Insbesondere ist
$`[L:K] = |G|`.
Die Gradaussage ist `FixedPoints.finrank_eq_card`; das gebündelte
Fixkörper-Objekt heißt dort `FixedPoints.subfield G L`.
> *Hauptsatz der Galoistheorie (Satz 16.3.2).* Es sei `L/K` eine Galoiserweiterung
> mit Galoisgruppe `G`. Dann sind die Abbildungen `Z Gal(L/Z)` und
> `H Fix H` zueinander inverse, inklusionsumkehrende Bijektionen
> zwischen Zwischenkörpern und Untergruppen.
> *Hauptsatz der Galoistheorie (Satz 16.3.2).* Es sei $`L/K` eine
Galoiserweiterung mit Galoisgruppe $`G`. Dann sind die Abbildungen
$`Z \mapsto \operatorname{Gal}(L/Z)` und
$`H \mapsto \operatorname{Fix} H` zueinander inverse,
inklusionsumkehrende Bijektionen zwischen Zwischenkörpern und
Untergruppen.
In Mathlib ist das der ordnungsumkehrende Isomorphismus
`IsGalois.intermediateFieldEquivSubgroup`; das (_order dual_)
in seinem Typ ist genau das inklusionsumkehrend des Skripts. Die
Gradaussage `|Gal(L/K)| = [L:K]` für Galoiserweiterungen heißt
Gradaussage $`|\operatorname{Gal}(L/K)| = [L:K]` für
Galoiserweiterungen heißt
`IsGalois.card_aut_eq_finrank`:
```lean
+16 -14
View File
@@ -31,7 +31,7 @@ Lean.
* Ein Ideal in einem kommutativen Ring `R` ist ein Term
`I : Ideal R`. Die beiden Bedingungen aus der Ideal-Definition des
Skripts heißen `I.add_mem` (für alle `a, b I` ist `a+b I`) und
`I.mul_mem_left` (für alle `r R` und `a I` ist `r·a I`);
`I.mul_mem_left` (für alle $`r \in R` und $`a \in I` ist $`r \cdot a \in I`);
außerdem ist `I.zero_mem : 0 I`.
* Das von einer Menge `s` erzeugte Ideal ist `Ideal.span s`; ein
@@ -46,15 +46,16 @@ Lean.
Satz 9.3.7 des Skripts, mit Beweis:
> Es sei `I ` ein Ideal und `I {0}`. Dann gibt es ein
> `x I{0}`. Beachte, dass dann auch `x = (1)·x` in `I` ist.
> Also enthält `I` positive Elemente. Sei `a I` jetzt das kleinste
> positive Element. Wir werden zeigen, dass `I = (a)` ist. Die
> Inklusion `(a) I` ist klar. Sei `b I` irgendein positives
> Element, dann teilen wir mit Rest: `b = q·a + r` mit `0 r < a`.
> Die Zahl `r` ist jetzt aber in `I`, denn `b` und `q·a` sind in `I`.
> Weiter muss wegen der Minimalität von `a` also `r = 0` sein und
> somit `b (a)`.
> Es sei $`I \subset \mathbb{Z}` ein Ideal und $`I \neq \{0\}`.
Dann gibt es ein $`x \in I \setminus \{0\}`. Beachte, dass dann
auch $`-x = (-1) \cdot x` in $`I` ist. Also enthält $`I` positive
Elemente. Sei $`a \in I` jetzt das kleinste positive Element. Wir
werden zeigen, dass $`I = (a)` ist. Die Inklusion
$`(a) \subseteq I` ist klar. Sei $`b \in I` irgendein positives
Element, dann teilen wir mit Rest: $`b = q \cdot a + r` mit
$`0 \leq r < a`. Die Zahl $`r` ist jetzt aber in $`I`, denn $`b`
und $`q \cdot a` sind in $`I`. Weiter muss wegen der Minimalität
von $`a` also $`r = 0` sein und somit $`b \in (a)`.
Zwei Anmerkungen zur Übersetzung. Das kleinste positive Element
liefert das Wohlordnungsprinzip, in Mathlib
@@ -133,10 +134,11 @@ aus diesem Kapitel steht in Mathlib: `Polynomial.isNoetherianRing`.
Das Beispiel Triviale Ideale des Skripts:
> Wenn `R` ein Körper und `I R` ein Ideal ist und `a I{0}`,
> dann ist auch jedes andere Körperelement in `I`. Sei nämlich
> irgendein Element `r R` gegeben. Nach Definition ist
> `r = (r·a⁻¹)·a I`. Also ist `I = R`.
> Wenn $`R` ein Körper und $`I \subset R` ein Ideal ist und
$`a \in I \setminus \{0\}`, dann ist auch jedes andere
Körperelement in $`I`. Sei nämlich irgendein Element $`r \in R`
gegeben. Nach Definition ist
$`r = (r \cdot a^{-1}) \cdot a \in I`. Also ist $`I = R`.
Übersetzen Sie dieses Argument: Öffnen Sie
`AlgebraInLean/Ideals.lean` und ersetzen Sie das `sorry` durch einen
+28 -24
View File
@@ -21,16 +21,20 @@ file := "little-fermat"
Unser erster Beweis übersetzt Satz 17.5.1 des Skripts. Hier das
deutsche Original, Aussage und Beweis:
> *Satz 17.5.1 (Kleiner Satz von Fermat).* Es sei `p ` eine Primzahl und es
> sei `a ` irgendeine Zahl. Dann ist `a^p a (mod p)`.
>
> _Beweis._ Falls `a` ein Vielfaches von `p` ist, ist die Sache klar.
> Ansonsten liefert die Restklasse von `a` ein nicht-verschwindendes
> Element `ā /(p) = 𝔽_p`, also ein Element der multiplikativen
> Gruppe `𝔽_p^*`, welche `p1` Elemente hat. Nach dem Satz von
> Lagrange ist die Ordnung von `ā`, also die Größe der von `ā`
> erzeugten Untergruppe, ein Teiler von `|𝔽_p^*| = p1`. Es gilt also
> `ā^(p1) = 1 𝔽_p^*`, oder äquivalent `a^p a (mod p)`.
> *Satz 17.5.1 (Kleiner Satz von Fermat).* Es sei $`p \in \mathbb{N}`
eine Primzahl und es sei $`a \in \mathbb{Z}` irgendeine Zahl. Dann
ist $`a^p \equiv a \pmod{p}`.
> _Beweis._ Falls $`a` ein Vielfaches von $`p` ist, ist die Sache
klar. Ansonsten liefert die Restklasse von $`a` ein
nicht-verschwindendes Element
$`\overline{a} \in \mathbb{Z}/(p) = \mathbb{F}_p`, also ein Element
der multiplikativen Gruppe $`\mathbb{F}_p^*`, welche $`p-1` Elemente
hat. Nach dem Satz von Lagrange ist die Ordnung von
$`\overline{a}`, also die Größe der von $`\overline{a}` erzeugten
Untergruppe, ein Teiler von $`|\mathbb{F}_p^*| = p-1`. Es gilt also
$`\overline{a}^{p-1} = 1 \in \mathbb{F}_p^*`, oder äquivalent
$`a^p \equiv a \pmod{p}`.
Fünf Sätze. Unser Ziel ist ein Lean-Beweis mit genau dieser Struktur
jeder deutsche Satz kehrt als Kommentar über dem Lean-Code wieder, der
@@ -41,24 +45,24 @@ ihn umsetzt.
Bevor wir den Satz überhaupt formulieren können, müssen wir wissen, wie
Mathlib über die beteiligten Objekte spricht.
* Der Restklassenring `/(p)` heißt `ZMod p`. Die Restklasse einer
ganzen Zahl `a : ` schreibt sich `(a : ZMod p)` Lean fügt den
kanonischen Ringmorphismus ` /(p)` automatisch ein (eine
_Koerzion_).
* Der Restklassenring $`\mathbb{Z}/(p)` heißt `ZMod p`. Die
Restklasse einer ganzen Zahl `a : ` schreibt sich `(a : ZMod p)`
Lean fügt den kanonischen Ringmorphismus
$`\mathbb{Z} \to \mathbb{Z}/(p)` automatisch ein (eine _Koerzion_).
* Die Kongruenz `a b (mod p)` für ganze Zahlen schreibt sich
* Die Kongruenz $`a \equiv b \pmod{p}` für ganze Zahlen schreibt sich
`a b [ZMOD p]`.
* Die multiplikative Gruppe `𝔽_p^*` ist die Gruppe der _Einheiten_ des
Rings `ZMod p`, geschrieben `(ZMod p)ˣ`. Eine Einheit
`u : (ZMod p)ˣ` kennt ihr Inverses; das zugrundeliegende Ringelement
schreibt sich wieder `(u : ZMod p)`.
* Die multiplikative Gruppe $`\mathbb{F}_p^*` ist die Gruppe der
_Einheiten_ des Rings `ZMod p`, geschrieben `(ZMod p)ˣ`. Eine
Einheit `u : (ZMod p)ˣ` kennt ihr Inverses; das zugrundeliegende
Ringelement schreibt sich wieder `(u : ZMod p)`.
* Das Skript sagt es sei `p` eine Primzahl. In Lean führen wir die
* Das Skript sagt es sei $`p` eine Primzahl. In Lean führen wir die
Primalität von `p` als Hypothese `hp : p.Prime` mit. Manche
Tatsachen etwa dass `/(p)` ein Körper ist findet Leans
Automatisierung nur, wenn die Hypothese als _Instanz_ registriert
ist; genau das leistet die erste Beweiszeile
Tatsachen etwa dass $`\mathbb{Z}/(p)` ein Körper ist findet
Leans Automatisierung nur, wenn die Hypothese als _Instanz_
registriert ist; genau das leistet die erste Beweiszeile
`have : Fact p.Prime := hp`.
# Der Beweis, Satz für Satz
@@ -135,7 +139,7 @@ example (p : ) [Fact p.Prime] (a : ZMod p) : a ^ p = a :=
Das ist die Bemerkung nach dem Satz im Skript: In Anwendungen
verwendet man häufig die äquivalente Formulierung
`a^(p1) 1 (mod p)` für nicht durch `p` teilbare `a`. Leiten Sie
$`a^{p-1} \equiv 1 \pmod{p}` für nicht durch $`p` teilbare $`a`. Leiten Sie
sie aus `little_fermat` ab oder geben Sie einen direkten Beweis nach
den Ideen oben. Öffnen Sie `AlgebraInLean/LittleFermat.lean` im
Übungs-Repository und ersetzen Sie das `sorry` durch einen Beweis.
+23 -17
View File
@@ -43,30 +43,34 @@ Polynom-API kennen.
Satz 7.2.1 des Skripts:
> *Satz 7.2.1 (Eisenstein-Kriterium).* Es sei `R` ein faktorieller Ring und
> es sei `f = a₀ + a₁·x + + aₙ·xⁿ R[x]` ein Polynom vom Grad
> `n > 0`. Weiter sei `ggT(a₀, , aₙ) = 1`. Wenn es ein Primelement
> `p R` gibt mit `p|a₀`, `p|a₁`, , `p|a_{n1}` und `p² a₀`, dann
> ist `f` irreduzibel in `R[x]`.
> *Satz 7.2.1 (Eisenstein-Kriterium).* Es sei $`R` ein faktorieller
Ring und es sei
$`f = a_0 + a_1 \cdot x + \dots + a_n \cdot x^n \in R[x]` ein
Polynom vom Grad $`n > 0`. Weiter sei
$`\operatorname{ggT}(a_0, \dots, a_n) = 1`. Wenn es ein
Primelement $`p \in R` gibt mit $`p \mid a_0`, $`p \mid a_1`, ,
$`p \mid a_{n-1}` und $`p^2 \nmid a_0`, dann ist $`f` irreduzibel
in $`R[x]`.
In Mathlib heißt der Satz
`Polynomial.irreducible_of_eisenstein_criterion`. Er ist dort über
ein Primideal `P` statt eines Primelements `p` formuliert für uns
ist `P` das Hauptideal `(p)` aus dem letzten Kapitel, und `p` teilt
`a` wird zu `f.coeff k Ideal.span {p}`. Statt `p aₙ` (was im
Skript aus `ggT = 1` folgt) verlangt Mathlib direkt
`f.leadingCoeff P`.
ein Primideal `P` statt eines Primelements $`p` formuliert für uns
ist `P` das Hauptideal $`(p)` aus dem letzten Kapitel, und $`p`
teilt $`a_k` wird zu `f.coeff k Ideal.span {p}`. Statt
$`p \nmid a_n` (was im Skript aus $`\operatorname{ggT} = 1` folgt)
verlangt Mathlib direkt `f.leadingCoeff P`.
# Erstes Beispiel: xⁿ r
> Das Polynom `xⁿ r [x]` ist irreduzibel, wenn `r` durch eine
> Primzahl `p`, aber nicht durch `p²` teilbar ist.
> Das Polynom $`x^n - r \in \mathbb{Z}[x]` ist irreduzibel, wenn
$`r` durch eine Primzahl $`p`, aber nicht durch $`p^2` teilbar
ist.
Das Skript lässt die Kontrolle der Eisenstein-Bedingungen als
Kopfrechnung weg in Lean führen wir sie aus und lernen dabei die
Koeffizienten-API kennen: `coeff_sub`, `coeff_X_pow` und `coeff_C`
berechnen die Koeffizienten von `xⁿ r`, nämlich `a = 1`, `a₀ = r`
und `a = 0` sonst.
berechnen die Koeffizienten von $`x^n - r`, nämlich $`a_n = 1`,
$`a_0 = -r` und $`a_k = 0` sonst.
```lean
open Polynomial
@@ -128,11 +132,13 @@ Körpererweiterungen wieder auf.
Genau dieses zweite Beispiel ist die Übung:
> Das Polynom `xⁿ p [x]` ist für jede Primzahl `p` irreduzibel.
> Das Polynom $`x^n - p \in \mathbb{Z}[x]` ist für jede Primzahl
$`p` irreduzibel.
Wenden Sie `xn_sub_r_irreduzibel` mit `r = p` an. Zu zeigen bleibt
`p² p`: Aus `p = p²·c` folgt durch Kürzen `1 = p·c` dann wäre `p`
eine Einheit, im Widerspruch zur Primalität. Nützlich sind
$`p^2 \nmid p`: Aus $`p = p^2 \cdot c` folgt durch Kürzen
$`1 = p \cdot c` dann wäre $`p` eine Einheit, im Widerspruch zur
Primalität. Nützlich sind
`mul_left_cancel₀`, `isUnit_of_dvd_one` und `Prime.not_isUnit`.
Öffnen Sie `AlgebraInLean/Polynomials.lean` und ersetzen Sie das
`sorry` durch einen Beweis.
+23 -19
View File
@@ -24,8 +24,8 @@ file := "quotients"
Dieses Kapitel übersetzt zwei Beweise rund um normale Untergruppen
Satz für Satz nach Lean: das Argument aus Erklärvideo 1-1 des
Skripts, dass Kerne von Gruppenmorphismen abgeschlossen unter
Konjugation sind, und die Beobachtung aus Kapitel 17.3, dass `U·N`
für normales `N` wieder eine Untergruppe ist.
Konjugation sind, und die Beobachtung aus Kapitel 17.3, dass
$`U \cdot N` für normales $`N` wieder eine Untergruppe ist.
# Das Mathlib-Wörterbuch
@@ -41,18 +41,19 @@ für normales `N` wieder eine Untergruppe ist.
* `N` ist Normalteiler ist das Prädikat `N.Normal`; sein Feld
`conj_mem` ist wörtlich die Bedingung des Skripts: für alle
`n N` und alle `g` ist `g·n·g⁻¹ N`.
$`n \in N` und alle $`g` ist $`g \cdot n \cdot g^{-1} \in N`.
# Kerne sind abgeschlossen unter Konjugation
Aus Erklärvideo 1-1 des Skripts:
> Die Antwort lautet: Nein! Nicht jede Untergruppe kann Kern eines
> Gruppenmorphismus sein. Wenn es nämlich einen Gruppenmorphismus
> : G Q` mit `H = ker(φ)` gibt, dann gilt für alle `h H` und
> alle `g G` die Gleichung (g·h·g⁻¹) = φ(g)·φ(h)·φ(g)⁻¹ = e`,
> wobei `e` das neutrale Element von `Q` bezeichnet. Also ist stets
> `g·h·g⁻¹ H`.
Gruppenmorphismus sein. Wenn es nämlich einen Gruppenmorphismus
$`\varphi : G \to Q` mit $`H = \ker(\varphi)` gibt, dann gilt für
alle $`h \in H` und alle $`g \in G` die Gleichung
$`\varphi(g \cdot h \cdot g^{-1}) = \varphi(g) \cdot \varphi(h) \cdot \varphi(g)^{-1} = e`,
wobei $`e` das neutrale Element von $`Q` bezeichnet. Also ist
stets $`g \cdot h \cdot g^{-1} \in H`.
```lean
theorem kern_konjugation {G Q : Type*} [Group G] [Group Q]
@@ -98,17 +99,20 @@ example {G H : Type*} [Group G] [Group H] (N : Subgroup G)
Beobachtung 17.3.5 des Skripts:
> Es sei `G` eine Gruppe und es sei `N G` eine normale
> Untergruppe. Weiter sei `U G` irgendeine Untergruppe. Dann ist
> `U·N = {u·n : u U, n N}` wieder eine Untergruppe. Zum Beweis
> müssen wir lediglich zeigen, dass `U·N` abgeschlossen unter der
> Gruppenoperation ist. Wir wissen, dass `N` normal ist. Also
> ist `ñ := u₂⁻¹·n₁·u₂ N` und es gilt `n₁·u₂ = u₂·ñ`. Demnach
> ist `u₁n₁u₂n₂ = u₁u₂·ñn₂ U·N`. Fertig ist der Beweis.
> Es sei $`G` eine Gruppe und es sei $`N \subset G` eine normale
Untergruppe. Weiter sei $`U \subset G` irgendeine Untergruppe.
Dann ist $`U \cdot N = \{u \cdot n : u \in U,\ n \in N\}` wieder
eine Untergruppe. Zum Beweis müssen wir lediglich zeigen, dass
$`U \cdot N` abgeschlossen unter der Gruppenoperation ist. Wir
wissen, dass $`N` normal ist. Also ist
$`\widetilde{n_1} := u_2^{-1} \cdot n_1 \cdot u_2 \in N` und es
gilt $`n_1 \cdot u_2 = u_2 \cdot \widetilde{n_1}`. Demnach ist
$`u_1 n_1 u_2 n_2 = u_1 u_2 \cdot \widetilde{n_1} n_2 \in U \cdot N`.
Fertig ist der Beweis.
Neu ist hier die Taktik `group`: Sie verrechnet Ausdrücke, die
allein mit den Gruppenaxiomen ineinander umgeformt werden können.
(Das `ñ` des Skripts heißt im Code `n₁'`.)
(Das $`\widetilde{n_1}` des Skripts heißt im Code `n₁'`.)
```lean
theorem mul_mem_mul {G : Type*} [Group G] (U N : Subgroup G)
@@ -136,9 +140,9 @@ theorem mul_mem_mul {G : Type*} [Group G] (U N : Subgroup G)
Das Skript sagt, es sei lediglich die Abgeschlossenheit unter der
Verknüpfung zu zeigen prüfen Sie den verschwiegenen Teil selbst
nach: `U·N` ist auch abgeschlossen unter Inversen. Tipp:
`(u·n)⁻¹ = u⁻¹·(u·n⁻¹·u⁻¹)`, und der zweite Faktor liegt nach
`conj_mem` in `N`. Öffnen Sie `AlgebraInLean/Quotients.lean` und
nach: $`U \cdot N` ist auch abgeschlossen unter Inversen. Tipp:
$`(u \cdot n)^{-1} = u^{-1} \cdot (u \cdot n^{-1} \cdot u^{-1})`,
und der zweite Faktor liegt nach `conj_mem` in $`N`. Öffnen Sie `AlgebraInLean/Quotients.lean` und
ersetzen Sie das `sorry` durch einen Beweis.
```lean
+13 -10
View File
@@ -24,8 +24,9 @@ schließt den Kreis zum Anfang des Kurses.
# Das Mathlib-Wörterbuch
* Das Legendre-Symbol `(a/p)` heißt `legendreSym p a : `; es setzt
eine `Fact (Nat.Prime p)`-Instanz voraus.
* Das Legendre-Symbol $`\left(\tfrac{a}{p}\right)` heißt
`legendreSym p a : `; es setzt eine `Fact (Nat.Prime p)`-Instanz
voraus.
* `a` ist ein quadratischer Rest modulo `p` ist einfach
`IsSquare (a : ZMod p)`.
@@ -39,14 +40,16 @@ schließt den Kreis zum Anfang des Kurses.
Satz 24.2.1 des Skripts:
> *Satz 24.2.1 (Quadratisches Reziprozitätsgesetz).* Es seien `p` und `q`
> zwei unterschiedliche, ungerade Primzahlen. Dann gilt die
> Gleichung `(p/q)·(q/p) = (1)^((p1)/2 · (q1)/2)`.
> *Satz 24.2.1 (Quadratisches Reziprozitätsgesetz).* Es seien $`p`
und $`q` zwei unterschiedliche, ungerade Primzahlen. Dann gilt
die Gleichung
$`\left(\tfrac{p}{q}\right) \cdot \left(\tfrac{q}{p}\right) = (-1)^{\frac{p-1}{2} \cdot \frac{q-1}{2}}`.
In Mathlib: `legendreSym.quadratic_reciprocity` dort mit den
Exponenten `p / 2` geschrieben, was für ungerade `p` dasselbe ist wie
`(p1)/2`. Dazu die beiden Ergänzungssätze: Der erste
`(1/p) = 1` genau für `p 1 (mod 4)` ist in der
Exponenten `p / 2` geschrieben, was für ungerade $`p` dasselbe ist
wie $`(p-1)/2`. Dazu die beiden Ergänzungssätze: Der erste
$`\left(\tfrac{-1}{p}\right) = 1` genau für
$`p \equiv 1 \pmod{4}` ist in der
Quadrat-Formulierung `ZMod.exists_sq_eq_neg_one_iff`:
```lean
@@ -60,8 +63,8 @@ example (p : ) [Fact p.Prime] (hp : p % 4 ≠ 3) :
Aus dem Skript:
> Ist 7 ein quadratischer Rest modulo 17?
> `(7/17) = (17/7)·(1)^{8·3} = (3/7) = (7/3)·(1)^{3·1} = (1/3)
> = 1`. Also ist die Antwort: Nein!
$$`\left(\tfrac{7}{17}\right) = \left(\tfrac{17}{7}\right) \cdot (-1)^{8 \cdot 3} = \left(\tfrac{3}{7}\right) = \left(\tfrac{7}{3}\right) \cdot (-1)^{3 \cdot 1} = -\left(\tfrac{1}{3}\right) = -1.`
Also ist die Antwort: Nein!
So rechnet man von Hand und in der Klausur. In Lean können wir
dieselbe Frage der Taktik `decide` übergeben, die das Legendre-Symbol
+4 -32
View File
@@ -7,6 +7,10 @@ open Verso.Genre.Manual
-- The static assets (theme, fonts, KaTeX) follow the setup of
-- Peter Pfaffelhuber's course notes, https://github.com/pfaffelh/leancourse.
-- Kein eigenes KaTeX mehr: Das aktuelle Verso liefert seinen eigenen
-- Mathe-Renderer mit (-verso-data/katex/…); eine zweite Kopie würde
-- jede Formel doppelt rendern.
open Verso.Output.Html in
def staticCss := {{
<link rel="stylesheet" href="/static/colors.css" />
@@ -14,45 +18,13 @@ def staticCss := {{
<link rel="stylesheet" href="/static/print.css" />
<link rel="stylesheet" href="/static/fonts/source-serif/source-serif-text.css" />
<link rel="stylesheet" href="/static/fonts/source-code-pro/source-code-pro.css" />
<link rel="stylesheet" href="/static/katex/katex.min.css" />
}}
open Verso.Output.Html in
def staticJs := {{
<script src="/static/katex/katex.min.js"></script>
<script src="/static/math.js"></script>
<script src="/static/print.js"></script>
}}
def KaTeXLicense : LicenseInfo where
identifier := "MIT"
dependency := "KaTeX"
howUsed := "KaTeX is used to render mathematical notation."
link := "https://katex.org/"
text := #[(some "The MIT License", text)]
where
text := r#"
Copyright (c) 2013-2020 Khan Academy and other contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"#
def main :=
manualMain (%doc Book) (config := config)
where
+1 -2
View File
@@ -6,5 +6,4 @@ set -e
lake build
lake exe algebrainlean --output _out
mkdir -p public
rsync -a --delete _out/html-multi/ public/
rsync -arvz --delete _out/html-multi/ kebekus@cplx.vm.uni-freiburg.de:/var/www/storage/algebra-in-lean
+3 -1
View File
@@ -1 +1,3 @@
The directory `katex` contains KaTeX v0.16.11 (MIT license)
Theme, Schriften und Druck-Stile nach dem Vorbild von
https://github.com/pfaffelh/leancourse.
Die Fonts (Source Serif, Source Code Pro) stehen unter der SIL OFL.
-125
View File
@@ -1,125 +0,0 @@
<h1><a href="https://katex.org/">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://katex.org/img/katex-logo.svg">
<img alt="KaTeX" width=130 src="https://katex.org/img/katex-logo-black.svg">
</picture>
</a></h1>
[![npm](https://img.shields.io/npm/v/katex.svg)](https://www.npmjs.com/package/katex)
[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)
[![CI](https://github.com/KaTeX/KaTeX/workflows/CI/badge.svg?branch=main&event=push)](https://github.com/KaTeX/KaTeX/actions?query=workflow%3ACI)
[![codecov](https://codecov.io/gh/KaTeX/KaTeX/branch/main/graph/badge.svg)](https://codecov.io/gh/KaTeX/KaTeX)
[![Discussions](https://img.shields.io/badge/Discussions-join-brightgreen)](https://github.com/KaTeX/KaTeX/discussions)
[![jsDelivr](https://data.jsdelivr.com/v1/package/npm/katex/badge?style=rounded)](https://www.jsdelivr.com/package/npm/katex)
![katex.min.js size](https://img.badgesize.io/https://unpkg.com/katex/dist/katex.min.js?compression=gzip)
[![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/KaTeX/KaTeX)
[![Financial Contributors on Open Collective](https://opencollective.com/katex/all/badge.svg?label=financial+contributors)](https://opencollective.com/katex)
KaTeX is a fast, easy-to-use JavaScript library for TeX math rendering on the web.
* **Fast:** KaTeX renders its math synchronously and doesn't need to reflow the page. See how it compares to a competitor in [this speed test](https://www.intmath.com/cg5/katex-mathjax-comparison.php).
* **Print quality:** KaTeX's layout is based on Donald Knuth's TeX, the gold standard for math typesetting.
* **Self contained:** KaTeX has no dependencies and can easily be bundled with your website resources.
* **Server side rendering:** KaTeX produces the same output regardless of browser or environment, so you can pre-render expressions using Node.js and send them as plain HTML.
KaTeX is compatible with all major browsers, including Chrome, Safari, Firefox, Opera, Edge, and IE 11.
KaTeX supports much (but not all) of LaTeX and many LaTeX packages. See the [list of supported functions](https://katex.org/docs/supported.html).
Try out KaTeX [on the demo page](https://katex.org/#demo)!
## Getting started
### Starter template
```html
<!DOCTYPE html>
<!-- KaTeX requires the use of the HTML5 doctype. Without it, KaTeX may not render properly -->
<html>
<head>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css" integrity="sha384-nB0miv6/jRmo5UMMR1wu3Gz6NLsoTkbqJghGIsx//Rlm+ZU03BU6SQNC66uf4l5+" crossorigin="anonymous">
<!-- The loading of KaTeX is deferred to speed up page rendering -->
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js" integrity="sha384-7zkQWkzuo3B5mTepMUcHkMB5jZaolc2xDwL6VFqjFALcbeS9Ggm/Yr2r3Dy4lfFg" crossorigin="anonymous"></script>
<!-- To automatically render math in text elements, include the auto-render extension: -->
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"
onload="renderMathInElement(document.body);"></script>
</head>
...
</html>
```
You can also [download KaTeX](https://github.com/KaTeX/KaTeX/releases) and host it yourself.
For details on how to configure auto-render extension, refer to [the documentation](https://katex.org/docs/autorender.html).
### API
Call `katex.render` to render a TeX expression directly into a DOM element.
For example:
```js
katex.render("c = \\pm\\sqrt{a^2 + b^2}", element, {
throwOnError: false
});
```
Call `katex.renderToString` to generate an HTML string of the rendered math,
e.g., for server-side rendering. For example:
```js
var html = katex.renderToString("c = \\pm\\sqrt{a^2 + b^2}", {
throwOnError: false
});
// '<span class="katex">...</span>'
```
Make sure to include the CSS and font files in both cases.
If you are doing all rendering on the server, there is no need to include the
JavaScript on the client.
The examples above use the `throwOnError: false` option, which renders invalid
inputs as the TeX source code in red (by default), with the error message as
hover text. For other available options, see the
[API documentation](https://katex.org/docs/api.html),
[options documentation](https://katex.org/docs/options.html), and
[handling errors documentation](https://katex.org/docs/error.html).
## Demo and Documentation
Learn more about using KaTeX [on the website](https://katex.org)!
## Contributors
### Code Contributors
This project exists thanks to all the people who contribute code. If you'd like to help, see [our guide to contributing code](CONTRIBUTING.md).
<a href="https://github.com/KaTeX/KaTeX/graphs/contributors"><img src="https://contributors-svg.opencollective.com/katex/contributors.svg?width=890&button=false" alt="Code contributors" /></a>
### Financial Contributors
Become a financial contributor and help us sustain our community.
#### Individuals
<a href="https://opencollective.com/katex"><img src="https://opencollective.com/katex/individuals.svg?width=890" alt="Contribute on Open Collective"></a>
#### Organizations
Support this project with your organization. Your logo will show up here with a link to your website.
<a href="https://opencollective.com/katex/organization/0/website"><img src="https://opencollective.com/katex/organization/0/avatar.svg" alt="Organization 1"></a>
<a href="https://opencollective.com/katex/organization/1/website"><img src="https://opencollective.com/katex/organization/1/avatar.svg" alt="Organization 2"></a>
<a href="https://opencollective.com/katex/organization/2/website"><img src="https://opencollective.com/katex/organization/2/avatar.svg" alt="Organization 3"></a>
<a href="https://opencollective.com/katex/organization/3/website"><img src="https://opencollective.com/katex/organization/3/avatar.svg" alt="Organization 4"></a>
<a href="https://opencollective.com/katex/organization/4/website"><img src="https://opencollective.com/katex/organization/4/avatar.svg" alt="Organization 5"></a>
<a href="https://opencollective.com/katex/organization/5/website"><img src="https://opencollective.com/katex/organization/5/avatar.svg" alt="Organization 6"></a>
<a href="https://opencollective.com/katex/organization/6/website"><img src="https://opencollective.com/katex/organization/6/avatar.svg" alt="Organization 7"></a>
<a href="https://opencollective.com/katex/organization/7/website"><img src="https://opencollective.com/katex/organization/7/avatar.svg" alt="Organization 8"></a>
<a href="https://opencollective.com/katex/organization/8/website"><img src="https://opencollective.com/katex/organization/8/avatar.svg" alt="Organization 9"></a>
<a href="https://opencollective.com/katex/organization/9/website"><img src="https://opencollective.com/katex/organization/9/avatar.svg" alt="Organization 10"></a>
## License
KaTeX is licensed under the [MIT License](https://opensource.org/licenses/MIT).
-341
View File
@@ -1,341 +0,0 @@
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory(require("katex"));
else if(typeof define === 'function' && define.amd)
define(["katex"], factory);
else if(typeof exports === 'object')
exports["renderMathInElement"] = factory(require("katex"));
else
root["renderMathInElement"] = factory(root["katex"]);
})((typeof self !== 'undefined' ? self : this), function(__WEBPACK_EXTERNAL_MODULE__771__) {
return /******/ (function() { // webpackBootstrap
/******/ "use strict";
/******/ var __webpack_modules__ = ({
/***/ 771:
/***/ (function(module) {
module.exports = __WEBPACK_EXTERNAL_MODULE__771__;
/***/ })
/******/ });
/************************************************************************/
/******/ // The module cache
/******/ var __webpack_module_cache__ = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ var cachedModule = __webpack_module_cache__[moduleId];
/******/ if (cachedModule !== undefined) {
/******/ return cachedModule.exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = __webpack_module_cache__[moduleId] = {
/******/ // no module.id needed
/******/ // no module.loaded needed
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/************************************************************************/
/******/ /* webpack/runtime/compat get default export */
/******/ !function() {
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function() { return module['default']; } :
/******/ function() { return module; };
/******/ __webpack_require__.d(getter, { a: getter });
/******/ return getter;
/******/ };
/******/ }();
/******/
/******/ /* webpack/runtime/define property getters */
/******/ !function() {
/******/ // define getter functions for harmony exports
/******/ __webpack_require__.d = function(exports, definition) {
/******/ for(var key in definition) {
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ }
/******/ }
/******/ };
/******/ }();
/******/
/******/ /* webpack/runtime/hasOwnProperty shorthand */
/******/ !function() {
/******/ __webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }
/******/ }();
/******/
/************************************************************************/
var __webpack_exports__ = {};
// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
!function() {
// EXPORTS
__webpack_require__.d(__webpack_exports__, {
"default": function() { return /* binding */ auto_render; }
});
// EXTERNAL MODULE: external "katex"
var external_katex_ = __webpack_require__(771);
var external_katex_default = /*#__PURE__*/__webpack_require__.n(external_katex_);
;// CONCATENATED MODULE: ./contrib/auto-render/splitAtDelimiters.js
/* eslint no-constant-condition:0 */
const findEndOfMath = function (delimiter, text, startIndex) {
// Adapted from
// https://github.com/Khan/perseus/blob/master/src/perseus-markdown.jsx
let index = startIndex;
let braceLevel = 0;
const delimLength = delimiter.length;
while (index < text.length) {
const character = text[index];
if (braceLevel <= 0 && text.slice(index, index + delimLength) === delimiter) {
return index;
} else if (character === "\\") {
index++;
} else if (character === "{") {
braceLevel++;
} else if (character === "}") {
braceLevel--;
}
index++;
}
return -1;
};
const escapeRegex = function (string) {
return string.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
};
const amsRegex = /^\\begin{/;
const splitAtDelimiters = function (text, delimiters) {
let index;
const data = [];
const regexLeft = new RegExp("(" + delimiters.map(x => escapeRegex(x.left)).join("|") + ")");
while (true) {
index = text.search(regexLeft);
if (index === -1) {
break;
}
if (index > 0) {
data.push({
type: "text",
data: text.slice(0, index)
});
text = text.slice(index); // now text starts with delimiter
} // ... so this always succeeds:
const i = delimiters.findIndex(delim => text.startsWith(delim.left));
index = findEndOfMath(delimiters[i].right, text, delimiters[i].left.length);
if (index === -1) {
break;
}
const rawData = text.slice(0, index + delimiters[i].right.length);
const math = amsRegex.test(rawData) ? rawData : text.slice(delimiters[i].left.length, index);
data.push({
type: "math",
data: math,
rawData,
display: delimiters[i].display
});
text = text.slice(index + delimiters[i].right.length);
}
if (text !== "") {
data.push({
type: "text",
data: text
});
}
return data;
};
/* harmony default export */ var auto_render_splitAtDelimiters = (splitAtDelimiters);
;// CONCATENATED MODULE: ./contrib/auto-render/auto-render.js
/* eslint no-console:0 */
/* Note: optionsCopy is mutated by this method. If it is ever exposed in the
* API, we should copy it before mutating.
*/
const renderMathInText = function (text, optionsCopy) {
const data = auto_render_splitAtDelimiters(text, optionsCopy.delimiters);
if (data.length === 1 && data[0].type === 'text') {
// There is no formula in the text.
// Let's return null which means there is no need to replace
// the current text node with a new one.
return null;
}
const fragment = document.createDocumentFragment();
for (let i = 0; i < data.length; i++) {
if (data[i].type === "text") {
fragment.appendChild(document.createTextNode(data[i].data));
} else {
const span = document.createElement("span");
let math = data[i].data; // Override any display mode defined in the settings with that
// defined by the text itself
optionsCopy.displayMode = data[i].display;
try {
if (optionsCopy.preProcess) {
math = optionsCopy.preProcess(math);
}
external_katex_default().render(math, span, optionsCopy);
} catch (e) {
if (!(e instanceof (external_katex_default()).ParseError)) {
throw e;
}
optionsCopy.errorCallback("KaTeX auto-render: Failed to parse `" + data[i].data + "` with ", e);
fragment.appendChild(document.createTextNode(data[i].rawData));
continue;
}
fragment.appendChild(span);
}
}
return fragment;
};
const renderElem = function (elem, optionsCopy) {
for (let i = 0; i < elem.childNodes.length; i++) {
const childNode = elem.childNodes[i];
if (childNode.nodeType === 3) {
// Text node
// Concatenate all sibling text nodes.
// Webkit browsers split very large text nodes into smaller ones,
// so the delimiters may be split across different nodes.
let textContentConcat = childNode.textContent;
let sibling = childNode.nextSibling;
let nSiblings = 0;
while (sibling && sibling.nodeType === Node.TEXT_NODE) {
textContentConcat += sibling.textContent;
sibling = sibling.nextSibling;
nSiblings++;
}
const frag = renderMathInText(textContentConcat, optionsCopy);
if (frag) {
// Remove extra text nodes
for (let j = 0; j < nSiblings; j++) {
childNode.nextSibling.remove();
}
i += frag.childNodes.length - 1;
elem.replaceChild(frag, childNode);
} else {
// If the concatenated text does not contain math
// the siblings will not either
i += nSiblings;
}
} else if (childNode.nodeType === 1) {
// Element node
const className = ' ' + childNode.className + ' ';
const shouldRender = optionsCopy.ignoredTags.indexOf(childNode.nodeName.toLowerCase()) === -1 && optionsCopy.ignoredClasses.every(x => className.indexOf(' ' + x + ' ') === -1);
if (shouldRender) {
renderElem(childNode, optionsCopy);
}
} // Otherwise, it's something else, and ignore it.
}
};
const renderMathInElement = function (elem, options) {
if (!elem) {
throw new Error("No element provided to render");
}
const optionsCopy = {}; // Object.assign(optionsCopy, option)
for (const option in options) {
if (options.hasOwnProperty(option)) {
optionsCopy[option] = options[option];
}
} // default options
optionsCopy.delimiters = optionsCopy.delimiters || [{
left: "$$",
right: "$$",
display: true
}, {
left: "\\(",
right: "\\)",
display: false
}, // LaTeX uses $…$, but it ruins the display of normal `$` in text:
// {left: "$", right: "$", display: false},
// $ must come after $$
// Render AMS environments even if outside $$…$$ delimiters.
{
left: "\\begin{equation}",
right: "\\end{equation}",
display: true
}, {
left: "\\begin{align}",
right: "\\end{align}",
display: true
}, {
left: "\\begin{alignat}",
right: "\\end{alignat}",
display: true
}, {
left: "\\begin{gather}",
right: "\\end{gather}",
display: true
}, {
left: "\\begin{CD}",
right: "\\end{CD}",
display: true
}, {
left: "\\[",
right: "\\]",
display: true
}];
optionsCopy.ignoredTags = optionsCopy.ignoredTags || ["script", "noscript", "style", "textarea", "pre", "code", "option"];
optionsCopy.ignoredClasses = optionsCopy.ignoredClasses || [];
optionsCopy.errorCallback = optionsCopy.errorCallback || console.error; // Enable sharing of global macros defined via `\gdef` between different
// math elements within a single call to `renderMathInElement`.
optionsCopy.macros = optionsCopy.macros || {};
renderElem(elem, optionsCopy);
};
/* harmony default export */ var auto_render = (renderMathInElement);
}();
__webpack_exports__ = __webpack_exports__["default"];
/******/ return __webpack_exports__;
/******/ })()
;
});
-1
View File
@@ -1 +0,0 @@
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("katex")):"function"==typeof define&&define.amd?define(["katex"],t):"object"==typeof exports?exports.renderMathInElement=t(require("katex")):e.renderMathInElement=t(e.katex)}("undefined"!=typeof self?self:this,(function(e){return function(){"use strict";var t={771:function(t){t.exports=e}},n={};function r(e){var o=n[e];if(void 0!==o)return o.exports;var i=n[e]={exports:{}};return t[e](i,i.exports,r),i.exports}r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,{a:t}),t},r.d=function(e,t){for(var n in t)r.o(t,n)&&!r.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)};var o={};return function(){r.d(o,{default:function(){return d}});var e=r(771),t=r.n(e);const n=function(e,t,n){let r=n,o=0;const i=e.length;for(;r<t.length;){const n=t[r];if(o<=0&&t.slice(r,r+i)===e)return r;"\\"===n?r++:"{"===n?o++:"}"===n&&o--,r++}return-1},i=/^\\begin{/;var a=function(e,t){let r;const o=[],a=new RegExp("("+t.map((e=>e.left.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"))).join("|")+")");for(;r=e.search(a),-1!==r;){r>0&&(o.push({type:"text",data:e.slice(0,r)}),e=e.slice(r));const a=t.findIndex((t=>e.startsWith(t.left)));if(r=n(t[a].right,e,t[a].left.length),-1===r)break;const l=e.slice(0,r+t[a].right.length),s=i.test(l)?l:e.slice(t[a].left.length,r);o.push({type:"math",data:s,rawData:l,display:t[a].display}),e=e.slice(r+t[a].right.length)}return""!==e&&o.push({type:"text",data:e}),o};const l=function(e,n){const r=a(e,n.delimiters);if(1===r.length&&"text"===r[0].type)return null;const o=document.createDocumentFragment();for(let e=0;e<r.length;e++)if("text"===r[e].type)o.appendChild(document.createTextNode(r[e].data));else{const i=document.createElement("span");let a=r[e].data;n.displayMode=r[e].display;try{n.preProcess&&(a=n.preProcess(a)),t().render(a,i,n)}catch(i){if(!(i instanceof t().ParseError))throw i;n.errorCallback("KaTeX auto-render: Failed to parse `"+r[e].data+"` with ",i),o.appendChild(document.createTextNode(r[e].rawData));continue}o.appendChild(i)}return o},s=function(e,t){for(let n=0;n<e.childNodes.length;n++){const r=e.childNodes[n];if(3===r.nodeType){let o=r.textContent,i=r.nextSibling,a=0;for(;i&&i.nodeType===Node.TEXT_NODE;)o+=i.textContent,i=i.nextSibling,a++;const s=l(o,t);if(s){for(let e=0;e<a;e++)r.nextSibling.remove();n+=s.childNodes.length-1,e.replaceChild(s,r)}else n+=a}else if(1===r.nodeType){const e=" "+r.className+" ";-1===t.ignoredTags.indexOf(r.nodeName.toLowerCase())&&t.ignoredClasses.every((t=>-1===e.indexOf(" "+t+" ")))&&s(r,t)}}};var d=function(e,t){if(!e)throw new Error("No element provided to render");const n={};for(const e in t)t.hasOwnProperty(e)&&(n[e]=t[e]);n.delimiters=n.delimiters||[{left:"$$",right:"$$",display:!0},{left:"\\(",right:"\\)",display:!1},{left:"\\begin{equation}",right:"\\end{equation}",display:!0},{left:"\\begin{align}",right:"\\end{align}",display:!0},{left:"\\begin{alignat}",right:"\\end{alignat}",display:!0},{left:"\\begin{gather}",right:"\\end{gather}",display:!0},{left:"\\begin{CD}",right:"\\end{CD}",display:!0},{left:"\\[",right:"\\]",display:!0}],n.ignoredTags=n.ignoredTags||["script","noscript","style","textarea","pre","code","option"],n.ignoredClasses=n.ignoredClasses||[],n.errorCallback=n.errorCallback||console.error,n.macros=n.macros||{},s(e,n)}}(),o=o.default}()}));
-244
View File
@@ -1,244 +0,0 @@
import katex from '../katex.mjs';
/* eslint no-constant-condition:0 */
var findEndOfMath = function findEndOfMath(delimiter, text, startIndex) {
// Adapted from
// https://github.com/Khan/perseus/blob/master/src/perseus-markdown.jsx
var index = startIndex;
var braceLevel = 0;
var delimLength = delimiter.length;
while (index < text.length) {
var character = text[index];
if (braceLevel <= 0 && text.slice(index, index + delimLength) === delimiter) {
return index;
} else if (character === "\\") {
index++;
} else if (character === "{") {
braceLevel++;
} else if (character === "}") {
braceLevel--;
}
index++;
}
return -1;
};
var escapeRegex = function escapeRegex(string) {
return string.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
};
var amsRegex = /^\\begin{/;
var splitAtDelimiters = function splitAtDelimiters(text, delimiters) {
var index;
var data = [];
var regexLeft = new RegExp("(" + delimiters.map(x => escapeRegex(x.left)).join("|") + ")");
while (true) {
index = text.search(regexLeft);
if (index === -1) {
break;
}
if (index > 0) {
data.push({
type: "text",
data: text.slice(0, index)
});
text = text.slice(index); // now text starts with delimiter
} // ... so this always succeeds:
var i = delimiters.findIndex(delim => text.startsWith(delim.left));
index = findEndOfMath(delimiters[i].right, text, delimiters[i].left.length);
if (index === -1) {
break;
}
var rawData = text.slice(0, index + delimiters[i].right.length);
var math = amsRegex.test(rawData) ? rawData : text.slice(delimiters[i].left.length, index);
data.push({
type: "math",
data: math,
rawData,
display: delimiters[i].display
});
text = text.slice(index + delimiters[i].right.length);
}
if (text !== "") {
data.push({
type: "text",
data: text
});
}
return data;
};
/* eslint no-console:0 */
/* Note: optionsCopy is mutated by this method. If it is ever exposed in the
* API, we should copy it before mutating.
*/
var renderMathInText = function renderMathInText(text, optionsCopy) {
var data = splitAtDelimiters(text, optionsCopy.delimiters);
if (data.length === 1 && data[0].type === 'text') {
// There is no formula in the text.
// Let's return null which means there is no need to replace
// the current text node with a new one.
return null;
}
var fragment = document.createDocumentFragment();
for (var i = 0; i < data.length; i++) {
if (data[i].type === "text") {
fragment.appendChild(document.createTextNode(data[i].data));
} else {
var span = document.createElement("span");
var math = data[i].data; // Override any display mode defined in the settings with that
// defined by the text itself
optionsCopy.displayMode = data[i].display;
try {
if (optionsCopy.preProcess) {
math = optionsCopy.preProcess(math);
}
katex.render(math, span, optionsCopy);
} catch (e) {
if (!(e instanceof katex.ParseError)) {
throw e;
}
optionsCopy.errorCallback("KaTeX auto-render: Failed to parse `" + data[i].data + "` with ", e);
fragment.appendChild(document.createTextNode(data[i].rawData));
continue;
}
fragment.appendChild(span);
}
}
return fragment;
};
var renderElem = function renderElem(elem, optionsCopy) {
for (var i = 0; i < elem.childNodes.length; i++) {
var childNode = elem.childNodes[i];
if (childNode.nodeType === 3) {
// Text node
// Concatenate all sibling text nodes.
// Webkit browsers split very large text nodes into smaller ones,
// so the delimiters may be split across different nodes.
var textContentConcat = childNode.textContent;
var sibling = childNode.nextSibling;
var nSiblings = 0;
while (sibling && sibling.nodeType === Node.TEXT_NODE) {
textContentConcat += sibling.textContent;
sibling = sibling.nextSibling;
nSiblings++;
}
var frag = renderMathInText(textContentConcat, optionsCopy);
if (frag) {
// Remove extra text nodes
for (var j = 0; j < nSiblings; j++) {
childNode.nextSibling.remove();
}
i += frag.childNodes.length - 1;
elem.replaceChild(frag, childNode);
} else {
// If the concatenated text does not contain math
// the siblings will not either
i += nSiblings;
}
} else if (childNode.nodeType === 1) {
(function () {
// Element node
var className = ' ' + childNode.className + ' ';
var shouldRender = optionsCopy.ignoredTags.indexOf(childNode.nodeName.toLowerCase()) === -1 && optionsCopy.ignoredClasses.every(x => className.indexOf(' ' + x + ' ') === -1);
if (shouldRender) {
renderElem(childNode, optionsCopy);
}
})();
} // Otherwise, it's something else, and ignore it.
}
};
var renderMathInElement = function renderMathInElement(elem, options) {
if (!elem) {
throw new Error("No element provided to render");
}
var optionsCopy = {}; // Object.assign(optionsCopy, option)
for (var option in options) {
if (options.hasOwnProperty(option)) {
optionsCopy[option] = options[option];
}
} // default options
optionsCopy.delimiters = optionsCopy.delimiters || [{
left: "$$",
right: "$$",
display: true
}, {
left: "\\(",
right: "\\)",
display: false
}, // LaTeX uses $…$, but it ruins the display of normal `$` in text:
// {left: "$", right: "$", display: false},
// $ must come after $$
// Render AMS environments even if outside $$…$$ delimiters.
{
left: "\\begin{equation}",
right: "\\end{equation}",
display: true
}, {
left: "\\begin{align}",
right: "\\end{align}",
display: true
}, {
left: "\\begin{alignat}",
right: "\\end{alignat}",
display: true
}, {
left: "\\begin{gather}",
right: "\\end{gather}",
display: true
}, {
left: "\\begin{CD}",
right: "\\end{CD}",
display: true
}, {
left: "\\[",
right: "\\]",
display: true
}];
optionsCopy.ignoredTags = optionsCopy.ignoredTags || ["script", "noscript", "style", "textarea", "pre", "code", "option"];
optionsCopy.ignoredClasses = optionsCopy.ignoredClasses || [];
optionsCopy.errorCallback = optionsCopy.errorCallback || console.error; // Enable sharing of global macros defined via `\gdef` between different
// math elements within a single call to `renderMathInElement`.
optionsCopy.macros = optionsCopy.macros || {};
renderElem(elem, optionsCopy);
};
export { renderMathInElement as default };
-127
View File
@@ -1,127 +0,0 @@
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define([], factory);
else {
var a = factory();
for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
}
})((typeof self !== 'undefined' ? self : this), function() {
return /******/ (function() { // webpackBootstrap
/******/ "use strict";
var __webpack_exports__ = {};
;// CONCATENATED MODULE: ./contrib/copy-tex/katex2tex.js
// Set these to how you want inline and display math to be delimited.
const defaultCopyDelimiters = {
inline: ['$', '$'],
// alternative: ['\(', '\)']
display: ['$$', '$$'] // alternative: ['\[', '\]']
}; // Replace .katex elements with their TeX source (<annotation> element).
// Modifies fragment in-place. Useful for writing your own 'copy' handler,
// as in copy-tex.js.
function katexReplaceWithTex(fragment, copyDelimiters) {
if (copyDelimiters === void 0) {
copyDelimiters = defaultCopyDelimiters;
}
// Remove .katex-html blocks that are preceded by .katex-mathml blocks
// (which will get replaced below).
const katexHtml = fragment.querySelectorAll('.katex-mathml + .katex-html');
for (let i = 0; i < katexHtml.length; i++) {
const element = katexHtml[i];
if (element.remove) {
element.remove();
} else if (element.parentNode) {
element.parentNode.removeChild(element);
}
} // Replace .katex-mathml elements with their annotation (TeX source)
// descendant, with inline delimiters.
const katexMathml = fragment.querySelectorAll('.katex-mathml');
for (let i = 0; i < katexMathml.length; i++) {
const element = katexMathml[i];
const texSource = element.querySelector('annotation');
if (texSource) {
if (element.replaceWith) {
element.replaceWith(texSource);
} else if (element.parentNode) {
element.parentNode.replaceChild(texSource, element);
}
texSource.innerHTML = copyDelimiters.inline[0] + texSource.innerHTML + copyDelimiters.inline[1];
}
} // Switch display math to display delimiters.
const displays = fragment.querySelectorAll('.katex-display annotation');
for (let i = 0; i < displays.length; i++) {
const element = displays[i];
element.innerHTML = copyDelimiters.display[0] + element.innerHTML.substr(copyDelimiters.inline[0].length, element.innerHTML.length - copyDelimiters.inline[0].length - copyDelimiters.inline[1].length) + copyDelimiters.display[1];
}
return fragment;
}
/* harmony default export */ var katex2tex = (katexReplaceWithTex);
;// CONCATENATED MODULE: ./contrib/copy-tex/copy-tex.js
// Return <div class="katex"> element containing node, or null if not found.
function closestKatex(node) {
// If node is a Text Node, for example, go up to containing Element,
// where we can apply the `closest` method.
const element = node instanceof Element ? node : node.parentElement;
return element && element.closest('.katex');
} // Global copy handler to modify behavior on/within .katex elements.
document.addEventListener('copy', function (event) {
const selection = window.getSelection();
if (selection.isCollapsed || !event.clipboardData) {
return; // default action OK if selection is empty or unchangeable
}
const clipboardData = event.clipboardData;
const range = selection.getRangeAt(0); // When start point is within a formula, expand to entire formula.
const startKatex = closestKatex(range.startContainer);
if (startKatex) {
range.setStartBefore(startKatex);
} // Similarly, when end point is within a formula, expand to entire formula.
const endKatex = closestKatex(range.endContainer);
if (endKatex) {
range.setEndAfter(endKatex);
}
const fragment = range.cloneContents();
if (!fragment.querySelector('.katex-mathml')) {
return; // default action OK if no .katex-mathml elements
}
const htmlContents = Array.prototype.map.call(fragment.childNodes, el => el instanceof Text ? el.textContent : el.outerHTML).join(''); // Preserve usual HTML copy/paste behavior.
clipboardData.setData('text/html', htmlContents); // Rewrite plain-text version.
clipboardData.setData('text/plain', katex2tex(fragment).textContent); // Prevent normal copy handling.
event.preventDefault();
});
__webpack_exports__ = __webpack_exports__["default"];
/******/ return __webpack_exports__;
/******/ })()
;
});
-1
View File
@@ -1 +0,0 @@
!function(e,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var n=t();for(var o in n)("object"==typeof exports?exports:e)[o]=n[o]}}("undefined"!=typeof self?self:this,(function(){return function(){"use strict";var e={};const t={inline:["$","$"],display:["$$","$$"]};var n=function(e,n){void 0===n&&(n=t);const o=e.querySelectorAll(".katex-mathml + .katex-html");for(let e=0;e<o.length;e++){const t=o[e];t.remove?t.remove():t.parentNode&&t.parentNode.removeChild(t)}const r=e.querySelectorAll(".katex-mathml");for(let e=0;e<r.length;e++){const t=r[e],o=t.querySelector("annotation");o&&(t.replaceWith?t.replaceWith(o):t.parentNode&&t.parentNode.replaceChild(o,t),o.innerHTML=n.inline[0]+o.innerHTML+n.inline[1])}const l=e.querySelectorAll(".katex-display annotation");for(let e=0;e<l.length;e++){const t=l[e];t.innerHTML=n.display[0]+t.innerHTML.substr(n.inline[0].length,t.innerHTML.length-n.inline[0].length-n.inline[1].length)+n.display[1]}return e};function o(e){const t=e instanceof Element?e:e.parentElement;return t&&t.closest(".katex")}return document.addEventListener("copy",(function(e){const t=window.getSelection();if(t.isCollapsed||!e.clipboardData)return;const r=e.clipboardData,l=t.getRangeAt(0),i=o(l.startContainer);i&&l.setStartBefore(i);const a=o(l.endContainer);a&&l.setEndAfter(a);const s=l.cloneContents();if(!s.querySelector(".katex-mathml"))return;const c=Array.prototype.map.call(s.childNodes,(e=>e instanceof Text?e.textContent:e.outerHTML)).join("");r.setData("text/html",c),r.setData("text/plain",n(s).textContent),e.preventDefault()})),e=e.default}()}));
-105
View File
@@ -1,105 +0,0 @@
// Set these to how you want inline and display math to be delimited.
var defaultCopyDelimiters = {
inline: ['$', '$'],
// alternative: ['\(', '\)']
display: ['$$', '$$'] // alternative: ['\[', '\]']
}; // Replace .katex elements with their TeX source (<annotation> element).
// Modifies fragment in-place. Useful for writing your own 'copy' handler,
// as in copy-tex.js.
function katexReplaceWithTex(fragment, copyDelimiters) {
if (copyDelimiters === void 0) {
copyDelimiters = defaultCopyDelimiters;
}
// Remove .katex-html blocks that are preceded by .katex-mathml blocks
// (which will get replaced below).
var katexHtml = fragment.querySelectorAll('.katex-mathml + .katex-html');
for (var i = 0; i < katexHtml.length; i++) {
var element = katexHtml[i];
if (element.remove) {
element.remove();
} else if (element.parentNode) {
element.parentNode.removeChild(element);
}
} // Replace .katex-mathml elements with their annotation (TeX source)
// descendant, with inline delimiters.
var katexMathml = fragment.querySelectorAll('.katex-mathml');
for (var _i = 0; _i < katexMathml.length; _i++) {
var _element = katexMathml[_i];
var texSource = _element.querySelector('annotation');
if (texSource) {
if (_element.replaceWith) {
_element.replaceWith(texSource);
} else if (_element.parentNode) {
_element.parentNode.replaceChild(texSource, _element);
}
texSource.innerHTML = copyDelimiters.inline[0] + texSource.innerHTML + copyDelimiters.inline[1];
}
} // Switch display math to display delimiters.
var displays = fragment.querySelectorAll('.katex-display annotation');
for (var _i2 = 0; _i2 < displays.length; _i2++) {
var _element2 = displays[_i2];
_element2.innerHTML = copyDelimiters.display[0] + _element2.innerHTML.substr(copyDelimiters.inline[0].length, _element2.innerHTML.length - copyDelimiters.inline[0].length - copyDelimiters.inline[1].length) + copyDelimiters.display[1];
}
return fragment;
}
function closestKatex(node) {
// If node is a Text Node, for example, go up to containing Element,
// where we can apply the `closest` method.
var element = node instanceof Element ? node : node.parentElement;
return element && element.closest('.katex');
} // Global copy handler to modify behavior on/within .katex elements.
document.addEventListener('copy', function (event) {
var selection = window.getSelection();
if (selection.isCollapsed || !event.clipboardData) {
return; // default action OK if selection is empty or unchangeable
}
var clipboardData = event.clipboardData;
var range = selection.getRangeAt(0); // When start point is within a formula, expand to entire formula.
var startKatex = closestKatex(range.startContainer);
if (startKatex) {
range.setStartBefore(startKatex);
} // Similarly, when end point is within a formula, expand to entire formula.
var endKatex = closestKatex(range.endContainer);
if (endKatex) {
range.setEndAfter(endKatex);
}
var fragment = range.cloneContents();
if (!fragment.querySelector('.katex-mathml')) {
return; // default action OK if no .katex-mathml elements
}
var htmlContents = Array.prototype.map.call(fragment.childNodes, el => el instanceof Text ? el.textContent : el.outerHTML).join(''); // Preserve usual HTML copy/paste behavior.
clipboardData.setData('text/html', htmlContents); // Rewrite plain-text version.
clipboardData.setData('text/plain', katexReplaceWithTex(fragment).textContent); // Prevent normal copy handling.
event.preventDefault();
});
-112
View File
@@ -1,112 +0,0 @@
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory(require("katex"));
else if(typeof define === 'function' && define.amd)
define(["katex"], factory);
else {
var a = typeof exports === 'object' ? factory(require("katex")) : factory(root["katex"]);
for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
}
})((typeof self !== 'undefined' ? self : this), function(__WEBPACK_EXTERNAL_MODULE__771__) {
return /******/ (function() { // webpackBootstrap
/******/ "use strict";
/******/ var __webpack_modules__ = ({
/***/ 771:
/***/ (function(module) {
module.exports = __WEBPACK_EXTERNAL_MODULE__771__;
/***/ })
/******/ });
/************************************************************************/
/******/ // The module cache
/******/ var __webpack_module_cache__ = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ var cachedModule = __webpack_module_cache__[moduleId];
/******/ if (cachedModule !== undefined) {
/******/ return cachedModule.exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = __webpack_module_cache__[moduleId] = {
/******/ // no module.id needed
/******/ // no module.loaded needed
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/************************************************************************/
/******/ /* webpack/runtime/compat get default export */
/******/ !function() {
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function() { return module['default']; } :
/******/ function() { return module; };
/******/ __webpack_require__.d(getter, { a: getter });
/******/ return getter;
/******/ };
/******/ }();
/******/
/******/ /* webpack/runtime/define property getters */
/******/ !function() {
/******/ // define getter functions for harmony exports
/******/ __webpack_require__.d = function(exports, definition) {
/******/ for(var key in definition) {
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ }
/******/ }
/******/ };
/******/ }();
/******/
/******/ /* webpack/runtime/hasOwnProperty shorthand */
/******/ !function() {
/******/ __webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }
/******/ }();
/******/
/************************************************************************/
var __webpack_exports__ = {};
// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
!function() {
/* harmony import */ var katex__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(771);
/* harmony import */ var katex__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(katex__WEBPACK_IMPORTED_MODULE_0__);
let scripts = document.body.getElementsByTagName("script");
scripts = Array.prototype.slice.call(scripts);
scripts.forEach(function (script) {
if (!script.type || !script.type.match(/math\/tex/i)) {
return -1;
}
const display = script.type.match(/mode\s*=\s*display(;|\s|\n|$)/) != null;
const katexElement = document.createElement(display ? "div" : "span");
katexElement.setAttribute("class", display ? "equation" : "inline-equation");
try {
katex__WEBPACK_IMPORTED_MODULE_0___default().render(script.text, katexElement, {
displayMode: display
});
} catch (err) {
//console.error(err); linter doesn't like this
katexElement.textContent = script.text;
}
script.parentNode.replaceChild(katexElement, script);
});
}();
__webpack_exports__ = __webpack_exports__["default"];
/******/ return __webpack_exports__;
/******/ })()
;
});
-1
View File
@@ -1 +0,0 @@
!function(e,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t(require("katex"));else if("function"==typeof define&&define.amd)define(["katex"],t);else{var n="object"==typeof exports?t(require("katex")):t(e.katex);for(var r in n)("object"==typeof exports?exports:e)[r]=n[r]}}("undefined"!=typeof self?self:this,(function(e){return function(){"use strict";var t={771:function(t){t.exports=e}},n={};function r(e){var o=n[e];if(void 0!==o)return o.exports;var i=n[e]={exports:{}};return t[e](i,i.exports,r),i.exports}r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,{a:t}),t},r.d=function(e,t){for(var n in t)r.o(t,n)&&!r.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)};var o={};return function(){var e=r(771),t=r.n(e);let n=document.body.getElementsByTagName("script");n=Array.prototype.slice.call(n),n.forEach((function(e){if(!e.type||!e.type.match(/math\/tex/i))return-1;const n=null!=e.type.match(/mode\s*=\s*display(;|\s|\n|$)/),r=document.createElement(n?"div":"span");r.setAttribute("class",n?"equation":"inline-equation");try{t().render(e.text,r,{displayMode:n})}catch(t){r.textContent=e.text}e.parentNode.replaceChild(r,e)}))}(),o=o.default}()}));
@@ -1,24 +0,0 @@
import katex from '../katex.mjs';
var scripts = document.body.getElementsByTagName("script");
scripts = Array.prototype.slice.call(scripts);
scripts.forEach(function (script) {
if (!script.type || !script.type.match(/math\/tex/i)) {
return -1;
}
var display = script.type.match(/mode\s*=\s*display(;|\s|\n|$)/) != null;
var katexElement = document.createElement(display ? "div" : "span");
katexElement.setAttribute("class", display ? "equation" : "inline-equation");
try {
katex.render(script.text, katexElement, {
displayMode: display
});
} catch (err) {
//console.error(err); linter doesn't like this
katexElement.textContent = script.text;
}
script.parentNode.replaceChild(katexElement, script);
});
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
-890
View File
@@ -1,890 +0,0 @@
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory(require("katex"));
else if(typeof define === 'function' && define.amd)
define(["katex"], factory);
else {
var a = typeof exports === 'object' ? factory(require("katex")) : factory(root["katex"]);
for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
}
})((typeof self !== 'undefined' ? self : this), function(__WEBPACK_EXTERNAL_MODULE__771__) {
return /******/ (function() { // webpackBootstrap
/******/ "use strict";
/******/ var __webpack_modules__ = ({
/***/ 771:
/***/ (function(module) {
module.exports = __WEBPACK_EXTERNAL_MODULE__771__;
/***/ })
/******/ });
/************************************************************************/
/******/ // The module cache
/******/ var __webpack_module_cache__ = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ var cachedModule = __webpack_module_cache__[moduleId];
/******/ if (cachedModule !== undefined) {
/******/ return cachedModule.exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = __webpack_module_cache__[moduleId] = {
/******/ // no module.id needed
/******/ // no module.loaded needed
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/************************************************************************/
/******/ /* webpack/runtime/compat get default export */
/******/ !function() {
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function() { return module['default']; } :
/******/ function() { return module; };
/******/ __webpack_require__.d(getter, { a: getter });
/******/ return getter;
/******/ };
/******/ }();
/******/
/******/ /* webpack/runtime/define property getters */
/******/ !function() {
/******/ // define getter functions for harmony exports
/******/ __webpack_require__.d = function(exports, definition) {
/******/ for(var key in definition) {
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ }
/******/ }
/******/ };
/******/ }();
/******/
/******/ /* webpack/runtime/hasOwnProperty shorthand */
/******/ !function() {
/******/ __webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }
/******/ }();
/******/
/************************************************************************/
var __webpack_exports__ = {};
// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
!function() {
/* harmony import */ var katex__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(771);
/* harmony import */ var katex__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(katex__WEBPACK_IMPORTED_MODULE_0__);
/**
* renderA11yString returns a readable string.
*
* In some cases the string will have the proper semantic math
* meaning,:
* renderA11yString("\\frac{1}{2}"")
* -> "start fraction, 1, divided by, 2, end fraction"
*
* However, other cases do not:
* renderA11yString("f(x) = x^2")
* -> "f, left parenthesis, x, right parenthesis, equals, x, squared"
*
* The commas in the string aim to increase ease of understanding
* when read by a screenreader.
*/
// NOTE: since we're importing types here these files won't actually be
// included in the build.
// $FlowIgnore: we import the types directly anyways
const stringMap = {
"(": "left parenthesis",
")": "right parenthesis",
"[": "open bracket",
"]": "close bracket",
"\\{": "left brace",
"\\}": "right brace",
"\\lvert": "open vertical bar",
"\\rvert": "close vertical bar",
"|": "vertical bar",
"\\uparrow": "up arrow",
"\\Uparrow": "up arrow",
"\\downarrow": "down arrow",
"\\Downarrow": "down arrow",
"\\updownarrow": "up down arrow",
"\\leftarrow": "left arrow",
"\\Leftarrow": "left arrow",
"\\rightarrow": "right arrow",
"\\Rightarrow": "right arrow",
"\\langle": "open angle",
"\\rangle": "close angle",
"\\lfloor": "open floor",
"\\rfloor": "close floor",
"\\int": "integral",
"\\intop": "integral",
"\\lim": "limit",
"\\ln": "natural log",
"\\log": "log",
"\\sin": "sine",
"\\cos": "cosine",
"\\tan": "tangent",
"\\cot": "cotangent",
"\\sum": "sum",
"/": "slash",
",": "comma",
".": "point",
"-": "negative",
"+": "plus",
"~": "tilde",
":": "colon",
"?": "question mark",
"'": "apostrophe",
"\\%": "percent",
" ": "space",
"\\ ": "space",
"\\$": "dollar sign",
"\\angle": "angle",
"\\degree": "degree",
"\\circ": "circle",
"\\vec": "vector",
"\\triangle": "triangle",
"\\pi": "pi",
"\\prime": "prime",
"\\infty": "infinity",
"\\alpha": "alpha",
"\\beta": "beta",
"\\gamma": "gamma",
"\\omega": "omega",
"\\theta": "theta",
"\\sigma": "sigma",
"\\lambda": "lambda",
"\\tau": "tau",
"\\Delta": "delta",
"\\delta": "delta",
"\\mu": "mu",
"\\rho": "rho",
"\\nabla": "del",
"\\ell": "ell",
"\\ldots": "dots",
// TODO: add entries for all accents
"\\hat": "hat",
"\\acute": "acute"
};
const powerMap = {
"prime": "prime",
"degree": "degrees",
"circle": "degrees",
"2": "squared",
"3": "cubed"
};
const openMap = {
"|": "open vertical bar",
".": ""
};
const closeMap = {
"|": "close vertical bar",
".": ""
};
const binMap = {
"+": "plus",
"-": "minus",
"\\pm": "plus minus",
"\\cdot": "dot",
"*": "times",
"/": "divided by",
"\\times": "times",
"\\div": "divided by",
"\\circ": "circle",
"\\bullet": "bullet"
};
const relMap = {
"=": "equals",
"\\approx": "approximately equals",
"≠": "does not equal",
"\\geq": "is greater than or equal to",
"\\ge": "is greater than or equal to",
"\\leq": "is less than or equal to",
"\\le": "is less than or equal to",
">": "is greater than",
"<": "is less than",
"\\leftarrow": "left arrow",
"\\Leftarrow": "left arrow",
"\\rightarrow": "right arrow",
"\\Rightarrow": "right arrow",
":": "colon"
};
const accentUnderMap = {
"\\underleftarrow": "left arrow",
"\\underrightarrow": "right arrow",
"\\underleftrightarrow": "left-right arrow",
"\\undergroup": "group",
"\\underlinesegment": "line segment",
"\\utilde": "tilde"
};
const buildString = (str, type, a11yStrings) => {
if (!str) {
return;
}
let ret;
if (type === "open") {
ret = str in openMap ? openMap[str] : stringMap[str] || str;
} else if (type === "close") {
ret = str in closeMap ? closeMap[str] : stringMap[str] || str;
} else if (type === "bin") {
ret = binMap[str] || str;
} else if (type === "rel") {
ret = relMap[str] || str;
} else {
ret = stringMap[str] || str;
} // If the text to add is a number and there is already a string
// in the list and the last string is a number then we should
// combine them into a single number
if (/^\d+$/.test(ret) && a11yStrings.length > 0 && // TODO(kevinb): check that the last item in a11yStrings is a string
// I think we might be able to drop the nested arrays, which would make
// this easier to type
// $FlowFixMe
/^\d+$/.test(a11yStrings[a11yStrings.length - 1])) {
a11yStrings[a11yStrings.length - 1] += ret;
} else if (ret) {
a11yStrings.push(ret);
}
};
const buildRegion = (a11yStrings, callback) => {
const regionStrings = [];
a11yStrings.push(regionStrings);
callback(regionStrings);
};
const handleObject = (tree, a11yStrings, atomType) => {
// Everything else is assumed to be an object...
switch (tree.type) {
case "accent":
{
buildRegion(a11yStrings, a11yStrings => {
buildA11yStrings(tree.base, a11yStrings, atomType);
a11yStrings.push("with");
buildString(tree.label, "normal", a11yStrings);
a11yStrings.push("on top");
});
break;
}
case "accentUnder":
{
buildRegion(a11yStrings, a11yStrings => {
buildA11yStrings(tree.base, a11yStrings, atomType);
a11yStrings.push("with");
buildString(accentUnderMap[tree.label], "normal", a11yStrings);
a11yStrings.push("underneath");
});
break;
}
case "accent-token":
{
// Used internally by accent symbols.
break;
}
case "atom":
{
const {
text
} = tree;
switch (tree.family) {
case "bin":
{
buildString(text, "bin", a11yStrings);
break;
}
case "close":
{
buildString(text, "close", a11yStrings);
break;
}
// TODO(kevinb): figure out what should be done for inner
case "inner":
{
buildString(tree.text, "inner", a11yStrings);
break;
}
case "open":
{
buildString(text, "open", a11yStrings);
break;
}
case "punct":
{
buildString(text, "punct", a11yStrings);
break;
}
case "rel":
{
buildString(text, "rel", a11yStrings);
break;
}
default:
{
tree.family;
throw new Error("\"" + tree.family + "\" is not a valid atom type");
}
}
break;
}
case "color":
{
const color = tree.color.replace(/katex-/, "");
buildRegion(a11yStrings, regionStrings => {
regionStrings.push("start color " + color);
buildA11yStrings(tree.body, regionStrings, atomType);
regionStrings.push("end color " + color);
});
break;
}
case "color-token":
{
// Used by \color, \colorbox, and \fcolorbox but not directly rendered.
// It's a leaf node and has no children so just break.
break;
}
case "delimsizing":
{
if (tree.delim && tree.delim !== ".") {
buildString(tree.delim, "normal", a11yStrings);
}
break;
}
case "genfrac":
{
buildRegion(a11yStrings, regionStrings => {
// genfrac can have unbalanced delimiters
const {
leftDelim,
rightDelim
} = tree; // NOTE: Not sure if this is a safe assumption
// hasBarLine true -> fraction, false -> binomial
if (tree.hasBarLine) {
regionStrings.push("start fraction");
leftDelim && buildString(leftDelim, "open", regionStrings);
buildA11yStrings(tree.numer, regionStrings, atomType);
regionStrings.push("divided by");
buildA11yStrings(tree.denom, regionStrings, atomType);
rightDelim && buildString(rightDelim, "close", regionStrings);
regionStrings.push("end fraction");
} else {
regionStrings.push("start binomial");
leftDelim && buildString(leftDelim, "open", regionStrings);
buildA11yStrings(tree.numer, regionStrings, atomType);
regionStrings.push("over");
buildA11yStrings(tree.denom, regionStrings, atomType);
rightDelim && buildString(rightDelim, "close", regionStrings);
regionStrings.push("end binomial");
}
});
break;
}
case "hbox":
{
buildA11yStrings(tree.body, a11yStrings, atomType);
break;
}
case "kern":
{
// No op: we don't attempt to present kerning information
// to the screen reader.
break;
}
case "leftright":
{
buildRegion(a11yStrings, regionStrings => {
buildString(tree.left, "open", regionStrings);
buildA11yStrings(tree.body, regionStrings, atomType);
buildString(tree.right, "close", regionStrings);
});
break;
}
case "leftright-right":
{
// TODO: double check that this is a no-op
break;
}
case "lap":
{
buildA11yStrings(tree.body, a11yStrings, atomType);
break;
}
case "mathord":
{
buildString(tree.text, "normal", a11yStrings);
break;
}
case "op":
{
const {
body,
name
} = tree;
if (body) {
buildA11yStrings(body, a11yStrings, atomType);
} else if (name) {
buildString(name, "normal", a11yStrings);
}
break;
}
case "op-token":
{
// Used internally by operator symbols.
buildString(tree.text, atomType, a11yStrings);
break;
}
case "ordgroup":
{
buildA11yStrings(tree.body, a11yStrings, atomType);
break;
}
case "overline":
{
buildRegion(a11yStrings, function (a11yStrings) {
a11yStrings.push("start overline");
buildA11yStrings(tree.body, a11yStrings, atomType);
a11yStrings.push("end overline");
});
break;
}
case "pmb":
{
a11yStrings.push("bold");
break;
}
case "phantom":
{
a11yStrings.push("empty space");
break;
}
case "raisebox":
{
buildA11yStrings(tree.body, a11yStrings, atomType);
break;
}
case "rule":
{
a11yStrings.push("rectangle");
break;
}
case "sizing":
{
buildA11yStrings(tree.body, a11yStrings, atomType);
break;
}
case "spacing":
{
a11yStrings.push("space");
break;
}
case "styling":
{
// We ignore the styling and just pass through the contents
buildA11yStrings(tree.body, a11yStrings, atomType);
break;
}
case "sqrt":
{
buildRegion(a11yStrings, regionStrings => {
const {
body,
index
} = tree;
if (index) {
const indexString = flatten(buildA11yStrings(index, [], atomType)).join(",");
if (indexString === "3") {
regionStrings.push("cube root of");
buildA11yStrings(body, regionStrings, atomType);
regionStrings.push("end cube root");
return;
}
regionStrings.push("root");
regionStrings.push("start index");
buildA11yStrings(index, regionStrings, atomType);
regionStrings.push("end index");
return;
}
regionStrings.push("square root of");
buildA11yStrings(body, regionStrings, atomType);
regionStrings.push("end square root");
});
break;
}
case "supsub":
{
const {
base,
sub,
sup
} = tree;
let isLog = false;
if (base) {
buildA11yStrings(base, a11yStrings, atomType);
isLog = base.type === "op" && base.name === "\\log";
}
if (sub) {
const regionName = isLog ? "base" : "subscript";
buildRegion(a11yStrings, function (regionStrings) {
regionStrings.push("start " + regionName);
buildA11yStrings(sub, regionStrings, atomType);
regionStrings.push("end " + regionName);
});
}
if (sup) {
buildRegion(a11yStrings, function (regionStrings) {
const supString = flatten(buildA11yStrings(sup, [], atomType)).join(",");
if (supString in powerMap) {
regionStrings.push(powerMap[supString]);
return;
}
regionStrings.push("start superscript");
buildA11yStrings(sup, regionStrings, atomType);
regionStrings.push("end superscript");
});
}
break;
}
case "text":
{
// TODO: handle other fonts
if (tree.font === "\\textbf") {
buildRegion(a11yStrings, function (regionStrings) {
regionStrings.push("start bold text");
buildA11yStrings(tree.body, regionStrings, atomType);
regionStrings.push("end bold text");
});
break;
}
buildRegion(a11yStrings, function (regionStrings) {
regionStrings.push("start text");
buildA11yStrings(tree.body, regionStrings, atomType);
regionStrings.push("end text");
});
break;
}
case "textord":
{
buildString(tree.text, atomType, a11yStrings);
break;
}
case "smash":
{
buildA11yStrings(tree.body, a11yStrings, atomType);
break;
}
case "enclose":
{
// TODO: create a map for these.
// TODO: differentiate between a body with a single atom, e.g.
// "cancel a" instead of "start cancel, a, end cancel"
if (/cancel/.test(tree.label)) {
buildRegion(a11yStrings, function (regionStrings) {
regionStrings.push("start cancel");
buildA11yStrings(tree.body, regionStrings, atomType);
regionStrings.push("end cancel");
});
break;
} else if (/box/.test(tree.label)) {
buildRegion(a11yStrings, function (regionStrings) {
regionStrings.push("start box");
buildA11yStrings(tree.body, regionStrings, atomType);
regionStrings.push("end box");
});
break;
} else if (/sout/.test(tree.label)) {
buildRegion(a11yStrings, function (regionStrings) {
regionStrings.push("start strikeout");
buildA11yStrings(tree.body, regionStrings, atomType);
regionStrings.push("end strikeout");
});
break;
} else if (/phase/.test(tree.label)) {
buildRegion(a11yStrings, function (regionStrings) {
regionStrings.push("start phase angle");
buildA11yStrings(tree.body, regionStrings, atomType);
regionStrings.push("end phase angle");
});
break;
}
throw new Error("KaTeX-a11y: enclose node with " + tree.label + " not supported yet");
}
case "vcenter":
{
buildA11yStrings(tree.body, a11yStrings, atomType);
break;
}
case "vphantom":
{
throw new Error("KaTeX-a11y: vphantom not implemented yet");
}
case "hphantom":
{
throw new Error("KaTeX-a11y: hphantom not implemented yet");
}
case "operatorname":
{
buildA11yStrings(tree.body, a11yStrings, atomType);
break;
}
case "array":
{
throw new Error("KaTeX-a11y: array not implemented yet");
}
case "raw":
{
throw new Error("KaTeX-a11y: raw not implemented yet");
}
case "size":
{
// Although there are nodes of type "size" in the parse tree, they have
// no semantic meaning and should be ignored.
break;
}
case "url":
{
throw new Error("KaTeX-a11y: url not implemented yet");
}
case "tag":
{
throw new Error("KaTeX-a11y: tag not implemented yet");
}
case "verb":
{
buildString("start verbatim", "normal", a11yStrings);
buildString(tree.body, "normal", a11yStrings);
buildString("end verbatim", "normal", a11yStrings);
break;
}
case "environment":
{
throw new Error("KaTeX-a11y: environment not implemented yet");
}
case "horizBrace":
{
buildString("start " + tree.label.slice(1), "normal", a11yStrings);
buildA11yStrings(tree.base, a11yStrings, atomType);
buildString("end " + tree.label.slice(1), "normal", a11yStrings);
break;
}
case "infix":
{
// All infix nodes are replace with other nodes.
break;
}
case "includegraphics":
{
throw new Error("KaTeX-a11y: includegraphics not implemented yet");
}
case "font":
{
// TODO: callout the start/end of specific fonts
// TODO: map \BBb{N} to "the naturals" or something like that
buildA11yStrings(tree.body, a11yStrings, atomType);
break;
}
case "href":
{
throw new Error("KaTeX-a11y: href not implemented yet");
}
case "cr":
{
// This is used by environments.
throw new Error("KaTeX-a11y: cr not implemented yet");
}
case "underline":
{
buildRegion(a11yStrings, function (a11yStrings) {
a11yStrings.push("start underline");
buildA11yStrings(tree.body, a11yStrings, atomType);
a11yStrings.push("end underline");
});
break;
}
case "xArrow":
{
throw new Error("KaTeX-a11y: xArrow not implemented yet");
}
case "cdlabel":
{
throw new Error("KaTeX-a11y: cdlabel not implemented yet");
}
case "cdlabelparent":
{
throw new Error("KaTeX-a11y: cdlabelparent not implemented yet");
}
case "mclass":
{
// \neq and \ne are macros so we let "htmlmathml" render the mathmal
// side of things and extract the text from that.
const atomType = tree.mclass.slice(1); // $FlowFixMe: drop the leading "m" from the values in mclass
buildA11yStrings(tree.body, a11yStrings, atomType);
break;
}
case "mathchoice":
{
// TODO: track which style we're using, e.g. display, text, etc.
// default to text style if even that may not be the correct style
buildA11yStrings(tree.text, a11yStrings, atomType);
break;
}
case "htmlmathml":
{
buildA11yStrings(tree.mathml, a11yStrings, atomType);
break;
}
case "middle":
{
buildString(tree.delim, atomType, a11yStrings);
break;
}
case "internal":
{
// internal nodes are never included in the parse tree
break;
}
case "html":
{
buildA11yStrings(tree.body, a11yStrings, atomType);
break;
}
default:
tree.type;
throw new Error("KaTeX a11y un-recognized type: " + tree.type);
}
};
const buildA11yStrings = function (tree, a11yStrings, atomType) {
if (a11yStrings === void 0) {
a11yStrings = [];
}
if (tree instanceof Array) {
for (let i = 0; i < tree.length; i++) {
buildA11yStrings(tree[i], a11yStrings, atomType);
}
} else {
handleObject(tree, a11yStrings, atomType);
}
return a11yStrings;
};
const flatten = function (array) {
let result = [];
array.forEach(function (item) {
if (item instanceof Array) {
result = result.concat(flatten(item));
} else {
result.push(item);
}
});
return result;
};
const renderA11yString = function (text, settings) {
const tree = katex__WEBPACK_IMPORTED_MODULE_0___default().__parse(text, settings);
const a11yStrings = buildA11yStrings(tree, [], "normal");
return flatten(a11yStrings).join(", ");
};
/* harmony default export */ __webpack_exports__["default"] = (renderA11yString);
}();
__webpack_exports__ = __webpack_exports__["default"];
/******/ return __webpack_exports__;
/******/ })()
;
});
File diff suppressed because one or more lines are too long
-800
View File
@@ -1,800 +0,0 @@
import katex from '../katex.mjs';
/**
* renderA11yString returns a readable string.
*
* In some cases the string will have the proper semantic math
* meaning,:
* renderA11yString("\\frac{1}{2}"")
* -> "start fraction, 1, divided by, 2, end fraction"
*
* However, other cases do not:
* renderA11yString("f(x) = x^2")
* -> "f, left parenthesis, x, right parenthesis, equals, x, squared"
*
* The commas in the string aim to increase ease of understanding
* when read by a screenreader.
*/
var stringMap = {
"(": "left parenthesis",
")": "right parenthesis",
"[": "open bracket",
"]": "close bracket",
"\\{": "left brace",
"\\}": "right brace",
"\\lvert": "open vertical bar",
"\\rvert": "close vertical bar",
"|": "vertical bar",
"\\uparrow": "up arrow",
"\\Uparrow": "up arrow",
"\\downarrow": "down arrow",
"\\Downarrow": "down arrow",
"\\updownarrow": "up down arrow",
"\\leftarrow": "left arrow",
"\\Leftarrow": "left arrow",
"\\rightarrow": "right arrow",
"\\Rightarrow": "right arrow",
"\\langle": "open angle",
"\\rangle": "close angle",
"\\lfloor": "open floor",
"\\rfloor": "close floor",
"\\int": "integral",
"\\intop": "integral",
"\\lim": "limit",
"\\ln": "natural log",
"\\log": "log",
"\\sin": "sine",
"\\cos": "cosine",
"\\tan": "tangent",
"\\cot": "cotangent",
"\\sum": "sum",
"/": "slash",
",": "comma",
".": "point",
"-": "negative",
"+": "plus",
"~": "tilde",
":": "colon",
"?": "question mark",
"'": "apostrophe",
"\\%": "percent",
" ": "space",
"\\ ": "space",
"\\$": "dollar sign",
"\\angle": "angle",
"\\degree": "degree",
"\\circ": "circle",
"\\vec": "vector",
"\\triangle": "triangle",
"\\pi": "pi",
"\\prime": "prime",
"\\infty": "infinity",
"\\alpha": "alpha",
"\\beta": "beta",
"\\gamma": "gamma",
"\\omega": "omega",
"\\theta": "theta",
"\\sigma": "sigma",
"\\lambda": "lambda",
"\\tau": "tau",
"\\Delta": "delta",
"\\delta": "delta",
"\\mu": "mu",
"\\rho": "rho",
"\\nabla": "del",
"\\ell": "ell",
"\\ldots": "dots",
// TODO: add entries for all accents
"\\hat": "hat",
"\\acute": "acute"
};
var powerMap = {
"prime": "prime",
"degree": "degrees",
"circle": "degrees",
"2": "squared",
"3": "cubed"
};
var openMap = {
"|": "open vertical bar",
".": ""
};
var closeMap = {
"|": "close vertical bar",
".": ""
};
var binMap = {
"+": "plus",
"-": "minus",
"\\pm": "plus minus",
"\\cdot": "dot",
"*": "times",
"/": "divided by",
"\\times": "times",
"\\div": "divided by",
"\\circ": "circle",
"\\bullet": "bullet"
};
var relMap = {
"=": "equals",
"\\approx": "approximately equals",
"≠": "does not equal",
"\\geq": "is greater than or equal to",
"\\ge": "is greater than or equal to",
"\\leq": "is less than or equal to",
"\\le": "is less than or equal to",
">": "is greater than",
"<": "is less than",
"\\leftarrow": "left arrow",
"\\Leftarrow": "left arrow",
"\\rightarrow": "right arrow",
"\\Rightarrow": "right arrow",
":": "colon"
};
var accentUnderMap = {
"\\underleftarrow": "left arrow",
"\\underrightarrow": "right arrow",
"\\underleftrightarrow": "left-right arrow",
"\\undergroup": "group",
"\\underlinesegment": "line segment",
"\\utilde": "tilde"
};
var buildString = (str, type, a11yStrings) => {
if (!str) {
return;
}
var ret;
if (type === "open") {
ret = str in openMap ? openMap[str] : stringMap[str] || str;
} else if (type === "close") {
ret = str in closeMap ? closeMap[str] : stringMap[str] || str;
} else if (type === "bin") {
ret = binMap[str] || str;
} else if (type === "rel") {
ret = relMap[str] || str;
} else {
ret = stringMap[str] || str;
} // If the text to add is a number and there is already a string
// in the list and the last string is a number then we should
// combine them into a single number
if (/^\d+$/.test(ret) && a11yStrings.length > 0 && // TODO(kevinb): check that the last item in a11yStrings is a string
// I think we might be able to drop the nested arrays, which would make
// this easier to type
// $FlowFixMe
/^\d+$/.test(a11yStrings[a11yStrings.length - 1])) {
a11yStrings[a11yStrings.length - 1] += ret;
} else if (ret) {
a11yStrings.push(ret);
}
};
var buildRegion = (a11yStrings, callback) => {
var regionStrings = [];
a11yStrings.push(regionStrings);
callback(regionStrings);
};
var handleObject = (tree, a11yStrings, atomType) => {
// Everything else is assumed to be an object...
switch (tree.type) {
case "accent":
{
buildRegion(a11yStrings, a11yStrings => {
buildA11yStrings(tree.base, a11yStrings, atomType);
a11yStrings.push("with");
buildString(tree.label, "normal", a11yStrings);
a11yStrings.push("on top");
});
break;
}
case "accentUnder":
{
buildRegion(a11yStrings, a11yStrings => {
buildA11yStrings(tree.base, a11yStrings, atomType);
a11yStrings.push("with");
buildString(accentUnderMap[tree.label], "normal", a11yStrings);
a11yStrings.push("underneath");
});
break;
}
case "accent-token":
{
// Used internally by accent symbols.
break;
}
case "atom":
{
var {
text
} = tree;
switch (tree.family) {
case "bin":
{
buildString(text, "bin", a11yStrings);
break;
}
case "close":
{
buildString(text, "close", a11yStrings);
break;
}
// TODO(kevinb): figure out what should be done for inner
case "inner":
{
buildString(tree.text, "inner", a11yStrings);
break;
}
case "open":
{
buildString(text, "open", a11yStrings);
break;
}
case "punct":
{
buildString(text, "punct", a11yStrings);
break;
}
case "rel":
{
buildString(text, "rel", a11yStrings);
break;
}
default:
{
tree.family;
throw new Error("\"" + tree.family + "\" is not a valid atom type");
}
}
break;
}
case "color":
{
var color = tree.color.replace(/katex-/, "");
buildRegion(a11yStrings, regionStrings => {
regionStrings.push("start color " + color);
buildA11yStrings(tree.body, regionStrings, atomType);
regionStrings.push("end color " + color);
});
break;
}
case "color-token":
{
// Used by \color, \colorbox, and \fcolorbox but not directly rendered.
// It's a leaf node and has no children so just break.
break;
}
case "delimsizing":
{
if (tree.delim && tree.delim !== ".") {
buildString(tree.delim, "normal", a11yStrings);
}
break;
}
case "genfrac":
{
buildRegion(a11yStrings, regionStrings => {
// genfrac can have unbalanced delimiters
var {
leftDelim,
rightDelim
} = tree; // NOTE: Not sure if this is a safe assumption
// hasBarLine true -> fraction, false -> binomial
if (tree.hasBarLine) {
regionStrings.push("start fraction");
leftDelim && buildString(leftDelim, "open", regionStrings);
buildA11yStrings(tree.numer, regionStrings, atomType);
regionStrings.push("divided by");
buildA11yStrings(tree.denom, regionStrings, atomType);
rightDelim && buildString(rightDelim, "close", regionStrings);
regionStrings.push("end fraction");
} else {
regionStrings.push("start binomial");
leftDelim && buildString(leftDelim, "open", regionStrings);
buildA11yStrings(tree.numer, regionStrings, atomType);
regionStrings.push("over");
buildA11yStrings(tree.denom, regionStrings, atomType);
rightDelim && buildString(rightDelim, "close", regionStrings);
regionStrings.push("end binomial");
}
});
break;
}
case "hbox":
{
buildA11yStrings(tree.body, a11yStrings, atomType);
break;
}
case "kern":
{
// No op: we don't attempt to present kerning information
// to the screen reader.
break;
}
case "leftright":
{
buildRegion(a11yStrings, regionStrings => {
buildString(tree.left, "open", regionStrings);
buildA11yStrings(tree.body, regionStrings, atomType);
buildString(tree.right, "close", regionStrings);
});
break;
}
case "leftright-right":
{
// TODO: double check that this is a no-op
break;
}
case "lap":
{
buildA11yStrings(tree.body, a11yStrings, atomType);
break;
}
case "mathord":
{
buildString(tree.text, "normal", a11yStrings);
break;
}
case "op":
{
var {
body,
name
} = tree;
if (body) {
buildA11yStrings(body, a11yStrings, atomType);
} else if (name) {
buildString(name, "normal", a11yStrings);
}
break;
}
case "op-token":
{
// Used internally by operator symbols.
buildString(tree.text, atomType, a11yStrings);
break;
}
case "ordgroup":
{
buildA11yStrings(tree.body, a11yStrings, atomType);
break;
}
case "overline":
{
buildRegion(a11yStrings, function (a11yStrings) {
a11yStrings.push("start overline");
buildA11yStrings(tree.body, a11yStrings, atomType);
a11yStrings.push("end overline");
});
break;
}
case "pmb":
{
a11yStrings.push("bold");
break;
}
case "phantom":
{
a11yStrings.push("empty space");
break;
}
case "raisebox":
{
buildA11yStrings(tree.body, a11yStrings, atomType);
break;
}
case "rule":
{
a11yStrings.push("rectangle");
break;
}
case "sizing":
{
buildA11yStrings(tree.body, a11yStrings, atomType);
break;
}
case "spacing":
{
a11yStrings.push("space");
break;
}
case "styling":
{
// We ignore the styling and just pass through the contents
buildA11yStrings(tree.body, a11yStrings, atomType);
break;
}
case "sqrt":
{
buildRegion(a11yStrings, regionStrings => {
var {
body,
index
} = tree;
if (index) {
var indexString = flatten(buildA11yStrings(index, [], atomType)).join(",");
if (indexString === "3") {
regionStrings.push("cube root of");
buildA11yStrings(body, regionStrings, atomType);
regionStrings.push("end cube root");
return;
}
regionStrings.push("root");
regionStrings.push("start index");
buildA11yStrings(index, regionStrings, atomType);
regionStrings.push("end index");
return;
}
regionStrings.push("square root of");
buildA11yStrings(body, regionStrings, atomType);
regionStrings.push("end square root");
});
break;
}
case "supsub":
{
var {
base,
sub,
sup
} = tree;
var isLog = false;
if (base) {
buildA11yStrings(base, a11yStrings, atomType);
isLog = base.type === "op" && base.name === "\\log";
}
if (sub) {
var regionName = isLog ? "base" : "subscript";
buildRegion(a11yStrings, function (regionStrings) {
regionStrings.push("start " + regionName);
buildA11yStrings(sub, regionStrings, atomType);
regionStrings.push("end " + regionName);
});
}
if (sup) {
buildRegion(a11yStrings, function (regionStrings) {
var supString = flatten(buildA11yStrings(sup, [], atomType)).join(",");
if (supString in powerMap) {
regionStrings.push(powerMap[supString]);
return;
}
regionStrings.push("start superscript");
buildA11yStrings(sup, regionStrings, atomType);
regionStrings.push("end superscript");
});
}
break;
}
case "text":
{
// TODO: handle other fonts
if (tree.font === "\\textbf") {
buildRegion(a11yStrings, function (regionStrings) {
regionStrings.push("start bold text");
buildA11yStrings(tree.body, regionStrings, atomType);
regionStrings.push("end bold text");
});
break;
}
buildRegion(a11yStrings, function (regionStrings) {
regionStrings.push("start text");
buildA11yStrings(tree.body, regionStrings, atomType);
regionStrings.push("end text");
});
break;
}
case "textord":
{
buildString(tree.text, atomType, a11yStrings);
break;
}
case "smash":
{
buildA11yStrings(tree.body, a11yStrings, atomType);
break;
}
case "enclose":
{
// TODO: create a map for these.
// TODO: differentiate between a body with a single atom, e.g.
// "cancel a" instead of "start cancel, a, end cancel"
if (/cancel/.test(tree.label)) {
buildRegion(a11yStrings, function (regionStrings) {
regionStrings.push("start cancel");
buildA11yStrings(tree.body, regionStrings, atomType);
regionStrings.push("end cancel");
});
break;
} else if (/box/.test(tree.label)) {
buildRegion(a11yStrings, function (regionStrings) {
regionStrings.push("start box");
buildA11yStrings(tree.body, regionStrings, atomType);
regionStrings.push("end box");
});
break;
} else if (/sout/.test(tree.label)) {
buildRegion(a11yStrings, function (regionStrings) {
regionStrings.push("start strikeout");
buildA11yStrings(tree.body, regionStrings, atomType);
regionStrings.push("end strikeout");
});
break;
} else if (/phase/.test(tree.label)) {
buildRegion(a11yStrings, function (regionStrings) {
regionStrings.push("start phase angle");
buildA11yStrings(tree.body, regionStrings, atomType);
regionStrings.push("end phase angle");
});
break;
}
throw new Error("KaTeX-a11y: enclose node with " + tree.label + " not supported yet");
}
case "vcenter":
{
buildA11yStrings(tree.body, a11yStrings, atomType);
break;
}
case "vphantom":
{
throw new Error("KaTeX-a11y: vphantom not implemented yet");
}
case "hphantom":
{
throw new Error("KaTeX-a11y: hphantom not implemented yet");
}
case "operatorname":
{
buildA11yStrings(tree.body, a11yStrings, atomType);
break;
}
case "array":
{
throw new Error("KaTeX-a11y: array not implemented yet");
}
case "raw":
{
throw new Error("KaTeX-a11y: raw not implemented yet");
}
case "size":
{
// Although there are nodes of type "size" in the parse tree, they have
// no semantic meaning and should be ignored.
break;
}
case "url":
{
throw new Error("KaTeX-a11y: url not implemented yet");
}
case "tag":
{
throw new Error("KaTeX-a11y: tag not implemented yet");
}
case "verb":
{
buildString("start verbatim", "normal", a11yStrings);
buildString(tree.body, "normal", a11yStrings);
buildString("end verbatim", "normal", a11yStrings);
break;
}
case "environment":
{
throw new Error("KaTeX-a11y: environment not implemented yet");
}
case "horizBrace":
{
buildString("start " + tree.label.slice(1), "normal", a11yStrings);
buildA11yStrings(tree.base, a11yStrings, atomType);
buildString("end " + tree.label.slice(1), "normal", a11yStrings);
break;
}
case "infix":
{
// All infix nodes are replace with other nodes.
break;
}
case "includegraphics":
{
throw new Error("KaTeX-a11y: includegraphics not implemented yet");
}
case "font":
{
// TODO: callout the start/end of specific fonts
// TODO: map \BBb{N} to "the naturals" or something like that
buildA11yStrings(tree.body, a11yStrings, atomType);
break;
}
case "href":
{
throw new Error("KaTeX-a11y: href not implemented yet");
}
case "cr":
{
// This is used by environments.
throw new Error("KaTeX-a11y: cr not implemented yet");
}
case "underline":
{
buildRegion(a11yStrings, function (a11yStrings) {
a11yStrings.push("start underline");
buildA11yStrings(tree.body, a11yStrings, atomType);
a11yStrings.push("end underline");
});
break;
}
case "xArrow":
{
throw new Error("KaTeX-a11y: xArrow not implemented yet");
}
case "cdlabel":
{
throw new Error("KaTeX-a11y: cdlabel not implemented yet");
}
case "cdlabelparent":
{
throw new Error("KaTeX-a11y: cdlabelparent not implemented yet");
}
case "mclass":
{
// \neq and \ne are macros so we let "htmlmathml" render the mathmal
// side of things and extract the text from that.
var _atomType = tree.mclass.slice(1); // $FlowFixMe: drop the leading "m" from the values in mclass
buildA11yStrings(tree.body, a11yStrings, _atomType);
break;
}
case "mathchoice":
{
// TODO: track which style we're using, e.g. display, text, etc.
// default to text style if even that may not be the correct style
buildA11yStrings(tree.text, a11yStrings, atomType);
break;
}
case "htmlmathml":
{
buildA11yStrings(tree.mathml, a11yStrings, atomType);
break;
}
case "middle":
{
buildString(tree.delim, atomType, a11yStrings);
break;
}
case "internal":
{
// internal nodes are never included in the parse tree
break;
}
case "html":
{
buildA11yStrings(tree.body, a11yStrings, atomType);
break;
}
default:
tree.type;
throw new Error("KaTeX a11y un-recognized type: " + tree.type);
}
};
var buildA11yStrings = function buildA11yStrings(tree, a11yStrings, atomType) {
if (a11yStrings === void 0) {
a11yStrings = [];
}
if (tree instanceof Array) {
for (var i = 0; i < tree.length; i++) {
buildA11yStrings(tree[i], a11yStrings, atomType);
}
} else {
handleObject(tree, a11yStrings, atomType);
}
return a11yStrings;
};
var flatten = function flatten(array) {
var result = [];
array.forEach(function (item) {
if (item instanceof Array) {
result = result.concat(flatten(item));
} else {
result.push(item);
}
});
return result;
};
var renderA11yString = function renderA11yString(text, settings) {
var tree = katex.__parse(text, settings);
var a11yStrings = buildA11yStrings(tree, [], "normal");
return flatten(a11yStrings).join(", ");
};
export { renderA11yString as default };
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large Load Diff
-18998
View File
File diff suppressed because it is too large Load Diff
-1
View File
File diff suppressed because one or more lines are too long
-1
View File
File diff suppressed because one or more lines are too long
-18458
View File
File diff suppressed because it is too large Load Diff
-8
View File
@@ -1,8 +0,0 @@
document.addEventListener("DOMContentLoaded", () => {
for (const m of document.querySelectorAll(".math.inline")) {
katex.render(m.textContent, m, {throwOnError: false, displayMode: false});
}
for (const m of document.querySelectorAll(".math.display")) {
katex.render(m.textContent, m, {throwOnError: false, displayMode: true});
}
});