/-
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.Cartan.Uniqueness
import BruhatTits.Lattice.Basic
import BruhatTits.Utils.Matrix

/-!
# Distance on lattices and vertices

In this file we define the distance function `dist` on lattices. For this
we apply the Cartan decomposition to bring every pair of lattices `M` and `L` into a
standard form:

There exists a basis `bM` of `M` and a basis `bL` of `L` such that `bLᵢ = ϖ ^ fᵢ • bMᵢ` for
`i = 1,2` and integers `fᵢ`. If we fix the order of `fᵢ` to be `f₁ ≥ f₂`, the `fᵢ` are unique,
i.e. don't depend on `ϖ`, `bM` or `bL`.

We then say that the distance of `M` and `L` is `f₁ - f₂` which is `≥ 0` by our assumption on
the `fᵢ`.

One then verifies that the difference `f₁ - f₂` is invariant under multiplying `M` or `L` with
units of `K` and hence we obtain a distance function `inv` on the vertices of the Bruhat-Tits
graph.

## Main definitions

- `BruhatTits.dist L M`: The distance between two lattices`L` and `M`.

## Main results

- `BruhatTits.exists_normal_basis`: Given two lattices, we can find good bases using the Cartan
  decomposition.
- `BruhatTits.dist_symm`: The distance function is symmetric.
- `BruhatTits.dist_inv_isSimilar`: The distance function is invariant under homothety.
-/

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

open Algebra BruhatTits Matrix GeneralLinearGroup

variable {k : ℕ}
variable {ι : Type*} [DecidableEq ι] [Fintype ι]

namespace BruhatTits

omit [IsDiscreteValuationRing R] in
lemma smulBasis_apply_of_isLattice {M : Submodule R (ι → K)} [IsLattice M] (g : GL ι R)
    (b : Basis ι R M) (i j : ι) :
    ((smulBasis g b) i).val j = (b.toGL * g) j i := by
  dsimp only [smulBasis, toLinearEquivOfBasis]
  simp only [Basis.map_apply, LinearEquiv.trans_apply, Basis.equivFun_apply, Basis.repr_self,
    Basis.equivFun_symm_apply, AddSubmonoidClass.coe_finset_sum, SetLike.val_smul, Finset.sum_apply,
    Pi.smul_apply, «GL».map, RingHom.mapMatrix_apply, Subring.coe_subtype, Units.inv_eq_val_inv,
    coe_units_inv, Units.val_mul]
  change (Finset.univ.sum fun x ↦ g.val.mulVec (Finsupp.single i 1) x * (b x).val j) =
    (b.toGL * g.val.map Subtype.val) j i
  rw [Finsupp.single_eq_pi_single]
  simp only [mulVec_single, mul_one]
  rw [Matrix.mul_apply]
  congr
  ext k
  simp [Basis.toGL_apply, map_apply, mul_comm]

/-- `GL₂(K)` acts transitively on `R`-lattices. -/
instance : MulAction.IsPretransitive (GL (Fin 2) K) (BruhatTits.Lattice R) where
  exists_smul_eq L M := by
    classical
    let bL : Basis (Fin 2) R L.M :=
      Module.finBasisOfFinrankEq R L.M (IsLattice.finrank L.M)
    let gL := bL.toGL
    have hL : gL.toSubmodule = L.M :=
      bL.toGL_toSubmodule
    let bM : Basis (Fin 2) R M.M :=
      Module.finBasisOfFinrankEq R M.M (IsLattice.finrank M.M)
    let gM := bM.toGL
    have hM : gM.toSubmodule = M.M :=
      bM.toGL_toSubmodule
    use gM * gL⁻¹
    apply BruhatTits.Lattice.ext
    rw [Lattice.smul_M, ← hM, ← hL, smul_toSubmodule]
    simp

