import Definitions.Def_AutomorphicForm_SigmaConjugacy
import Mathlib.Tactic.Group
import Mathlib.Logic.Function.Iterate
import Mathlib.Algebra.BigOperators.Group.List.Basic
import Mathlib.LinearAlgebra.Matrix.GeneralLinearGroup.Defs
import Mathlib.LinearAlgebra.Matrix.Charpoly.Basic
import Mathlib.Algebra.Polynomial.Lifts
import Mathlib.FieldTheory.Galois.Basic
import Mathlib.RingTheory.Norm.Transitivity
import Mathlib.LinearAlgebra.Matrix.NonsingularInverse
import Mathlib.Algebra.Group.Conj
import Mathlib.Tactic.FinCases
import Mathlib.Tactic.Ring
import Mathlib.LinearAlgebra.Matrix.Trace

set_option autoImplicit false

namespace LT.TwistedNorm

private theorem iterate_succ_apply'' {α : Type*} (f : α → α) (r : ℕ) (x : α) :
    f^[r + 1] x = f (f^[r] x) := by
  induction r generalizing x with
  | zero => rfl
  | succ r ih =>
      calc f^[r + 1 + 1] x = f^[r + 1] (f x) := rfl
        _ = f (f^[r] (f x)) := ih (f x)
        _ = f (f^[r + 1] x) := rfl

section PartialNorm

variable {G : Type*} [Group G]

def sigmaPartialNorm (σ : G →* G) (x : G) : ℕ → G
  | 0 => 1
  | r + 1 => x * σ (sigmaPartialNorm σ x r)

@[simp] theorem sigmaPartialNorm_zero (σ : G →* G) (x : G) :
    sigmaPartialNorm σ x 0 = 1 := rfl

theorem sigmaPartialNorm_succ' (σ : G →* G) (x : G) (r : ℕ) :
    sigmaPartialNorm σ x (r + 1) = x * σ (sigmaPartialNorm σ x r) := rfl

