-- ArkLib/Data/FieldTheory/BinaryField/Tower/Prelude.lean

import Mathlib.FieldTheory.Finite.GaloisField

import ArkLib.Data.Fin.BigOperators

import ArkLib.Data.Nat.Bitwise

import Mathlib.LinearAlgebra.StdBasis

noncomputable section Preliminaries

open Polynomial

open AdjoinRoot

open Module

notation : 10 "GF(" term : 10 ")" => GaloisField term 1

structure TraceMapProperty (F : Type*) [Field F] (u : F) (k : ℕ) : Prop where
  element_trace : ∑ i ∈ Finset.range (2 ^ k), u ^ (2 ^ i) = 1
  inverse_trace : ∑ i ∈ Finset.range (2 ^ k), (u⁻¹) ^ (2 ^ i) = 1

-- Repository definitions used:
content: ...

-- Repository lemmas used:
content: theorem List.getElem_append_left{α : Type u_1} {l₁ l₂ : List α} {i : Nat} (hn : i < l₁.length) :\n(l₁ ++ l₂)[i] = l₁[i]

-- Local definitions used:
content: structure TraceMapProperty (F : Type*) [Field F] (u : F) (k : ℕ) : Prop where
  element_trace : ∑ i ∈ Finset.range (2 ^ k), u ^ (2 ^ i) = 1
  inverse_trace : ∑ i ∈ Finset.range (2 ^ k), (u⁻¹) ^ (2 ^ i) = 1

-- Local lemmas used:
content: theorem one_le_two_pow_n (n : ℕ) : 1 ≤ 2 ^ n

-- Target theorem and proof:
theorem rsum_eq_t1_square_aux
  {curBTField : Type*} [Field curBTField] -- curBTField ≃ 𝔽_{2^{2^k}}
  (u : curBTField) -- here u is already lifted to curBTField
  (k : ℕ)
  (x_pow_card : ∀ (x : curBTField), x ^ (2 ^ (2 ^ (k))) = x)
  (u_ne_zero : u ≠ 0)
  (trace_map_prop : TraceMapProperty curBTField u k):
   ∑ j ∈ Finset.Icc 1 (2 ^ (k)), u ^ (2 ^ 2 ^ (k) - 2 ^ j) = u
