记录一道Lean练习题，主要涉及$p$-adic的东西。通过这题主要是学一下无穷乘积（tprod），Finset的attach和map。[alert type="blue"]题面：$\text{Let }0 \neq x \in \mathbb{Q}.\text{ Show that }|x| \cdot \prod_{p \text { prime }}|x|_{p}=1.$[/alert]数学背景先说一下$\mathbb{Q}$上的$p$-adic Norm的定义：给定一个有理数$x$和素数$p$，我们总可以把$x$写成$p^k \cdot \frac{m}{n}$，使得$k$为整数并且$m$和$n$都是不能再被$p$整除的整数（就是把$x$里面的$p$提干净），然后我们就把$x$的$p$进范数$|x|_p$定义为$p^{-k}$。特殊地，我们一般令$|0|_p = 0$。与实数中“距离原点越远数值越大”不同，$p$进范数衡量的是数被$p$整除的“次数”。$x$被$p$整除的次数越多（就是$k$越大），$|x|_p$就越小。很明显$|x|_p$总是等于$|-x|_p$，所以我们总是考虑正的$x$（或者说$|x|$）。对这题，我们可以先把$|x|$写成$\frac{m}{n}$，然后对$m$和$n$分别做素因数分解得到$$ |x|=\frac{m}{n}=\frac{\prod_i p_i^{a_i}}{\prod_i p_i^{b_i}} = \prod_i p_i^{a_i - b_1} $$$\{p_i\}$是$m$和$n$的素因子之集的并集。因此，$$ |x|_{p_i} = p_i^{- a_i + b_i} $$而对于不属于$\{p_i\}$的素数$r$（也就是和$m$和$n$都互质的），$|x|_r = 1$。所以，$$ \prod_{p \text { prime }}|x|_{p} = \prod_{i} |x|_{p_i} \\ = \prod_i p_i^{- a_i + b_i} = \frac{\prod_i p_i^{b_i}}{\prod_i p_i^{a_i}} = \frac{n}{m} = |x|^{-1} $$这样就证完了。Lean准备有理数variable (x : ℚ) #check x.den --分母，ℕ #check x.num --分子，ℤ #check x.num.natAbs --分子绝对值，ℕ素数variable (p : ℕ) #check p.Prime -- p是素数，Prop -- The type of prime numbers def Primes := { p : ℕ // p.Prime } deriving DecidableEq -- `primesBelow n` is the set of primes less than `n` as a `Finset`. def primesBelow (n : ℕ) : Finset ℕ := (Finset.range n).filter (fun p ↦ p.Prime) #check Nat.Prime #check Nat.Primes #check Nat.primesBelow #check Nat.prod_pow_prime_padicValNat -- 素因数分解，分解成p ^ padicValNat p n的乘积 #check Finset.mem_filter -- 用来取出上面filter的性质（p.Prime） #check Finset.mem_range -- 用来取出p < np-adic-- If `q ≠ 0`, the `p`-adic norm of a rational `q` is `p ^ (-padicValRat p q)`. -- If `q = 0`, the `p`-adic norm of `q` is `0`. def padicNorm (p : ℕ) (q : ℚ) : ℚ := if q = 0 then 0 else (p : ℚ) ^ (-padicValRat p q) --`padicValRat` defines the valuation of a rational `q` to be the valuation of `q.num` minus the -- valuation of `q.den`. If `q = 0` or `p = 1`, then `padicValRat p q` defaults to `0`. def padicValRat (p : ℕ) (q : ℚ) : ℤ := padicValInt p q.num - padicValNat p q.den -- For `p ≠ 1`, the `p`-adic valuation of an integer `z ≠ 0` is the largest natural number `k` such -- that `p^k` divides `z`. If `x = 0` or `p = 1`, then `padicValInt p q` defaults to `0`. def padicValInt (p : ℕ) (z : ℤ) : ℕ := padicValNat p z.natAbs -- padicValNat (p : ℕ) (n : ℕ) 就是求p在n中的次数， #eval padicValNat 2 4 -- 2 #eval padicValNat 2 5 -- 0 -- padicValInt (p : ℕ) (z : ℤ) 就是z的绝对值的padicValNat #eval padicValInt 2 (- 4) -- 2 -- padicValRat (p : ℕ) (q : ℚ) 就是把有理数里p的次数提出来 #eval padicValRat 2 (4 / 3) -- 2 -- padicNorm (p : ℕ) (q : ℚ) 就是前面定义的p进范数 #eval padicNorm 2 (4 / 3) -- 1 / 4连乘号-- `∏ x ∈ s, f x` is the product of `f x` as `x` ranges over the elements of the finite set `s`. -- When the index type is a `Fintype`, the notation `∏ x, f x`, is a shorthand for -- `∏ x ∈ Finset.univ, f x`. #check Finset.prod -- - ∑\∏ x, f x -- x's type should be a Fintype -- - ∑\∏ x ∈ s, f x -- s should be a Finset -- - ∑\∏ x ∈ s with p x, f x -- p is some property for ele in s -- `∏' i, f i` is the product of `f` if it exists and is unconditionally convergent, -- or 1 otherwise. #check tprod -- `Multipliable f` means that `f` has some (infinite) product. Use `tprod` to get the value. #check Multipliable -- ∃ a, HasProd f a -- `HasProd f a` means that the (potentially infinite) product of the `f b` for `b : β` -- converges to `a`. #check HasProd -- `mulSupport` of a function is the set of points `x` such that `f x ≠ 1`. #check Function.mulSupport -- 要写（有定义的）无穷乘积，首先要说明`Multipliable`，也就是可乘，一般也就是乘积收敛。 -- 最好还可以再说明乘性支集`Function.mulSupport`是有限的，也即乘积项几乎都为1，只有有限项不为1， -- 这样就可以把无穷乘积限制成在有限支集上的有限乘积`finprod`。 -- 但是`finprod`还不是我们最想要的`Finset.prod` -- ↓ 把`finprod`转换成乘性支集（的超集）上的`Finset.prod` #check finprod_eq_finset_prod_of_mulSupport_subset开写~首先我们从Finset的prod出发。精确地说，$\prod_{p \text { prime }}|x|_{p}$的乘性支集是x.den和x.num.natAbs的素因子集之并（ℚ要求分子分母互质，所以支集恰好是...之并），即$$ \prod_{p \text { prime }}|x|_{p} = \prod_{p \in \text{prime factors of x.den or x.num.natAbs}}|x|_p $$但是我感觉写什么什么的素因子集之并比较麻烦，我们直接粗放地说，对足够大的$m$（只要比x.den和x.num.natAbs都大），$|x|_p = 1, \forall p > m$。因此有$$ \prod_{p \text { prime }}|x|_{p} = \prod_{p < \text{max(x.den,x.num.natAbs)}}|x|_p $$第一个lemma所以我们写出我们要证的第一个定理：对于足够大的$m, \ \prod_{p < m} p^{\text{padicValRat p x}} = |x|$（基本上就是有理数版的素数分解）import Mathlib variable {x : ℚ} (h : x ≠ 0) include h in lemma prod_pow_prime_padicValRat : ∀ m, x.den < m ∧ x.num.natAbs < m → ∏ p ∈ Finset.filter Nat.Prime (Finset.range m), (p : ℚ) ^ padicValRat p x = |x| := sorry首先用自然数的分解把x.den和x.num.natAbs拆开：（x.den不为0因为分母不为零，x.num.natAbs不为0因为x非零） intro m ⟨hden, hnum⟩ have prod1 := Nat.prod_pow_prime_padicValNat x.den x.den_nz m hden have prod2 := Nat.prod_pow_prime_padicValNat x.num.natAbs (Int.natAbs_ne_zero.2 ((x.num_ne_zero).2 h)) m hnum然后把两个分解相除得到：（用Finset.prod_div_distrib） have eq1 : ∏ p ∈ Finset.filter Nat.Prime (Finset.range m), (p ^ padicValNat p x.num.natAbs : ℚ) / (p ^ padicValNat p x.den : ℚ) = x.num.natAbs / x.den := by nth_rw 2 [← prod1, ← prod2] simp only [Finset.prod_div_distrib, Nat.cast_prod, Nat.cast_pow]再把(p ^ padicValNat p x.num.natAbs : ℚ) / (p ^ padicValNat p x.den : ℚ)转成(p : ℚ) ^ padicValRat p x，有have eq2 : ∏ p ∈ Finset.filter Nat.Prime (Finset.range m), (p : ℚ) ^ padicValRat p x = x.num.natAbs / x.den := sorry这个转换理应是显然的，但是有很多zpow和pow的转换问题，首先我们证明两个连乘相等可以用Finset.prod_congr转成逐项相等，然后写一个lemma去证逐项相等：lemma Rat.pow_padiv {p : ℕ} (hp : p ≠ 0) : (p : ℚ) ^ padicValRat p x = (p : ℚ) ^ padicValNat p x.num.natAbs / (p : ℚ) ^ padicValNat p x.den := by have : ((p : ℚ) ^ padicValRat p x) * (p : ℚ) ^ padicValNat p x.den = (p : ℚ) ^ padicValNat p x.num.natAbs := by unfold padicValRat padicValInt calc _ = (p : ℚ) ^ (((padicValNat p x.num.natAbs) : ℤ) - ((padicValNat p x.den) : ℤ) + (padicValNat p x.den : ℤ)) := by rw [zpow_add', zpow_natCast] refine Or.intro_left _ (Nat.cast_ne_zero.2 hp) _ = _ := by rw [sub_add_cancel, zpow_natCast] rw [← this, ← mul_div, div_self, mul_one] simp only [ne_eq, pow_eq_zero_iff', Nat.cast_eq_zero, hp, padicValNat.eq_zero_iff, Rat.den_ne_zero, false_or, not_or, Decidable.not_not, false_and, not_false_eq_true] have eq2 : ∏ p ∈ Finset.filter Nat.Prime (Finset.range m), (p : ℚ) ^ padicValRat p x = x.num.natAbs / x.den := by rw [← eq1] refine Finset.prod_congr rfl ?_ simp only [Finset.mem_filter, Finset.mem_range, and_imp] exact fun p hp₁ hp₂ ↦ Rat.pow_padiv (Nat.Prime.ne_zero hp₂)之后我们把x.num.natAbs / x.den转换回|x|就行了： have eq3 : |x| = x.num.natAbs / x.den := by rw [Rat.abs_def, (Rat.natCast_div_eq_divInt x.num.natAbs x.den).symm] rwa [← eq3] at eq2贴一个着色比较好的codesnap（lean的highlight.js在干什么(￣ ‘i ￣;)）：第二个lemma然后我们取倒数就可以得到p-adic Norm的乘积：对于足够大的$m, \ \prod_{p < m} |x|_p = |x|^{-1}$include h in lemma prod_prime_padicNorm : ∀ m, x.den < m ∧ x.num.natAbs < m → ∏ p ∈ Finset.filter Nat.Prime (Finset.range m), padicNorm p x = |x|⁻¹ := by simp only [padicNorm, h, ↓reduceIte, zpow_neg, Finset.prod_inv_distrib, inv_inj] exact prod_pow_prime_padicValRat h从数学上，这就是$\prod_{p \text{prime}} |x|_p$的部分乘积，所以接下来我们只需要“令$m$趋于无穷大”就行了，我们可以再明确地指出对足够大的$p$，$|x|_p = 1$：include h in lemma padicNorm_ne_one {m : ℕ} (hm : m ≠ 1) : x.den < m ∧ x.num.natAbs < m → padicNorm m x = 1 := by intro hp' simp only [padicNorm, h, ↓reduceIte, zpow_neg, inv_eq_one, padicValRat] rw [zpow_eq_one_iff_right₀ m.cast_nonneg' (Nat.cast_ne_one.2 hm), sub_eq_zero] congr have ndvd1 : ¬↑m ∣ x.num := by convert_to ¬↑m ∣ x.num.natAbs exact Int.ofNat_dvd_left exact Nat.not_dvd_of_pos_of_lt (Int.natAbs_pos.2 (Rat.num_ne_zero.2 h)) hp'.2 have ndvd2 : ¬m ∣ x.den := Nat.not_dvd_of_pos_of_lt x.den_pos hp'.1 rw [padicValInt.eq_zero_of_not_dvd ndvd1, padicValNat.eq_zero_of_not_dvd ndvd2]这段没什么要解释的，多用congr和convert_to可以增强书写体验（构造Finset Nat.Primes我们最后要写的是∏' p : Nat.Primes, padicNorm p x = |x|⁻¹，在证明这个padicNorm · x的Multipliable时，我们会需要一个Finset Nat.Primes。这里其实卡了我很久因为我至今没太看懂Finset怎么构造的（似乎是要一个Multiset，但是Multiset是List的一个Quotient？）长话短说，我们要的是所有小于$m$的素数组成的Finset Nat.Primes，而Nat.primesBelow是一个Finset ℕ。但是我们可以用Finset.attach把(Nat.primesBelow m : Finset ℕ)转换成Finset { x // x ∈ m.primesBelow }，然后再用Finset.map（通过一个嵌入）打到Finset Nat.Primes。如下：def Primes.below (m : ℕ) : Finset Nat.Primes := let inj_fun : { x // x ∈ m.primesBelow } ↪ Nat.Primes := { toFun := fun x ↦ ⟨x.val, Nat.prime_of_mem_primesBelow x.property⟩ inj' := fun _ _ h12 ↦ let_fun this := Subtype.val_inj.mpr h12; Subtype.eq this } -- ↓ attach ↓ map -- Finset ℕ → Finset { x : ℕ // x ∈ m.primesBelow } → Finset Nat.Primes (Nat.primesBelow m).attach.map inj_fun一些杂项这些定理都是为了描述前面定义的Primes.below的性质的：lemma Primes.below_not_mem_le {m : ℕ} {p : Nat.Primes} : p ∉ (Primes.below m) → m ≤ p := by contrapose! refine fun hpm ↦ Finset.mem_map.2 ⟨⟨p.val, Nat.mem_primesBelow.2 ⟨hpm, p.property⟩⟩, ?_⟩ simp only [Finset.mem_attach, Function.Embedding.coeFn_mk, Subtype.coe_eta, and_self] lemma Primes.below_mem_lt {m : ℕ} {p : Nat.Primes} : p ∈ (Primes.below m) → p < m := by intro hpa obtain ⟨q, ⟨hq1, hq2⟩⟩ := Finset.mem_map.1 hpa suffices q < m from lt_of_eq_of_lt (congrArg Subtype.val hq2.symm) this exact (Nat.mem_primesBelow.1 q.2).1 lemma Primes.below_prime_mem_iff {m : ℕ} {p : Nat.Primes} : p ∈ (Primes.below m) ↔ p < m := by constructor · exact Primes.below_mem_lt · contrapose!; exact Primes.below_not_mem_le include h lemma Primes.below_not_mem_padicNorm {m : ℕ} {p : Nat.Primes} (hm : x.den < m ∧ x.num.natAbs < m) : p ∉ (Primes.below m) → padicNorm p x = 1 := by intro hp replace hp : m ≤ p := Primes.below_not_mem_le hp exact padicNorm_ne_one h p.property.ne_one ⟨(LE.le.trans hm.1 hp), LE.le.trans hm.2 hp⟩注意一下Finset.mem_map，其它的应该没有什么需要说明的。Primes.below上的prod然后我们就可以把之前在Nat.primesBelow上的Finset.prod换成在Primes.below上的了：lemma prod_primesBelow_padic {m : ℕ} (hm : x.den < m ∧ x.num.natAbs < m) : ∏ p ∈ (Primes.below m), padicNorm p x = |x|⁻¹ := by rw [← prod_prime_padicNorm h m hm] refine Finset.prod_bij ?_ ?_ ?_ ?_ ?_ (α := ℚ) · exact fun p _ ↦ p.val · exact fun p hp ↦ Finset.mem_filter.2 ⟨Finset.mem_range.2 <| Primes.below_mem_lt hp, p.property⟩ · exact fun a₁ _ a₂ _ h12 ↦ (Nat.Primes.coe_nat_inj a₁ a₂).mp h12 · intro b hb have := Finset.mem_filter.1 hb exact ⟨⟨b, this.2⟩, ⟨Primes.below_prime_mem_iff.2 (Finset.mem_range.1 this.1), rfl⟩⟩ · exact fun _ _ ↦ rfl注意证明两个连乘相等还可以用Finset.prod_bij（实际上就是重新排序）Last Kick我们现在可以向final goal进发了（为此有两个需要的事情：Multipliable fun (p : Nat.Primes) ↦ padicNorm p x和(Function.mulSupport fun (p : Nat.Primes) ↦ padicNorm p x).Finite，也即无穷乘积收敛和乘性支集有限。Multipliablelemma multipliable_padicNorm : Multipliable fun (p : Nat.Primes) ↦ padicNorm p x := by sorry起手先把Multipliable, HasProd, nhds都simp掉，这时的goal就会变成：⊢ ∃ a, ∀ (i : Set ℚ), a ∈ i → IsOpen i → ∃ a, ∀ (b : Finset Nat.Primes), a ⊆ b → ∏ x_1 ∈ b, padicNorm (↑x_1) x ∈ i其中第一个a是ℚ，第二个是Finset Nat.Primes，那这个定义和我们熟悉的极限的定义就一样了。从前面可以知道，这个乘积的极限应该就是|x|⁻¹。所以 refine ⟨|x|⁻¹, fun i i_one i_open ↦ ?_⟩把第一个a取成|x|⁻¹。那么前面也证明了对比x.den和x.num.natAbs都大的m，连乘的值都是|x|⁻¹，所以我们先取出这样的m， obtain ⟨m , ⟨hden, hnum⟩⟩ := exists_ge_of_linear x.den.succ x.num.natAbs.succ第二个a我们就取成(Primes.below m)， refine ⟨(Primes.below m), fun b hab ↦ ?_⟩此时的goal为⊢ ∏ x_1 ∈ b, padicNorm (↑x_1) x ∈ i也即是说这个乘积在|x|⁻¹的开邻域里，那我只要说明这个乘积等于|x|⁻¹就好了 suffices ∏ p ∈ b, padicNorm p x = |x|⁻¹ from this.symm ▸ i_one随后我们把b拆成(Primes.below m) ∪ (b \ (Primes.below m))，这样对于在前一半里的p，这个乘积就是|x|⁻¹ have : b = (Primes.below m) ∪ (b \ (Primes.below m)) := (Finset.union_sdiff_of_subset hab).symm rw [this, Finset.prod_union, prod_primesBelow_padic h ⟨hden, hnum⟩] suffices ∏ p ∈ b \ (Primes.below m), padicNorm p x = 1 from by rw [this, mul_one]那最后只需要说明对于后一半里的p这个乘积就是1就行了 refine Finset.prod_eq_one fun p hp ↦ ?_ exact Primes.below_not_mem_padicNorm h ⟨hden, hnum⟩ (Finset.mem_sdiff.1 hp).2 exact Finset.disjoint_sdiffFinsupportlemma finsupport_padicNorm : (Function.mulSupport fun (p : Nat.Primes) ↦ padicNorm p x).Finite := by sorry这个乘性支集是有限的，因为它是Primes.below的子集，也就是这个支集中的素数肯定小于某个m。还是一样的我们先取出比x.den和x.num.natAbs都大的数m，然后， refine Set.Finite.subset (Primes.below m).finite_toSet <| Function.mulSupport_subset_iff.2 fun p ↦ ?_ contrapose! exact Primes.below_not_mem_padicNorm h hmFinal Goal最后的目标就近在咫尺了lemma tprod_prime_padicNorm : ∏' p : Nat.Primes, padicNorm p x = |x|⁻¹ := by sorry取出m，tprod_def展开，用multipliable_padicNorm h和finsupport_padicNorm h把ite(if...then...else...)和dite(if h : ... then...else...)都simp掉 obtain ⟨m , hm⟩ := exists_ge_of_linear x.den.succ x.num.natAbs.succ simp only [tprod_def, multipliable_padicNorm h, ↓reduceDIte, finsupport_padicNorm h, ↓reduceIte, ← prod_primesBelow_padic h hm]这里的goal会变成⊢ ∏ᶠ (p : Nat.Primes), padicNorm (↑p) x = ∏ p ∈ Primes.below m, padicNorm (↑p) x那证明一个finprod和Finset.prod相等，我们可以用finprod_eq_finset_prod_of_mulSupport_subset，也就是把finprod换成在Function.mulSupport的超集上的Finset.prod。这里我们refine一下 refine finprod_eq_finset_prod_of_mulSupport_subset (fun (p : Nat.Primes) ↦ padicNorm p x) ?_然后就只用证明⊢ (Function.mulSupport fun p ↦ padicNorm (↑p) x) ⊆ ↑(Primes.below m)然而这就很显然了 simp only [Function.mulSupport_subset_iff, ne_eq, Finset.mem_coe] intro p hp by_contra hpa exact hp <| Primes.below_not_mem_padicNorm h hm hpa总结先贴一张由Gemini注释的总的代码：import Mathlib /- Let $0 \neq x \in \mathbb{Q}$. Show that $|x| \prod_{p \text { prime }}|x|_{p}=1$. (First show that $|x|_{p}=1$ for almost all primes $p$. ) -/ variable {x : ℚ} (h : x ≠ 0) /--$(p : \mathbb{Q})^{\text{padicValRat } p x} = \frac{(p : \mathbb{Q})^{\text{padicValNat } p |x.num|}}{(p : \mathbb{Q})^{\text{padicValNat } p x.den}}$.-/ lemma Rat.pow_padiv {p : ℕ} (hp : p ≠ 0) : (p : ℚ) ^ padicValRat p x = (p : ℚ) ^ padicValNat p x.num.natAbs / (p : ℚ) ^ padicValNat p x.den := by have : ((p : ℚ) ^ padicValRat p x) * (p : ℚ) ^ padicValNat p x.den = (p : ℚ) ^ padicValNat p x.num.natAbs := by -- Start with the definition of p-adic valuation for rationals: $\text{padicValRat } p x = \text{padicValInt } p x = \text{padicValNat } p |x.num| - \text{padicValNat } p x.den$. unfold padicValRat padicValInt calc -- Substitute the definition into the exponent: $(p : \mathbb{Q})^{(\text{padicValNat } p |x.num| - \text{padicValNat } p x.den)} \cdot (p : \mathbb{Q})^{\text{padicValNat } p x.den}$. _ = (p : ℚ) ^ (((padicValNat p x.num.natAbs) : ℤ) - ((padicValNat p x.den) : ℤ) + (padicValNat p x.den : ℤ)) := by -- Apply the exponent rule $a^{b+c} = a^b \cdot a^c$ in reverse, $a^{u-v} \cdot a^v = a^{(u-v)+v} = a^u$. rw [zpow_add', zpow_natCast] -- Ensure the base $(p : \mathbb{Q}) \neq 0$ for the power rule, which is true as $p \neq 0$. refine Or.intro_left _ (Nat.cast_ne_zero.2 hp) -- Simplify the exponent by canceling $-(\text{padicValNat } p x.den) + (\text{padicValNat } p x.den)$. _ = _ := by -- Apply basic arithmetic to simplify the exponent: $(u-v)+v = u$. rw [sub_add_cancel, zpow_natCast] -- Divide both sides of the equation by $(p : \mathbb{Q})^{\text{padicValNat } p x.den}$ to isolate $(p : \mathbb{Q})^{\text{padicValRat } p x}$. rw [← this, ← mul_div, div_self, mul_one] -- Simplify the resulting expression by canceling the common term and using $a/a = 1$ and $1 \cdot b = b$. simp only [ne_eq, pow_eq_zero_iff', Nat.cast_eq_zero, hp, padicValNat.eq_zero_iff, Rat.den_ne_zero, false_or, not_or, Decidable.not_not, false_and, not_false_eq_true] include h in /--The product of $(p : \mathbb{Q})^{\text{padicValRat } p x}$ for primes $p < m$ yields $|x|$ for sufficiently large $m$. -/ lemma prod_pow_prime_padicValRat : ∀ m, x.den < m ∧ x.num.natAbs < m → ∏ p ∈ Finset.filter Nat.Prime (Finset.range m), (p : ℚ) ^ padicValRat p x = |x| := by intro m ⟨hden, hnum⟩ -- Use the prime factorization of the denominator: $x.den = \prod_{p< m, p \text{ prime}} p^{\text{padicValNat } p x.den}$. have prod1 := Nat.prod_pow_prime_padicValNat x.den x.den_nz m hden -- Use the prime factorization of the numerator's absolute value: $|x.num| = \prod_{p< m, p \text{ prime}} p^{\text{padicValNat } p |x.num|}$. have prod2 := Nat.prod_pow_prime_padicValNat x.num.natAbs (Int.natAbs_ne_zero.2 ((x.num_ne_zero).2 h)) m hnum -- Express the ratio of numerator and denominator factorizations as a product of ratios. have eq1 : ∏ p ∈ Finset.filter Nat.Prime (Finset.range m), (p ^ padicValNat p x.num.natAbs : ℚ) / (p ^ padicValNat p x.den : ℚ) = x.num.natAbs / x.den := by -- Substitute the prime factorizations into the ratio $x.num.natAbs / x.den$. nth_rw 2 [← prod1, ← prod2] -- Distribute the division over the product: $\frac{\prod a_i}{\prod b_i} = \prod \frac{a_i}{b_i}$. simp only [Finset.prod_div_distrib, Nat.cast_prod, Nat.cast_pow] -- Relate the product of $(p : \mathbb{Q})^{\text{padicValRat } p x}$ to the ratio $x.num.natAbs / x.den$. have eq2 : ∏ p ∈ Finset.filter Nat.Prime (Finset.range m), (p : ℚ) ^ padicValRat p x = x.num.natAbs / x.den := by -- Replace the product of ratios with the product of powers using the previous lemma. rw [← eq1] -- Show term-wise equality inside the product using the lemma `Rat.pow_padiv`. refine Finset.prod_congr rfl ?_ simp only [Finset.mem_filter, Finset.mem_range, and_imp] -- Apply `Rat.pow_padiv` to each term in the product, using the fact that $p$ is prime hence $p \neq 0$. exact fun p hp₁ hp₂ ↦ Rat.pow_padiv (Nat.Prime.ne_zero hp₂) -- Use the definition of the absolute value of a rational number: $|x| = \frac{|x.num|}{|x.den|} = \frac{x.num.natAbs}{x.den}$. have eq3 : |x| = x.num.natAbs / x.den := by rw [Rat.abs_def, (Rat.natCast_div_eq_divInt x.num.natAbs x.den).symm] -- Replace $x.num.natAbs / x.den$ with $|x|$ in the equation to obtain the desired approximation. rwa [← eq3] at eq2 include h in /--The product of $|x|_p$ for primes $p < m$ yields $|x|^{-1}$ for sufficiently large $m$. -/ lemma prod_prime_padicNorm : ∀ m, x.den < m ∧ x.num.natAbs < m → ∏ p ∈ Finset.filter Nat.Prime (Finset.range m), padicNorm p x = |x|⁻¹ := by -- Rewrite the product using the definition of p-adic norm: $|x|_p = p^{-\text{padicValRat } p x} = (p^{\text{padicValRat } p x})^{-1}$. simp only [padicNorm, h, ↓reduceIte, zpow_neg, Finset.prod_inv_distrib, inv_inj] -- After simplification, the goal is equivalent to the previous lemma `prod_pow_prime_padicValRat h`. exact prod_pow_prime_padicValRat h include h in /--If $m$ is coprime to both numerator and denominator (implied by $m > \max(|x.num|, x.den)$ and $m \neq 1$), then $|x|_m = 1$.-/ lemma padicNorm_ne_one {m : ℕ} (hm : m ≠ 1) : x.den < m ∧ x.num.natAbs < m → padicNorm m x = 1 := by intro hp' simp only [padicNorm, h, ↓reduceIte, zpow_neg, inv_eq_one, padicValRat] -- $|x|_m = 1 \iff |(m : \mathbb{Q})^{-\text{padicValRat } m x}| = 1 \iff (m : \mathbb{Q})^{-\text{padicValRat } m x} = \pm 1$. Since norm is non-negative, it means $(m : \mathbb{Q})^{-\text{padicValRat } m x} = 1$. -- This is equivalent to $-\text{padicValRat } m x = 0 \iff \text{padicValRat } m x = 0 \iff \text{padicValNat } m |x.num| - \text{padicValNat } m x.den = 0$. rw [zpow_eq_one_iff_right₀ m.cast_nonneg' (Nat.cast_ne_one.2 hm), sub_eq_zero] -- We need to show $\text{padicValNat } m |x.num| = \text{padicValNat } m x.den$. congr -- If $m > |x.num|$, then $m \nmid x.num$, hence $\text{padicValNat } m |x.num| = 0$. have ndvd1 : ¬↑m ∣ x.num := by convert_to ¬↑m ∣ x.num.natAbs exact Int.ofNat_dvd_left exact Nat.not_dvd_of_pos_of_lt (Int.natAbs_pos.2 (Rat.num_ne_zero.2 h)) hp'.2 -- If $m > x.den$, then $m \nmid x.den$, hence $\text{padicValNat } m x.den = 0$. have ndvd2 : ¬m ∣ x.den := Nat.not_dvd_of_pos_of_lt x.den_pos hp'.1 -- If $m \nmid x.num$ and $m \nmid x.den$, then both valuations are 0. rw [padicValInt.eq_zero_of_not_dvd ndvd1, padicValNat.eq_zero_of_not_dvd ndvd2] /--Define a finset containing all prime numbers less than $m$.-/ def Primes.below (m : ℕ) : Finset Nat.Primes := -- This definition constructs a finset of `Nat.Primes` from `Nat.primesBelow m` (primes below m as natural numbers). let inj_fun : { x // x ∈ m.primesBelow } ↪ Nat.Primes := { toFun := fun x ↦ ⟨x.val, Nat.prime_of_mem_primesBelow x.property⟩ inj' := fun _ _ h12 ↦ let_fun this := Subtype.val_inj.mpr h12; Subtype.eq this } (Nat.primesBelow m).attach.map inj_fun /--If a prime $p$ is not in $\text{Primes.below } m$, it means $p \ge m$.-/ lemma Primes.below_not_mem_le {m : ℕ} {p : Nat.Primes} : p ∉ (Primes.below m) → m ≤ p := by contrapose! -- Prove the contrapositive: $p < m \implies p \in \text{Primes.below } m$. refine fun hpm ↦ Finset.mem_map.2 ⟨⟨p.val, Nat.mem_primesBelow.2 ⟨hpm, p.property⟩⟩, ?_⟩ simp only [Finset.mem_attach, Function.Embedding.coeFn_mk, Subtype.coe_eta, and_self] -- This follows directly from the definition of `Primes.below` and `Nat.primesBelow`. /--If a prime $p$ is in $\text{Primes.below } m$, it means $p < m$.-/ lemma Primes.below_mem_lt {m : ℕ} {p : Nat.Primes} : p ∈ (Primes.below m) → p < m := by intro hpa obtain ⟨q, ⟨hq1, hq2⟩⟩ := Finset.mem_map.1 hpa suffices q < m from lt_of_eq_of_lt (congrArg Subtype.val hq2.symm) this exact (Nat.mem_primesBelow.1 q.2).1 -- This also follows directly from the definition of `Primes.below` and `Nat.primesBelow`. /--Equivalence: $p \in \text{Primes.below } m$ if and only if $p < m$.-/ lemma Primes.below_prime_mem_iff {m : ℕ} {p : Nat.Primes} : p ∈ (Primes.below m) ↔ p < m := by -- Prove both directions of the equivalence. constructor · exact Primes.below_mem_lt -- $p \in \text{Primes.below } m \implies p < m$. · contrapose!; exact Primes.below_not_mem_le -- $p < m \implies p \in \text{Primes.below } m$ (by contrapositive). include h /--If $p \ge m$ and $m > \max(|x.den|, |x.num.natAbs|)$, then $|x|_p = 1$.-/ lemma Primes.below_not_mem_padicNorm {m : ℕ} {p : Nat.Primes} (hm : x.den < m ∧ x.num.natAbs < m) : p ∉ (Primes.below m) → padicNorm p x = 1 := by intro hp replace hp : m ≤ p := Primes.below_not_mem_le hp exact padicNorm_ne_one h p.property.ne_one ⟨(LE.le.trans hm.1 hp), LE.le.trans hm.2 hp⟩ -- Combine `Primes.below_not_mem_le` and `padicNorm_ne_one` to show $|x|_p = 1$ for primes outside `Primes.below m`. /--The product of $|x|_p$ throughout $\text{Primes.below } m$ is $|x|^{-1}$-/ lemma prod_primesBelow_padic {m : ℕ} (hm : x.den < m ∧ x.num.natAbs < m) : ∏ p ∈ (Primes.below m), padicNorm p x = |x|⁻¹ := by -- Rewrite the product over primes in $\text{Primes.below } m$ in terms of $|x|^{-1}$. rw [← prod_prime_padicNorm h m hm] -- We need to show that the product over `Primes.below m` is the same as the product over `Finset.filter Nat.Prime (Finset.range m)`. refine Finset.prod_bij ?_ ?_ ?_ ?_ ?_ (α := ℚ) -- Construct a bijection between `Primes.below m` and `Finset.filter Nat.Prime (Finset.range m)`. · exact fun p _ ↦ p.val -- Function: `Nat.Primes` -> `ℕ`, map prime structure to natural number value. · exact fun p hp ↦ Finset.mem_filter.2 ⟨Finset.mem_range.2 <| Primes.below_mem_lt hp, p.property⟩ -- Show that if $p \in \text{Primes.below } m$, then $p.val \in \text{Finset.filter Nat.Prime (Finset.range m)}$. -- $p \in \text{Primes.below } m \implies p < m \implies p.val < m \implies p.val \in \text{Finset.range } m$. Also $p.val$ is prime by definition of `Nat.Primes`. · exact fun a₁ _ a₂ _ h12 ↦ (Nat.Primes.coe_nat_inj a₁ a₂).mp h12 -- Injectivity of the map: if $p_1.val = p_2.val$, then $p_1 = p_2$ for $p_1, p_2 \in \text{Primes.below } m$. · intro b hb have := Finset.mem_filter.1 hb exact ⟨⟨b, this.2⟩, ⟨Primes.below_prime_mem_iff.2 (Finset.mem_range.1 this.1), rfl⟩⟩ -- Surjectivity: for each $b \in \text{Finset.filter Nat.Prime (Finset.range m)}$, there exists $p \in \text{Primes.below } m$ such that $p.val = b$. -- If $b \in \text{Finset.filter Nat.Prime (Finset.range m)}$, then $b$ is prime and $b < m$. So $b \in \text{Nat.primesBelow } m$. We can construct $p = \langle b, \text{prime proof}\rangle \in \text{Primes.below } m$. · exact fun _ _ ↦ rfl -- Map preserves the value: $\text{padicNorm } p x = \text{padicNorm } (p.val) x$. /--The infinite product $\prod_{p} |x|_p$ converges.-/ lemma multipliable_padicNorm : Multipliable fun (p : Nat.Primes) ↦ padicNorm p x := by simp only [Multipliable, HasProd, nhds, Set.mem_setOf_eq, Filter.tendsto_iInf, Filter.tendsto_principal, Filter.eventually_atTop, ge_iff_le, Finset.le_eq_subset, and_imp] -- Choose $m$ large enough such that $m > \max(|x.den|, |x.num.natAbs|)$. obtain ⟨m , ⟨hden, hnum⟩⟩ := exists_ge_of_linear x.den.succ x.num.natAbs.succ refine ⟨|x|⁻¹, fun i i_one i_open ↦ ?_⟩ refine ⟨(Primes.below m), fun b hab ↦ ?_⟩ suffices ∏ p ∈ b, padicNorm p x = |x|⁻¹ from this.symm ▸ i_one -- Split the product over a finite set $b$ into product over $\text{Primes.below } m$ and the remaining primes. have : b = (Primes.below m) ∪ (b \ (Primes.below m)) := (Finset.union_sdiff_of_subset hab).symm rw [this, Finset.prod_union, prod_primesBelow_padic h ⟨hden, hnum⟩] -- For primes $p \in b \setminus \text{Primes.below } m$, we have $p \ge m$, so $|x|_p = 1$. suffices ∏ p ∈ b \ (Primes.below m), padicNorm p x = 1 from by rw [this, mul_one] refine Finset.prod_eq_one fun p hp ↦ ?_ exact Primes.below_not_mem_padicNorm h ⟨hden, hnum⟩ (Finset.mem_sdiff.1 hp).2 exact Finset.disjoint_sdiff -- The product over primes $p \ge m$ is 1 because $|x|_p = 1$ for such primes. /--The set of primes $p$ where $|x|_p \neq 1$ is finite.-/ lemma finsupport_padicNorm : (Function.mulSupport fun (p : Nat.Primes) ↦ padicNorm p x).Finite := by -- Choose $m$ large enough such that $m > \max(|x.den|, |x.num.natAbs|)$. obtain ⟨m , hm⟩ := exists_ge_of_linear x.den.succ x.num.natAbs.succ -- The set of primes with $|x|_p \neq 1$ is a subset of $\text{Primes.below } m$, which is finite. refine Set.Finite.subset (Primes.below m).finite_toSet <| Function.mulSupport_subset_iff.2 fun p ↦ ?_ contrapose! exact Primes.below_not_mem_padicNorm h hm /--Prove the product formula: $\prod_{p \text{ prime }}|x|_{p} = |x|^{-1}$.-/ lemma tprod_prime_padicNorm : ∏' p : Nat.Primes, padicNorm p x = |x|⁻¹ := by -- Choose $m$ large enough such that $m > \max(|x.den|, |x.num.natAbs|)$. obtain ⟨m , hm⟩ := exists_ge_of_linear x.den.succ x.num.natAbs.succ simp only [tprod_def, multipliable_padicNorm h, ↓reduceDIte, finsupport_padicNorm h, ↓reduceIte, ← prod_primesBelow_padic h hm] -- Reduce the infinite product to a finite product over the finset `Primes.below m` because $|x|_p = 1$ outside this finset. refine finprod_eq_finset_prod_of_mulSupport_subset (fun (p : Nat.Primes) ↦ padicNorm p x) ?_ simp only [Function.mulSupport_subset_iff, ne_eq, Finset.mem_coe] intro p hp by_contra hpa exact hp <| Primes.below_not_mem_padicNorm h hm hpa -- For $p \notin \text{Primes.below } m$, $|x|_p = 1$, so these terms do not affect the product. 总结一下，功能定理证明两个Finset.prod相等：Finset.prod_congr：逐项相等 Finset.prod_bij：重排证明Finset.prod和finprod相等：finprod_eq_finset_prod_of_mulSupport_subset合并两个Finset.prod相除：Finset.prod_div_distrib构建Subtype的FinsetFinset.attach + Finset.map[window type="yellow" title="无穷乘积（mulSupport有限情况）："]1. 证Multipliable + mulSupport.Finite，换成finprod2. finprod_eq_finset_prod_of_mulSupport_subset换成Finset.prod[/window]
写两个东西，一是Mathematica的字符串差异分析，二是批处理。后面还有一个比较长的Mathematica文本库构建和管理打算单独写一篇。字符串差异分析问题的背景是，前两天给网站加了APlayer，然后APlayer的字体和颜色和主题很不搭，所以想改一下APlayer.min.css。改完之后想确认一下改了哪些部分，因为APlayer.min.css整个文件所有内容都在一行里，所以用WinMerge和Git都不太顺手。因此想着用Mathematica写一个能做字符串比较的东西。先把两个CSS文件作为文本（Text）导入进来i = Import[file, "Text"]; j = Import[file, "Text"];然后用内建的SequenceAlignment比较差异d = SequenceAlignment[i, j, Method -> "Local"];SequenceAlignment的作用是In[1] := SequenceAlignment["abcXXabcXabc", "abcabcYYYabc"] Out[1] := {"abc", {"XX", ""}, "abc", {"X", "YYY"}, "abc"} In[2] := SequenceAlignment["abcXXabcXabc", "abcabcYYYabc", Method -> "Local"] Out[2] := {{"abcXX", ""}, "abc", {"X", ""}, "abc", {"", "YYYabc"}}这个结果的解读方式是{"abc", {"XX", ""}, "abc", {"X", "YYY"}, "abc"} ↑ ↑ ↑ ↑ ↑ <-第一个文本 ↑ ↑ ↑ ↑ ↑ <-第二个文本 {{"abcXX", ""}, "abc", {"X", ""}, "abc", {"", "YYYabc"}} ↑ ↑ ↑ ↑ ↑ <-第一个文本 ↑ ↑ ↑ ↑ ↑ <-第二个文本Method -> "Local"感觉就是让结果的“块”更整体一点。当然这种括号嵌套的结果表示方式非常难阅读，特别是文本很长的时候，所以我们可以给差异内容染色，使用coloredDiff = Style[#, Piecewise[{{FontColor -> Red, Length@# != 0}}, FontColor -> Black]] & /@ d;这里我们利用差异项都是长度为2的列表（比如之前的{"X", ""}），而一样的项都是长度为0的列表（比如"abc"）。因此我们定义一个分段函数，它在列表长度不为0时取FontColor -> Red，为0时取FontColor -> Black，然后把这个Style连同分段函数逐项作用于结果列表上。效果如下这样就可以看到，我改了字体和背景颜色，然后把border-bottom删掉了之类的。批处理（以批量格式转换为例）这个问题的背景是，想要把图片批量转成webp来缩小网页体积。首先我们用SetDirectory["path"]设定当前工作目录，这样可以免去每次都要输路径的麻烦，然后使用imageList = Select[FileNames[], MemberQ[{"jpg", "png"}, FileExtension[#]] &];来读取所有扩展名为jpg和png的文件，FileNames[]会返回当前目录下所有文件文件名的列表，然后Select在这个列表里选出所有FileExtension含在{"jpg", "png"}中的文件构成的子列。接下来对于得到的子列中的每个文件有process = Function[path, Export[FileBaseName[path] <> ".webp", Import@path, CompressionLevel -> 0.5]; DeleteFile[path]];其中我们用FileBaseName取出不带扩展名的文件名，然后添加扩展名".webp"，这样就得到了输出的文件名。我们用Import导入对应的图片文件，然后CompressionLevel对它进行压缩，这个得到的结果被当作Export的输入，并被输入到前面的输出文件名中。接着DeleteFile删除原始文件。最后我们只需要运行就可以了。Parallelize[process /@ imageList];
$\nwarrow$ 左上角那条分割线的Mathematica处理原本的图片是这个样子的如果把黑色背景去掉至透明然后直接用的话，暗色模式是可以的，亮色模式下因为图形是白的就会看不见。于是想做一个轮廓勾勒，这样暗色亮色就都能看到了。用Mathematica的Binarize首先进行二值化得到更锐利的图片方便后面操作之后用Dilation把线条做粗一点，如果不这样的话待会勾勒完在暗色模式下线条末端会消失在背景中然后EdgeDetect得到边框的轮廓，并再次Dilation调整线条为了使轮廓在暗色的时候完美融入背景，我们要先反色，然后修改边框的颜色为背景色RGB(29,31,32)o = Image[ Replace[ImageData[e], {1 -> {29/255, 31/255, 32/255}, 0 -> {1, 1, 1}}, {2}], ColorSpace -> "RGB"];这里我们读取每个像素的颜色，如果是1（白色）就换成背景色，是0（黑色）就换成白色，这一规则应用是在第二层进行的。其中e是之前得到的轮廓图。这时再去掉白色背景就行了（￣︶￣）↗ 接下来我们还可以通过p = {#2, -#1} & @@@ Position[ImageData[Thinning @ image], 1, {2}]; ListPlot[p, AspectRatio -> Automatic, Axes -> False]来得到在图3中颜色值为1（即白色）的点的坐标，这样可以完成在图像边缘取点的任务。
[alert type="blue"]Try it out on: https://live.lean-lang.org/[/alert]$$ \text{If }P\text{ is a finite simple }p\text{-group, show that }P\text{ has order }p. $$Formalizing the Statementimport Mathlib open Subgroup in example {p : ℕ} {P : Type u_1} [Group P] (hP : IsPGroup p P) [hp : Fact (Nat.Prime p)] [Finite P] [IsSimpleGroup P]: Nat.card P = p := by sorryStep-by-Step SolutionThe center of $P$ must be nontrivial since it is a $p$-group.have center_nontriv := hP.center_nontrivialSince $P$ is simple, its center (which is a normal subgroup) must be either trivial or $P$ itself.have center_bot_top := (center P).normal_of_characteristic.eq_bot_or_eq_topThe center is not trivial, therefore it is $P$ itself.have center_not_bot := (center P).nontrivial_iff_ne_bot.mp center_nontriv replace center_bot_top := center_bot_top.resolve_left center_not_botWhich means that $P$ should be abelian.letI : CommGroup P := Group.commGroupOfCenterEqTop center_bot_topOf course $P$ is nontrivial since its center is nontrivial.letI : Nontrivial P := Set.nontrivial_of_nontrivial_coe center_nontrivAnd the cardinality of $P$ should be divisible by $p$ since $P$ has cardinality $p^n$ for some $n$ and $n \neq 0$ for: $P$ is nontrivial, and nontrivial implies a cardinality strictly greater than 1.letI : Fintype P := Fintype.ofFinite P have : p ∣ Fintype.card P := by /- since $P$ has cardinality $p^n$ for some $n$--/ obtain ⟨n, hnp⟩ := IsPGroup.iff_card.1 hP rw [← Nat.card_eq_fintype_card, hnp] /- and $n \neq 0$--/ have : n ≠ 0 := by by_contra nzero rw [nzero, pow_zero] at hnp /- for $P$ is nontrivial, and nontrivial implies a cardinality strictly greater than 1.--/ have := hnp ▸ Finite.one_lt_card_iff_nontrivial (α := P) tauto exact Prime.dvd_pow_iff_dvd hp.out.prime this |>.2 p.dvd_reflBy Cauchy's theorem, this admits the existence of an element whose order is $p$. We define the subgroup generated by the element as $sub$.obtain ⟨x, hx⟩ := exists_prime_orderOf_dvd_card p this set sub := zpowers x with hsubIt must be of cardinality $p$ since the element has order $p$.have sub_card := hsub ▸ hx ▸ Nat.card_zpowers xAnd it must be normal since $P$ is abelian.have sub_normal : sub.Normal := sub.normal_of_commFurthermore it mustn't be trivial since its generator is not the identity (identity won't have order $p$).have sub_ntriv : sub ≠ ⊥ := by /- since its generator is not the identity (identity won't have order $p$).--/ refine zpowers_ne_bot.mpr ?_ by_contra hx_one rw [hx_one, orderOf_one] at hx rw [← hx] at hp tautoThus, since $P$ is simple, $sub$ must be $P$ it self. Therefore the cardinality of $P$ is exactly $p$.have sub_eq_top := sub_normal.eq_bot_or_eq_top.resolve_left sub_ntriv exact card_top (G := P) ▸ sub_eq_top ▸ sub_cardComplete Codeimport Mathlib open Subgroup in example {p : ℕ} {P : Type u_1} [Group P] (hP : IsPGroup p P) [hp : Fact (Nat.Prime p)] [Finite P] [IsSimpleGroup P]: Nat.card P = p := by have center_nontriv := hP.center_nontrivial have center_bot_top := (center P).normal_of_characteristic.eq_bot_or_eq_top have center_not_bot := (center P).nontrivial_iff_ne_bot.mp center_nontriv replace center_bot_top := center_bot_top.resolve_left center_not_bot letI : CommGroup P := Group.commGroupOfCenterEqTop center_bot_top letI : Fintype P := Fintype.ofFinite P letI : Nontrivial P := Set.nontrivial_of_nontrivial_coe center_nontriv have : p ∣ Fintype.card P := by obtain ⟨n, hnp⟩ := IsPGroup.iff_card.1 hP rw [← Nat.card_eq_fintype_card, hnp] have : n ≠ 0 := by by_contra nzero rw [nzero, pow_zero] at hnp have := hnp ▸ Finite.one_lt_card_iff_nontrivial (α := P) tauto exact Prime.dvd_pow_iff_dvd hp.out.prime this |>.2 p.dvd_refl obtain ⟨x, hx⟩ := exists_prime_orderOf_dvd_card p this set sub := zpowers x with hsub have sub_card := hsub ▸ hx ▸ Nat.card_zpowers x have sub_normal : sub.Normal := sub.normal_of_comm have sub_ntriv : sub ≠ ⊥ := by refine zpowers_ne_bot.mpr ?_ by_contra hx_one rw [hx_one, orderOf_one] at hx rw [← hx] at hp tauto have sub_eq_top := sub_normal.eq_bot_or_eq_top.resolve_left sub_ntriv exact card_top (G := P) ▸ sub_eq_top ▸ sub_card
[alert type="red"]请刷新页面以加载LaTeX | Refresh to load LaTeX[/alert]$$ \begin{equation} \notag \uparrow \text{已经解决啦，看评论！} \end{equation} $$$[LK' : K'] \geq e$:$p$ is unramified in $K'$ and $p$ is totally ramified in $L$ with ramification $e = [L : \mathbb{Q}]$ so $p$ ramified in $LK'$ with ramification index $e$, by Theorem 2.1, $e \mid [LK':K']$, thus $[LK' : K'] \geq e$.$e \geq [LK : K'] \geq [LK' : K']$:$[LK : K'] = |I'_0|$ according to the setup. $\mathfrak{P}$ is tamely ramified since $p$ is tamely ramified in both $L$ and $K$. By Theorem 2.6, $I'_0 \leq (k(p))^\times = (\mathbb{Z}_p)^\times$, so $I'_0$ is a cyclic group. Similarly, $Gal(LK/\mathbb{Q})$ injects into $Gal(K/\mathbb{Q}) \times Gal(L/\mathbb{Q})$ and so $I'_0$ does as well.Let $\mathfrak{p}' = \mathfrak{P} \cap K$. Then by definition, $I'_0$ restricted to $K$ gives an element in the inertia group $I_0(\mathfrak{p}'/p)$ but $\mathfrak{p}'$ is conjugate to $\mathfrak{p}$ so the order of $I_0(\mathfrak{p}'|p)$ is $|I_0| = e$.Thus $I'_0$ lives in the subgroup $I_0(\mathfrak{p}'|p) \times Gal(L/\mathbb{Q})$, both of which are groups of order $e$ so $I'_0$ has exponent $e$. Since it is cyclic and of exponent $e$, $|I'_0| \leq e$.
b（￣▽￣）d 好写一下这次用Typecho建的。先给一个credit：这次用的是MoXi大佬的主题，好看！所以为啥换呢？因为上一版感觉稍微有点卡，而且WP的文字编辑体验not great...[alert type="pink"]非IT相关专业，写的可能是错的哦(∪.∪ )...zzz[/alert]服务器这次迁到华为云Flexus应用服务器L实例（什么奇怪的名字×2🤣），2C2G也是三十块。然后我感觉华为云控制台的页面设计极其有问题（你像在阿里云，在服务器的控制台页面能直接访问服务器的监控、DDOS、备份和镜像、安全组，基本上不用跳转。但是华为云Flexus的页面整体利用率极低，图标巨大无比然后整个页面没几个字，监控要点进去实例概览，在云主机VM里再跳转；DDOS直接是个独立的控制台页面，没办法从Flexus的页面访问；备份和镜像在分开的云备份CBR里；安全组要点进去虚拟私有云VPC的产品页面，左边往下滑到访问控制里的安全组，点进服务器绑定的安全组配置出入站规则。再有，我还没找到Flexus怎么设置密钥SSH，而且我在宝塔面板尝试改成仅密钥登录会失败，how strange.But however，在华为云我配置进程监控成功了，阿里云那个一直失败不知道为什么。安装Typecho服务器和宝塔配置和之前的一样，之后详见https://blog.zeruns.tech/archives/749.html然后因为我是双域名，所以要配置跨域补救，在网站根目录的config.inc.php的__TYPECHO_ADMIN_DIR__那行下面添加：/** 开启动态域名(跨域补救) */ define('__TYPECHO_DYNAMIC_SITE_URL__', true);之后当我们要在网站里面引用网站文件（比如图片啥的），不要用https://misaka-yu.com/usr/uploads/2024/10/836983279.webp，要用/usr/uploads/2024/10/836983279.webp，这样使得用vertin.me访问的时候不会出现跨域资源请求。主题最重要的就是主题×2安装见https://github.com/MoXiaoXi233/PureSuck-theme/的说明字体然后这次仿照MoXi改成了霞鹜文楷。首先在https://github.com/lxgw/LxgwWenkaiGB下载最新的字体release，然后上传到https://chinese-font.netlify.app/zh-cn/online-split/进行分包（我用Firefox访问好像总是会卡住，Edge会报错，用Chrome就行）分包完的压缩包传到服务器解压待用。然后我把PureSuck_Style.css里约1500行的位置改成了：body, .post--cover .post-body .post-wrapper>p:first-child, .comment-title, h1, h2, h3, h4, h5, h6 { font-family: "LXGW WenKai GB", -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Helvetica, Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", SimSun, sans-serif; font-weight: 400; color: var(--text-color); }总之就是调整了font-family，字体名字可以到分包生成的result.css中查看。然后我在header.php的 <link rel="stylesheet" href="<?= $this->options->themeUrl('css/PureSuck_Style.css'); ?>">前加入了： 这里的href即为刚刚上传的分包文件中result.css的位置。（好，我完全不知道为什么加在这里，或者是不是错的，anyway it works for now🤣）理论上有条件的亲应该尝试把分包文件挂载到CDN上再在这里调用CDN缓存。但是改完还不行，Firefox调试报错downloadable font: download failed。这个疑似是Nginx伪静态的问题，在宝塔设置Nginx配置文件： location ~ .*\.(eot|otf|ttf|woff|woff2|svg)$ { add_header Access-Control-Allow-Origin *; add_header Cache-Control max-age=315360000; }其中add_header Cache-Control指示浏览器缓存字体文件，这样下一次访问就不需要重新从服务器下载了。这个方法相较之前节极大缩减了网页大小，现在网页会根据result.css按需分块加载字体文件，不需要像之前一样一次性加载完。在Firefox打开调试可以看到（在写这篇文章的时候）网页大小从5MB降到了2MB。网页通知在Typecho后台主题设置的Script标签里添加： 评论评论开启Markdown见https://github.com/MoXiaoXi233/PureSuck-theme/的说明然后关于头像，我先把comments.php里的$avatarUrl = 'https://gravatar.loli.net/avatar/' . md5(strtolower($comments->mail)) . '?s=80&r=X&d=mm';改成了https://cravatar.cn/avatar/，因为我发现我新传的头像在loli镜像没刷新，but这个无关紧要首页我把index.php的 <?php $this->author(); ?> 拿掉了，因为我好多文章头图都很暗，就和这里的文字不是很搭...LaTeX参考https://nwdan.com/tutorials/typecho-latex-support.html，但是themeFields已经被定义了可以直接在里面加。然后改了字节的CDN： <?php if ($this->is('post') && $this->fields->isLatex == 1): ?> <?php endif; ?>但是每次进文章还是要主动刷新一次才能加载LaTeX。看LaTeX示例那篇！安全控制Typecho疑似没有Wordfence之类比较成熟的安全插件。但是我们总可以像之前的WP一样隐藏后台地址看这里：https://docs.typecho.org/help/faq/admin-dir但是我用这个方法会直接进不去后台（解决方法是在网站根目录把文件夹admin改名字改成刚自定义的后台地址名。性能控制还是老样子，CDN和缓存。vertin.me自带Cloudflare的CDN，跟上一版WP网站一样可以去Cache Rule启用CF缓存。misaka-yu.com暂时没办法配置CDN，见前两篇（哦对前两篇忘说了，记得图片格式全部改成webp，这样可以大幅缩减图片大小，然后在宝塔Nginx配置文件里：location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ { expires 30d; error_log /dev/null; access_log /dev/null; }把gif|jpg|jpeg|png|bmp|swf改成gif|jpg|jpeg|png|bmp|swf|webp以启用webp的缓存。杂项伪静态顺便再加一下伪静态：if (-f $request_filename/index.php){ rewrite (.*) $1/index.php; } if (!-e $request_filename){ rewrite (.*) /index.php; }这个可以做什么呢？现在你访问网站会发现地址栏会有.php，这玩意看起来挺烦所以我们通过伪静态把这个后缀去掉使它看起来像一个静态页面。这里添加完之后，去Typecho后台设置-永久链接启用地址重写功能。宝塔伪静态有Typecho的模板，但是我用那个再启用地址重写会报错。同时有教程的伪静态会加上：if (-f $request_filename/index.html){ rewrite (.*) $1/index.html break; }但是我加这段会进不去首页（其它页面可以），所以我现在页面访问还会有.html...应该没了？想起来再加[collapsible-panel title="DEPRECATED-20241102"]以下内容已经过期，仅作存档然后这次仿照MoXi改成了霞鹜文楷（但是我用的是轻便版）。参照这个，首先在https://github.com/lxgw/LxgwWenKai-Lite/下载最新的字体release。然后把ttf上传到https://transfonter.org/，我直接选中所有formats，然后把Family support、Add local rule、Fix vertical metrics全部打开。按我理解这里改字体格式是因为不同浏览器对字体文件的兼容性不一样，我们直接准备好所有格式好让浏览器自己选兼容的。转换完下载下来传到服务器解压待用。然后我们打开/PureSuck/css/PureSuck_Style.css，找到@font-face字段，主题自带的font-face好像是用来显示深色/亮色模式图标的，所以这个我没动。然后在这个font-face下面添加：@font-face { font-family: 'LXGWWenKaiLite-Regular'; src: url('../fonts/LXGWWenKaiLite-Regular.eot'); src: url('../fonts/LXGWWenKaiLite-Regular.eot#iefix') format('embedded-opentype'), url('../fonts/LXGWWenKaiLite-Regular.woff2') format('woff2'), url('../fonts/LXGWWenKaiLite-Regular.woff') format('woff'), url('../fonts/LXGWWenKaiLite-Regular.ttf') format('truetype'), url('../fonts/LXGWWenKaiLite-Regular.svg#LXGWWenKaiLite-Regular') format('svg'); font-weight: normal; font-style: normal; }其中../fonts/LXGWWenKaiLite-Regular.*改成你的字体文件路径，format和#iefix之类的东西建议留着，因为我删掉用Firefox调试访问会报Error: downloadable font: rejected by sanitizer错误。还有就是，bilibili嵌入自适应窗口，跟WP一样在主题文件夹的footer.php添加： 然后嵌入视频： 还有仿照原作者把标题下面的线条改成半透明的：PureSuck_Style.css添加：.post-title::after { content: ""; display: block; position: absolute; pointer-events: none; border-radius: 0.45rem; background: linear-gradient(to right, var(--themecolor), rgb(255 255 255 / 0%)); opacity: .55; transition: all .3s cubic-bezier(0.345, 0.045, 0.345, 1); }网页通知：footer.php注释掉原来 下的东西并添加： 原版模板英文断字有问题，可以在PureSuck_Style.css539行修改成：（本来word-break是break-all）.wrapper>:last-child { margin-bottom: 0; word-break: break-word; }[/collapsible-panel]
还挺顺利？总之我就搭好了WP网站的基本框架。接下来写一下https://web.archive.org/web/20240917172240/https://vertin.me/archives/395的心路历程。[alert type="pink"]非IT相关专业，写的可能是错的哦(∪.∪ )...zzz[/alert]主题最重要的事情就是主题。我选的是Wordpress 2017官方主题，看起来是这样的，就感觉……好看！并且它是有暗色模式的。好，直接应用主题，在EasyWP上，它直接变成演示页面的样子了，有背景图片啥啥的。而在阿里云上它变到了集合文章的页面：这个时候只需要去Wordpress设置-阅读页重新指定。然后我第一次花了好长时间尝试弄懂怎么自定义主页那几张图片🤣，最上面的头图可以在Wordpress后台进入主题-自己那个主题-自定义（或者直接点最上面管理员栏的铅笔按钮）然后更改，中间的图片实际上只需要在每个文章的编辑页面设置“特征图片”或者“Feathered Image”就行。但是我感觉首页图片纵向太高了，直接占满整个屏幕，这好么？这不好。改这个东西也花了好长时间。参考这个和这个，我们首先在Wordpress后台进入主题-自己那个主题-自定义，然后添加额外CSS类：/*Computer screen */ @media screen and (min-width: 48em) { .twentyseventeen-front-page.has-header-image .custom-header-image { /*height: 1200px;*/ /*height: 100vh;*/ height: 62vh; /*max-height: 100%;*/ /*overflow: hidden;*/ } .panel-image { height: 70vh; /*max-height: 1200px;*/ } .single-featured-image-header { height: 40vw; /*not vh because it creates black edge on longer screen*/ } } /* Mobile screen*/ .has-header-image.twentyseventeen-front-page .custom-header { /*display: table;*/ /*height: 300px;*/ /*height: 75vh;*/ height: 62vh; /*width: 100%;*/ } /* Computer screen with logged in user and admin bar showing on front end*/ @media screen and (min-width: 48em) { .admin-bar.twentyseventeen-front-page.has-header-image .custom-header-image { /*height: calc(100vh - 32px);*/ height: calc(62vh - 32px); } }其中，根据我的测试，height控制头图header image的纵向长度. panel-image和single-featured-image-header分别控制子页面的panel image和首页中间的featured image. Mobile screen实际上似乎会影响所有平台。以及wp-content/themes/twentyseventeen/style.css里：.background-fixed .panel-image { background-attachment: fixed; }好像没用，理论上应该是控制panel image在移动端是固定还是滑动的？Anyway其它的就OK了。性能控制跟我之前说的一样，EasyWP完全不需要管这个。否则要在Wordpress安装WP Super Cache插件，安装好之后要改掉默认目录结构才能用。我记得安这个之前要装redis的memcached？忘了/_ \，反正要装，教程可另查。如果你发现服务器内存占用过高，可以参照这里更改FPM配置文件（在宝塔php应用设置里）：pm = dynamic #指定进程管理方式，有3种可供选择：static、dynamic和ondemand。 pm.max_children = 16 #static模式下创建的子进程数或dynamic模式下同一时刻允许最大的php-fpm子进程数量。 pm.start_servers = 10 #动态方式下的起始php-fpm进程数量。 pm.min_spare_servers = 8 #动态方式下服务器空闲时最小php-fpm进程数量。 pm.max_spare_servers = 16 #动态方式下服务器空闲时最大php-fpm进程数量。 pm.max_requests = 2000 #php-fpm子进程能处理的最大请求数。 pm.process_idle_timeout = 10s request_terminate_timeout = 120改完重启：$ sudo systemctl start php-fpm # 启动php-fpm $ sudo systemctl stop php-fpm # 停止php-fpm $ sudo systemctl reload php-fpm # 重载php-fpm $ sudo systemctl restart php-fpm # 重启php-fpm然后设置伪静态：location / { try_files $uri $uri/ /index.php?$args; } # Add trailing slash to */wp-admin requests. rewrite /wp-admin$ $scheme://$host$uri/ permanent;然后Cloudflare是自带CDN的，所以可以设置Cache Rules来加速网页加载，但为保证wp后台工作正常，要设置规则为：绕过URI等于/*.php*，缓存其它全部：URI包含/（这个要排到最后，免得影响前面）。验证规则可以去CF首页的“跟踪”里。安全控制Again，高贵的EasyWP用户不用管，因为EasyWP的后台和数据库只能通过EasyWP的Dashboard访问。首先，WP默认的后台地址都是域名/wp-admin/，这是非常危险的。我在没改后台前安装过WP安全插件Wordfence安全，然后发现每天都有二三十个bots疯狂试密码。所以为了让它们直接找不到在哪试，我们可以参照这个，在functions.php文末添加：add_action('login_enqueue_scripts', 'tb_wp_login_protection'); function tb_wp_login_protection(){ if( !isset($_GET['mima']) ){ header( 'Location: ' . home_url() ); exit; } }这个GET['']里的就是自己定义的新入口，这个例子里我们的后台地址就变成了/wp-login.php?mima进一步地，我们还可以参考这个在宝塔设置伪静态来隐藏管理员账户名（顺便解决xmlrpc的问题）：location ~ ^/wp-json/wp/v2/users { deny all; } location ~ ^/wp-includes/wlwmanifest.xml { deny all; } location ~ ^/xmlrpc.php$ { deny all; }还有一段在宝塔网页配置文件里：location = /xmlrpc.php { deny all; access_log off; log_not_found off; }然后还有，Wordfence可以开启管理员二次验证、xmlrpc屏蔽。Wordfence防火墙开启的时候好像遇到了问题但是忘了怎么解决的了_(:3 」∠ )_。以及要记得设安全组，改宝塔面板端口、访问路径，关闭phpmyadmin公开访问，SSH只用密钥登录。CDN可以隐藏服务器的真实IP所以有还是用一下。然后CF用户会发现WP的health check经常提示环回测试和REST API错误，这是因为Cloudflare会拦截环回测试和REST API，要设置WAF来解决：https://www.wordfence.com/help/advanced/compatibility/我们还可以让Wordfence把安全报告自动发送邮件给我们，设置WP管理员发件邮箱（在functions.php里）：// 配置邮件 add_action('phpmailer_init', 'mail_smtp'); function mail_smtp( $phpmailer ) { $phpmailer->FromName = '***'; // 发件人昵称 $phpmailer->Host = '***'; // 邮箱SMTP服务器 $phpmailer->Port = 465; // SMTP端口，不需要改 $phpmailer->Username = '***@***'; // 邮箱账户 $phpmailer->Password = '***'; // 此处填写邮箱生成的授权码，不是邮箱登录密码 $phpmailer->From = '***@***'; // 邮箱账户同上 $phpmailer->SMTPAuth = true; $phpmailer->SMTPSecure = 'ssl'; // 端口25时 留空，465时 ssl，不需要改 $phpmailer->IsSMTP(); }phew......好多事各种媒体嵌入首先是bilibili嵌入：插入自定义html 但是直接这么插入会导致播放器大小很奇怪，这时就需要在footer.php里添加： 来使它自适应窗口大小。图片嵌入直接用编辑器就行。PDF也可以用原生嵌入，但是原生那个在移动端显示不了——一个免费的解决方案是使用PDF.js Viewer插件。音乐插件我选的是Cue，但是跳转页面时播放会中断。总结插件总之我用了这些插件：Akismet 反垃圾评论：垃圾评论保护只要你评论区是开着的就会遇到各种各样奇形怪状的评论，这个插件可以挡掉很多没用的Cue音乐插件，挺好看的FileBird LiteWP的媒体文件管理可以说是完全没有，这个插件可以提供一些最基本的文件夹来分类媒体Remove Footer Credit看到Proudly Powered by Wordpress了吗？这个插件可以删掉credit/改成其它文字Syntax-highlighting Code Block (with Server-side Rendering)用来显示代码的，好看Wordfence安全冲浪必备安全插件你好多莉唯一自带插件，卵用没有，留着也行超级缓存就是WP Super Cache，提速用一点怀旧？Well，其实我真挺喜欢这版的设计的，我记得第一次做完截了好多屏，但是写这篇文章的时候又找不到了，怅然若失。所以，你可以去给你的第一个网站做个网页存档留个纪念。That being said，说不定我哪天又换回去了呢，希望别来无恙~趁服务器没过期截了一些，，，
写一写之前用Wordpress的时候踩过的坑~先放一个Internet Archive的存档：https://web.archive.org/web/20240917230021/https://vertin.me/[alert type="pink"]非IT相关专业，写的可能是错的哦(∪.∪ )...zzz[/alert]然后建站域名的情况：[tabs][tab title="misaka-yu.com"]起始时间2022-07注册商Godaddy CNY 159DNSGodaddy邮件系统Microsoft CNY 279微软停止了365家庭版附带的域名邮箱服务，因此变更域名配置可能导致邮箱不再可用。特别地，Outlook域名邮箱要求域名在Godaddy注册。Godaddy续费费用是Cloudflare的两倍(￣ ‘i ￣;)Outlook域名邮箱不支持DMARC和DKIM，并且PTR记录无法对齐之后可能会转移注册商并手搓域名邮箱（DNSSEC已启用[/tab][tab title="vertin.me"]起始时间2023-07注册商Cloudflare USD 15DNSCloudflare邮件系统网易阿里云阿里云和网易的域名邮箱是白嫖的，不保证长期可用（自己搓邮件系统的必要性+1[/tab][/tabs]最开始最开始，misaka-yu.com是用EasyWP做的，一键式建站，完全不需要管理服务器和环境。但是有三个问题，第一是EasyWP从国内访问非常非常慢，而且做CDN并不方便（Namecheap给所有EasyWP用户赠送了免费CDN，但是要求域名必须托管在Namecheap下，然而misaka-yu.com因为上面写的原因不可能变更注册）；第二是EasyWP标准版严格限制只能解析一个域名，但是我想把vertin.me和misaka-yu.com解析到同一台服务器上；第三当然是自由度问题，EasyWP没有办法访问命令行和文件系统，感觉非常局限。对于第二个问题我进行过一些尝试，比如说改成用vertin.me建站，然后在Godaddy设置misaka-yu.com隐式转发到vertin.me，但是这种做法不兼容https，并且有的浏览器会拦截这种行为，很别扭；我还尝试过misaka-yu.com建站，vertin.me用Cloudflare的页面规则替换链接，但是我不想让浏览器地址栏域名变化，总之并没有找到很好的方法。但是其实从入门角度我觉得EasyWP是相当不错的，基础版完全不贵，而且它帮你配好了各种环境和基础插件，你不需要管诸如cache-rule和伪静态之类的加速技巧，所以也不用担心缓存了动态页面导致登不进后台q(≧▽≦q)，再加上它有整站和数据库一键备份的功能，几乎不用担心搞崩网站之后恢复不了，总之我是推荐的。Anyway在几个月之后我就着手迁移到阿里云，彼时阿里云新加坡的轻量应用服务器每月只要二三十块，还是很便宜的。阿里云镜像选择的是宝塔（尽管说不喜欢用封装好的但是宝塔是真的好用2333，从SSL自动续期到环境管理安全加固，宝塔满足了整个生命周期的大部分需求。所以就讲讲宝塔＋WP的配置过程。首次进入宝塔面板如果你选择的是阿里云轻量应用服务器/华为云Flexus应用服务器L实例（什么奇怪的名字）的宝塔镜像，那么在服务器初始化完成后，你应当能在服务器控制台找到宝塔面板的入口。一般来讲宝塔镜像默认后台入口是8888端口的，于是我们首先前往服务器的安全组配置防火墙出入规则，放行8888端口，再进入后台用镜像生成的账号密码登录并绑定宝塔账号。这里可能出现两个问题：放行了8888端口但是依然进不去后台？这时可以先ssh到服务器，建入bt命令并选择设置面板端口。值得注意的是第一次正常进入宝塔面板都会被提示修改默认8888端口，所以在这里不妨先行将其改为其它随便什么端口（只要记得放行并且别和22，80之类的撞了就行）没告诉我账号密码啊？有些时候云服务商没告诉你宝塔面板的初始账号密码，但是我们依然可以ssh并使用bt重设。安装LNMP似乎大部分现代网页都依赖LNMP组合运行，即Linux+Nginx+MySQL+PHP。刚进入宝塔面板时它会提示你安装LNMP/LNAMP之类的：FTP不总是需要，反正我从来没装过。你可以用宝塔面板自带的文件管理功能和本地传输文件。应用的版本尽量选高点，特别是php，否则进wordpress里health check会一直提示什么什么版本过低↑但是不总能选最高，比如如果你的云服务器如果是二三十的月租，那么它大概率跟我的一样只有2C2G或2C1G，而byd MySQL8推荐6G的内存。你可以在网上搜罗到诸多绕过宝塔MySQL内存检查的方法，但是就我经验，安不上还是安不上。编译安装 v.s. 极速安装？理论上编译安装性能会更好一点，但是对个人小网站影响不大。建站？此时我们似乎就可以开始建站了，宝塔的应用管理里有Wordpress的一键部署脚本，具体细节我已经忘记了但是应该问题不大。之后我们要在面板的“网站”一页里新建一个网站，名字随意但是记得选上安好的php和MySQL，账号密码可以不记，之后能查。我遇到的第一个问题是域名设置。我想链接两个域名：vertin.me和misaka-yu.com，vertin.me托管在Cloudflare，misaka-yu.com托管在Godaddy。首先在宝塔新建的网站可用设置域名，直接填入vertin.me和（换行）misaka-yu.com——在这里如果不用宝塔手动Nginx配置多域名我感觉会很麻烦，反正我没成功🤣。然后讲ssl设置，Cloudflare之神赠予每一个托管的域名免费SSL证书。并且在SSL/TLS页的概述选项，你可以配置源服务器到Cloudflare代理的多种加密模式，宝塔面板自带一个自签的SSL证书，所以我选择了完全加密（完全严格加密疑似需要源服务器有CA签发的证书）。同时我在宝塔申请了misaka-yu.com的Let's Encrypt证书并启用了强制https。我其实不是很理解这玩意怎么运行的但是总之这样就可以成功配置两个域名分开的证书和https连接。另，一个关于DNS的小问题。如果你的A记录是泛域名，即*，那么宝塔没有办法给你自动申请let's encrypt证书，但是手动申请也不是很麻烦；DNSSEC开启的域名的CNAME记录不能为@，即不允许裸CNAME，这一点让我比较困惑因为腾讯云的CDN会要求你做一个裸的CNAME记录。基本弄好之后，在域名/wp-admin/可以登录网站后台。我忘了什么时候设置Wordpress管理员账号密码了但是应该有完善的引导流程。
如果您看到这篇文章,表示您的 blog 已经安装成功.