theorem sigmaPartialNorm_one (σ : G →* G) (x : G) :
    sigmaPartialNorm σ x 1 = x := by
  rw [sigmaPartialNorm_succ', sigmaPartialNorm_zero, map_one, mul_one]

attribute [local simp] sigmaPartialNorm_one

theorem sigmaPartialNorm_succ (σ : G →* G) (x : G) (r : ℕ) :
    sigmaPartialNorm σ x (r + 1) = sigmaPartialNorm σ x r * (⇑σ)^[r] x := by
  induction r with
  | zero => simp
  | succ r ih =>
      calc sigmaPartialNorm σ x (r + 1 + 1)
          = x * σ (sigmaPartialNorm σ x (r + 1)) := rfl
        _ = x * σ (sigmaPartialNorm σ x r * (⇑σ)^[r] x) := by rw [ih]
        _ = x * σ (sigmaPartialNorm σ x r) * σ ((⇑σ)^[r] x) := by rw [map_mul, mul_assoc]
        _ = sigmaPartialNorm σ x (r + 1) * (⇑σ)^[r + 1] x := by
              rw [← sigmaPartialNorm_succ', iterate_succ_apply'']

def sigmaNormPow (σ : G →* G) (ℓ : ℕ) (x : G) : G :=
  sigmaPartialNorm σ x ℓ

theorem sigmaNormPow_def (σ : G →* G) (ℓ : ℕ) (x : G) :
    sigmaNormPow σ ℓ x = sigmaPartialNorm σ x ℓ := rfl

theorem sigmaPartialNorm_sigmaConjElt (σ : G →* G) (h x : G) (r : ℕ) :
    sigmaPartialNorm σ (h⁻¹ * x * σ h) r
      = h⁻¹ * sigmaPartialNorm σ x r * (⇑σ)^[r] h := by
  induction r with
  | zero => simp
  | succ r ih =>
      calc sigmaPartialNorm σ (h⁻¹ * x * σ h) (r + 1)
          = (h⁻¹ * x * σ h) * σ (sigmaPartialNorm σ (h⁻¹ * x * σ h) r) := rfl
        _ = (h⁻¹ * x * σ h) * σ (h⁻¹ * sigmaPartialNorm σ x r * (⇑σ)^[r] h) := by rw [ih]
        _ = h⁻¹ * (x * σ (sigmaPartialNorm σ x r)) * (⇑σ)^[r + 1] h := by
              simp only [map_mul, map_inv, iterate_succ_apply'']
              group
        _ = h⁻¹ * sigmaPartialNorm σ x (r + 1) * (⇑σ)^[r + 1] h := by
              rw [← sigmaPartialNorm_succ']

theorem sigmaNormPow_sigmaConjElt (σ : G →* G) (ℓ : ℕ) {h : G}
    (hσh : (⇑σ)^[ℓ] h = h) (x : G) :
    sigmaNormPow σ ℓ (h⁻¹ * x * σ h) = h⁻¹ * sigmaNormPow σ ℓ x * h := by
  rw [sigmaNormPow_def, sigmaNormPow_def, sigmaPartialNorm_sigmaConjElt, hσh]

theorem map_sigmaNormPow (σ : G →* G) (ℓ : ℕ) {x : G} (hσx : (⇑σ)^[ℓ] x = x) :
    σ (sigmaNormPow σ ℓ x) = x⁻¹ * sigmaNormPow σ ℓ x * x := by
  have hkey : x * σ (sigmaPartialNorm σ x ℓ) = sigmaPartialNorm σ x ℓ * x := by
    have h2 := sigmaPartialNorm_succ σ x ℓ
    rw [hσx] at h2
    rw [← sigmaPartialNorm_succ' σ x ℓ, h2]
  rw [sigmaNormPow_def]
  calc σ (sigmaPartialNorm σ x ℓ)
      = x⁻¹ * (x * σ (sigmaPartialNorm σ x ℓ)) := by group
    _ = x⁻¹ * (sigmaPartialNorm σ x ℓ * x) := by rw [hkey]
    _ = x⁻¹ * sigmaPartialNorm σ x ℓ * x := by group

theorem iterate_map_sigmaNormPow (σ : G →* G) (ℓ : ℕ) {x : G}
    (hσx : (⇑σ)^[ℓ] x = x) (r : ℕ) :
    (⇑σ)^[r] (sigmaNormPow σ ℓ x)
      = (sigmaPartialNorm σ x r)⁻¹ * sigmaNormPow σ ℓ x * sigmaPartialNorm σ x r := by
  induction r with
  | zero => simp
  | succ r ih =>
      calc (⇑σ)^[r + 1] (sigmaNormPow σ ℓ x)
          = σ ((⇑σ)^[r] (sigmaNormPow σ ℓ x)) := iterate_succ_apply'' _ _ _
        _ = σ ((sigmaPartialNorm σ x r)⁻¹ * sigmaNormPow σ ℓ x * sigmaPartialNorm σ x r) := by
              rw [ih]
        _ = (σ (sigmaPartialNorm σ x r))⁻¹ * σ (sigmaNormPow σ ℓ x)
              * σ (sigmaPartialNorm σ x r) := by
              simp only [map_mul, map_inv]
        _ = (σ (sigmaPartialNorm σ x r))⁻¹ * (x⁻¹ * sigmaNormPow σ ℓ x * x)
              * σ (sigmaPartialNorm σ x r) := by rw [map_sigmaNormPow σ ℓ hσx]
        _ = (x * σ (sigmaPartialNorm σ x r))⁻¹ * sigmaNormPow σ ℓ x
              * (x * σ (sigmaPartialNorm σ x r)) := by group
        _ = (sigmaPartialNorm σ x (r + 1))⁻¹ * sigmaNormPow σ ℓ x
              * sigmaPartialNorm σ x (r + 1) := by rw [← sigmaPartialNorm_succ']

end PartialNorm

section Transport

variable {G H : Type*} [Group G] [Group H]

theorem hom_sigmaPartialNorm (σ : G →* G) (τ : H →* H) (f : G →* H)
    (hf : ∀ x, f (σ x) = τ (f x)) (x : G) (r : ℕ) :
    f (sigmaPartialNorm σ x r) = sigmaPartialNorm τ (f x) r := by
  induction r with
  | zero => simp
  | succ r ih => rw [sigmaPartialNorm_succ', sigmaPartialNorm_succ', map_mul, hf, ih]

theorem hom_sigmaNormPow (σ : G →* G) (τ : H →* H) (f : G →* H)
    (hf : ∀ x, f (σ x) = τ (f x)) (ℓ : ℕ) (x : G) :
    f (sigmaNormPow σ ℓ x) = sigmaNormPow τ ℓ (f x) :=
  hom_sigmaPartialNorm σ τ f hf x ℓ

theorem sigmaPartialNorm_id (x : G) (r : ℕ) :
    sigmaPartialNorm (MonoidHom.id G) x r = x ^ r := by
  induction r with
  | zero => simp
  | succ r ih => rw [sigmaPartialNorm_succ', MonoidHom.id_apply, ih, pow_succ']

theorem sigmaPartialNorm_eq_prod_map_range (σ : G →* G) (x : G) (r : ℕ) :
    sigmaPartialNorm σ x r = ((List.range r).map fun i => (⇑σ)^[i] x).prod := by
  induction r with
  | zero => simp
  | succ r ih =>
      rw [sigmaPartialNorm_succ, ih, List.range_succ, List.map_append, List.prod_append,
        List.map_singleton, List.prod_singleton]

theorem sigmaPartialNorm_of_map_eq (σ : G →* G) {x : G} (hx : σ x = x) (r : ℕ) :
    sigmaPartialNorm σ x r = x ^ r := by
  induction r with
  | zero => simp
  | succ r ih =>
      have hiter : (⇑σ)^[r] x = x := Function.iterate_fixed hx r
      rw [sigmaPartialNorm_succ, ih, hiter, pow_succ]

end Transport

section CommTransport

variable {G : Type*} [CommGroup G]

theorem sigmaPartialNorm_eq_prod_range (σ : G →* G) (x : G) (r : ℕ) :
    sigmaPartialNorm σ x r = ∏ i ∈ Finset.range r, (⇑σ)^[i] x := by
  induction r with
  | zero => simp
  | succ r ih => rw [sigmaPartialNorm_succ, ih, Finset.prod_range_succ]

end CommTransport

section GLn

variable {n : Type*} [Fintype n] [DecidableEq n]
variable {L : Type*} [CommRing L]

open Matrix

theorem iterate_generalLinearGroup_map_apply (σ : L →+* L) (r : ℕ) (δ : GL n L) (i j : n) :
    ((Matrix.GeneralLinearGroup.map (n := n) σ)^[r] δ) i j = (⇑σ)^[r] (δ i j) := by
  induction r generalizing δ with
  | zero => rfl
  | succ r ih =>
      rw [Function.iterate_succ_apply, Function.iterate_succ_apply, ih,
        Matrix.GeneralLinearGroup.map_apply]

theorem iterate_generalLinearGroup_map_eq_self {σ : L →+* L} {ℓ : ℕ}
    (hσ : ∀ a : L, (⇑σ)^[ℓ] a = a) (δ : GL n L) :
    (Matrix.GeneralLinearGroup.map (n := n) σ)^[ℓ] δ = δ := by
  ext i j
  rw [iterate_generalLinearGroup_map_apply, hσ]

theorem det_sigmaNormPow (σ : L →+* L) (ℓ : ℕ) (δ : GL n L) :
    Matrix.GeneralLinearGroup.det (sigmaNormPow (Matrix.GeneralLinearGroup.map σ) ℓ δ)
      = sigmaNormPow (Units.map (σ : L →* L)) ℓ (Matrix.GeneralLinearGroup.det δ) :=
  hom_sigmaNormPow _ _ Matrix.GeneralLinearGroup.det
    (fun x => Matrix.GeneralLinearGroup.map_det σ x) ℓ δ

theorem sigmaNormPow_sigmaConj_generalLinearGroup {σ : L →+* L} {ℓ : ℕ}
    (hσ : ∀ a : L, (⇑σ)^[ℓ] a = a) (h δ : GL n L) :
    sigmaNormPow (Matrix.GeneralLinearGroup.map σ) ℓ
        (h⁻¹ * δ * Matrix.GeneralLinearGroup.map σ h)
      = h⁻¹ * sigmaNormPow (Matrix.GeneralLinearGroup.map σ) ℓ δ * h :=
  sigmaNormPow_sigmaConjElt _ ℓ (iterate_generalLinearGroup_map_eq_self hσ h) δ

theorem map_sigmaNormPow_generalLinearGroup {σ : L →+* L} {ℓ : ℕ}
    (hσ : ∀ a : L, (⇑σ)^[ℓ] a = a) (δ : GL n L) :
    Matrix.GeneralLinearGroup.map σ (sigmaNormPow (Matrix.GeneralLinearGroup.map σ) ℓ δ)
      = δ⁻¹ * sigmaNormPow (Matrix.GeneralLinearGroup.map σ) ℓ δ * δ :=
  map_sigmaNormPow _ ℓ (iterate_generalLinearGroup_map_eq_self hσ δ)

theorem coe_generalLinearGroup_map (σ : L →+* L) (δ : GL n L) :
    ((Matrix.GeneralLinearGroup.map σ δ : GL n L) : Matrix n n L)
      = (δ : Matrix n n L).map σ := by
  ext i j
  rfl

theorem charpoly_sigmaNormPow_map {σ : L →+* L} {ℓ : ℕ}
    (hσ : ∀ a : L, (⇑σ)^[ℓ] a = a) (δ : GL n L) :
    (Matrix.charpoly
        ((sigmaNormPow (Matrix.GeneralLinearGroup.map σ) ℓ δ : GL n L) : Matrix n n L)).map σ
      = Matrix.charpoly
        ((sigmaNormPow (Matrix.GeneralLinearGroup.map σ) ℓ δ : GL n L) : Matrix n n L) := by
  rw [← Matrix.charpoly_map, ← coe_generalLinearGroup_map,
    map_sigmaNormPow_generalLinearGroup hσ δ, Units.val_mul, Units.val_mul,
    Matrix.coe_units_inv]
  exact Matrix.charpoly_units_conj' δ _

end GLn

section Galois

variable {F L : Type*} [Field F] [Field L] [Algebra F L] [FiniteDimensional F L] [IsGalois F L]

omit [FiniteDimensional F L] [IsGalois F L] in

theorem forall_algEquiv_apply_eq_of_generator {σ : L ≃ₐ[F] L}
    (hgen : ∀ τ : L ≃ₐ[F] L, τ ∈ Subgroup.zpowers σ) {a : L} (ha : σ a = a)
    (τ : L ≃ₐ[F] L) : τ a = a := by
  have hle : Subgroup.zpowers σ ≤ MulAction.stabilizer (L ≃ₐ[F] L) a :=
    (Subgroup.zpowers_le).mpr (MulAction.mem_stabilizer_iff.mpr ha)
  exact MulAction.mem_stabilizer_iff.mp (hle (hgen τ))

theorem mem_range_algebraMap_of_generator_apply_eq {σ : L ≃ₐ[F] L}
    (hgen : ∀ τ : L ≃ₐ[F] L, τ ∈ Subgroup.zpowers σ) {a : L} (ha : σ a = a) :
    a ∈ Set.range (algebraMap F L) :=
  (IsGalois.mem_range_algebraMap_iff_fixed a).mpr
    (forall_algEquiv_apply_eq_of_generator hgen ha)

theorem iterate_finrank_apply_of_generator {σ : L ≃ₐ[F] L}
    (hgen : ∀ τ : L ≃ₐ[F] L, τ ∈ Subgroup.zpowers σ) (a : L) :
    (⇑σ)^[Module.finrank F L] a = a := by
  have horder : orderOf σ = Module.finrank F L := by
    rw [orderOf_eq_card_of_forall_mem_zpowers hgen, IsGalois.card_aut_eq_finrank]
  rw [← AlgEquiv.coe_pow, ← horder, pow_orderOf_eq_one, AlgEquiv.one_apply]

variable {n : Type*} [Fintype n] [DecidableEq n]

theorem charpoly_sigmaNormPow_mem_lifts {σ : L ≃ₐ[F] L}
    (hgen : ∀ τ : L ≃ₐ[F] L, τ ∈ Subgroup.zpowers σ) (δ : GL n L) :
    Matrix.charpoly
        ((sigmaNormPow (Matrix.GeneralLinearGroup.map (σ : L →+* L)) (Module.finrank F L) δ :
            GL n L) : Matrix n n L)
      ∈ Polynomial.lifts (algebraMap F L) := by
  rw [Polynomial.lifts_iff_coeff_lifts]
  intro k
  apply mem_range_algebraMap_of_generator_apply_eq hgen
  have h := charpoly_sigmaNormPow_map (n := n) (σ := (σ : L →+* L))
    (ℓ := Module.finrank F L) (fun a => iterate_finrank_apply_of_generator hgen a) δ
  have hk := congrArg (fun p => Polynomial.coeff p k) h
  simp only [Polynomial.coeff_map] at hk
  exact hk

theorem prod_algEquiv_eq_prod_range_of_generator {M : Type*} [CommMonoid M] {σ : L ≃ₐ[F] L}
    (hgen : ∀ τ : L ≃ₐ[F] L, τ ∈ Subgroup.zpowers σ) (f : (L ≃ₐ[F] L) → M) :
    ∏ τ : L ≃ₐ[F] L, f τ = ∏ i ∈ Finset.range (Module.finrank F L), f (σ ^ i) := by
  classical
  have horder : orderOf σ = Module.finrank F L := by
    rw [orderOf_eq_card_of_forall_mem_zpowers hgen, IsGalois.card_aut_eq_finrank]
  have hinj : Function.Injective fun i : Fin (orderOf σ) => σ ^ (i : ℕ) := by
    intro i j hij
    exact Fin.ext (pow_injOn_Iio_orderOf i.2 j.2 hij)
  have hbij : Function.Bijective fun i : Fin (orderOf σ) => σ ^ (i : ℕ) := by
    rw [Fintype.bijective_iff_injective_and_card]
    exact ⟨hinj, by rw [Fintype.card_fin, horder, ← Nat.card_eq_fintype_card, IsGalois.card_aut_eq_finrank]⟩
  rw [← Fintype.prod_bijective _ hbij (fun i => f (σ ^ (i : ℕ))) f (fun _ => rfl),
    ← horder, ← Fin.prod_univ_eq_prod_range]

theorem coe_sigmaNormPow_units_eq_algebraMap_norm {σ : L ≃ₐ[F] L}
    (hgen : ∀ τ : L ≃ₐ[F] L, τ ∈ Subgroup.zpowers σ) (z : Lˣ) :
    ((sigmaNormPow (Units.map ((σ : L →+* L) : L →* L)) (Module.finrank F L) z : Lˣ) : L)
      = algebraMap F L (Algebra.norm F (z : L)) := by
  rw [Algebra.norm_eq_prod_automorphisms, prod_algEquiv_eq_prod_range_of_generator hgen,
    sigmaNormPow_def, sigmaPartialNorm_eq_prod_range, Units.coe_prod]
  refine Finset.prod_congr rfl fun i _ => ?_
  have hiter : ∀ (r : ℕ) (w : Lˣ),
      (((Units.map ((σ : L →+* L) : L →* L))^[r] w : Lˣ) : L) = (⇑σ)^[r] (w : L) := by
    intro r
    induction r with
    | zero => intro w; rfl
    | succ r ih =>
        intro w
        rw [Function.iterate_succ_apply', Function.iterate_succ_apply', Units.coe_map,
          MonoidHom.coe_coe, ih]
        rfl
  rw [hiter, AlgEquiv.coe_pow]

end Galois

end LT.TwistedNorm

namespace LT.TwistedNorm.GL2

open Matrix

section Companion

variable {K : Type*} [Field K]

def traceDetCompanion (t d : K) : Matrix (Fin 2) (Fin 2) K := !![0, -d; 1, t]

@[simp] theorem traceDetCompanion_apply_00 (t d : K) : traceDetCompanion t d 0 0 = 0 := rfl
@[simp] theorem traceDetCompanion_apply_01 (t d : K) : traceDetCompanion t d 0 1 = -d := rfl
@[simp] theorem traceDetCompanion_apply_10 (t d : K) : traceDetCompanion t d 1 0 = 1 := rfl
@[simp] theorem traceDetCompanion_apply_11 (t d : K) : traceDetCompanion t d 1 1 = t := rfl

theorem det_traceDetCompanion (t d : K) : (traceDetCompanion t d).det = d := by
  simp [Matrix.det_fin_two]

theorem trace_traceDetCompanion (t d : K) : (traceDetCompanion t d).trace = t := by
  simp [Matrix.trace_fin_two]

theorem traceDetCompanion_map {K' : Type*} [Field K'] (f : K →+* K') (t d : K) :
    (traceDetCompanion t d).map f = traceDetCompanion (f t) (f d) := by
  ext i j
  fin_cases i <;> fin_cases j <;> simp [traceDetCompanion]

theorem eq_smul_one_of_entries (A : Matrix (Fin 2) (Fin 2) K)
    (h10 : A 1 0 = 0) (h01 : A 0 1 = 0) (hdiag : A 0 0 = A 1 1) :
    A = A 0 0 • (1 : Matrix (Fin 2) (Fin 2) K) := by
  ext i j
  fin_cases i <;> fin_cases j <;> simp [h10, h01, hdiag]

theorem entries_of_ne_smul_one (A : Matrix (Fin 2) (Fin 2) K)
    (hA : ∀ c : K, A ≠ c • (1 : Matrix (Fin 2) (Fin 2) K)) :
    A 1 0 ≠ 0 ∨ A 0 1 ≠ 0 ∨ A 0 0 ≠ A 1 1 := by
  by_contra h
  push Not at h
  exact hA (A 0 0) (eq_smul_one_of_entries A h.1 h.2.1 h.2.2)

theorem mul_basis10_eq (A : Matrix (Fin 2) (Fin 2) K) :
    A * !![1, A 0 0; 0, A 1 0] = !![1, A 0 0; 0, A 1 0] * traceDetCompanion A.trace A.det := by
  ext i j
  fin_cases i <;> fin_cases j <;>
    simp [Matrix.mul_apply, Fin.sum_univ_two, traceDetCompanion, Matrix.det_fin_two,
      Matrix.trace_fin_two] <;> ring

theorem mul_basis01_eq (A : Matrix (Fin 2) (Fin 2) K) :
    A * !![0, A 0 1; 1, A 1 1] = !![0, A 0 1; 1, A 1 1] * traceDetCompanion A.trace A.det := by
  ext i j
  fin_cases i <;> fin_cases j <;>
    simp [Matrix.mul_apply, Fin.sum_univ_two, traceDetCompanion, Matrix.det_fin_two,
      Matrix.trace_fin_two] <;> ring

theorem mul_basis11_eq (A : Matrix (Fin 2) (Fin 2) K) (h10 : A 1 0 = 0) (h01 : A 0 1 = 0) :
    A * !![1, A 0 0; 1, A 1 1] = !![1, A 0 0; 1, A 1 1] * traceDetCompanion A.trace A.det := by
  ext i j
  fin_cases i <;> fin_cases j <;>
    simp [Matrix.mul_apply, Fin.sum_univ_two, traceDetCompanion, Matrix.det_fin_two,
      Matrix.trace_fin_two, h10, h01] <;> ring

theorem inv_mul_mul_eq_of_mul_eq {A P C : Matrix (Fin 2) (Fin 2) K} (hP : P.det ≠ 0)
    (h : A * P = P * C) : P⁻¹ * A * P = C := by
  have hPu : IsUnit P.det := isUnit_iff_ne_zero.mpr hP
  rw [Matrix.mul_assoc, h, ← Matrix.mul_assoc, Matrix.nonsing_inv_mul _ hPu, Matrix.one_mul]

theorem exists_conj_eq_traceDetCompanion (A : Matrix (Fin 2) (Fin 2) K)
    (hA : ∀ c : K, A ≠ c • (1 : Matrix (Fin 2) (Fin 2) K)) :
    ∃ P : GL (Fin 2) K,
      (P : Matrix (Fin 2) (Fin 2) K)⁻¹ * A * P = traceDetCompanion A.trace A.det := by
  rcases entries_of_ne_smul_one A hA with h10 | h01 | hdiag
  · have hdet : Matrix.det !![1, A 0 0; 0, A 1 0] ≠ 0 := by
      simpa [Matrix.det_fin_two] using h10
    exact ⟨Matrix.GeneralLinearGroup.mkOfDetNeZero _ hdet,
      inv_mul_mul_eq_of_mul_eq hdet (mul_basis10_eq A)⟩
  · have hdet : Matrix.det !![0, A 0 1; 1, A 1 1] ≠ 0 := by
      simpa [Matrix.det_fin_two] using h01
    exact ⟨Matrix.GeneralLinearGroup.mkOfDetNeZero _ hdet,
      inv_mul_mul_eq_of_mul_eq hdet (mul_basis01_eq A)⟩
  · by_cases h10 : A 1 0 = 0
    · by_cases h01 : A 0 1 = 0
      · have hdet : Matrix.det !![1, A 0 0; 1, A 1 1] ≠ 0 := by
          simpa [Matrix.det_fin_two, sub_eq_zero] using (Ne.symm hdiag)
        exact ⟨Matrix.GeneralLinearGroup.mkOfDetNeZero _ hdet,
          inv_mul_mul_eq_of_mul_eq hdet (mul_basis11_eq A h10 h01)⟩
      · have hdet : Matrix.det !![0, A 0 1; 1, A 1 1] ≠ 0 := by
          simpa [Matrix.det_fin_two] using h01
        exact ⟨Matrix.GeneralLinearGroup.mkOfDetNeZero _ hdet,
          inv_mul_mul_eq_of_mul_eq hdet (mul_basis01_eq A)⟩
    · have hdet : Matrix.det !![1, A 0 0; 0, A 1 0] ≠ 0 := by
        simpa [Matrix.det_fin_two] using h10
      exact ⟨Matrix.GeneralLinearGroup.mkOfDetNeZero _ hdet,
        inv_mul_mul_eq_of_mul_eq hdet (mul_basis10_eq A)⟩

theorem exists_conj_eq_traceDetCompanion_units (A : GL (Fin 2) K)
    (hA : ∀ c : K, (A : Matrix (Fin 2) (Fin 2) K) ≠ c • (1 : Matrix (Fin 2) (Fin 2) K)) :
    ∃ P : GL (Fin 2) K, ((P⁻¹ * A * P : GL (Fin 2) K) : Matrix (Fin 2) (Fin 2) K)
      = traceDetCompanion (A : Matrix (Fin 2) (Fin 2) K).trace
          (A : Matrix (Fin 2) (Fin 2) K).det := by
  obtain ⟨P, hP⟩ := exists_conj_eq_traceDetCompanion (A : Matrix (Fin 2) (Fin 2) K) hA
  refine ⟨P, ?_⟩
  rw [Units.val_mul, Units.val_mul, Matrix.coe_units_inv]
  exact hP

theorem exists_conj_of_trace_eq_det_eq (A A' : GL (Fin 2) K)
    (hA : ∀ c : K, (A : Matrix (Fin 2) (Fin 2) K) ≠ c • (1 : Matrix (Fin 2) (Fin 2) K))
    (hA' : ∀ c : K, (A' : Matrix (Fin 2) (Fin 2) K) ≠ c • (1 : Matrix (Fin 2) (Fin 2) K))
    (htr : (A : Matrix (Fin 2) (Fin 2) K).trace = (A' : Matrix (Fin 2) (Fin 2) K).trace)
    (hdet : (A : Matrix (Fin 2) (Fin 2) K).det = (A' : Matrix (Fin 2) (Fin 2) K).det) :
    ∃ Q : GL (Fin 2) K, A' = Q⁻¹ * A * Q := by
  obtain ⟨P, hP⟩ := exists_conj_eq_traceDetCompanion_units A hA
  obtain ⟨P', hP'⟩ := exists_conj_eq_traceDetCompanion_units A' hA'
  have hPP : P⁻¹ * A * P = P'⁻¹ * A' * P' := by
    apply Units.ext
    rw [hP, hP', htr, hdet]
  refine ⟨P * P'⁻¹, ?_⟩
  calc A' = P' * (P'⁻¹ * A' * P') * P'⁻¹ := by group
    _ = P' * (P⁻¹ * A * P) * P'⁻¹ := by rw [hPP]
    _ = (P * P'⁻¹)⁻¹ * A * (P * P'⁻¹) := by group

end Companion

section Descent

variable {F L : Type*} [Field F] [Field L] [Algebra F L]

theorem generalLinearGroup_map_smul_one_coe (c : F) (γ : GL (Fin 2) F)
    (hγ : (γ : Matrix (Fin 2) (Fin 2) F) = c • (1 : Matrix (Fin 2) (Fin 2) F)) :
    ((Matrix.GeneralLinearGroup.map (algebraMap F L) γ : GL (Fin 2) L) :
        Matrix (Fin 2) (Fin 2) L)
      = algebraMap F L c • (1 : Matrix (Fin 2) (Fin 2) L) := by
  ext i j
  rw [Matrix.GeneralLinearGroup.map_apply, hγ]
  fin_cases i <;> fin_cases j <;> simp

theorem conj_eq_self_of_coe_eq_smul_one {K : Type*} [Field K] (A P : GL (Fin 2) K) (c : K)
    (hA : (A : Matrix (Fin 2) (Fin 2) K) = c • (1 : Matrix (Fin 2) (Fin 2) K)) :
    P⁻¹ * A * P = A := by
  apply Units.ext
  rw [Units.val_mul, Units.val_mul, hA, Matrix.mul_smul, Matrix.mul_one, Matrix.smul_mul,
    Matrix.coe_units_inv, Matrix.nonsing_inv_mul _ (Matrix.isUnits_det_units P)]

theorem exists_map_eq_conj_of_trace_det_mem (A : GL (Fin 2) L)
    (htr : (A : Matrix (Fin 2) (Fin 2) L).trace ∈ Set.range (algebraMap F L))
    (hdet : (A : Matrix (Fin 2) (Fin 2) L).det ∈ Set.range (algebraMap F L))
    (hsc : ∀ c : L, (A : Matrix (Fin 2) (Fin 2) L) = c • (1 : Matrix (Fin 2) (Fin 2) L) →
      c ∈ Set.range (algebraMap F L)) :
    ∃ γ : GL (Fin 2) F, ∃ P : GL (Fin 2) L,
      P⁻¹ * A * P = Matrix.GeneralLinearGroup.map (algebraMap F L) γ := by
  by_cases hscalar : ∃ c : L, (A : Matrix (Fin 2) (Fin 2) L) = c • (1 : Matrix (Fin 2) (Fin 2) L)
  ·
    obtain ⟨c, hc⟩ := hscalar
    obtain ⟨t, rfl⟩ := hsc c hc
    have ht0 : t ≠ 0 := by
      rintro rfl
      have hA := Matrix.isUnits_det_units A
      rw [hc, map_zero, zero_smul] at hA
      simp [Matrix.det_fin_two] at hA
    have ht : (t • (1 : Matrix (Fin 2) (Fin 2) F)).det ≠ 0 := by
      simp [ht0]
    refine ⟨Matrix.GeneralLinearGroup.mkOfDetNeZero _ ht, 1, ?_⟩
    apply Units.ext
    rw [inv_one, one_mul, mul_one, generalLinearGroup_map_smul_one_coe t _ rfl, hc]
  · push Not at hscalar
    obtain ⟨t, htt⟩ := htr
    obtain ⟨d, hdd⟩ := hdet
    obtain ⟨P, hP⟩ := exists_conj_eq_traceDetCompanion_units A hscalar
    have hd : (traceDetCompanion t d).det ≠ 0 := by
      rw [det_traceDetCompanion]
      intro h0
      have hA := Matrix.isUnits_det_units A
      rw [← hdd, h0, map_zero] at hA
      exact not_isUnit_zero hA
    refine ⟨Matrix.GeneralLinearGroup.mkOfDetNeZero _ hd, P, ?_⟩
    apply Units.ext
    rw [hP, ← htt, ← hdd, ← traceDetCompanion_map]
    rfl

theorem exists_conj_of_map_conj_map (γ γ' : GL (Fin 2) F) (P : GL (Fin 2) L)
    (h : Matrix.GeneralLinearGroup.map (algebraMap F L) γ'
      = P⁻¹ * Matrix.GeneralLinearGroup.map (algebraMap F L) γ * P) :
    ∃ Q : GL (Fin 2) F, γ' = Q⁻¹ * γ * Q := by
  have hinj : Function.Injective
      (Matrix.GeneralLinearGroup.map (n := Fin 2) (algebraMap F L)) := by
    intro x y hxy
    apply Matrix.GeneralLinearGroup.ext
    intro i j
    have hij := congrArg (fun g : GL (Fin 2) L => (g : Matrix (Fin 2) (Fin 2) L) i j) hxy
    simpa only [Matrix.GeneralLinearGroup.map_apply] using
      (algebraMap F L).injective hij
  by_cases hscalar : ∃ c : F, (γ : Matrix (Fin 2) (Fin 2) F) = c • (1 : Matrix (Fin 2) (Fin 2) F)
  ·
    obtain ⟨c, hc⟩ := hscalar
    refine ⟨1, ?_⟩
    rw [inv_one, one_mul, mul_one]
    apply hinj
    rw [h, conj_eq_self_of_coe_eq_smul_one _ P (algebraMap F L c)
      (generalLinearGroup_map_smul_one_coe c γ hc)]
  · push Not at hscalar

    have hscalar' : ∀ c : F,
        (γ' : Matrix (Fin 2) (Fin 2) F) ≠ c • (1 : Matrix (Fin 2) (Fin 2) F) := by
      intro c hc
      have hcen := conj_eq_self_of_coe_eq_smul_one _ P⁻¹ (algebraMap F L c)
        (generalLinearGroup_map_smul_one_coe c γ' hc)
      have hγ : Matrix.GeneralLinearGroup.map (algebraMap F L) γ
          = Matrix.GeneralLinearGroup.map (algebraMap F L) γ' := by
        rw [← hcen, h]; group
      exact hscalar c (by rw [hinj hγ, hc])

    have hmat : ((Matrix.GeneralLinearGroup.map (algebraMap F L) γ' : GL (Fin 2) L) :
          Matrix (Fin 2) (Fin 2) L)
        = ((P⁻¹ : GL (Fin 2) L) : Matrix (Fin 2) (Fin 2) L)
          * ((Matrix.GeneralLinearGroup.map (algebraMap F L) γ : GL (Fin 2) L) :
              Matrix (Fin 2) (Fin 2) L) * P := by
      rw [h, Units.val_mul, Units.val_mul]
    have hmapγ : ((Matrix.GeneralLinearGroup.map (algebraMap F L) γ : GL (Fin 2) L) :
          Matrix (Fin 2) (Fin 2) L) = (γ : Matrix (Fin 2) (Fin 2) F).map (algebraMap F L) := by
      ext i j; rfl
    have hmapγ' : ((Matrix.GeneralLinearGroup.map (algebraMap F L) γ' : GL (Fin 2) L) :
          Matrix (Fin 2) (Fin 2) L) = (γ' : Matrix (Fin 2) (Fin 2) F).map (algebraMap F L) := by
      ext i j; rfl
    have htr : (γ : Matrix (Fin 2) (Fin 2) F).trace = (γ' : Matrix (Fin 2) (Fin 2) F).trace := by
      apply (algebraMap F L).injective
      have := congrArg Matrix.trace hmat
      rw [Matrix.trace_units_conj', hmapγ, hmapγ'] at this
      rw [Matrix.trace_fin_two, Matrix.trace_fin_two, map_add, map_add]
      simpa [Matrix.trace_fin_two] using this.symm
    have hdet : (γ : Matrix (Fin 2) (Fin 2) F).det = (γ' : Matrix (Fin 2) (Fin 2) F).det := by
      apply (algebraMap F L).injective
      have := congrArg Matrix.det hmat
      rw [Matrix.det_units_conj', hmapγ, hmapγ', ← RingHom.mapMatrix_apply,
        ← RingHom.mapMatrix_apply, ← RingHom.map_det, ← RingHom.map_det] at this
      exact this.symm
    exact exists_conj_of_trace_eq_det_eq γ γ' hscalar hscalar' htr hdet

variable [FiniteDimensional F L] [IsGalois F L]

theorem exists_map_eq_conj_of_map_eq_conj {σ : L ≃ₐ[F] L}
    (hgen : ∀ τ : L ≃ₐ[F] L, τ ∈ Subgroup.zpowers σ) (A B : GL (Fin 2) L)
    (hA : Matrix.GeneralLinearGroup.map (σ : L →+* L) A = B⁻¹ * A * B) :
    ∃ γ : GL (Fin 2) F, ∃ P : GL (Fin 2) L,
      P⁻¹ * A * P = Matrix.GeneralLinearGroup.map (algebraMap F L) γ := by

  have hfix : ∀ a : L, σ a = a → a ∈ Set.range (algebraMap F L) := by
    intro a ha
    refine (IsGalois.mem_range_algebraMap_iff_fixed a).mpr fun τ => ?_
    have hle : Subgroup.zpowers σ ≤ MulAction.stabilizer (L ≃ₐ[F] L) a :=
      (Subgroup.zpowers_le).mpr (MulAction.mem_stabilizer_iff.mpr ha)
    exact MulAction.mem_stabilizer_iff.mp (hle (hgen τ))
  have hmat : ((A : Matrix (Fin 2) (Fin 2) L).map σ)
      = ((B⁻¹ : GL (Fin 2) L) : Matrix (Fin 2) (Fin 2) L) * A * B := by
    have := congrArg (fun g : GL (Fin 2) L => (g : Matrix (Fin 2) (Fin 2) L)) hA
    simp only [Units.val_mul] at this
    rw [← this]
    ext i j; rfl
  apply exists_map_eq_conj_of_trace_det_mem
  · apply hfix
    have := congrArg Matrix.trace hmat
    rw [Matrix.trace_units_conj'] at this
    calc σ (A : Matrix (Fin 2) (Fin 2) L).trace
        = ((A : Matrix (Fin 2) (Fin 2) L).map σ).trace := by simp [Matrix.trace_fin_two]
      _ = (A : Matrix (Fin 2) (Fin 2) L).trace := this
  · apply hfix
    have := congrArg Matrix.det hmat
    rw [Matrix.det_units_conj'] at this
    calc σ (A : Matrix (Fin 2) (Fin 2) L).det
        = ((A : Matrix (Fin 2) (Fin 2) L).map σ).det := by simp [Matrix.det_fin_two]
      _ = (A : Matrix (Fin 2) (Fin 2) L).det := this
  · intro c hc
    apply hfix

    have h00 := congrArg (fun M : Matrix (Fin 2) (Fin 2) L => M 0 0) hmat
    have hcen : ((B⁻¹ : GL (Fin 2) L) : Matrix (Fin 2) (Fin 2) L) * A * B = A := by
      rw [hc, Matrix.mul_smul, Matrix.mul_one, Matrix.smul_mul, Matrix.coe_units_inv,
        Matrix.nonsing_inv_mul _ (Matrix.isUnits_det_units B)]
    rw [hcen, hc] at h00
    simpa using h00

end Descent

end LT.TwistedNorm.GL2

namespace LT.TwistedNorm

open Matrix

section NormCorrespondence

variable {F L : Type*} [Field F] [Field L] [Algebra F L] [FiniteDimensional F L] [IsGalois F L]

theorem exists_map_eq_conj_sigmaNormPow {σ : L ≃ₐ[F] L}
    (hgen : ∀ τ : L ≃ₐ[F] L, τ ∈ Subgroup.zpowers σ) (δ : GL (Fin 2) L) :
    ∃ γ : GL (Fin 2) F, ∃ P : GL (Fin 2) L,
      P⁻¹ * sigmaNormPow (Matrix.GeneralLinearGroup.map (σ : L →+* L)) (Module.finrank F L) δ * P
        = Matrix.GeneralLinearGroup.map (algebraMap F L) γ :=
  GL2.exists_map_eq_conj_of_map_eq_conj hgen _ δ
    (map_sigmaNormPow_generalLinearGroup (n := Fin 2) (σ := (σ : L →+* L))
      (fun a => iterate_finrank_apply_of_generator hgen a) δ)

omit [FiniteDimensional F L] [IsGalois F L] in

theorem exists_conj_of_conj_eq_map_of_conj_eq_map {N : GL (Fin 2) L} {γ γ' : GL (Fin 2) F}
    {P P' : GL (Fin 2) L}
    (hγ : P⁻¹ * N * P = Matrix.GeneralLinearGroup.map (algebraMap F L) γ)
    (hγ' : P'⁻¹ * N * P' = Matrix.GeneralLinearGroup.map (algebraMap F L) γ') :
    ∃ Q : GL (Fin 2) F, γ' = Q⁻¹ * γ * Q := by
  refine GL2.exists_conj_of_map_conj_map γ γ' (P⁻¹ * P') ?_
  rw [← hγ, ← hγ']
  group

omit [FiniteDimensional F L] [IsGalois F L] in

theorem exists_conj_of_conj_sigmaNormPow_of_conj_sigmaNormPow {σ : L ≃ₐ[F] L} {ℓ : ℕ}
    {δ : GL (Fin 2) L} {γ γ' : GL (Fin 2) F} {P P' : GL (Fin 2) L}
    (hγ : P⁻¹ * sigmaNormPow (Matrix.GeneralLinearGroup.map (σ : L →+* L)) ℓ δ * P
      = Matrix.GeneralLinearGroup.map (algebraMap F L) γ)
    (hγ' : P'⁻¹ * sigmaNormPow (Matrix.GeneralLinearGroup.map (σ : L →+* L)) ℓ δ * P'
      = Matrix.GeneralLinearGroup.map (algebraMap F L) γ') :
    ∃ Q : GL (Fin 2) F, γ' = Q⁻¹ * γ * Q :=
  exists_conj_of_conj_eq_map_of_conj_eq_map hγ hγ'

theorem conj_sigmaNormPow_sigmaConj {σ : L ≃ₐ[F] L}
    (hgen : ∀ τ : L ≃ₐ[F] L, τ ∈ Subgroup.zpowers σ) {δ : GL (Fin 2) L} {γ : GL (Fin 2) F}
    {P : GL (Fin 2) L}
    (hγ : P⁻¹ * sigmaNormPow (Matrix.GeneralLinearGroup.map (σ : L →+* L)) (Module.finrank F L) δ
      * P = Matrix.GeneralLinearGroup.map (algebraMap F L) γ)
    (h : GL (Fin 2) L) :
    (h⁻¹ * P)⁻¹ * sigmaNormPow (Matrix.GeneralLinearGroup.map (σ : L →+* L)) (Module.finrank F L)
        (h⁻¹ * δ * Matrix.GeneralLinearGroup.map (σ : L →+* L) h) * (h⁻¹ * P)
      = Matrix.GeneralLinearGroup.map (algebraMap F L) γ := by
  rw [sigmaNormPow_sigmaConj_generalLinearGroup (n := Fin 2) (σ := (σ : L →+* L))
    (fun a => iterate_finrank_apply_of_generator hgen a) h δ, ← hγ]
  group

omit [FiniteDimensional F L] [IsGalois F L] in

theorem algebraMap_det_eq_det_of_conj_eq_map {N : GL (Fin 2) L} {γ : GL (Fin 2) F}
    {P : GL (Fin 2) L} (hγ : P⁻¹ * N * P = Matrix.GeneralLinearGroup.map (algebraMap F L) γ) :
    algebraMap F L (γ : Matrix (Fin 2) (Fin 2) F).det = (N : Matrix (Fin 2) (Fin 2) L).det := by
  have h := congrArg (fun g : GL (Fin 2) L => (g : Matrix (Fin 2) (Fin 2) L).det) hγ
  simp only [Units.val_mul, Matrix.det_units_conj'] at h
  rw [h, RingHom.map_det, RingHom.mapMatrix_apply]
  rfl

theorem det_eq_norm_det_of_conj_sigmaNormPow {σ : L ≃ₐ[F] L}
    (hgen : ∀ τ : L ≃ₐ[F] L, τ ∈ Subgroup.zpowers σ) {δ : GL (Fin 2) L} {γ : GL (Fin 2) F}
    {P : GL (Fin 2) L}
    (hγ : P⁻¹ * sigmaNormPow (Matrix.GeneralLinearGroup.map (σ : L →+* L)) (Module.finrank F L) δ
      * P = Matrix.GeneralLinearGroup.map (algebraMap F L) γ) :
    (γ : Matrix (Fin 2) (Fin 2) F).det = Algebra.norm F ((δ : Matrix (Fin 2) (Fin 2) L).det) := by
  apply (algebraMap F L).injective
  rw [algebraMap_det_eq_det_of_conj_eq_map hγ]
  have hdet := congrArg (fun u : Lˣ => (u : L))
    (det_sigmaNormPow (n := Fin 2) (σ : L →+* L) (Module.finrank F L) δ)
  simp only [Matrix.GeneralLinearGroup.val_det_apply] at hdet
  rw [hdet, coe_sigmaNormPow_units_eq_algebraMap_norm hgen]
  rfl

omit [FiniteDimensional F L] [IsGalois F L] in

theorem charpoly_map_eq_charpoly_of_conj_eq_map {N : GL (Fin 2) L} {γ : GL (Fin 2) F}
    {P : GL (Fin 2) L} (hγ : P⁻¹ * N * P = Matrix.GeneralLinearGroup.map (algebraMap F L) γ) :
    (γ : Matrix (Fin 2) (Fin 2) F).charpoly.map (algebraMap F L)
      = (N : Matrix (Fin 2) (Fin 2) L).charpoly := by
  have h := congrArg (fun g : GL (Fin 2) L => (g : Matrix (Fin 2) (Fin 2) L).charpoly) hγ
  simp only [Units.val_mul, Matrix.coe_units_inv, Matrix.charpoly_units_conj'] at h
  rw [h, ← Matrix.charpoly_map]
  rfl

end NormCorrespondence

end LT.TwistedNorm

namespace LT.TwistedNorm

section NormEquivVocab

variable {F L : Type*} [Field F] [Field L] [Algebra F L]

theorem isSigmaConj_iff_exists_eq_inv_mul_mul_map (σ : L →+* L) (δ₁ δ₂ : GL (Fin 2) L) :
    AutomorphicForm.IsSigmaConj σ δ₁ δ₂
      ↔ ∃ h : GL (Fin 2) L, δ₂ = h⁻¹ * δ₁ * Matrix.GeneralLinearGroup.map σ h := by
  constructor
  · rintro ⟨g, rfl⟩
    exact ⟨g⁻¹, by simp only [AutomorphicForm.sigmaConj, map_inv, inv_inv]⟩
  · rintro ⟨h, rfl⟩
    exact ⟨h⁻¹, by simp only [AutomorphicForm.sigmaConj, map_inv, inv_inv]⟩

def sigmaConjSetoid (σ : L ≃ₐ[F] L) : Setoid (GL (Fin 2) L) where
  r := AutomorphicForm.IsSigmaConj (σ : L →+* L)
  iseqv :=
    { refl := AutomorphicForm.IsSigmaConj.refl (σ : L →+* L)
      symm := by
        intro δ₁ δ₂ h
        obtain ⟨k, rfl⟩ := (isSigmaConj_iff_exists_eq_inv_mul_mul_map _ _ _).mp h
        exact (isSigmaConj_iff_exists_eq_inv_mul_mul_map _ _ _).mpr ⟨k⁻¹, by rw [map_inv]; group⟩
      trans := by
        intro δ₁ δ₂ δ₃ h h'
        obtain ⟨k, rfl⟩ := (isSigmaConj_iff_exists_eq_inv_mul_mul_map _ _ _).mp h
        obtain ⟨k', rfl⟩ := (isSigmaConj_iff_exists_eq_inv_mul_mul_map _ _ _).mp h'
        exact (isSigmaConj_iff_exists_eq_inv_mul_mul_map _ _ _).mpr ⟨k * k', by rw [map_mul]; group⟩ }

def SigmaConjClasses (σ : L ≃ₐ[F] L) : Type _ := Quotient (sigmaConjSetoid σ)

def SigmaConjClasses.mk (σ : L ≃ₐ[F] L) (δ : GL (Fin 2) L) : SigmaConjClasses σ :=
  Quotient.mk (sigmaConjSetoid σ) δ

theorem SigmaConjClasses.mk_surjective (σ : L ≃ₐ[F] L) :
    Function.Surjective (SigmaConjClasses.mk σ) :=
  Quotient.mk_surjective

theorem SigmaConjClasses.mk_eq_mk_iff {σ : L ≃ₐ[F] L} {δ₁ δ₂ : GL (Fin 2) L} :
    SigmaConjClasses.mk σ δ₁ = SigmaConjClasses.mk σ δ₂
      ↔ AutomorphicForm.IsSigmaConj (σ : L →+* L) δ₁ δ₂ :=
  Quotient.eq (r := sigmaConjSetoid σ)

theorem SigmaConjClasses.mk_eq_mk_iff_exists_eq_inv_mul_mul_map {σ : L ≃ₐ[F] L} {δ₁ δ₂ : GL (Fin 2) L} :
    SigmaConjClasses.mk σ δ₁ = SigmaConjClasses.mk σ δ₂
      ↔ ∃ h : GL (Fin 2) L, δ₂ = h⁻¹ * δ₁ * Matrix.GeneralLinearGroup.map (σ : L →+* L) h :=
  SigmaConjClasses.mk_eq_mk_iff.trans (isSigmaConj_iff_exists_eq_inv_mul_mul_map _ _ _)

def IsNormRep (σ : L ≃ₐ[F] L) (γ : GL (Fin 2) F) (δ : GL (Fin 2) L) : Prop :=
  ∃ P : GL (Fin 2) L,
    P⁻¹ * sigmaNormPow (Matrix.GeneralLinearGroup.map (σ : L →+* L)) (Module.finrank F L) δ * P
      = Matrix.GeneralLinearGroup.map (algebraMap F L) γ

end NormEquivVocab

section NormEquiv

variable {F L : Type*} [Field F] [Field L] [Algebra F L] [FiniteDimensional F L] [IsGalois F L]

noncomputable def normRep {σ : L ≃ₐ[F] L} (hgen : ∀ τ : L ≃ₐ[F] L, τ ∈ Subgroup.zpowers σ)
    (δ : GL (Fin 2) L) : GL (Fin 2) F :=
  Classical.choose (exists_map_eq_conj_sigmaNormPow hgen δ)

theorem isNormRep_normRep {σ : L ≃ₐ[F] L} (hgen : ∀ τ : L ≃ₐ[F] L, τ ∈ Subgroup.zpowers σ)
    (δ : GL (Fin 2) L) : IsNormRep σ (normRep hgen δ) δ :=
  Classical.choose_spec (exists_map_eq_conj_sigmaNormPow hgen δ)

theorem isConj_of_isNormRep_of_sigmaConj {σ : L ≃ₐ[F] L}
    (hgen : ∀ τ : L ≃ₐ[F] L, τ ∈ Subgroup.zpowers σ) {δ₁ δ₂ : GL (Fin 2) L}
    {γ₁ γ₂ : GL (Fin 2) F} (h₁ : IsNormRep σ γ₁ δ₁) (h₂ : IsNormRep σ γ₂ δ₂)
    (h : ∃ k : GL (Fin 2) L, δ₂ = k⁻¹ * δ₁ * Matrix.GeneralLinearGroup.map (σ : L →+* L) k) :
    IsConj γ₁ γ₂ := by
  obtain ⟨P₁, hP₁⟩ := h₁
  obtain ⟨P₂, hP₂⟩ := h₂
  obtain ⟨k, rfl⟩ := h
  have h₁' := conj_sigmaNormPow_sigmaConj hgen hP₁ k
  obtain ⟨Q, hQ⟩ := exists_conj_of_conj_sigmaNormPow_of_conj_sigmaNormPow h₁' hP₂
  exact isConj_iff.mpr ⟨Q⁻¹, by rw [hQ]; group⟩

noncomputable def normClassMap {σ : L ≃ₐ[F] L}
    (hgen : ∀ τ : L ≃ₐ[F] L, τ ∈ Subgroup.zpowers σ) :
    SigmaConjClasses σ → ConjClasses (GL (Fin 2) F) :=
  Quotient.lift (fun δ => ConjClasses.mk (normRep hgen δ)) fun δ₁ δ₂ h =>
    ConjClasses.mk_eq_mk_iff_isConj.mpr
      (isConj_of_isNormRep_of_sigmaConj hgen (isNormRep_normRep hgen δ₁)
        (isNormRep_normRep hgen δ₂) ((isSigmaConj_iff_exists_eq_inv_mul_mul_map _ _ _).mp h))

theorem normClassMap_mk {σ : L ≃ₐ[F] L} (hgen : ∀ τ : L ≃ₐ[F] L, τ ∈ Subgroup.zpowers σ)
    (δ : GL (Fin 2) L) :
    normClassMap hgen (SigmaConjClasses.mk σ δ) = ConjClasses.mk (normRep hgen δ) := rfl

theorem normClassMap_mk_eq_of_isNormRep {σ : L ≃ₐ[F] L}
    (hgen : ∀ τ : L ≃ₐ[F] L, τ ∈ Subgroup.zpowers σ) {δ : GL (Fin 2) L} {γ : GL (Fin 2) F}
    (h : IsNormRep σ γ δ) :
    normClassMap hgen (SigmaConjClasses.mk σ δ) = ConjClasses.mk γ :=
  ConjClasses.mk_eq_mk_iff_isConj.mpr
    (isConj_of_isNormRep_of_sigmaConj hgen (isNormRep_normRep hgen δ) h ⟨1, by simp⟩)

def IsNormClass {σ : L ≃ₐ[F] L} (hgen : ∀ τ : L ≃ₐ[F] L, τ ∈ Subgroup.zpowers σ)
    (c : ConjClasses (GL (Fin 2) F)) : Prop :=
  c ∈ Set.range (normClassMap hgen)

theorem isNormClass_mk_iff {σ : L ≃ₐ[F] L} (hgen : ∀ τ : L ≃ₐ[F] L, τ ∈ Subgroup.zpowers σ)
    (γ : GL (Fin 2) F) :
    IsNormClass hgen (ConjClasses.mk γ) ↔ ∃ δ : GL (Fin 2) L, IsNormRep σ γ δ := by
  constructor
  · rintro ⟨c, hc⟩
    obtain ⟨δ, rfl⟩ := SigmaConjClasses.mk_surjective σ c
    rw [normClassMap_mk, ConjClasses.mk_eq_mk_iff_isConj, isConj_iff] at hc
    obtain ⟨Q, hQ⟩ := hc
    obtain ⟨P, hP⟩ := isNormRep_normRep hgen δ
    refine ⟨δ, P * Matrix.GeneralLinearGroup.map (algebraMap F L) Q⁻¹, ?_⟩
    rw [← hQ, map_mul, map_mul, map_inv, ← hP]
    group
  · rintro ⟨δ, h⟩
    exact ⟨SigmaConjClasses.mk σ δ, normClassMap_mk_eq_of_isNormRep hgen h⟩

end NormEquiv

end LT.TwistedNorm

namespace LT.TwistedNorm

section Inhabitants

theorem sigmaPartialNorm_one_left {G : Type*} [Group G] (σ : G →* G) (r : ℕ) :
    sigmaPartialNorm σ 1 r = 1 := by
  induction r with
  | zero => rfl
  | succ r ih => rw [sigmaPartialNorm_succ', ih, map_one, mul_one]

theorem sigmaNormPow_one_left {G : Type*} [Group G] (σ : G →* G) (ℓ : ℕ) :
    sigmaNormPow σ ℓ 1 = 1 :=
  sigmaPartialNorm_one_left σ ℓ

variable {F L : Type*} [Field F] [Field L] [Algebra F L]

theorem isNormRep_one (σ : L ≃ₐ[F] L) : IsNormRep σ (1 : GL (Fin 2) F) (1 : GL (Fin 2) L) :=
  ⟨1, by rw [sigmaNormPow_one_left, map_one]; group⟩

theorem nonempty_sigmaConjClasses (σ : L ≃ₐ[F] L) : Nonempty (SigmaConjClasses σ) :=
  ⟨SigmaConjClasses.mk σ 1⟩

variable [FiniteDimensional F L] [IsGalois F L]

theorem isNormClass_mk_one {σ : L ≃ₐ[F] L} (hgen : ∀ τ : L ≃ₐ[F] L, τ ∈ Subgroup.zpowers σ) :
    IsNormClass hgen (ConjClasses.mk (1 : GL (Fin 2) F)) :=
  (isNormClass_mk_iff hgen 1).mpr ⟨1, isNormRep_one σ⟩

theorem nonempty_isNormClass {σ : L ≃ₐ[F] L} (hgen : ∀ τ : L ≃ₐ[F] L, τ ∈ Subgroup.zpowers σ) :
    Nonempty {c : ConjClasses (GL (Fin 2) F) // IsNormClass hgen c} :=
  ⟨⟨_, isNormClass_mk_one hgen⟩⟩

end Inhabitants

end LT.TwistedNorm