omit [IsDiscreteValuationRing ↥R] in
lemma basis_smul_def_of_isLattice {M : Submodule R (ι → K)} [IsLattice M] (g : GL ι R)
    (b : Basis ι R M) (i j : ι) :
    (((MulOpposite.op g) • b) i).val j = (b.toGL * g) j i :=
  smulBasis_apply_of_isLattice g b i j

set_option synthInstance.maxHeartbeats 300000 in
omit [IsDiscreteValuationRing ↥R] in
@[simp]
lemma basis_smul_toGL {M : Submodule R (ι → K)} [IsLattice M] (g : GL ι R)
    (b : Basis ι R M) :
    ((MulOpposite.op g) • b).toGL = b.toGL * g := by
  ext i j
  simp [basis_smul_def_of_isLattice]

section

variable (ϖ : R) (hϖ : Irreducible ϖ)

include hϖ in
/--
Given two `R`-lattices `M` and `L`, there exists a basis `bM` of `M`
and a basis `bL` of `L` such that `bLᵢ = ϖ ^ fᵢ • bMᵢ` for `i = 1,2` and integers
`fᵢ`. If we fix the order of `fᵢ` to be `f₁ ≥ f₂`, the `fᵢ` are unique, i.e. don't
depend on `ϖ`, `bM` or `bL`.
-/
lemma exists_normal_basis (M L : BruhatTits.Lattice R) :
    ∃ (bM : Basis (Fin 2) R M.M) (bL : Basis (Fin 2) R L.M) (f : Fin 2 → ℤ),
    Antitone f ∧ ∀ i, (bL i).val = (ϖ ^ f i : K) • (bM i).val := by
  classical
  let bL : Basis (Fin 2) R L.M := L.basis
  let bM : Basis (Fin 2) R M.M := M.basis
  obtain ⟨(k₁ : GL (Fin 2) R), (k₂ : GL (Fin 2) R), f, hf, hk⟩ :=
    cartan_decomposition' (k := 2) ϖ hϖ (bM.toGL⁻¹ * bL.toGL)
  rw [eq_inv_mul_iff_mul_eq] at hk
  let bM' : Basis (Fin 2) R M.M :=
    (MulOpposite.op k₁) • bM
  let bL' : Basis (Fin 2) R L.M :=
    (MulOpposite.op k₂⁻¹) • bL
  use bM', bL', f, hf
  intro i
  simp only [bL', bM']
  ext j
  rw [basis_smul_def_of_isLattice, ← hk]
  simp only [PNat.val_ofNat, GL.map, RingHom.mapMatrix_apply, Subring.coe_subtype,
    Units.inv_eq_val_inv, coe_units_inv, inv_inv, Units.val_mul, val_cartanDiag, Pi.smul_apply,
    smul_eq_mul]
  rw [basis_smul_def_of_isLattice]
  simp only [GL.map, RingHom.mapMatrix_apply, Subring.coe_subtype, Units.inv_eq_val_inv,
    coe_units_inv, Units.val_mul]
  rw [mul_assoc, mul_assoc]
  erw [Subring.coe_map_mul_map_inv R k₂]
  rw [mul_one, ← mul_assoc, mul_diagonal, mul_comm]

end

/-- A variant of `BruhatTits.exists_normal_basis` where also a uniformizer is provided. -/
lemma exists_normal_basis_uniformizer (M L : BruhatTits.Lattice R) :
    ∃ (ϖ : R) (bM : Basis (Fin 2) R M.M) (bL : Basis (Fin 2) R L.M) (f : Fin 2 → ℤ),
    Irreducible ϖ ∧ Antitone f ∧ ∀ i, (bL i).val = (ϖ ^ f i : K) • (bM i).val := by
  obtain ⟨ϖ, hϖ⟩ := IsDiscreteValuationRing.exists_irreducible R
  obtain ⟨bM, bL, f, hf, heq⟩ := exists_normal_basis ϖ hϖ M L
  use ϖ, bM, bL, f

