/-
Copyright (c) 2024 Judith Ludwig, Christian Merten. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Judith Ludwig, Christian Merten
-/
import BruhatTits.Graph.Vertices
import BruhatTits.Lattice.Quotient
import BruhatTits.Utils.GLSubmoduleAction
import BruhatTits.Utils.List

/-!
# The edges of the Bruhat-Tits graph

In this file we define the edge relation of the Bruhat-Tits graph. Two vertices
are connected by an edge if they are neighbours, i.e. if their distance, in the
sense of `inv`, is one.

## Main definitions

- `BruhatTits.IsNeighbour`: vertices `x` and `y` are neighbours if `inv x y = 1`.
- `BruhatTits.IsStandardNeighbour`: lattices `L` and `M` are standard neighbours, if
  `ϖ • L < M < L`.
- `List.IsChain`: a non-empty list of lattices is a chain, if adjacent elements are standard
  neighbours.
- `List.IsSimpleChain`: a chain is simple if it does not backtrack.
- `List.IsStandard`: a simple chain `L₀, ..., Lₙ` is standard if there exists a basis
  `b = (b₁, b₂)` of `L₀` such that `Lᵢ` is the span of `(ϖ ^ i • b₁, b₂)` for all `i`.

## Main results

- `BruhatTits.isNeighbour_iff`: vertices are neighbours if and only if there exist standard neighbour
  representatives.
- `BruhatTits.length_eq_dist_add_one_of_isStandard`: the length of a standard chain agrees with
  the distance of its endpoints plus one.
- `BruhatTits.exists_trafo_to_isStandard`: every simple chain can be transformed into a standard
  chain by the action of `GL₂(K)`.

-/

suppress_compilation

variable {K : Type*} [Field K] {R : Subring K}

namespace BruhatTits

open Pointwise

section IsNeighbour

variable [IsDiscreteValuationRing R] [IsFractionRing R K]

/-- Two vertices `x` and `y` in the Bruhat-Tits tree are neighbours if `inv L M = 1`.
For a common alternative definition see `BruhatTits.isNeighbour_iff`. -/
def IsNeighbour (x y : Vertices R) : Prop := inv x y = 1

lemma isNeighbour_def (x y : Vertices R) :
    IsNeighbour x y ↔ inv x y = 1 :=
  Iff.rfl

/-- If `M` is a lattice and `x` a neighbour of `⟦M⟧`, there exists a representative
`L` of `x` such that `L ≤ M` and `dist M L = 1`. -/
lemma exists_repr_le_of_isNeighbour (M : Lattice R) (x : Vertices R)
    (h : IsNeighbour ⟦M⟧ x) :
    ∃ (L : Lattice R), ⟦L⟧ = x ∧ L.M ≤ M.M ∧ dist M L = 1 := by
  classical
  obtain ⟨ϖ, hϖ, b, hM, hL⟩ := exists_repr_inv'_of_fixed M x
  refine ⟨(b.ntwist₂ hϖ (inv ⟦M⟧ x) 0).toLattice, hL, ?_, ?_⟩
  · rw [← hM, Basis.toLattice_module]
    apply b.ntwist₂_toSubmodule_le
  · rw [isNeighbour_def] at h
    rw [h, ← hM, Basis.ntwist₂, Nat.cast_one, CharP.cast_eq_zero, ← Nat.cast_inj (R := ℤ),
      dist_twist₂ b hϖ]
    simp

lemma isNeighbour_symm (x y : Vertices R) :
    IsNeighbour x y ↔ IsNeighbour y x := by
  classical
  rw [isNeighbour_def, isNeighbour_def, inv_symm]

end IsNeighbour

section StandardNeighbour

/-- `M` is a standard neighbour of `L` if `ϖ • L < M < L`. -/
structure IsStandardNeighbour (M L : Lattice R) : Prop where
  lt : M.M < L.M
  ϖlt : ∀ (ϖ : R) (_ : Irreducible ϖ), ϖ • L.M < M.M

/-- `GL₂(K)` preserves `IsStandardNeighbour`. -/
lemma smul_isStandardNeighbour (g : GL (Fin 2) K) (M L : Lattice R) (h : IsStandardNeighbour M L) :
    IsStandardNeighbour (g • M) (g • L) where
  lt := by
    rw [Lattice.smul_M, Lattice.smul_M, smul_lt_iff]
    exact h.lt
  ϖlt ϖ hϖ := by
    rw [Lattice.smul_M, Lattice.smul_M, Subring.smul_def, scalar_smul_GL_smul, smul_lt_iff]
    exact h.ϖlt ϖ hϖ

variable [IsDiscreteValuationRing R]

