import Mathlib.Data.Array.Defs
import Mathlib.Data.List.Range
import Mathlib.Tactic

/-!
# Krasikov--Lagarias finite certificates

This module defines the exact finite `L^NT_k(lambda)` certificate surface used
for predecessor-count lower bounds.  Real coefficients are replaced by smaller
dyadic coefficients.  Their validity is expressed by integer power inequalities.

This file proves only finite-certificate soundness.  It does not yet formalize
the analytic implication from the Krasikov--Lagarias difference inequalities to
an asymptotic bound for the predecessor-counting function.
-/

namespace Erdos1135
namespace KrasikovLagarias

/-- Dyadic lower numerators for the three coefficients in `L^NT_k(lambda)`. -/
structure DyadicCoefficients where
  scale : Nat
  lambdaNeg2 : Nat
  lambdaAlphaMinus2 : Nat
  lambdaAlphaMinus1 : Nat
  deriving DecidableEq, Repr

/-- Finite certificate data.  Weight index `i` represents residue `3*i+2`. -/
structure FiniteCertificate where
  k : Nat
  gammaNum : Nat
  gammaDen : Nat
  coefficients : DyadicCoefficients
  weights : Array Nat
  deriving DecidableEq, Repr

namespace FiniteCertificate

/-- Number of principal variables, one for each residue `2 mod 3` modulo `3^k`. -/
def principalCount (cert : FiniteCertificate) : Nat :=
  3 ^ (cert.k - 1)

/-- Number of auxiliary variables modulo `3^(k-1)`. -/
def auxiliaryCount (cert : FiniteCertificate) : Nat :=
  3 ^ (cert.k - 2)

def modulus (cert : FiniteCertificate) : Nat :=
  3 ^ cert.k

def auxiliaryModulus (cert : FiniteCertificate) : Nat :=
  3 ^ (cert.k - 1)

/-- Residue represented by a principal-variable index. -/
def residue (index : Nat) : Nat :=
  3 * index + 2

/-- Totalized access used by the executable checker. -/
def weight (cert : FiniteCertificate) (index : Nat) : Nat :=
  cert.weights.getD index 0

/-- Principal index of `4m mod 3^k`. -/
def fourIndex (cert : FiniteCertificate) (index : Nat) : Nat :=
  ((4 * residue index % cert.modulus) - 2) / 3

/-- Auxiliary index in the `m = 2 mod 9` row. -/
def l1AuxiliaryIndex (cert : FiniteCertificate) (index : Nat) : Nat :=
  ((((4 * residue index - 2) / 3) % cert.auxiliaryModulus) - 2) / 3

/-- Auxiliary index in the `m = 8 mod 9` row. -/
def l3AuxiliaryIndex (cert : FiniteCertificate) (index : Nat) : Nat :=
  ((((2 * residue index - 1) / 3) % cert.auxiliaryModulus) - 2) / 3

/-- Maximum admissible auxiliary variable: the minimum of its three lifts. -/
def auxiliaryWeight (cert : FiniteCertificate) (index : Nat) : Nat :=
  min (cert.weight index)
    (min (cert.weight (index + cert.auxiliaryCount))
      (cert.weight (index + 2 * cert.auxiliaryCount)))

inductive RowKind where
  | l1
  | l2
  | l3
  deriving DecidableEq, Repr

/-- Since `m = 3*i+2`, `i mod 3` selects `m mod 9`. -/
def rowKind (index : Nat) : RowKind :=
  if index % 3 = 0 then RowKind.l1
  else if index % 3 = 1 then RowKind.l2
  else RowKind.l3

/-- Integer numerator of the right side of the dyadic KL row. -/
def rowRhs (cert : FiniteCertificate) (index : Nat) : Nat :=
  let base := cert.coefficients.lambdaNeg2 * cert.weight (cert.fourIndex index)
  match rowKind index with
  | RowKind.l1 =>
      base + cert.coefficients.lambdaAlphaMinus2 *
        cert.auxiliaryWeight (cert.l1AuxiliaryIndex index)
  | RowKind.l2 => base
  | RowKind.l3 =>
      base + cert.coefficients.lambdaAlphaMinus1 *
        cert.auxiliaryWeight (cert.l3AuxiliaryIndex index)