/-- The integers `fᵢ` of `BruhatTits.exists_normal_basis` are unique. More precisely,
given two lattices `M` and `L`, uniformizers `ϖ`, `ϖ'`, basis
`bM`, `bM'` of `M` and `bL`, `bL'` of `L` and integers `fᵢ`, `fᵢ'` such that
`bLᵢ = ϖ ^ fᵢ • bMᵢ` and `bL'ᵢ = ϖ' ^ f'ᵢ • bM'ᵢ` and with `f₂ ≥ f₁`, `f'₁ ≥ f'₂`,
then `fᵢ = fᵢ'`. This unique pair of integers `fᵢ` is called the signature of `L` and `M`.
-/
lemma signature_unique {M L : BruhatTits.Lattice R}
    {ϖ ϖ' : R} (hϖ : Irreducible ϖ) (hϖ' : Irreducible ϖ')
    {bM bM' : Basis (Fin 2) R M.M} {bL bL' : Basis (Fin 2) R L.M}
    {f f' : Fin 2 → ℤ} (hf : Antitone f) (hf' : Antitone f')
    (h : ∀ i, (bL i).val = (ϖ ^ f i : K) • (bM i).val)
    (h' : ∀ i, (bL' i).val = (ϖ' ^ f' i : K) • (bM' i).val) :
    f = f' := by
  obtain ⟨⟨k₁⟩, hk₁⟩ := MulAction.IsPretransitive.exists_smul_eq (M := (GL (Fin 2) R)ᵐᵒᵖ)
    bM bM'
  obtain ⟨⟨k₂⟩, hk₂⟩ := MulAction.IsPretransitive.exists_smul_eq (M := (GL (Fin 2) R)ᵐᵒᵖ)
    bL bL'
  let d : GL (Fin 2) K := cartanDiag (k := 2) ϖ hϖ f
  have hd : bL.toGL = bM.toGL * d := by
    ext i j
    simp only [Basis.toGL_apply, Units.val_mul, val_cartanDiag, PNat.val_ofNat, mul_diagonal, d]
    rw [mul_comm, h j]
    simp
  let d' : GL (Fin 2) K := cartanDiag (k := 2) ϖ' hϖ' f'
  have hd' : bL'.toGL = bM'.toGL * d' := by
    ext i j
    simp only [Basis.toGL_apply, Units.val_mul, val_cartanDiag, PNat.val_ofNat, mul_diagonal, d']
    rw [mul_comm, h' j]
    simp
  have hbMd' : bM'.toGL * d' = bM.toGL * d * k₂ := by
    rw [← hd', ← hk₂]
    erw [basis_smul_toGL, hd]
  have hbM : bM.toGL = bM'.toGL * k₁⁻¹ := by
    rw [← hk₁]
    erw [basis_smul_toGL k₁ bM]
    rw [mul_assoc, GL.map_mul_map_inv, mul_one]
  have : (1 : GL (Fin 2) R) * d' * (1 : GL (Fin 2) R) = k₁⁻¹ * d * k₂ := by
    calc (1 : GL (Fin 2) R) * d' * (1 : GL (Fin 2) R) = bM'.toGL⁻¹ * (bM'.toGL * d') :=
        by rw [GL.map_one]; group
                  _ = bM'.toGL⁻¹ * bM.toGL * d * k₂ := by rw [hbMd']; group
                  _ = bM'.toGL⁻¹ * bM'.toGL * k₁⁻¹ * d * k₂ := by rw [hbM]; group
                  _ = k₁⁻¹ * d * k₂ := by group
  apply cartan_decomposition_unique_uniformizer (k := 2) hϖ hϖ' hf hf'
    (k₁ := k₁⁻¹) (k₂ := k₂) (k₁' := 1) (k₂' := 1)
    this.symm

section Choices

/-!
## Choice of signature

In this section for every pair of lattices `M` and `L`, we fix arbitrarily chosen
basis `bM` of `M` and `bL` of `L` and the (antitone) signature `fᵢ` such that
`bLᵢ = ϖ ^ fᵢ • bMᵢ`.

-/

/-- A choice of uniformizer. -/
noncomputable def signatureϖ (M L : BruhatTits.Lattice R) : R :=
  (exists_normal_basis_uniformizer M L).choose

noncomputable def signatureϖ_irreducible (M L : BruhatTits.Lattice R) :
    Irreducible (signatureϖ M L) :=
  (exists_normal_basis_uniformizer M L).choose_spec.choose_spec.choose_spec.choose_spec.left

/-- A choice of basis for `M`. -/
noncomputable def signatureBasisSource (M L : BruhatTits.Lattice R) :
    Basis (Fin 2) R M.M :=
  (exists_normal_basis_uniformizer M L).choose_spec.choose

/-- A choice of basis for `L`. -/
noncomputable def signatureBasisTarget (M L : BruhatTits.Lattice R) :
    Basis (Fin 2) R L.M :=
  (exists_normal_basis_uniformizer M L).choose_spec.choose_spec.choose

/-- The unique (antitone) signature of `M` and `L`. -/
noncomputable def signature (M L : BruhatTits.Lattice R) : Fin 2 → ℤ :=
  (exists_normal_basis_uniformizer M L).choose_spec.choose_spec.choose_spec.choose

lemma signature_antitone (M L : BruhatTits.Lattice R) :
    Antitone (signature M L) :=
  (exists_normal_basis_uniformizer M L).choose_spec.choose_spec.choose_spec.choose_spec.right.left

lemma signatureBasisTarget_eq (M L : BruhatTits.Lattice R) (i : Fin 2) :
    signatureBasisTarget M L i =
      (signatureϖ M L ^ signature M L i : K) • (signatureBasisSource M L i).val :=
  (exists_normal_basis_uniformizer M L).choose_spec.choose_spec.choose_spec.choose_spec.right.right i

/-- To show that an antitone pair of integers `fᵢ` is the signature of `M` and `L` it
suffices to find basis that satisfy the defining property of the signature. -/
lemma eq_signature_iff (M L : BruhatTits.Lattice R)
    (f : Fin 2 → ℤ) (hf : Antitone f) :
    signature M L = f ↔ ∃ (ϖ : R) (bM : Basis (Fin 2) R M.M) (bL : Basis (Fin 2) R L.M),
      Irreducible ϖ ∧ ∀ i, (bL i).val = (ϖ ^ f i : K) • (bM i).val := by
  constructor
  · rintro rfl
    use signatureϖ M L
    use signatureBasisSource M L
    use signatureBasisTarget M L
    exact ⟨signatureϖ_irreducible M L, signatureBasisTarget_eq M L⟩
  · intro ⟨ϖ, bM, bL, hϖ, h⟩
    exact signature_unique (signatureϖ_irreducible M L) hϖ (signature_antitone M L) hf
      (signatureBasisTarget_eq M L)
      h

end Choices

/-- We define the distance between lattices `M` and `L` to be `f₁ - f₂` where
`fᵢ` is the (antitone) signature. This is always `≥ 0`, because `f₁ ≥ f₂`. -/
noncomputable def dist (M L : BruhatTits.Lattice R) : ℕ :=
  (signature M L 0 - signature M L 1).toNat

lemma signature_diff_eq_dist (M L : BruhatTits.Lattice R) :
    signature M L 0 - signature M L 1 = dist M L := by
  simp only [dist]
  have : signature M L 0 - signature M L 1 ≥ 0 := by
    apply Int.sub_nonneg_of_le
    apply signature_antitone M L
    exact Fin.zero_le 1
  exact (Int.toNat_of_nonneg this).symm

/-- A rephrasing of uniqueness of signatures in terms of the distance of lattices. -/
lemma eq_dist_iff (M L : BruhatTits.Lattice R) (n : ℕ) :
    dist M L = n ↔ ∃ (ϖ : R) (bM : Basis (Fin 2) R M.M) (bL : Basis (Fin 2) R L.M)
      (f : Fin 2 → ℤ),
      Irreducible ϖ ∧
        Antitone f ∧
        (∀ i, (bL i).val = (ϖ ^ f i : K) • (bM i).val) ∧ f 0 - f 1 = n := by
  constructor
  · rintro rfl
    use signatureϖ M L
    use signatureBasisSource M L
    use signatureBasisTarget M L
    use signature M L
    refine ⟨?_, ?_, ?_, ?_⟩
    · exact signatureϖ_irreducible M L
    · exact signature_antitone M L
    · exact signatureBasisTarget_eq M L
    · exact signature_diff_eq_dist M L
  · intro ⟨ϖ, bM, bL, f, hϖ, hf, hi, hn⟩
    have : signature M L = f := by
      apply (eq_signature_iff M L f hf).mpr
      use ϖ, bM, bL
    subst this
    rw [signature_diff_eq_dist M L] at hn
    exact Int.ofNat_inj.mp hn

/-- Variant of `BruhatTits.eq_dist_iff` where the signature is unwrapped as two integers `a` and
`b`. -/
lemma eq_dist_iff₂ (M L : BruhatTits.Lattice R) (n : ℕ) :
    dist M L = n ↔ ∃ (ϖ : R) (bM : Basis (Fin 2) R M.M) (bL : Basis (Fin 2) R L.M)
      (a b : ℤ), Irreducible ϖ ∧
         b ≤ a ∧ bL 0 = ϖ.val ^ a • (bM 0).val ∧ bL 1 = ϖ.val ^ b • (bM 1).val ∧ a - b = n := by
  rw [eq_dist_iff]
  constructor
  · rintro ⟨ϖ, bM, bL, f, hϖ, _, hrep, hdiff⟩
    refine ⟨ϖ, bM, bL, f 0, f 1, hϖ, Int.le.intro_sub n hdiff, hrep 0, hrep 1, hdiff⟩
  · rintro ⟨ϖ, bM, bL, a, b, hϖ, hle, h0, h1, hdiff⟩
    let f (i : Fin 2) : ℤ := match i with
      | 0 => a
      | 1 => b
    refine ⟨ϖ, bM, bL, f, hϖ, ?_, ?_, hdiff⟩
    · intro i j hij
      match i, j with
      | 0, 0 => rfl
      | 0, 1 => exact hle
      | 1, 0 => simp at hij
      | 1, 1 => rfl
    · intro i
      match i with
      | 0 => exact h0
      | 1 => exact h1

/-- Monotone variant of `BruhatTits.eq_dist_iff`. -/
lemma eq_dist_iff_monotone (M L : BruhatTits.Lattice R) (n : ℕ) :
    dist M L = n ↔ ∃ (ϖ : R) (bM : Basis (Fin 2) R M.M) (bL : Basis (Fin 2) R L.M)
      (f : Fin 2 → ℤ),
      Irreducible ϖ ∧ Monotone f ∧
        (∀ i, (bL i).val = (ϖ ^ f i : K) • (bM i).val) ∧ f 1 - f 0 = n := by
  rw [eq_dist_iff]
  constructor
  · intro ⟨ϖ, bM, bL, f, hϖ, hf, hi, hn⟩
    use ϖ, bM.reindex Fin.revPerm, bL.reindex Fin.revPerm, f ∘ Fin.revPerm
    refine ⟨hϖ, ?_, fun i ↦ ?_, ?_⟩
    · exact hf.comp (Fin.rev_antitone 2)
    · simp only [Basis.coe_reindex, Fin.revPerm_symm, Function.comp_apply, Fin.revPerm_apply]
      exact hi i.rev
    · simpa
  · intro ⟨ϖ, bM, bL, f, hϖ, hf, hi, hn⟩
    use ϖ, bM.reindex Fin.revPerm, bL.reindex Fin.revPerm, f ∘ Fin.revPerm
    refine ⟨hϖ, ?_, fun i ↦ ?_, ?_⟩
    · exact hf.comp_antitone (Fin.rev_antitone 2)
    · simp only [Basis.coe_reindex, Fin.revPerm_symm, Function.comp_apply, Fin.revPerm_apply]
      exact hi i.rev
    · simpa

lemma exists_repr_dist (M L : BruhatTits.Lattice R) :
    ∃ (ϖ : R) (bM : Basis (Fin 2) R M.M) (bL : Basis (Fin 2) R L.M) (f : Fin 2 → ℤ),
      Irreducible ϖ ∧ Antitone f ∧
        (∀ i, (bL i).val = (ϖ ^ f i : K) • (bM i).val) ∧ f 0 - f 1 = dist M L := by
  set n := dist M L with hn
  exact (eq_dist_iff M L n).mp hn.symm

/-- The distance on lattices is symmetric. -/
lemma dist_symm (M L : BruhatTits.Lattice R) :
    dist M L = dist L M := by
  obtain ⟨ϖ, bM, bL, f, hϖ, hf, hrep, hdiff⟩ := exists_repr_dist M L
  symm
  rw [eq_dist_iff_monotone]
  use ϖ, bL, bM, (fun i ↦ - f i)
  refine ⟨hϖ, ?_, ?_, ?_⟩
  · exact hf.neg
  · intro i
    have : ϖ.val ^ f i ≠ 0 := by
      apply zpow_ne_zero
      simpa using hϖ.ne_zero
    simp only [_root_.zpow_neg, hrep i, smul_smul]
    field_simp
  · simpa only [Fin.isValue, sub_neg_eq_add, add_comm]

/-- The distance of a lattice to itself is zero. -/
@[simp]
lemma dist_self (L : BruhatTits.Lattice R) :
    dist L L = 0 := by
  rw [eq_dist_iff]
  obtain ⟨ϖ, hϖ⟩ := IsDiscreteValuationRing.exists_irreducible R
  use ϖ, L.basis, L.basis, fun _ ↦ 0, hϖ, antitone_const
  simp

def mulBasis (g : GL ι K) {M : Submodule R (ι → K)} (b : Basis ι R M) :
    Basis ι R (g • M : Submodule R (ι → K)) :=
  let e' : M ≃ₗ[R] (g • M : Submodule R (ι → K)) := equivSMulGL g M
  b.map e'

open Pointwise

def equivSMul (a : Kˣ) (M : Submodule R (ι → K)) :
    M ≃ₗ[R] (a • M : Submodule R (ι → K)) :=
  let e : (ι → K) ≃ₗ[R] (ι → K) := (LinearEquiv.smulOfUnit a).restrictScalars R
  e.submoduleMap M

def mulBasisScalar (a : Kˣ) {M : BruhatTits.Lattice R} (b : Basis ι R M.M) :
    Basis ι R (a • M).M :=
  let e : M.M ≃ₗ[R] (a • M.M : Submodule R (Fin 2 → K)) := equivSMul a M.M
  b.map e

omit [IsDiscreteValuationRing ↥R] [IsFractionRing (↥R) K]

omit [DecidableEq ι] [Fintype ι] in
@[simp]
lemma mulBasisScalar_apply (a : Kˣ) (M : BruhatTits.Lattice R) (b : Basis ι R M.M) (i : ι) :
    (mulBasisScalar a b i).val = a.val • (b i).val := by
  simp only [Lattice.smul_module, mulBasisScalar, equivSMul, Basis.map_apply]
  rfl

lemma mulBasis_apply (g : GL ι K) {M : Submodule R (ι → K)} (b : Basis ι R M) (i : ι) :
    (mulBasis g b i).val = g *ᵥ (b i).val := by
  simp [mulBasis, equivSMulGL]
  rfl

lemma mulBasisScalar_toGL [IsFractionRing R K] (a : Kˣ) (M : BruhatTits.Lattice R)
    (b : Basis (Fin 2) R M.M) : (mulBasisScalar a b).toGL = GL.diagonal (fun _ ↦ a) * b.toGL := by
  ext i j
  simp only [Lattice.smul_module, Basis.toGL_apply, Units.val_mul, GL.val_diagonal, diagonal_mul]
  rfl

lemma unit_smul_eq (u : Rˣ) (M : BruhatTits.Lattice R) : (Units.map R.subtype u : Kˣ) • M = M := by
  ext x
  constructor
  · intro hx
    simp only [Lattice.smul_module] at hx
    obtain ⟨y, hy, rfl⟩ := hx
    simp only [Units.coe_map, MonoidHom.coe_coe, Subring.coe_subtype,
      DistribMulAction.toLinearMap_apply]
    exact Submodule.smul_mem _ u.val hy
  · intro hx
    simp only [Lattice.smul_module]
    refine ⟨u⁻¹ • x, ?_, ?_⟩
    · exact Submodule.smul_mem _ u.inv hx
    · simp only [Units.coe_map, MonoidHom.coe_coe, Subring.coe_subtype, LinearMap.map_smul_of_tower,
        DistribMulAction.toLinearMap_apply]
      rw [← Subring.smul_def, Units.smul_def, smul_smul]
      simp

variable [IsDiscreteValuationRing ↥R] [IsFractionRing (↥R) K]

/-- The distance of a lattice `M` to a given lattice `L` does not change when `M` is scaled
by some `a : Kˣ`.-/
lemma dist_smul_eq_dist (M L : BruhatTits.Lattice R) (a : Kˣ) :
    dist (a • M) L = dist M L := by
  obtain ⟨ϖ, bM, bL, f, hϖ, hf, hrep, hdiff⟩ := exists_repr_dist M L
  obtain ⟨n, u, ha⟩ := eq_unit_mul_pow_irreducible' ϖ hϖ a
  have h0 : ϖ.val ≠ 0 := by simpa using hϖ.ne_zero
  have : a • M = (Units.mk0 ϖ.val h0) ^ n • M := by
    rw [ha, mul_comm, ← smul_smul, unit_smul_eq]
  rw [this]
  rw [mul_comm] at ha
  have : ϖ.val ^ n ≠ 0 := by
    apply zpow_ne_zero
    simpa using hϖ.ne_zero
  let bM' : Basis (Fin 2) R ((Units.mk0 ϖ.val h0) ^ n • M).M :=
    mulBasisScalar ((Units.mk0 ϖ.val h0) ^ n) bM
  have hbM' (i : Fin 2) : ϖ.val ^ (-n) • (bM' i).val = bM i := by
    simp only [bM', mulBasisScalar_apply]
    simp only [_root_.zpow_neg, Units.val_zpow_eq_zpow_val, Units.val_mk0, smul_smul]
    field_simp
  let f' (i : Fin 2) : ℤ := f i + (-n)
  rw [eq_dist_iff]
  use ϖ, bM', bL, f', hϖ
  refine ⟨?_, ?_, ?_⟩
  · apply hf.add_const
  · intro i
    simp only [Lattice.smul_module, f']
    rw [zpow_add₀]
    rw [← smul_smul]
    erw [hbM' i]
    exact hrep i
    simp only [ne_eq, ZeroMemClass.coe_eq_zero]
    exact hϖ.ne_zero
  · simp [f', hdiff]

/-- The distance of lattices is invariant under the equivalence relation `IsSimilar`. -/
lemma dist_inv_isSimilar (M L M' L' : BruhatTits.Lattice R)
    (hM : Lattice.IsSimilar R M M') (hL : Lattice.IsSimilar R L L') : dist M L = dist M' L' := by
  obtain ⟨a, rfl⟩ := hM
  obtain ⟨b, rfl⟩ := hL
  rw [dist_smul_eq_dist]
  nth_rw 2 [dist_symm]
  rw [dist_smul_eq_dist, dist_symm]

end BruhatTits