/-- If `b = (b₁, b₂)` is a basis of `Fin 2 → K`, the span of `(ϖ • b₁, b₂)` is a standard neighbour
of the span of `b`. -/
lemma ntwist_isStandardNeighbour (b : Basis (Fin 2) K (Fin 2 → K)) {ϖ : R} (hϖ : Irreducible ϖ) :
    IsStandardNeighbour (b.ntwist₂ hϖ 1 0).toLattice (b.toLattice (R := R)) where
  lt := by
    simp [Basis.ntwist₂, Basis.twist₂]
    nth_rw 2 [← b.twist_zero hϖ]
    rw [b.twist_lt_twist_iff]
    constructor
    intro i
    match i with
    | 0 => simp
    | 1 => simp
    use 0
    simp
  ϖlt := by
    intro ϖ' hϖ'
    rw [← maximalIdeal_smul_eq_uniformizer_smul _ hϖ', maximalIdeal_smul_eq_uniformizer_smul _ hϖ]
    simp [Basis.ntwist₂, Basis.twist₂]
    nth_rw 1 [← b.twist_zero hϖ]
    rw [b.smul_twist]
    rw [b.twist_lt_twist_iff]
    constructor
    intro i
    match i with
    | 0 => simp
    | 1 => simp
    use 1
    simp

variable [IsFractionRing R K]

/-- If `M` is a standard neighbour of `L`, the vertices `⟦M⟧` and `⟦L⟧` are neighbours. -/
lemma isNeighbour_of_isStandardNeighbour {M L : Lattice R} (h : IsStandardNeighbour M L) :
    IsNeighbour ⟦M⟧ ⟦L⟧ := by
  classical
  obtain ⟨ϖ, hϖ, b, f, hf, rfl, rfl, hdiff⟩ := exists_repr_dist' L M
  have h1 := h.lt
  simp at h1
  nth_rw 2 [← b.twist_zero hϖ] at h1
  rw [b.twist_lt_twist_iff] at h1
  have h2 := h.ϖlt ϖ hϖ
  simp at h2
  nth_rw 1 [← b.twist_zero hϖ] at h2
  rw [b.smul_twist] at h2
  rw [b.twist_lt_twist_iff] at h2
  simp at h2 h1
  have h3 : f 1 ≤ f 0 := by
    apply hf
    simp
  have : 0 ≤ f 1 := h1.left 1
  have : 0 ≤ f 0 := h1.left 0
  have : f 0 ≤ 1 := h2.left 0
  have : f 1 ≤ 1 := h2.left 1
  have : f 1 = 0 ∧ f 0 = 1 := by
    obtain ⟨i, hi⟩ := h1.right
    obtain ⟨j, hj⟩ := h2.right
    match i, j with
    | 0, 0 =>
      have : f 0 = 1 := by omega
      simp [this] at hj
    | 0, 1 => omega
    | 1, 0 =>
      have hf1 : f 1 = 1 := by omega
      have hf2 : f 0 = 0 := by omega
      simp [hf1, hf2] at h3
    | 1, 1 => omega
  rw [this.left, this.right] at hdiff
  simp at hdiff
  show dist _ _ = 1
  rw [dist_symm]
  omega

lemma exists_basis_eq_ntwist_of_isNeighbour (M : Lattice R) (L : Vertices R)
    (h : IsNeighbour ⟦M⟧ L) :
    ∃ (b : Basis (Fin 2) K (Fin 2 → K)) (ϖ : R) (hϖ : Irreducible ϖ),
    b.toLattice = M ∧ ⟦(b.ntwist₂ hϖ 1 0).toLattice⟧ = L := by
  classical
  obtain ⟨ϖ, hϖ, b, rfl, h1⟩ := exists_repr_inv'_of_fixed M L
  have : inv _ _ = 1 := h
  rw [this] at h1
  use b, ϖ, hϖ

/-- If `M` is a lattice with neighbour `x`, there exists a representative `L` of `x` such that
`L` is a standard neighbour of `M`. -/
lemma exists_repr_isStandardNeighbour_of_isNeighbour (M : Lattice R) (x : Vertices R)
    (h : IsNeighbour ⟦M⟧ x) :
    ∃ (L : Lattice R), ⟦L⟧ = x ∧ IsStandardNeighbour L M := by
  obtain ⟨b, ϖ, hϖ, rfl, h1⟩ := exists_basis_eq_ntwist_of_isNeighbour M x h
  refine ⟨(b.ntwist₂ hϖ 1 0).toLattice (R := R), h1, ntwist_isStandardNeighbour b hϖ⟩

/--
Two vertices are neighbours if and only if there exist representatives L and M and a uniformizer ϖ
such that ϖ • L < M < L, i.e. if `L` is a standard neighbour of `M`.

This is a common alternative way of defining `BruhatTits.IsNeighbour`.
-/
theorem isNeighbour_iff (L M : Vertices R) :
    IsNeighbour L M ↔ ∃ (ϖ : R) (_ : Irreducible ϖ) (L' M' : Lattice R),
      ⟦L'⟧ = L ∧ ⟦M'⟧ = M ∧ ϖ • L'.M < M'.M ∧ M'.M < L'.M := by
  constructor
  · refine Quotient.inductionOn L (fun L h ↦ ?_)
    obtain ⟨M', hM, hstd⟩ := exists_repr_isStandardNeighbour_of_isNeighbour L M h
    obtain ⟨ϖ, hϖ⟩ := IsDiscreteValuationRing.exists_irreducible R
    exact ⟨ϖ, hϖ, L, M', rfl, hM, hstd.ϖlt ϖ hϖ, hstd.lt⟩
  · intro ⟨ϖ, hϖ, L', M', hL, hM, hϖlt, hlt⟩
    have hstd : IsStandardNeighbour M' L' := {
      lt := hlt
      ϖlt := by
        intro ϖ' hϖ'
        rw [← maximalIdeal_smul_eq_uniformizer_smul _ hϖ',
          maximalIdeal_smul_eq_uniformizer_smul _ hϖ]
        exact hϖlt
    }
    rw [← hL, ← hM, isNeighbour_symm]
    exact isNeighbour_of_isStandardNeighbour hstd

end StandardNeighbour

open IsLocalRing

section Chain

/-- A list `(Lᵢ)` of lattices is a chain, if `Lᵢ ≤ Lᵢ₊₁` and `dist Lᵢ Lᵢ₊₁ = 1` forall i. -/
structure _root_.List.IsChain (l : List (Lattice R)) : Prop where
  ne_nil : l ≠ []
  isStandardNeighbour : l.Chain' IsStandardNeighbour

lemma singleton_isChain (L : Lattice R) : [L].IsChain where
  ne_nil := by simp
  isStandardNeighbour := by simp

lemma cons_isChain_of {l : List (Lattice R)} {M L : Lattice R} (hl : (L :: l).IsChain)
    (hML : IsStandardNeighbour M L) : (M :: L :: l).IsChain where
  ne_nil := by simp
  isStandardNeighbour := by
    simp only [List.chain'_cons]
    exact ⟨hML, hl.isStandardNeighbour⟩

lemma isChain_of_cons_isChain {l : List (Lattice R)} {M : Lattice R} (hl : l ≠ [])
    (h : (M :: l).IsChain) : l.IsChain where
  ne_nil := hl
  isStandardNeighbour := by
    have := h.isStandardNeighbour
    rw [List.chain'_cons'] at this
    exact this.right

/-- A chain is simple if it does not backtrack in the interior (i.e. in principle
first and last entry can coincide). We will show that every chain is simple. -/
structure _root_.List.IsSimpleChain (l : List (Lattice R)) : Prop extends List.IsChain l where
  no_backtrack :
    (List.zipWith₃ (fun L₁ _ L₃ ↦ ¬ Lattice.IsSimilar R L₁ L₃) l l.tail l.tail.tail).Forall id

lemma singleton_isSimpleChain (L : Lattice R) : [L].IsSimpleChain where
  toIsChain := singleton_isChain L
  no_backtrack := by simp [List.zipWith₃]

lemma isSimpleChain_of_cons_isSimpleChain {l : List (Lattice R)} {M : Lattice R}
    (hl : l ≠ [])
    (h : (M :: l).IsSimpleChain) : l.IsSimpleChain where
  toIsChain := isChain_of_cons_isChain hl h.toIsChain
  no_backtrack := by
    match l with
    | [_] => simp [List.zipWith₃]
    | L₁ :: L₂ :: l =>
    have := h.no_backtrack
    simp [List.zipWith₃] at this
    simp only [List.tail_cons]
    exact this.right

/-- A chain `(Lᵢ)` is a standard chain, if `L₀` has a basis `b` such that
each `Lᵢ` is given by `b.ntwist₂ hϖ n 0`. -/
structure _root_.List.IsStandard (l : List (Lattice R)) : Prop extends List.IsSimpleChain l where
  exists_basis : ∃ (b : Basis (Fin 2) K (Fin 2 → K)) (ϖ : R) (hϖ : Irreducible ϖ),
    l.zipIdx.Forall (fun (L, n) ↦ L = (b.ntwist₂ hϖ (l.length - n - 1) 0).toLattice)

instance : SMul (GL (Fin 2) K) (List (Lattice R)) where
  smul g l := l.map (fun L ↦ g • L)

lemma _root_.List.smul_lattice_def (g : GL (Fin 2) K) (l : List (Lattice R)) :
    g • l = l.map (fun L ↦ g • L) :=
  rfl

@[simp]
lemma _root_.List.smul_lattice_length (g : GL (Fin 2) K) (l : List (Lattice R)) :
    (g • l).length = l.length := by
  simp [l.smul_lattice_def]

set_option synthInstance.maxHeartbeats 30000

/-- `GL₂(K)` preserves `IsSimilar`. -/
lemma smul_isSimilar_iff (g : GL (Fin 2) K) (M L : Lattice R) :
    Lattice.IsSimilar R (g • M) (g • L) ↔ Lattice.IsSimilar R M L := by
  simp [Lattice.IsSimilar]
  constructor
  · intro ⟨a, ha⟩
    use a
    apply_fun Lattice.M at ha
    rw [Lattice.smul_M] at ha
    simp at ha
    rw [Lattice.smul_M] at ha
    apply Lattice.ext
    simp
    rw [Units.smul_def] at ha ⊢
    rw [scalar_smul_GL_smul] at ha
    rw [smul_eq_iff] at ha
    assumption
  · intro ⟨a, ha⟩
    use a
    apply Lattice.ext
    simp [Lattice.smul_M, Units.smul_def, scalar_smul_GL_smul]
    apply_fun Lattice.M at ha
    simp [Units.smul_def] at ha
    rw [ha]

/-- `GL₂(K)` preserves `IsSimpleChain`. -/
lemma smul_isChain (g : GL (Fin 2) K) {l : List (Lattice R)} (hl : l.IsSimpleChain) :
    (g • l).IsSimpleChain where
  ne_nil := by
    rw [List.smul_lattice_def]
    simpa using hl.ne_nil
  isStandardNeighbour := by
    rw [List.smul_lattice_def]
    apply List.chain'_map_of_chain' _ ?_ hl.isStandardNeighbour
    exact smul_isStandardNeighbour g
  no_backtrack := by
    rw [List.smul_lattice_def]
    repeat rw [← List.map_tail]
    rw [List.zipWith₃_map]
    simp_rw [smul_isSimilar_iff]
    exact hl.no_backtrack

variable [IsDiscreteValuationRing R] [IsFractionRing R K]

/-- If `L₀, ..., Lₙ` is a standard chain of length `n + 1`, `dist L₀ Lₙ = n`. -/
lemma length_eq_dist_add_one_of_isStandard {l : List (Lattice R)} (hl : l.IsStandard) :
    l.length = dist (l.head hl.ne_nil) (l.getLast hl.ne_nil) + 1 := by
  classical
  obtain ⟨b, ϖ, hϖ, h⟩ := hl.exists_basis
  rw [List.forall_iff_forall_mem] at h
  have h1 : l.head hl.ne_nil = (b.ntwist₂ hϖ (l.length - 1) 0).toLattice := by
    have : (l.head hl.ne_nil, 0) ∈ l.zipIdx := by
      simp [List.mem_iff_getElem?, ← List.head?_eq_getElem?, List.head?_eq_head hl.ne_nil]
    replace h := h (l.head hl.ne_nil, 0) this
    simpa using h
  have h2 : l.getLast hl.ne_nil = (b.ntwist₂ hϖ 0 0).toLattice := by
    have : (l.getLast hl.ne_nil, l.length - 1) ∈ l.zipIdx := by
      rw [List.mem_iff_getElem?]
      simp only [List.getElem?_zipIdx, zero_add, Option.map_eq_some', Prod.mk.injEq, existsAndEq,
        true_and, exists_eq_right]
      rw [← List.getLast?_eq_getElem?, List.getLast?_eq_getLast hl.ne_nil]
    replace h := h (l.getLast hl.ne_nil, l.length - 1) this
    simp at h
    have : l.length - (l.length - 1) - 1 = 0 := by omega
    rw [this] at h
    simpa using h
  rw [h1, h2, Basis.ntwist₂_zero_zero]
  rw [dist_symm, dist_ntwist₂]
  have : 1 ≤ l.length := by
    match l with | (_ :: l) => simp
  omega

end Chain

section TrafoStandard

/-!
### Transformation of a simple chain to a standard chain

In this section we show that every simple chain can be transformed under `GL₂(K)` to a standard
chain. This is done by induction on the length of the chain.
-/

variable [IsDiscreteValuationRing R] [IsFractionRing R K]

/-- If `M` is a lattice, `b = (b₁, b₂)` a basis of `K^2` and `M` is a standard
neighbour of `b.toLattice`, there exists `g : GL₂(K)` preserving `b.toLattice` and
transforming `M` into the span of `(ϖ • b₁, b₂)`. -/
lemma exists_trafo_step_one (M : Lattice R) (b : Basis (Fin 2) K (Fin 2 → K))
    {ϖ : R} (hϖ : Irreducible ϖ)
    (h1 : M.M < b.toSubmodule)
    (h2 : ϖ • b.toSubmodule < M.M) :
    ∃ (g : GL (Fin 2) K),
        g • M = (b.ntwist₂ hϖ 1 0).toLattice ∧ g • b.toLattice (R := R) = b.toLattice := by
  let L : Lattice R := b.toLattice
  let κ := ResidueField R
  let bQ : Basis (Fin 2) κ L.quotient := b.toQuotient'
  let T : Lattice R := (b.ntwist₂ hϖ 1 0).toLattice
  let T' : Submodule κ L.quotient := L.mapIntermediate T
  have hT' : T' = Submodule.span κ { bQ 1 } :=
    mapIntermediate_stdLine₀ b hϖ
  by_cases h : M.M = (b.ntwist₂ hϖ 0 1).toSubmodule
  · refine ⟨b.swapMatrix, ?_, ?_⟩
    · apply Lattice.ext
      simp [Lattice.smul_M, h, b.swapMatrix_smul_ntwist₂]
    · apply Lattice.ext
      rw [Lattice.smul_M]
      nth_rw 2 [← b.ntwist₂_zero_zero hϖ]
      simp only [Basis.toLattice_module, b.swapMatrix_smul_ntwist₂]
      rw [b.ntwist₂_zero_zero hϖ]
  · obtain ⟨α, hα⟩ := Lattice.mapIntermediate_eq_span'' b M hϖ h1 h2 h
    use b.unipotent (-α)
    have huniL : b.unipotent (-α) • L.M = L.M := by
      simp only [Basis.toLattice_module, L]
      have : -α = ϖ ^ 0 * (-α) := by simp
      nth_rw 2 [← b.ntwist₂_zero_zero hϖ]
      nth_rw 3 [← b.ntwist₂_zero_zero hϖ]
      rw [this]
      apply unipotent_pow_irred_smul_eq_submodule
      simp
    refine ⟨?_, ?_⟩
    · apply L.mapIntermediate_inj_of
      · rw [Lattice.smul_M]
        exact le_trans (smul_GL_mono _ (le_of_lt h1)) (le_of_eq huniL)
      · rw [Lattice.smul_M, ← huniL, maximalIdeal_smul_eq_uniformizer_smul _ hϖ, Subring.smul_def,
          scalar_smul_GL_smul]
        exact smul_GL_mono _ (le_of_lt h2)
      · apply Basis.ntwist₂_toSubmodule_le
      · simp only [maximalIdeal_smul_eq_uniformizer_smul _ hϖ, Basis.toLattice_module, L]
        conv_lhs => rw [← b.ntwist₂_zero_zero hϖ]
        rw [b.smul_ntwist₂]
        apply b.ntwist₂_toSubmodule_le_ntwist₂_toSubmodule
        · simp
        · simp
      · rw [mapIntermediate_unipotent_smul]
        show Submodule.map (b.unipotentResidue (-α)) (b.toLattice.mapIntermediate M) =
          b.toLattice.mapIntermediate T
        rw [hα, LinearMap.map_span, Set.image_singleton, map_add, Basis.unipotentResidue_apply₁]
        erw [LinearMap.CompatibleSMul.map_smul]
        simpa [Basis.unipotentResidue_apply₀] using hT'.symm
    · apply Lattice.ext
      rwa [Lattice.smul_M]

/--
Core of induction step in `Lattice.exists_GL_smul_eq_ntwist₂_of_isSimpleChain_cons`:

If `M` is a lattice and `b` a basis of `K^2` such that
- `M` is a standard neighbour of the lattice spanned by `(ϖ ^ (n + 1) • b₁, b₂)`, and
- `M` is not equal to the lattice spanned by `(ϖ ^ n • b₁, b₂)`, then
there exists a unipotent matrix transforming `M` to `(ϖ ^ (n + 2) • b₁, b₂)`.

In more geometric terms, when we later apply this to a walk in the Bruhat-Tits graph,
the second condition requires the walk to not backtrack, i.e. to be a trail.
-/
lemma IsStandardNeighbour.exists_unipotent_smul_eq_ntwist₂ {ϖ : R} (hϖ : Irreducible ϖ) (n : ℕ)
    (M : Lattice R) (b : Basis (Fin 2) K (Fin 2 → K))
    (h : IsStandardNeighbour M (b.ntwist₂ hϖ (n + 1) 0).toLattice)
    (hne : ϖ • (b.ntwist₂ hϖ n 0).toSubmodule ≠ M.M) :
    ∃ (x : R), b.unipotent (ϖ ^ (n + 1) * x) • M = (b.ntwist₂ hϖ (n + 2) 0).toLattice := by
  let L : Lattice R := (b.ntwist₂ hϖ (n + 1) 0).toLattice
  let κ := ResidueField R
  let M' : Submodule κ L.quotient := L.mapIntermediate M
  let ϖL' : Submodule κ L.quotient :=
    L.mapIntermediate (b.ntwist₂ hϖ (n + 1) 1).toLattice
  have : M' ≠ ϖL' := by
    intro heq
    apply hne
    have : (b.ntwist₂ hϖ (n + 1) 1).toLattice = M := by
      refine L.mapIntermediate_inj_of _ _ ?_ ?_ (le_of_lt h.1) ?_ heq.symm
      · simp only [Basis.toLattice_module, L]
        exact b.ntwist₂_toSubmodule_le_ntwist₂_toSubmodule _ le_rfl zero_le_one
      · simp only [maximalIdeal_smul_eq_uniformizer_smul _ hϖ, Basis.toLattice_module, L,
          b.smul_ntwist₂]
        exact b.ntwist₂_toSubmodule_le_ntwist₂_toSubmodule _ (Nat.le_succ _) le_rfl
      · rw [maximalIdeal_smul_eq_uniformizer_smul _ hϖ]
        exact le_of_lt (h.2 _ hϖ)
    simp only [← this, Basis.toLattice_module, L, b.smul_ntwist₂]
  have hT : L.mapIntermediate ((b.ntwist₂ hϖ (n + 2) 0).toLattice (R := R)) =
      Submodule.span κ { (b.ntwist₂ hϖ (n + 1) 0).toQuotient' (R := R) 1 } := by
    have : b.ntwist₂ hϖ (n + 2) 0 = (b.ntwist₂ hϖ (n + 1) 0).ntwist₂ hϖ 1 0 := by
      rw [b.ntwist₂_ntwist₂]
    rw [this]
    apply mapIntermediate_stdLine₀ (b.ntwist₂ hϖ (n + 1) 0) hϖ
  have ha : ∃ (α : R),
      M' = Submodule.span κ { α • (b.ntwist₂ hϖ (n + 1) 0).toQuotient' (R := R) 0 +
        (b.ntwist₂ hϖ (n + 1) 0).toQuotient' (R := R) 1 } := by
    refine Lattice.mapIntermediate_eq_span'' _ _ hϖ h.1 (h.2 _ hϖ) fun heq ↦ hne ?_
    rw [b.smul_ntwist₂]
    rw [b.ntwist₂_ntwist₂] at heq
    exact heq.symm
  obtain ⟨α, ha⟩ := ha
  use (- α)
  have huniL : b.unipotent (ϖ ^ (n + 1) * (- α)) • L.M = L.M := by
    simp only [L]
    apply unipotent_pow_irred_smul_eq_submodule
    simp
  apply L.mapIntermediate_inj_of
  · rw [← huniL]
    exact smul_GL_mono _ (le_of_lt h.1)
  · rw [Lattice.smul_M, ← huniL, maximalIdeal_smul_eq_uniformizer_smul _ hϖ, Subring.smul_def,
      scalar_smul_GL_smul]
    exact smul_GL_mono _ (le_of_lt <| h.2 _ hϖ)
  · simp [L]
    apply b.ntwist₂_toSubmodule_le_ntwist₂_toSubmodule _ (by simp) le_rfl
  · rw [maximalIdeal_smul_eq_uniformizer_smul _ hϖ]
    simp only [Basis.toLattice_module, L, b.smul_ntwist₂]
    exact b.ntwist₂_toSubmodule_le_ntwist₂_toSubmodule _ le_rfl zero_le_one
  · rw [mapIntermediate_unipotent_smul']
    show Submodule.map _ M' = _
    rw [ha, LinearMap.map_span]
    simp only [Fin.isValue, Set.image_singleton, map_add, LinearMapClass.map_smul]
    show Submodule.span _ _ = _
    erw [LinearMap.CompatibleSMul.map_smul]
    rw [Basis.unipotentResidue_apply₁]
    simpa [Basis.unipotentResidue_apply₀] using hT.symm

/--
Core of lemma 2.2 in Casselman: If `M :: l` is a simple chain of lattices and `b` a basis of `K^2`
such that `l` is a standard chain defined by `b`, then there exists `x : R` such that the upper
unipotent matrix defined by `x` transforms `M` into the lattice spanned by
`(ϖ ^ (l.length - 1) • b₁, b₂)`.

We don't yet show that this transformation does not change the entries in `l`, see
`Lattice.exists_forall_GL_smul_eq_ntwist₂_of_isSimpleChain_cons` for the full result.

Implementation detail: We require that the `n`-th entry of `l` equals the lattice generated by
`(ϖ ^ (l.length - n - 1) • b₁, b₂)` and not `(ϖ ^ n • b₁, b₂)`, because it is easier
to work with `M :: l` than with `l ++ [M]`.
-/
lemma Lattice.exists_GL_smul_eq_ntwist₂_of_isSimpleChain_cons (l : List (Lattice R))
    (hlen : 2 ≤ l.length) (M : Lattice R) (hMl : (M :: l).IsSimpleChain)
    (b : Basis (Fin 2) K (Fin 2 → K)) (ϖ : R) (hϖ : Irreducible ϖ)
    (hl : l.zipIdx.Forall (fun (L, n) ↦ L = (b.ntwist₂ hϖ (l.length - n - 1) 0).toLattice)) :
    ∃ (x : R), b.unipotent (ϖ ^ (l.length - 1) * x) • M =
      (b.ntwist₂ hϖ l.length 0).toLattice := by
  have h1 : l.length - 1 = l.length - 2 + 1 := by omega
  have h2 : l.length = l.length - 2 + 2 := by omega
  rw [h1, h2]
  dsimp at hl
  match l with
  | (L₁ :: L₂ :: l) =>
  simp at hl
  have hstd : IsStandardNeighbour M (b.ntwist₂ hϖ (l.length + 1) 0).toLattice := by
    rw [← hl.left]
    have := hMl.isStandardNeighbour
    simp at this
    exact this.left
  refine hstd.exists_unipotent_smul_eq_ntwist₂ hϖ l.length M b fun heq ↦ ?_
  have hϖ' : ϖ.val ≠ 0 := by simpa using hϖ.ne_zero
  have : (Units.mk0 ϖ.val hϖ') • L₂ = M := by
    rw [hl.right.left]
    apply Lattice.ext
    simpa [Units.smul_def]
  have hsim : Lattice.IsSimilar R L₂ M := ⟨(Units.mk0 ϖ.val hϖ'), this⟩
  have := hMl.no_backtrack
  simp only [List.tail_cons, List.zipWith₃, List.forall_cons, id_eq] at this
  exact this.left <| (Lattice.IsSimilar.equivalence R).symm hsim

/--
Lemma 2.2 in Casselman and the induction step of `exists_trafo_to_isStandard` (used via:
`IsSimpleChain.exists_GL_forall_smul`):

If `M :: l` is a simple chain of lattices and `b` a basis of `K^2`
such that `l` is a standard chain defined by `b`, then there exists `g : GL₂(K)`
such that `g • M :: g • l` is a standard chain defined by `b`.

Implementation detail: We require that the `n`-th entry of `l` equals the lattice generated by
`(ϖ ^ (l.length - n - 1) • b₁, b₂)` and not `(ϖ ^ n • b₁, b₂)`, because it is easier
to work with `M :: l` than with `l ++ [M]`.
-/
lemma Lattice.exists_GL_forall_smul_eq_ntwist₂_of_isSimpleChain_cons (l : List (Lattice R)) (M : Lattice R)
    (hMl : (M :: l).IsSimpleChain) (b : Basis (Fin 2) K (Fin 2 → K)) (ϖ : R) (hϖ : Irreducible ϖ)
    (hl : l.zipIdx.Forall (fun (L, n) ↦ L = (b.ntwist₂ hϖ (l.length - n - 1) 0).toLattice)) :
    ∃ (g : GL (Fin 2) K), (M :: l).zipIdx.Forall
      (fun (L, n) ↦ g • L = (b.ntwist₂ hϖ (l.length - n) 0).toLattice) := by
  match l with
  | [] =>
    simp only [List.Forall, List.length_nil, ge_iff_le, le_refl, tsub_eq_zero_of_le]
    apply MulAction.IsPretransitive.exists_smul_eq
  | [L] =>
    simp only [List.length_cons, List.length_nil, zero_add, List.zipIdx_cons, Nat.reduceAdd,
      List.zipIdx_nil, List.Forall, tsub_zero, tsub_self]
    simp only [List.length_cons, List.length_nil, zero_add, tsub_le_iff_right,
      le_add_iff_nonneg_right, zero_le, Nat.sub_eq_zero_of_le, List.zipIdx_cons, List.zipIdx_nil,
      List.forall_cons, List.Forall, and_true] at hl
    rw [hl]
    have : b.ntwist₂ hϖ 0 0 = b := by
      ext i
      fin_cases i <;> simp
    rw [this]
    have hbLM : b.toSubmodule = L.M := by simp [hl, this]
    have hstd : IsStandardNeighbour M L := by simpa using hMl.isStandardNeighbour
    apply exists_trafo_step_one
    · rw [hbLM]
      exact hstd.lt
    · rw [hbLM]
      exact hstd.ϖlt ϖ hϖ
  | L₁ :: L₂ :: l' =>
    generalize hldef : L₁ :: L₂ :: l' = l at *
    have hlen : 2 ≤ l.length := by simp [← hldef]
    obtain ⟨x, h⟩ := M.exists_GL_smul_eq_ntwist₂_of_isSimpleChain_cons l hlen hMl b ϖ hϖ hl
    use b.unipotent (ϖ ^ (l.length - 1) * x)
    simp only [List.zipIdx_cons, zero_add, List.forall_cons, tsub_zero]
    refine ⟨h, ?_⟩
    dsimp only at hl
    rw [show 1 = 1 + 0 by simp, ← List.map_snd_add_zipIdx_eq_zipIdx]
    simp only [add_zero, List.forall_map_iff]
    show List.Forall (fun (L, n) ↦ b.unipotent (ϖ ^ (l.length - 1) * x) • L =
      (b.ntwist₂ hϖ (l.length - (n + 1)) 0).toLattice) l.zipIdx
    rw [List.forall_iff_forall_mem] at hl ⊢
    intro ⟨L, n⟩ h
    have : l.length - (n + 1) = l.length - n - 1 := by omega
    replace hl := hl (L, n) h
    dsimp only at hl ⊢
    rw [this, hl]
    apply unipotent_pow_irred_smul_eq
    omega

/-- (Implementation detail) Intermediate lemma for `exists_trafo_to_isStandard`, more adapted
to induction. -/
lemma _root_.List.IsSimpleChain.exists_GL_forall_smul (l : List (Lattice R))
    (hl : l.IsSimpleChain) :
    ∃ (g : GL (Fin 2) K) (b : Basis (Fin 2) K (Fin 2 → K)) (ϖ : R) (hϖ : Irreducible ϖ),
    l.zipIdx.Forall (fun (L, n) ↦ g • L = (b.ntwist₂ hϖ (l.length - n - 1) 0).toLattice) := by
  match l with
  | [] =>
    obtain ⟨ϖ, hϖ⟩ := IsDiscreteValuationRing.exists_irreducible R
    refine ⟨1, Pi.basisFun K (Fin 2), ϖ, hϖ, by simp⟩
  | [M] =>
    obtain ⟨ϖ, hϖ⟩ := IsDiscreteValuationRing.exists_irreducible R
    use 1, M.basis.fromLattice, ϖ, hϖ
    simp [Basis.ntwist₂_zero_zero]
  | (M :: L :: l') =>
    generalize hldef : L :: l' = l at *
    have : l ≠ [] := by simp [← hldef]
    have hl' : l.IsSimpleChain := isSimpleChain_of_cons_isSimpleChain this hl
    obtain ⟨g, b, ϖ, hϖ, h⟩ := hl'.exists_GL_forall_smul l
    obtain ⟨g', hg'⟩ := by
      refine (g • M).exists_GL_forall_smul_eq_ntwist₂_of_isSimpleChain_cons (g • l) ?_ b ϖ hϖ ?_
      · exact smul_isChain g hl
      · simpa [List.smul_lattice_def, List.length_map, ← List.map_zipIdx]
    use g' * g, b, ϖ, hϖ
    simp only [List.smul_lattice_length, List.zipIdx_cons, zero_add, List.forall_cons, tsub_zero,
      List.length_cons, add_tsub_cancel_right] at hg' ⊢
    simp_rw [mul_smul]
    refine ⟨?_, ?_⟩
    · exact hg'.left
    · rw [List.smul_lattice_def, ← List.map_zipIdx, List.forall_map_iff] at hg'
      convert hg'.right with ⟨L, n⟩
      have : l.length + 1 - n - 1 = l.length - n := by omega
      simp [this]

/--
Any simple chain of lattices can be transformed by an element of `GL₂(K)` to a standard
chain, i.e. up to a change of coordinates is every simple chain a standard chain.
This is later used to show acyclicity of the Bruhat-Tits graph.

Prop. 2.1 in Casselman.
-/
lemma exists_trafo_to_isStandard (l : List (Lattice R)) (hl : l.IsSimpleChain) :
    ∃ (g : GL (Fin 2) K), (g • l).IsStandard := by
  obtain ⟨g, b, ϖ, hϖ, h⟩ := hl.exists_GL_forall_smul l
  refine ⟨g, ⟨smul_isChain g hl, ⟨b, ϖ, hϖ, ?_⟩⟩⟩
  rw [List.smul_lattice_def, List.zipIdx_map]
  simpa

end TrafoStandard

end BruhatTits
