import Erdos1135.KrasikovLagarias.EliminationCriticalPath

/-!
# Critical-history bounds for KL deletion

The semantic deletion argument does not require every generated subtree to be
bounded by its own root label.  It only requires that bound for a subtree lying
on a critical path, together with the bounds inherited from earlier labels on
that path.  This module isolates the local contradiction at a newly created
minimum: a dominated fresh leaf cannot attain that minimum.
-/

namespace Erdos1135
namespace KrasikovLagarias
namespace EliminationCriticalTree

open EliminationPolicy
open EliminationResidue

namespace Tree

/-- A critical subtree together with the bounds inherited from the earlier
labels in its canonical root-to-node state. -/
structure CriticalHistoryInvariant {k : Nat}
    (values : PrincipalIndex k → Real → Real) (y : Real)
    (state : State k) (tree : Tree k) : Prop where
  rootLabel_eq : tree.rootLabel = state.current
  critical : CriticalInvariant values y tree
  prior_bound : ∀ earlier ∈ state.prior,
    tree.eval values y ≤ values earlier.index (y + earlier.value)

namespace CriticalHistoryInvariant

/-- The critical subtree is bounded by the current state label. -/
theorem current_bound {k : Nat} {values : PrincipalIndex k → Real → Real}
    {y : Real} {state : State k} {tree : Tree k}
    (invariant : CriticalHistoryInvariant values y state tree) :
    tree.eval values y ≤
      values state.current.index (y + state.current.value) := by
  simpa [Tree.rootIndex, invariant.rootLabel_eq] using
    invariant.critical.root_bound

/-- Every label in the complete state history bounds the critical subtree. -/
theorem history_bound {k : Nat} {values : PrincipalIndex k → Real → Real}
    {y : Real} {state : State k} {tree : Tree k}
    (invariant : CriticalHistoryInvariant values y state tree)
    {earlier : Label k} (hearlier : earlier ∈ state.history) :
    tree.eval values y ≤ values earlier.index (y + earlier.value) := by
  change earlier ∈ state.current :: state.prior at hearlier
  rcases List.mem_cons.mp hearlier with heq | hprior
  · subst earlier
    exact invariant.current_bound
  · exact invariant.prior_bound earlier hprior

/-- Following a critical edge transfers the complete root-to-node history
bounds once the concrete child contribution is known not to exceed its parent. -/
theorem descend_of_eval_le {k : Nat}
    {values : PrincipalIndex k → Real → Real}
    {y : Real} {state : State k} {parent child : Tree k} {label : Label k}
    (invariant : CriticalHistoryInvariant values y state parent)
    (edge : CriticalChild values y child parent)
    (hroot : child.rootLabel = label)
    (hchildLe : child.eval values y ≤ parent.eval values y) :
    CriticalHistoryInvariant values y (state.descend label) child := by
  refine
    { rootLabel_eq := hroot
      critical := edge.invariant invariant.critical
      prior_bound := ?_ }
  intro earlier hearlier
  change earlier ∈ state.current :: state.prior at hearlier
  rcases List.mem_cons.mp hearlier with rfl | hearlier
  · exact hchildLe.trans invariant.current_bound
  · exact hchildLe.trans (invariant.prior_bound earlier hearlier)

/-- Global nonnegativity is a convenient sufficient condition for
`descend_of_eval_le`. -/
theorem descend {k : Nat} {values : PrincipalIndex k → Real → Real}
    {y : Real} {state : State k} {parent child : Tree k} {label : Label k}
    (invariant : CriticalHistoryInvariant values y state parent)
    (edge : CriticalChild values y child parent)
    (hroot : child.rootLabel = label)
    (hvalues : ∀ index time, 0 ≤ values index time) :
    CriticalHistoryInvariant values y (state.descend label) child :=
  invariant.descend_of_eval_le edge hroot (edge.eval_le hvalues)

end CriticalHistoryInvariant

/-- The exact local contradiction behind the KL deletion rule.

Assume a critical parent has just been split into a positive principal subtree
and an auxiliary minimum.  If a fresh auxiliary leaf is dominated by an
earlier equal-residue label in the state history, then that leaf cannot realize
the auxiliary minimum. -/
theorem dominated_terminal_ne_evalMinimum {k : Nat}
    {values : PrincipalIndex k → Real → Real} {y : Real}
    (hmono : ∀ index, Monotone (values index))
    {state : State k} {principal : Tree k} {auxiliary : NonemptyFamily k}
    {child : Label k}
    (invariant : CriticalHistoryInvariant values y state
      (Tree.principalMin state.current principal auxiliary))
    (hprincipal : 0 < principal.eval values y)
    (hdominated : Dominated state.history child) :
    (Tree.terminal child).eval values y ≠ auxiliary.evalMinimum values y := by
  rintro hminimum
  obtain ⟨_, earlier, hearlier, hindex, hshift⟩ := hdominated
  have hparentBound :
      (Tree.principalMin state.current principal auxiliary).eval values y ≤
        values earlier.index (y + earlier.value) :=
    invariant.history_bound hearlier
  have hchildLtEarlier :
      values child.index (y + child.value) <
        values earlier.index (y + earlier.value) := by
    have hchildLtParent :
      values child.index (y + child.value) <
          (Tree.principalMin state.current principal auxiliary).eval values y := by
      simpa [Tree.eval] using
        (show (Tree.terminal child).eval values y <
            principal.eval values y + auxiliary.evalMinimum values y by
          rw [hminimum]
          linarith)
    exact hchildLtParent.trans_le hparentBound
  have hearlierLeChild :
      values earlier.index (y + earlier.value) ≤
        values child.index (y + child.value) := by
    rw [hindex]
    exact hmono child.index (by linarith)
  exact (not_lt_of_ge hearlierLeChild) hchildLtEarlier

end Tree
end EliminationCriticalTree
end KrasikovLagarias
end Erdos1135