:= by

  have trace_map_icc_t1 : ∑ j ∈ Finset.Icc 0 (2^(k)-1), u ^ (2^j) = 1 := by
    rw [←Nat.range_succ_eq_Icc_zero (2^(k)-1), Nat.sub_add_cancel (h:=one_le_two_pow_n (k))]
    exact trace_map_prop.1
  have trace_map_icc_t1_inv : ∑ j ∈ Finset.Icc 0 (2^(k)-1), u⁻¹ ^ (2^j) = 1 := by
    rw [←Nat.range_succ_eq_Icc_zero (2^(k)-1), Nat.sub_add_cancel (h:=one_le_two_pow_n (k))]
    exact trace_map_prop.2

  calc
    ∑ j ∈ Finset.Icc 1 (2 ^ (k)), u ^ (2 ^ 2 ^ (k) - 2 ^ j)
      = ∑ j ∈ Finset.Icc 1 (2 ^ (k)), (u ^ (2 ^ 2 ^ (k)) * ((u) ^ 2 ^ j)⁻¹) := by
      apply Finset.sum_congr rfl (fun j hj => by
        simp [Finset.mem_Icc] at hj -- hj : 1 ≤ j ∧ j ≤ 2 ^ (k)
        have h_gte_0_pow : 2 ^ j ≤ 2 ^ 2 ^ (k) := by
          apply pow_le_pow_right₀ (by decide) (hj.2)
        rw [pow_sub₀ (a := u) (ha := u_ne_zero) (h := h_gte_0_pow)]
      )
    _ = ∑ j ∈ Finset.Icc 1 (2 ^ (k)), ((u) * ((u) ^ 2 ^ j)⁻¹) := by
      rw [x_pow_card (u)]
    _ = u * ∑ j ∈ Finset.Icc 1 (2 ^ (k)), ((u) ^ 2 ^ j)⁻¹ := by rw [Finset.mul_sum]
    _ = u * ∑ j ∈ Finset.Icc 1 (2 ^ (k)), (((u)⁻¹) ^ 2 ^ j) := by
      congr
      ext j
      rw [←inv_pow (a := u) (n := 2 ^ j)]
    _ = u * ∑ j ∈ Finset.Icc 0 (2 ^ (k) - 1), ((u⁻¹) ^ 2 ^ j) := by
      rw [mul_right_inj' (a := u) (ha := u_ne_zero)]
      apply Finset.sum_bij' (fun i _ => if i = 2^(k) then 0 else i)
        (fun i _ => if i = 0 then 2^(k) else i)
        -- hi : Maps to Icc 0 (2^(k))
      · intro i hi
        simp [Finset.mem_Icc] at hi ⊢
        by_cases h : i = 2^(k)
        · simp [h];
        · simp [h] -- ⊢ i = 0 → 2 ^ (k) = i
          intro i_eq
          have this_is_false : False := by
            have h1 := hi.left  -- h1 : 1 ≤ i
            rw [i_eq] at h1     -- h1 : 1 ≤ 0
            have ne_one_le_zero : ¬(1 ≤ 0) := Nat.not_le_of_gt (by decide)
            exact ne_one_le_zero h1
          exact False.elim this_is_false
      -- hj : Maps back
      · intro i hi
        simp [Finset.mem_Icc] at hi -- hi : i ≤ 2 ^ (k) - 1
        by_cases h : i = 0
        · simp [h];
        · simp [h];
          intro i_eq
          have this_is_false : False := by
            rw [i_eq] at hi
            conv at hi =>
              lhs
              rw [←add_zero (a:=2^(k))]
            -- conv at hi =>
            --   rhs
            have zero_lt_2_pow_k_plus_1 : 0 < 2^(k) := by
              norm_num
            have h_contra : ¬(2^(k) ≤ 2^(k) - 1) := by
              apply Nat.not_le_of_gt
              exact Nat.sub_lt zero_lt_2_pow_k_plus_1 (by norm_num)
            exact h_contra hi
          exact False.elim this_is_false
      -- hij : j (i a) = a
      · intro i hi -- hi : 1 ≤ i ∧ i ≤ 2 ^ (k)
        simp [Finset.mem_Icc] at hi
        by_cases h : i = 2^(k)
        · simp [h]; exact x_pow_card u
        · simp [h]
      -- hji : i (j b) = b
      · intro i hi
        simp [Finset.mem_Icc] at hi
        by_cases h : i = 0
        · simp [h]
        · simp only [Finset.mem_Icc, zero_le, true_and]; -- hi : 1 ≤ i ∧ i ≤ 2 ^ (k)
          -- h : ¬i = 0
          -- ⊢ (if i = 2 ^ (k) then 0 else i) ≤ 2 ^ (k) - 1
          split_ifs with h2
          · -- Case : i = 2 ^ (k)
            -- Goal : 0 ≤ 2 ^ (k) - 1
            exact Nat.zero_le _
          · -- Case : i ≠ 2 ^ (k)
            -- Goal : i ≤ 2 ^ (k) - 1
            have : i < 2 ^ (k) := by
              apply lt_of_le_of_ne hi.right h2
            exact Nat.le_pred_of_lt this
      -- h_sum : Values match
      · intro i hi
        simp [Finset.mem_Icc] at hi
        rw [Finset.mem_Icc]
        split_ifs with h2
        · -- hi : i ≤ 2 ^ (k) - 1, h2 : i = 0
          -- ⊢ 1 ≤ 2 ^ (k) ∧ 2 ^ (k) ≤ 2 ^ (k)
          exact ⟨one_le_two_pow_n (k), le_refl _⟩
        · -- Case : hi : i ≤ 2 ^ (k) - 1, h2 : ¬i = 0
          -- ⊢ 1 ≤ i ∧ i ≤ 2 ^ (k)
          have one_le_i : 1 ≤ i := by
            apply Nat.succ_le_of_lt
            exact Nat.pos_of_ne_zero h2
          have tmp : i ≤ 2^(k):= by
            calc i ≤ (2^(k)-1).succ := Nat.le_succ_of_le hi
              _ = 2^(k) := by rw [Nat.succ_eq_add_one, Nat.sub_add_cancel
                (h:=one_le_two_pow_n (k))]
          exact ⟨one_le_i, tmp⟩
    _ = u := by rw [trace_map_icc_t1_inv, mul_one]