/-
  LocalExpansion.lean - Fixed-Point Local Taylor Expansion
-/

import Semantics.DynamicCanal
import Semantics.LocalDerivative

namespace Semantics.LocalExpansion

open DynamicCanal
open Semantics.LocalDerivative

abbrev Scalar := Fix16

structure LocalExpansion where
  base : Scalar
  gradient : List Scalar
  hessian : List (List Scalar)

-- No deriving Repr/DecidableEq due to List (List Scalar) issues

def localExpansionInvariant (expansion : LocalExpansion) : Prop :=
  squareMatrixInvariant expansion.hessian = true ∧
  expansion.gradient.length = expansion.hessian.length

def fromLocalDerivative (base : Scalar) (derivative : LocalDerivative) : LocalExpansion :=
  { base := base
  , gradient := diagonalEntries derivative.jacobian  -- Use diagonal as gradient approximation
  , hessian := derivative.hessian }

def listGet? {α : Type} (list : List α) (n : Nat) : Option α :=
  match list, n with
  | [], _ => none
  | a :: _, 0 => some a
  | _ :: as, n+1 => listGet? as n

def evaluateLinear (expansion : LocalExpansion) (offset : List Scalar) : Scalar :=
  let linear := List.zipWith (fun g x => Fix16.mul g x) expansion.gradient offset
    |>.foldl (fun acc val => Fix16.add acc val) Fix16.zero
  Fix16.add expansion.base linear

def quadraticForm (hessian : List (List Scalar)) (offset : List Scalar) : Scalar :=
  let rowContribs :=
    List.zip (List.range hessian.length) hessian |>.map (fun (rowIndex, row) =>
      let xi := match listGet? offset rowIndex with | some value => value | none => Fix16.zero
      List.zip (List.range row.length) row |>.foldl (fun acc (columnIndex, hij) =>
        let xj := match listGet? offset columnIndex with | some value => value | none => Fix16.zero
        Fix16.add acc (Fix16.mul (Fix16.mul xi hij) xj)) Fix16.zero)
  rowContribs.foldl (fun acc val => Fix16.add acc val) Fix16.zero

def evaluateTaylor2 (expansion : LocalExpansion) (offset : List Scalar) : Scalar :=
  let linear := evaluateLinear expansion offset
  let quad := quadraticForm expansion.hessian offset
  Fix16.add linear (Fix16.mk (quad.raw.toNat / 2).toUInt32)  -- 0.5 * quad approx

end Semantics.LocalExpansion