/-- The three integer-power checks proving the dyadic coefficients are lower bounds. -/
def CoefficientBounds (cert : FiniteCertificate) : Prop :=
  cert.coefficients.lambdaNeg2 ^ cert.gammaDen * 2 ^ (2 * cert.gammaNum) ≤
      cert.coefficients.scale ^ cert.gammaDen ∧
    cert.coefficients.lambdaAlphaMinus2 ^ cert.gammaDen * 2 ^ (2 * cert.gammaNum) ≤
      cert.coefficients.scale ^ cert.gammaDen * 3 ^ cert.gammaNum ∧
    cert.coefficients.lambdaAlphaMinus1 ^ cert.gammaDen * 2 ^ cert.gammaNum ≤
      cert.coefficients.scale ^ cert.gammaDen * 3 ^ cert.gammaNum

instance coefficientBoundsDecidable (cert : FiniteCertificate) :
    Decidable cert.CoefficientBounds := by
  unfold CoefficientBounds
  infer_instance

/-- Positivity plus the stronger dyadic row inequality at one principal index. -/
def RowValid (cert : FiniteCertificate) (index : Nat) : Prop :=
  0 < cert.weight index ∧
    cert.coefficients.scale * cert.weight index ≤ cert.rowRhs index

instance rowValidDecidable (cert : FiniteCertificate) (index : Nat) :
    Decidable (cert.RowValid index) := by
  unfold RowValid
  infer_instance

/-- Metadata obligations independent of the individual residue rows. -/
def MetadataValid (cert : FiniteCertificate) : Prop :=
  2 ≤ cert.k ∧
    0 < cert.gammaNum ∧ cert.gammaNum < cert.gammaDen ∧
    0 < cert.coefficients.scale ∧
    cert.CoefficientBounds ∧
    cert.weights.size = cert.principalCount

instance metadataValidDecidable (cert : FiniteCertificate) :
    Decidable cert.MetadataValid := by
  unfold MetadataValid
  infer_instance

/-- Mathematical meaning of a successful finite certificate check. -/
def Valid (cert : FiniteCertificate) : Prop :=
  cert.MetadataValid ∧
    ∀ index, index < cert.principalCount → cert.RowValid index

/-- Executable finite checker. -/
def check (cert : FiniteCertificate) : Bool :=
  decide cert.MetadataValid &&
    (List.range cert.principalCount).all fun index => decide (cert.RowValid index)

theorem check_eq_true_iff (cert : FiniteCertificate) :
    cert.check = true ↔ cert.Valid := by
  simp [check, Valid, List.all_eq_true]

theorem valid_of_check {cert : FiniteCertificate} (hcheck : cert.check = true) :
    cert.Valid :=
  (cert.check_eq_true_iff).mp hcheck

theorem Valid.row {cert : FiniteCertificate} (hvalid : cert.Valid)
    {index : Nat} (hindex : index < cert.principalCount) :
    cert.coefficients.scale * cert.weight index ≤ cert.rowRhs index :=
  (hvalid.2 index hindex).2

theorem Valid.weight_pos {cert : FiniteCertificate} (hvalid : cert.Valid)
    {index : Nat} (hindex : index < cert.principalCount) :
    0 < cert.weight index :=
  (hvalid.2 index hindex).1

theorem auxiliaryWeight_le_first (cert : FiniteCertificate) (index : Nat) :
    cert.auxiliaryWeight index ≤ cert.weight index := by
  exact min_le_left _ _

theorem auxiliaryWeight_le_second (cert : FiniteCertificate) (index : Nat) :
    cert.auxiliaryWeight index ≤ cert.weight (index + cert.auxiliaryCount) := by
  exact le_trans (min_le_right _ _) (min_le_left _ _)

theorem auxiliaryWeight_le_third (cert : FiniteCertificate) (index : Nat) :
    cert.auxiliaryWeight index ≤ cert.weight (index + 2 * cert.auxiliaryCount) := by
  exact le_trans (min_le_right _ _) (min_le_right _ _)

end FiniteCertificate
end KrasikovLagarias
end Erdos1135
