Theory TD_plain

section ‹The plain Top-Down Solver›
text ‹\label{sec:td_plain}›

text‹TD\_plain is a simplified version of the original TD which only keeps track of already called
  unknowns to avoid infinite descend in case of recursive dependencies. In contrast to the TD, it
  does, however, not track stable unknowns and the dependencies between unknowns. Instead, it
  re-iterates every unknown when queried again.
›

theory TD_plain
  imports Basics
begin

subsection ‹Definition of the Solver Algorithm›

record ('x,'d) state =
  called :: "'x set"
  σ :: "('x,'d) map"

locale TD_plain =
  fixes T :: "('x,'d::bot) eqsT"
begin

text ‹The recursively descending solver algorithm is defined with three mutual recursive functions.
  Initially, the function @{term iterate} is called from the top-level @{term solve} function for
  the requested unknown. @{term iterate} keeps evaluating the right-hand side by calling the
  function @{term eval} and updates the value mapping @{term σ} until the value stabilizes. The
  function @{term eval} walks through a strategy tree and chooses the path based on the result for
  queried unknowns. These queries are delegated to the third mutual recursive function @{term query}
  which checks that the unknown is not already being evaluated and iterates it otherwise. The
  function keyword is used for the definition, since, without further assumptions, the solver may
  not terminate.›

function (domintros)
    query :: "'x ⇒ 'x ⇒ ('x,'d) state ⇒ 'd × ('x,'d) state" and
  iterate :: "'x ⇒ ('x,'d) state ⇒ 'd × ('x,'d) state" and
     eval :: "'x ⇒ ('x, 'd) strategy_tree ⇒ ('x,'d) state ⇒ 'd × ('x,'d) state" where
  "query y x state = (
    if x ∈ called state then
      (mlup (σ state) x, state)
    else
      iterate x (state⦇called := insert x (called state)⦈))"
| "iterate x state = (
    let (d_new, state) = eval x (T x) state in
    if mlup (σ state) x = d_new then
      (d_new, state⦇called := called state - {x}⦈)
    else
      iterate x (state⦇σ := (σ state)(x ↦ d_new)⦈))"
| "eval x t state = (case t of
      Answer d ⇒ (d, state)
    | Query y g ⇒ (let (yd, state) = query x y state in eval x (g yd) state))"
  by pat_completeness auto

definition solve :: "'x ⇒ ('x, 'd) map" where
  "solve x = (let (_, state) = iterate x ⦇called = {x}, σ = Map.empty⦈ in σ state)"

definition query_dom where
  "query_dom x y state = query_iterate_eval_dom (Inl (x, y, state))"
declare query_dom_def [simp]
definition iterate_dom where
  "iterate_dom x state = query_iterate_eval_dom (Inr (Inl (x, state)))"
declare iterate_dom_def [simp]
definition eval_dom where
  "eval_dom x t state = query_iterate_eval_dom (Inr (Inr (x, t, state)))"
declare eval_dom_def [simp]

definition solve_dom where
  "solve_dom x = iterate_dom x ⦇called = {x}, σ = Map.empty⦈"

lemmas dom_defs = query_dom_def iterate_dom_def eval_dom_def


subsubsection ‹Refinement of Auto-Generated Rules›

text ‹The auto-generated \texttt{pinduct} rule contains a redundant assumption. This lemma removes
  this redundant assumption for easier instantiation and assigns each case a comprehensible name.›

lemmas query_iterate_eval_pinduct[consumes 1, case_names Query Iterate Eval]
  = query_iterate_eval.pinduct(1)[
      folded query_dom_def iterate_dom_def eval_dom_def,
      of y x state for y x state
    ]
    query_iterate_eval.pinduct(2)[
      folded query_dom_def iterate_dom_def eval_dom_def,
      of x state for x state
    ]
    query_iterate_eval.pinduct(3)[
      folded query_dom_def iterate_dom_def eval_dom_def,
      of x t state for x t state
    ]

lemmas iterate_pinduct[consumes 1, case_names Iterate]
  = query_iterate_eval_pinduct(2)[where ?P="λx y state. True" and ?R="λx t state. True",
    simplified (no_asm_use), folded query_dom_def iterate_dom_def eval_dom_def]

declare query.psimps [simp]
declare iterate.psimps [simp]
declare eval.psimps [simp]

subsubsection ‹Domain Lemmas›

lemma dom_backwards_pinduct:
  shows "query_dom y x state
    ⟹ x ∉ called state ⟹ iterate_dom x (state⦇called := insert x (called state)⦈)"
  and "iterate_dom x state
    ⟹ (eval_dom x (T x) state ∧
        ((xd_new, state') = eval x (T x) state
          ⟶ mlup (σ state') x ≠ xd_new ⟶
          iterate_dom x (state'⦇σ := (σ state')(x ↦ xd_new)⦈)))"
  and "eval_dom x (Query y g) state
    ⟹ (query_dom x y state ∧ ((yd, state') = query x y state ⟶ eval_dom x (g yd) state'))"
proof (induction y x state and x state and x "Query y g" state
    arbitrary: and xd_new state' and y g yd state'
    rule: query_iterate_eval_pinduct)
  case (Query T x state)
  then show ?case
    using query_iterate_eval.domintros(2) by fastforce
next
  case (Iterate T x state)
  then show ?case
    using query_iterate_eval.domintros(2,3)[folded eval_dom_def iterate_dom_def query_dom_def]
    by metis
next
  case (Eval T state)
  then show ?case
    using query_iterate_eval.domintros(1,3) by simp
qed

subsubsection ‹Case Rules›

lemma iterate_continue_fixpoint_cases[consumes 3]:
  assumes "iterate_dom x state"
    and "(xd, state') = iterate x state"
  obtains (Fixpoint) state1
  where "eval_dom x (T x) state"
    and "(xd, state1) = eval x (T x) state"
    and "mlup (σ state') x = xd"
    and "state' = state1⦇called := called state1 - {x}⦈"
  | (Continue) state1 xd_new
  where "eval_dom x (T x) state"
    and "(xd_new, state1) = eval x (T x) state"
    and "mlup (σ state1) x ≠ xd_new"
    and "iterate_dom x (state1⦇σ := (σ state1)(x ↦ xd_new)⦈)"
    and "(xd, state') = iterate x (state1⦇σ := (σ state1)(x ↦ xd_new)⦈)"
proof -
 obtain xd_new state1
    where "eval x (T x) state = (xd_new, state1)"
    by (cases "eval x (T x) state")
  then show ?thesis
    using assms that dom_backwards_pinduct(2)[OF assms(1)]
    by (cases "mlup (σ state1) x = xd_new") fastforce+
qed

lemma query_iterate_lookup_cases_basic [consumes 2]:
  assumes "query_dom y x state"
    and "(xd, state') = query y x state"
  obtains (Iterate)
        "iterate_dom x (state⦇called := insert x (called state)⦈)"
    and "(xd, state') = iterate x (state⦇called := insert x (called state)⦈)"
    and "x ∉ called state"
  | (Lookup) "mlup (σ state) x = xd"
    and "state' = state"
    and "x ∈ called state"
  using assms that dom_backwards_pinduct(1)
  by (cases "x ∈ called state"; auto split: prod.splits)

lemma eval_query_answer_cases_basic [consumes 2]:
  assumes "eval_dom x t state"
    and "(xd, state') = eval x t state"
  obtains (Query) y g yd state1
  where "t = Query y g"
    and "query_dom x y state"
    and "(yd, state1) = query x y state"
    and "eval_dom x (g yd) state1"
    and "(xd, state') = eval x (g yd) state1"
  | (Answer) "t = Answer xd"
    and "state' = state"
  using assms dom_backwards_pinduct(3) that
  by (cases t; auto split: prod.splits)

lemma c_same:
  shows
    "query_dom y x state ⟹ (xd, state') = query y x state ⟹ called state' = called state"
    "iterate_dom x state ⟹ (xd, state') = iterate x state ⟹ called state' = called state - {x}"
    "eval_dom x t state ⟹ (xd, state') = eval x t state ⟹ called state' = called state"
proof (induction y x state and x state and x t state
       arbitrary: xd state' and xd state' and xd state'
       rule: query_iterate_eval_pinduct)
  case (Query y x state)
  show ?case using Query(1,3)
  proof (cases rule: query_iterate_lookup_cases_basic)
    case Iterate
    then show ?thesis using Query(2)[of xd state'] by simp
  next
    case Lookup
    then show ?thesis by fastforce
  qed
next
  case (Iterate x state)
  show ?case using Iterate(1,4)
  proof (cases rule: iterate_continue_fixpoint_cases)
    case Fixpoint
    then show ?thesis using Iterate.IH(2) by simp
  next
    case (Continue state1)
    then show ?thesis
      using Iterate.IH(2)[OF Continue(2)] Iterate.IH(3)[OF Continue(2) _ _ Continue(5)]
      by simp
  qed
next
  case (Eval x t state)
  show ?case using Eval(1,4)
  proof (cases rule: eval_query_answer_cases_basic)
    case (Query y g yd state1)
    show ?thesis
      using Eval.IH(2)[OF Query(1,3)] Eval.IH(3)[OF Query(1,3) _ Query(5)] by simp
  qed fast
qed

lemma iterate_fmlookup:
  assumes "iterate_dom x state"
    and "(xd, state') = iterate x state"
  shows "mlup (σ state') x = xd"
  using assms
proof(induction rule: iterate_pinduct)
  case (Iterate x state)
  show ?case
    using Iterate.hyps Iterate.prems(1)
  proof(cases rule: iterate_continue_fixpoint_cases)
    case (Continue state1 xd_new)
    then show ?thesis using Iterate.IH Continue by simp+
  qed (simp add: Iterate.prems(1))
qed

corollary query_fmlookup:
  assumes "query_dom y x state"
    and "(xd, state') = query y x state"
  shows "mlup (σ state') x = xd"
  using assms iterate_fmlookup dom_backwards_pinduct(1)[OF assms(1)]
  by (auto simp del: iterate.psimps split: if_splits)

lemma query_iterate_lookup_cases [consumes 2]:
  assumes "query_dom y x state"
    and "(xd, state') = query y x state"
  obtains (Iterate)
        "iterate_dom x (state⦇called := insert x (called state)⦈)"
    and "(xd, state') = iterate x (state⦇called := insert x (called state)⦈)"
    and "mlup (σ state') x = xd"
    and "x ∉ called state"
  | (Lookup) "mlup (σ state) x = xd"
    and "state' = state"
    and "x ∈ called state"
  apply (rule query_iterate_lookup_cases_basic[OF assms])
  using iterate_fmlookup[of x "state⦇called := insert x (called state)⦈"]
  by (simp_all split: if_splits del: dom_defs)

lemma eval_query_answer_cases [consumes 2]:
  assumes "eval_dom x t state"
    and "(xd, state') = eval x t state"
  obtains (Query) y g yd state1
  where "t = Query y g"
    and "query_dom x y state"
    and "(yd, state1) = query x y state"
    and "eval_dom x (g yd) state1"
    and "(xd, state') = eval x (g yd) state1"
    and "mlup (σ state1) y = yd"
  | (Answer) "t = Answer xd"
    and "state' = state"
  by (auto intro: eval_query_answer_cases_basic[OF assms] simp add: query_fmlookup[of x _ state])


subsection ‹Program Refinement for Code Generation›

text‹For code generation, we define a refined version of the solver function using the
partial\_function keyword with the option attribute.›

datatype ('a,'b) rec_state = Q "'a × 'a × ('a,'b) state"
  | I "'a × ('a,'b) state"  | E "'a × ('a,'b) strategy_tree × ('a,'b) state"

partial_function (option)
  solve_rec_c :: "('x, 'd) rec_state ⇒ ('d × ('x, 'd) state) option"
 where
  "solve_rec_c s = (case s of Q (y, x, state) ⇒
      if x ∈ called state then
        Some (mlup (σ state) x, state)
      else
        solve_rec_c (I (x, state⦇called := insert x (called state)⦈))
    | I (x, state) ⇒
      Option.bind (solve_rec_c (E (x, (T x), state))) (λ(d_new, state).
      if d_new = mlup (σ state) x then
        Some (d_new, state⦇called := called state - {x}⦈)
      else
        solve_rec_c (I (x, state⦇σ := (σ state)(x ↦ d_new)⦈)))
    | E (x, t, state) ⇒
      (case t of
        Answer d ⇒ Some (d, state)
      | Query y g ⇒ Option.bind (solve_rec_c (Q (x, y, state)))
        (λ(yd, state). solve_rec_c (E (x, (g yd), state)))))"

declare solve_rec_c.simps[code]

definition solve_rec_c_dom where "solve_rec_c_dom p ≡ ∃result. solve_rec_c p = Some result"

definition solve_c :: "'x ⇒ (('x, 'd) map) option" where
  "solve_c x = Option.bind (solve_rec_c (I (x, ⦇called = {x}, σ = Map.empty⦈)))
    (λ(_, state). Some (σ state))"

definition solve_c_dom :: "'x ⇒ bool" where "solve_c_dom x ≡ ∃σ. solve_c x = Some σ"


text‹We proof the equivalence between the refined solver function for code generation and the
initial version used for the partial correctness proof.›

lemma query_iterate_eval_solve_rec_c_equiv:
  shows "query_dom y x state ⟹ solve_rec_c_dom (Q (y,x,state))
    ∧ query y x state = the (solve_rec_c (Q (y,x,state)))"
  and "iterate_dom x state ⟹ solve_rec_c_dom (I (x,state))
    ∧ iterate x state = the (solve_rec_c (I (x,state)))"
  and "eval_dom x t state ⟹ solve_rec_c_dom (E (x,t,state))
    ∧ eval x t state = the (solve_rec_c (E (x,t,state)))"
proof (induction y x state and x state and x t state rule: query_iterate_eval_pinduct)
  case (Query y x state)
  show ?case
  proof (cases "x ∈ called state")
    case True
    then have "solve_rec_c (Q (y, x, state)) = Some (mlup (σ state) x, state)"
      by (simp add: solve_rec_c.simps)
    moreover have "query y x state = (mlup (σ state) x, state)"
      using query.psimps[folded dom_defs] Query(1) True by force
    ultimately show ?thesis unfolding solve_rec_c_dom_def by auto
  next
    case False
    then have "query y x state = iterate x (state⦇called := insert x (called state)⦈)"
      using Query.IH(1) query.pelims[folded dom_defs] by simp
    then have "query y x state = the (solve_rec_c (Q (y, x, state)))"
      using Query False False by (simp add: solve_rec_c.simps)
    moreover have "solve_rec_c_dom (Q (y, x, state))"
      using Query(2) False unfolding solve_rec_c_dom_def by (simp add: solve_rec_c.simps)
    ultimately show ?thesis using Query unfolding solve_rec_c_dom_def by auto
  qed
next
  case (Iterate x state)
  obtain d1 state1 where eval: "(d1, state1) = eval x (T x) state"
    and "solve_rec_c (E (x, T x, state)) = Some (d1, state1)"
      using Iterate(2) solve_rec_c_dom_def by force
  show ?case
  proof (cases "mlup (σ state1) x = d1")
    case True
    have "iterate x state = (d1, state1⦇called := called state1 - {x}⦈)"
      using eval iterate.psimps[folded dom_defs, OF Iterate(1)] True by (simp split: prod.splits)
    then show ?thesis
      using Iterate unfolding solve_rec_c_dom_def dom_defs
      by (fastforce simp add: solve_rec_c.simps)
  next
    case False
    then have "solve_rec_c_dom (I (x, state1⦇σ := (σ state1)(x ↦ d1)⦈))"
        and "iterate x (state1⦇σ := (σ state1)(x ↦ d1)⦈) =
          the (solve_rec_c (I (x, state1⦇σ := (σ state1)(x ↦ d1)⦈)))"
      using Iterate(3)[OF eval _ False] by blast+
    moreover have "iterate x state = iterate x (state1⦇σ := (σ state1)(x ↦ d1)⦈)"
      using eval iterate.psimps[folded dom_defs, OF Iterate(1)] False by (simp split: prod.splits)
    moreover have "solve_rec_c (I (x, state1⦇σ := (σ state1)(x ↦ d1)⦈)) =
        solve_rec_c (I (x, state))"
      using False eval Iterate(2) unfolding solve_rec_c_dom_def
      by (auto simp add: solve_rec_c.simps)
    ultimately show ?thesis unfolding solve_rec_c_dom_def by auto
  qed
next
  case (Eval x t state)
  show ?case
  proof (cases t)
    case (Answer d)
    then have "eval x t state = (d, state)"
      using eval.psimps query_iterate_eval.domintros(3) dom_defs(3)
      by fastforce
    then show ?thesis
      using Eval Answer unfolding solve_rec_c_dom_def by (simp add: solve_rec_c.simps)
  next
    case (Query y g)
    then obtain d1 state1 where query_c: "solve_rec_c (Q (x, y, state)) = Some (d1, state1)"
        and query: "(d1, state1) = query x y state"
      using Eval(2) unfolding solve_rec_c_dom_def by (auto simp add: solve_rec_c.simps)
    moreover have "solve_rec_c_dom (E (x, g d1, state1))"
        and eval_c: "eval x (g d1) state1 = the (solve_rec_c (E (x, g d1, state1)))"
      using Eval.IH(3)[OF Query ‹(d1, state1) = query x y state› refl] Query by simp+
    ultimately show ?thesis
      unfolding solve_rec_c_dom_def using Query Eval.IH(1)
      by (auto simp add: solve_rec_c.simps split: prod.splits)
  qed
qed

lemma solve_rec_c_query_iterate_eval_equiv:
  shows "solve_rec_c s = Some r ⟹ (case s of
        Q (y,x,state) ⇒ query_dom y x state ∧ query y x state = r
      | I (x,state) ⇒ iterate_dom x state ∧ iterate x state = r
      | E (x,t,state) ⇒ eval_dom x t state ∧ eval x t state = r)"
proof (induction arbitrary: s r rule: solve_rec_c.fixp_induct)
  case 1
  then show ?case
    using option_admissible unfolding curry_def by fastforce
next
  case 2
  then show ?case by simp
next
  case (3 S)
  show ?case
  proof (cases s)
    case (Q a)
    obtain y x state where "a = (y, x, state)" using prod_cases3 by blast
    have "query_dom y x state ∧ query y x state = r"
    proof (cases "x ∈ called state")
      case True
      then have "Some (mlup (σ state) x, state) = Some r" using 3(2) Q ‹a = (y, x, state)› by simp
      then show ?thesis
        by (metis query.psimps query_dom_def
            query_iterate_eval.domintros(1) True option.inject)
    next
      case False
      then have "S (I (x, state⦇called := insert x (called state)⦈)) = Some r"
        using 3(2) Q ‹a = (y, x, state)› by auto
      then have "iterate_dom x (state⦇called := insert x (called state)⦈)
          ∧ iterate x (state⦇called := insert x (called state)⦈) = r"
        using 3(1) unfolding iterate_dom_def by fastforce
      then show ?thesis using False
        by (simp add: query_iterate_eval.domintros(1))
    qed
    then show ?thesis using Q ‹a = (y, x, state)› unfolding query_dom_def by simp
  next
    case (I a)
    obtain x state where "a = (x, state)" by fastforce
    then have IH1: "Option.bind (S (E (x, T x, state)))
       (λ(d_new, state).
         if d_new = mlup (σ state) x then Some (d_new, state⦇called := called state - {x}⦈)
         else S (I (x, state⦇σ := (σ state)(x ↦ d_new)⦈))) = Some r"
      using 3(2) I by simp
    then obtain d_new state1 where eval_some: "S (E (x, T x, state)) = Some (d_new, state1)"
      using 3(2) I
      by (cases "S (E (x, T x, state))") auto
    then have eval: "eval_dom x (T x) state" "eval x (T x) state = (d_new, state1)"
      using 3(1) unfolding eval_dom_def by force+
    have "iterate_dom x state ∧ iterate x state = r"
    proof (cases "mlup (σ state1) x = d_new")
      case True
      then show ?thesis
        using eval IH1 dom_defs(2) dom_defs(3) iterate.psimps
          query_iterate_eval.domintros(2) eval_some
        by fastforce
    next
      case False
      then have "S (I (x, state1⦇σ := (σ state1)(x ↦ d_new)⦈)) = Some r"
        using IH1 eval_some by simp
      then have "iterate_dom x (state1⦇σ := (σ state1)(x ↦ d_new)⦈)"
          "iterate x (state1⦇σ := (σ state1)(x ↦ d_new)⦈) = r"
        using 3(1) unfolding iterate_dom_def by fastforce+
      then show ?thesis
        using eval False query_iterate_eval.domintros(2)[folded dom_defs]
        by fastforce
    qed
    then show ?thesis using I ‹a = (x, state)› unfolding iterate_dom_def by simp
  next
    case (E a)
    obtain x t state where "a = (x, t, state)" using prod_cases3 by blast
    then have "s = E (x, t, state)" using E by auto
    have "eval_dom x t state ∧ eval x t state = r"
    proof (cases t)
      case (Answer d)
      then have "eval_dom x t state" unfolding eval_dom_def
        using query_iterate_eval.domintros(3) by fastforce
      moreover have "eval x t state = (d, state)"
        using Answer query_iterate_eval.domintros(3) by fastforce
      moreover have "(d, state) = r" using 3(2) ‹s = E (x, t, state)› Answer by simp
      ultimately show ?thesis by simp
    next
      case (Query y g)
      then have A: "Option.bind (S (Q (x, y, state))) (λ(yd, state). S (E (x, g yd, state)))
        = Some r" using ‹s = E (x, t, state)› 3(2) by simp
      then obtain yd state1 where S1: "S (Q (x, y, state)) = Some (yd, state1)"
          and S2: "S (E (x, g yd, state1)) = Some r"
        by (cases "S (Q (x, y, state))") auto
      then have "query_dom x y state ∧ query x y state = (yd, state1)"
          and "eval_dom x (g yd) state1 ∧ eval x (g yd) state1 = r"
        using 3(1)[OF S1] 3(1)[OF S2] unfolding dom_defs by force+
      then show ?thesis
        using query_iterate_eval.domintros(3)[folded dom_defs, of t x state] Query
        by fastforce
    qed
    then show ?thesis using E ‹a = (x, t, state)› unfolding eval_dom_def by simp
  qed
qed

theorem term_equivalence: "solve_dom x ⟷ solve_c_dom x"
  using query_iterate_eval_solve_rec_c_equiv(2)[of x "⦇called = {x}, σ = λx. None⦈"]
    solve_rec_c_query_iterate_eval_equiv[of "I (x, ⦇called = {x}, σ = λx. None⦈)"]
  unfolding solve_dom_def solve_c_dom_def solve_rec_c_dom_def solve_c_def
  by (cases "solve_rec_c (I (x, ⦇called = {x}, σ = λx. None⦈))") force+

theorem value_equivalence:
  "solve_dom x ⟹ ∃σ. solve_c x = Some σ ∧ solve x = σ"
proof goal_cases
  case 1
  then obtain r where "solve_rec_c (I (x, ⦇called = {x}, σ = λx. None⦈)) = Some r
    ∧ iterate x ⦇called = {x}, σ = λx. None⦈ = r"
    using query_iterate_eval_solve_rec_c_equiv(2)
    unfolding solve_rec_c_dom_def solve_dom_def
    by fastforce
  then show ?case unfolding solve_def solve_c_def by (auto split: prod.split)
qed

text‹Then, we can define the code equation for @{term solve} based on the refined solver program
  @{term solve_c}.›

lemma solve_code_equation [code]:
  "solve x = (case solve_c x of Some r ⇒ r
  | None ⇒ Code.abort (STR ''Input not in domain'') (λ_. solve x))"
proof (cases "solve_dom x")
  case True
  then show ?thesis using value_equivalence unfolding solve_def solve_c_def by simp
next
  case False
  then have "solve_c x = None" using term_equivalence unfolding solve_c_dom_def by auto
  then show ?thesis by auto
qed

end ― ‹end of TD_plain locale›

text‹To setup the code generation for the solver locale we use a dedicated rewrite definition.›

global_interpretation TD_plain_Interp: TD_plain T for T
  defines TD_plain_Interp_solve = TD_plain_Interp.solve
  done


subsection ‹Partial Correctness›

context TD_plain
begin

subsubsection ‹Predicate for Valid Input States›

text‹We define a predicate for valid input solver states. @{term c} is the set of called unknowns,
  i.e., the unknowns currently being evaluated and @{term σ} is an unknown-to-value mapping. Both
  are data structures maintained by the solver. In contrast, the parameter @{term s} describing a
  set of unknowns, for which a partial solution has already been computed or which are currently
  being evaluated, is introduced for the proof. Although it is similar to the set @{term stabl}
  maintained by the original TD, it is only an under-approximation of it.
  A valid solver state is one, where @{term σ} is a partial solution for all truly stable unknowns,
  i.e., unknowns in @{term "s - c"}, and where these truly stable unknowns only depend on unknowns
  which are also truly stable or currently being evaluated.
  A substantial part of the partial correctness proof is to show that this property about the
  solver's state is preserved during a solver's run.›

definition invariant where
  "invariant s state ≡ (∀u ∈ s - called state. dep T (σ state) u ⊆ s)
    ∧ (∀u ∈ s - called state. eq T u (σ state) = mlup (σ state) u)"

lemma invariant_simp:
  assumes "x ∈ called state"
    and "invariant s (state⦇called := called state - {x}⦈)"
  shows "invariant (insert x s) state"
  using assms unfolding invariant_def by auto

lemma invariant_continue:
  assumes "x ∉ s"
    and "invariant s state"
    and "∀u∈s. mlup (σ state) u = mlup (σ state1) u"
    and "called state1 - {x} = called state - {x}"
  shows "invariant s (state1⦇σ := (σ state1)(x ↦ xd'), called := called state1 - {x}⦈)"
proof -
  show ?thesis
    using assms(1,2,4) mlup_eq_mupd_set[OF assms(1,3)] unfolding invariant_def
  proof(intro conjI, goal_cases)
    case 1
    have "dep T ((σ state1)(x ↦ xd')) u = dep T (σ state) u" if "u ∈ s - called state" for u
      using dep_eq[of T "σ state"] that 1(2,4) by blast
    then show ?case using 1(1-3) by fastforce
  next
    case 2
    have "traverse_rhs (T u) ((state.σ state1)(x ↦ xd')) =
      traverse_rhs (T u) (state.σ state)" if "u ∈ s - called state" for u
      apply (rule solution_sufficient[symmetric, unfolded eq_def])
      using 2(2,4) that by blast
    then show ?case using 2(1-4) by auto
  qed
qed

subsubsection ‹Auxiliary Lemmas›

text‹With the following lemma we establish, that whenever the solver is called for an unknown
  in @{term s} and where the solver state and @{term s} fulfill the invariant, the output value
  mapping is unchanged compared to the input value mapping.›

lemma already_solution:
  shows "query_dom y x state
    ⟹ (xd, state') = query y x state
    ⟹ x ∈ s
    ⟹ invariant s state
    ⟹ σ state = σ state'"
    and "iterate_dom x state
    ⟹ (xd, state') = iterate x state
    ⟹ x ∈ called state
    ⟹ x ∈ s
    ⟹ invariant s (state⦇called := called state - {x}⦈)
    ⟹ σ state = σ state'"
    and "eval_dom x t state
    ⟹ (xd, state') = eval x t state
    ⟹ dep_aux (σ state) t ⊆ s
    ⟹ invariant s state
    ⟹ traverse_rhs t (σ state') = xd ∧ σ state = σ state'"
proof(induction y x state and x state and x t state
      arbitrary: xd s state' and xd s state' and xd s state'
      rule: query_iterate_eval_pinduct)
  case (Query y x state)
  show ?case using Query.IH(1) Query.prems(1-3) Query.IH 
    by (cases rule: query_iterate_lookup_cases; simp)
next
  case (Iterate x state)
  show ?case using Iterate.IH(1) Iterate.prems(1)
  proof(cases rule: iterate_continue_fixpoint_cases)
    case Fixpoint
    then show ?thesis
      using Iterate.prems(3,4) Iterate.IH(2)[of _ _ "insert x s"]
        invariant_simp[OF Iterate.prems(2,4)]
      unfolding dep_def invariant_def by auto
  next
    case (Continue state1 xd')
    show ?thesis
    proof(rule ccontr)
      have IH: "eq T x (σ state1) = xd' ∧ σ state = σ state1"
        using Iterate.prems(2-4) Iterate.IH(2)[OF Continue(2), of s]
          invariant_simp[OF Iterate.prems(2,4)] unfolding dep_def invariant_def by auto
      then show False
        using Iterate.prems(2-4) Continue(3) unfolding invariant_def by simp
    qed
  qed
next
  case (Eval x t state)
  show ?case using Eval.IH(1) Eval.prems(1)
  proof(cases rule: eval_query_answer_cases)
    case (Query y g yd state1)
    have "σ state = σ state1"
      using Eval.IH(2)[OF Query(1,3) _ Eval.prems(3)] Eval.prems(2) Query(1) by simp
    then show ?thesis
      using Query(1,6) Eval.prems(2-3) Eval.IH(3)[OF Query(1) Query(3) refl Query(5), of s]
        c_same(1)[OF Query(2,3)]
      unfolding invariant_def
      by (auto split: if_splits)
  qed simp
qed


subsubsection ‹Induction Proof›

text‹Furthermore, we show that whenever the solver is called with a valid solver state, the valid
  solver state invariant also holds for its output state and the set of stable unknowns increases
  by the set @{term [source = true] reach_cap} of the current unknown.›

lemma partial_correctness_ind:
  shows "query_dom y x state
    ⟹ (xd, state') = query y x state
    ⟹ invariant s state
    ⟹ invariant (s ∪ reach_cap T (σ state') (called state') x) state'
      ∧ (∀ξ ∈ s. mlup (σ state) ξ = mlup (σ state') ξ)"
    and "iterate_dom x state
    ⟹ (xd, state') = iterate x state
    ⟹ x ∈ called state
    ⟹ invariant s (state⦇called := called state - {x}⦈)
    ⟹ invariant (s ∪ (reach_cap T (σ state') (called state') x))
        (state'⦇called := called state' - {x}⦈)
      ∧ (∀ξ ∈ s. mlup (σ state) ξ = mlup (σ state') ξ)"
    and "eval_dom x t state
    ⟹ (xd, state') = eval x t state
    ⟹ invariant s state
    ⟹ invariant (s ∪ reach_cap_tree T (σ state') (called state') t) state'
      ∧ (∀ξ ∈ s. mlup (σ state) ξ = mlup (σ state') ξ)
      ∧ traverse_rhs t (σ state') = xd"
proof(induction y x state and x state and x t state
      arbitrary: xd s state' and xd s state' and xd s state'
      rule: query_iterate_eval_pinduct)
  case (Query y x state)
  show ?case
    using Query.IH(1) Query.prems(1)
  proof (cases rule: query_iterate_lookup_cases)
    case Iterate
    note IH = Query.IH(2)[simplified, OF Iterate(4,2) Query.prems(2)]
    then show ?thesis
      using Iterate(4) c_same(2)[OF Iterate(1,2)] unfolding invariant_def by simp
  next
    case Lookup
    then show ?thesis
      using Query.prems(2) unfolding invariant_def by auto
  qed
next
  case (Iterate x state)
  show ?case
    using Iterate.IH(1) Iterate.prems(1)
  proof(cases rule: iterate_continue_fixpoint_cases)
    case (Fixpoint state1)
    note IH = Iterate.IH(2)[OF Fixpoint(2) invariant_simp[OF Iterate.prems(2,3)], folded eq_def]
    then show ?thesis
      using Fixpoint(3,4) Iterate.prems(2) reach_cap_tree_simp2[of x "called _ - {x}" T]
        dep_subset_reach_cap_tree[of _ "T x"] insert_absorb c_same(3)[OF Fixpoint(1,2)]
      unfolding invariant_def dep_def by fastforce
  next
    case (Continue state1 xd')
    note IH = Iterate.IH(2)[OF Continue(2) invariant_simp[OF Iterate.prems(2,3)]]

    note c_same = c_same(3)[OF Continue(1,2)]
    then have x_called1: "x ∈ called state1" using Iterate.prems(2) by simp
    have "∀u ∈ s - (called state1 - {x}). eq T u (σ state1) = mlup (σ state1) u"
      apply (rule part_solution_coinciding_sigma_called[where ?σ = "σ state"])
      using IH Iterate.prems(3) c_same
      unfolding invariant_def
      by auto
    then have x_not_stable: "x ∉ s" using IH Continue(3) by auto
    then have inv: "invariant s (state1⦇σ := (σ state1)(x ↦ xd'), called := called state1 - {x}⦈)"
      apply (intro invariant_continue[OF x_not_stable Iterate.prems(3)])
      using IH c_same unfolding invariant_def by auto
    note ih = Iterate.IH(3)[OF Continue(2) refl Continue(3) Continue(5), simplified,
        OF x_called1 inv]
    then show ?thesis
      using IH mlup_eq_mupd_set[OF x_not_stable, of "σ state"]
      unfolding mlup_def
      by auto
  qed
next
  case (Eval x t state)
  show ?case using Eval.IH(1) Eval.prems(1)
  proof(cases rule: eval_query_answer_cases)
    case (Query y g yd state1)
    note IH = Eval.IH(2)[OF Query(1,3) Eval.prems(2)]
    note ih = Eval.IH(3)[OF Query(1) Query(3) _ Query(5) conjunct1[OF IH], simplified]
    note c_same = c_same(1)[OF Query(2,3)] c_same(3)[OF Query(4,5)]
    show ?thesis
      using Query IH ih reach_cap_tree_step[where ?T=T] c_same
        reach_cap_tree_eq[of T "σ state1" "insert y (called state)" "T y" "σ state'"]
      by (auto simp add: Un_assoc)
  next
    case Answer
    then show ?thesis
      using Eval.prems(2) by simp
  qed
qed

text‹Since the initial solver state fulfills the valid solver state predicate, we can conclude from
  the above lemma, that the solve function returns a partial solution for the queried unknown
  @{term x} and all unknowns on which it transitively depends.›

corollary partial_correctness:
  assumes "solve_dom x"
    and "solve x = sigma"
  shows "part_solution T x sigma (reach T sigma x)"
proof -
  obtain xd state where iterate: "(xd, state) = iterate x ⦇called = {x}, σ = Map.empty⦈"
      "sigma = σ state"
    using assms(2) unfolding solve_def by (auto split: prod.splits)
  have "invariant (reach_cap T (σ state) (called state) x) (state⦇called := called state - {x}⦈)"
    using assms(1) partial_correctness_ind(2)[OF _ iterate(1), where ?s="{}"]
    unfolding solve_dom_def iterate_dom_def invariant_def by auto
  then show ?thesis
    using reach_empty_capped[where ?T=T] c_same(2)[OF assms(1)[unfolded solve_dom_def] iterate(1)]
      iterate(2)
    unfolding invariant_def by simp
qed

end ― ‹end context TD\_plain for partial correctness proof›


subsection ‹Exactness›

locale TD_plain_mono = TD_plain T
  for T :: "('x, 'd :: complete_lattice) eqsT" +
  assumes mono_eq: "is_mono_eq T"
begin

subsubsection ‹Definitions for (least-)(post-)solution›

abbreviation solution where
  "solution sigma ≡ (∀x. eq T x sigma = mlup sigma x)"

abbreviation least_solution where
  "least_solution sigma ≡ solution sigma ∧ (∀sigma'. solution sigma' ⟶ sigma ⊑m sigma')"

abbreviation post_solution where
  "post_solution sigma ≡ (∀x. eq T x sigma ≤ mlup sigma x)"

abbreviation least_post_solution where
  "least_post_solution sigma ≡
    post_solution sigma ∧ (∀sigma'. post_solution sigma' ⟶ sigma ⊑m sigma')"


subsubsection ‹Inductive properties of sigma›

lemma less_than_solution_ind:
  shows "query_dom y x state
    ⟹ (xd, state') = query y x state
    ⟹ invariant s state
    ⟹ (∀σl. solution σl ⟶ σ state ⊑m σl)
    ⟹ (∀ξ. mlup (σ state) ξ ≤ eq T ξ (σ state))
    ⟹ σ state ⊑m σ state'
      ∧ (∀σl. solution σl ⟶ σ state' ⊑m σl)
      ∧ (∀ξ. mlup (σ state') ξ ≤ eq T ξ (σ state'))"
    and "iterate_dom x state
    ⟹ (xd, state') = iterate x state
    ⟹ x ∈ called state
    ⟹ invariant s (state⦇called := called state - {x}⦈)
    ⟹ (∀σl. solution σl ⟶ σ state ⊑m σl)
    ⟹ (∀ξ. mlup (σ state) ξ ≤ eq T ξ (σ state))
    ⟹ σ state ⊑m σ state'
      ∧ (∀σl. solution σl ⟶ σ state' ⊑m σl)
      ∧ (∀ξ. mlup (σ state') ξ ≤ eq T ξ (σ state'))"
    and "eval_dom x t state
    ⟹ (xd, state') = eval x t state
    ⟹ invariant s state
    ⟹ (∀σl. solution σl ⟶ σ state ⊑m σl)
    ⟹ (∀ξ. mlup (σ state) ξ ≤ eq T ξ (σ state))
    ⟹ σ state ⊑m σ state'
      ∧ (∀σl. solution σl ⟶ σ state' ⊑m σl)
      ∧ (∀ξ. mlup (σ state') ξ ≤ eq T ξ (σ state'))"
proof(induction y x state and x state and x t state
    arbitrary: xd s state' and xd s state' and xd s state'
    rule: query_iterate_eval_pinduct)
  case (Query y x state)
  show ?case
    using Query.IH(1) Query.prems(1)
  proof (cases rule: query_iterate_lookup_cases)
    case Iterate
    have A: "invariant s (state⦇called := called state - {x}⦈)"
      using Query.prems(2) Iterate(4) by auto
    then have IH: "σ state ⊑m σ state'" "∀σl. solution σl ⟶ σ state' ⊑m σl"
      "∀ξ. mlup (σ state') ξ ≤ eq T ξ (σ state')"
      using Query.IH(2)[OF Iterate(4,2)] Query.prems(3,4) by auto
    then show ?thesis
      using Iterate(4) IH Query.prems(3) by fastforce
  next
    case Lookup
    then show ?thesis
      using Query.prems(2,3,4) unfolding invariant_def map_le_def by auto
  qed
next
  case (Iterate x state)
  show ?case
    using Iterate.IH(1) Iterate.prems(1)
  proof(cases rule: iterate_continue_fixpoint_cases)
    case Fixpoint
    note IH = Iterate.IH(2)[OF Fixpoint(2) invariant_simp[OF Iterate.prems(2,3)] Iterate.prems(4,5)]
    then show ?thesis
      using Fixpoint(3,4) Iterate.prems(2) reach_cap_tree_simp2[of x "called state - {x}"]
        dep_subset_reach_cap_tree[of "σ state'" "T x", folded dep_def] IH
      unfolding invariant_def
      by (auto simp add: insert_absorb)
  next
    case (Continue state1 xd')
    note IH = Iterate.IH(2)[OF Continue(2) invariant_simp[OF Iterate.prems(2,3)] Iterate.prems(4,5)]

    have x_not_stable: "x ∉ s"
      using Continue(1-3) Iterate.prems(3) already_solution(3)[of x "T x" state xd' state1 s]
      unfolding eq_def dep_def invariant_def
      by force
    have part_sol:
      "invariant (insert x s ∪ reach_cap_tree T (σ state1) (called state1) (T x)) state1 ∧
      (∀ξ∈insert x s. mlup (σ state) ξ = mlup (σ state1) ξ) ∧ traverse_rhs (T x) (σ state1) = xd'"
      using invariant_simp[OF Iterate.prems(2)] partial_correctness_ind(3)[OF Continue(1,2),
          of "insert x s"]Iterate.prems(3)
      by auto
    then have inv: "invariant s (state1⦇σ := (σ state1)(x ↦ xd'), called := called state1 - {x}⦈)"
      apply (intro invariant_continue[OF x_not_stable Iterate.prems(3)])
      using c_same(3)[OF Continue(1,2)]
      by auto

    have "σ state1 ⊑m (σ state1)(x ↦ xd')"
      unfolding map_le_def
    proof (intro allI)
      fix a
      show "mlup (σ state1) a ≤ mlup ((σ state1)(x ↦ xd')) a"
      proof (cases "a = x")
        case True
        have "option_def_bot ((σ state1) x) ≤ xd'"
          using part_sol IH unfolding eq_def mlup_def by auto
        then show ?thesis using True unfolding mlup_def by simp
      next
        case False
        have "option_def_bot ((σ state) a) ≤ option_def_bot ((σ state1) a)"
          using IH unfolding map_le_def mlup_def by auto
        then show ?thesis using False unfolding mlup_def by simp
      qed
    qed

    have A: "∀σl. solution σl ⟶ (σ state1)(x ↦ xd') ⊑m σl"
    proof (intro allI impI)
      fix σl y
      assume "solution σl"
      have "mlup ((σ state1)(x ↦ xd')) y ≤ mlup σl y"
      proof (cases "y = x")
        case True
        have "eq T x (σ state1) ≤ eq T x σl"
        proof -
          have "eq T x ((σ state1)(x ↦ xd')) ≤ eq T x σl"
          proof -
            have "σ state1 ⊑m σl" using IH ‹solution σl› by blast
            then have "eq T x (σ state1) ≤ eq T x σl"
              using mono_eq unfolding is_mono_eq_def by force
            then have "xd' ≤ mlup σl x" using ‹solution σl› part_sol by auto
            then have "(σ state1)(x ↦ xd') ⊑m σl"
              using ‹σ state1 ⊑m σl› unfolding map_le_def mlup_def by simp
            then show ?thesis using mono_eq unfolding is_mono_eq_def by simp
          qed
          moreover have "eq T x (σ state1) ≤ eq T x ((σ state1)(x ↦ xd'))"
            using mono_eq ‹σ state1 ⊑m (σ state1)(x ↦ xd')› unfolding is_mono_eq_def by simp
          ultimately show ?thesis by simp
        qed
        moreover have "eq T x (σ state1) = xd'" using part_sol by auto
        moreover have "eq T x σl = mlup σl x" using ‹solution σl› by blast
        ultimately show ?thesis by (simp add: True mlup_def)
      next
        case False
        then have "option_def_bot (((σ state1)(x ↦ xd')) y) = option_def_bot ((σ state1) y)"
          by auto
        then show ?thesis using IH ‹solution σl› unfolding map_le_def mlup_def by simp
      qed
      then show "mlup ((σ state1)(x ↦ xd')) y ≤ mlup σl y" unfolding map_le_def mlup_def by auto
    qed

    have "∀a. (σ state) a ⊑o ((σ state1)(x ↦ xd')) a"
      using IH ‹σ state1 ⊑m (σ state1)(x ↦ xd')›
      by (metis dual_order.trans mlup_def)
    have B: "∀ξ. mlup ((σ state1)(x ↦ xd')) ξ ≤ eq T ξ ((σ state1)(x ↦ xd'))"
      using IH ‹σ state1 ⊑m (σ state1)(x ↦ xd')› mono_eq part_sol
      unfolding is_mono_eq_def eq_def mlup_def
      by (metis order.trans fun_upd_apply option.simps(5))
    have xcalled1: "x ∈ called (state1⦇σ := (σ state1)(x ↦ xd')⦈)"
      using Iterate.prems(2) c_same(3)[OF Continue(1,2)] by simp

    have ih: "σ (state1⦇σ := (σ state1)(x ↦ xd')⦈) ⊑m σ state'"
        "∀σl. solution σl ⟶ σ state' ⊑m σl"
        "∀ξ. mlup (σ state') ξ ≤ eq T ξ (σ state')"
      using Iterate.IH(3)[OF Continue(2) _ Continue(3) Continue(5) xcalled1] inv A B
      by (auto simp del: fun_upd_apply)

    have "σ state ⊑m σ state'"
      using IH ih ‹σ state1 ⊑m (σ state1)(x ↦ xd')›
        order.trans[OF order.trans, of "mlup (σ state) _" "mlup (σ state1) _"]
      unfolding map_le_def
      by fastforce

    then show ?thesis using IH mlup_eq_mupd_set[OF x_not_stable, of "σ state"] ih by fastforce
  qed
next
  case (Eval x t state)
  show ?case using Eval.IH(1) Eval.prems(1)
  proof(cases rule: eval_query_answer_cases)
    case (Query y g yd state1)
    note IH = Eval.IH(2)[OF Query(1,3) Eval.prems(2,3,4)]
    have B: "∀σl. solution σl ⟶ σ state1 ⊑m σl" using IH by auto
    have C: "∀ξ. mlup (σ state1) ξ ≤ eq T ξ (σ state1)" using IH by auto
    have inv1: "invariant (s ∪ reach_cap T (σ state1) (called state1) y) state1"
      using partial_correctness_ind(1)[OF Query(2,3) Eval.prems(2)] by auto
    note ih = Eval.IH(3)[OF Query(1) Query(3) _ Query(5) inv1 B C]
    have "σ state ⊑m σ state'" using IH ih unfolding map_le_def eq_def
      by (meson dual_order.trans)
    then show ?thesis
      using Query IH ih reach_cap_tree_step reach_cap_tree_eq
      by (auto simp add: Un_assoc)
  next
    case Answer
    then show ?thesis
      using Eval.prems(2,3,4) unfolding map_le_def by simp
  qed
qed

lemma less_than_solution:
  assumes "solve_dom x"
    and "solve x = sigma"
  shows "∀σl. solution σl ⟶ sigma ⊑m σl"
  using less_than_solution_ind(2)[of x "⦇called = {x}, σ = Map.empty⦈"] assms
  unfolding solve_dom_def solve_def invariant_def map_le_def mlup_def
  by (auto split: prod.splits)


subsubsection ‹Existence of (unique) least (post-)solution›

paragraph ‹Option Inf›

definition option_inf :: "'d option set ⇒ 'd option" ("⨅o _" 900) where
  "⨅o os ≡ Some (⨅{option_def_bot op |op. op ∈ os})" (* TODO: return None if meet is bot?*)

lemma option_inf_lower: "x ∈ A ⟹ (⨅o A) ⊑o x"
proof -
  assume "x ∈ A"
  show "(⨅o A) ⊑o x"
  proof (cases x)
    case None
    then have "⊥ ∈ {option_def_bot op |op. op ∈ A}" using ‹x ∈ A› by force
    then have "⨅{option_def_bot op |op. op ∈ A} = ⊥" by (simp add: Inf_eqI)
    then have "(⨅o A) = Some ⊥" using option_inf_def by auto
    then show ?thesis using None by auto
  next
    case (Some a)
    then have "a ∈ {option_def_bot op |op. op ∈ A}" using ‹x ∈ A› by force
    then have "⨅{option_def_bot op |op. op ∈ A} ≤ a" by (simp add: Inf_lower)
    then show ?thesis unfolding option_inf_def by (simp add: Some)
  qed
qed

lemma option_inf_greatest: "(⋀x. x ∈ A ⟹ z ⊑o x) ⟹ z ⊑o (⨅o A)"
proof -
  assume assms: "x ∈ A ⟹ z ⊑o x" for x
  show "z ⊑o (⨅o A)"
  proof (cases "⨅o A")
    case None
    then have "None ∈ A" by (simp add: option_inf_def)
    then have "z ⊑o None" using assms by blast
    then show ?thesis using None by auto
  next
    case (Some d)
    have "⋀x. x ∈ {option_def_bot op |op. op ∈ A} ⟹ option_def_bot z ≤ x"
      using assms by force
    then have "option_def_bot z ≤ d" using Inf_greatest Some unfolding option_inf_def by blast
    then show ?thesis by (simp add: Some)
  qed
qed

paragraph ‹Map Inf›

definition
    map_inf :: "(('x, 'd) map) set ⇒ ('x, 'd) map"  ("⨅m _" 900) where
  "(⨅m ms) ≡ (λk. option_inf ((λm. m k) ` ms))"

lemma map_inf_lower: "x ∈ A ⟹ (⨅m A) ⊑m x"
proof -
  assume "x ∈ A"
  {
    fix a
    have "mlup (⨅m A) a ≤ mlup x a"
    proof (cases "x a")
      case None
      then have "None ∈ (λm. m a) ` A" using ‹x ∈ A›
        using image_iff by fastforce
      then have "Some ⊥ = ⨅o ((λm. m a) ` A)"
        unfolding option_inf_def
        by (smt (verit, ccfv_SIG) Inf_lower bot.extremum_unique mem_Collect_eq option.simps(4))
      then show ?thesis using ‹x a = None›
        by (simp add: map_inf_def mlup_def option_inf_def)
    next
      case (Some d)
      then have "Some d ∈ (λm. m a) ` A" using ‹x ∈ A›
        using image_iff by fastforce
      then have "(⨅o ((λm. m a) ` A)) ⊑o Some d"
        using option_inf_def option_inf_lower by meson
      then show ?thesis using ‹x a = Some d›
        unfolding map_inf_def mlup_def by (auto split: option.split)
    qed
  }
  then show ?thesis unfolding map_le_def mlup_def by auto
qed

lemma map_inf_greatest: "(⋀x. x ∈ A ⟹ z ⊑m x) ⟹ z ⊑m (⨅m A)"
proof -
  assume "x ∈ A ⟹ z ⊑m x" for x
  then have "x ∈ (λm. m a) ` A ⟹ z a ⊑o x" for x a
    unfolding mlup_def map_le_def by blast
  then have "z a ⊑o (⨅o ((λm. m a) ` A))" for a using option_inf_greatest by auto
  then show ?thesis unfolding mlup_def map_le_def map_inf_def by blast
qed


paragraph ‹Knaster-Tarski›

theorem solution_exists:
  shows "least_solution (⨅m {u. (λx. Some (eq T x u)) ⊑m u})"
proof -
  let ?H = "{u. (λx. Some (eq T x u)) ⊑m u}"
  let ?a = "⨅m?H"
  have "(λx. Some (eq T x ?a)) = ?a"
  proof -
    have 1: "(λy. Some (eq T y ?a)) ⊑m ?a"
    proof -
      {
        fix s
        assume "s ∈ ?H"
        then have "?a ⊑m s" by (rule map_inf_lower)
        with mono_eq have "(λx. Some (eq T x ?a)) ⊑m (λx. Some (eq T x s))"
          unfolding map_le_def is_mono_eq_def mlup_def by auto
        with ‹s ∈ ?H› have "(λx. Some (eq T x ?a)) ⊑m s"
          unfolding map_le_def mlup_def
          using order.trans by fastforce
      }
      then show ?thesis using map_inf_greatest[of ?H] by blast
    qed
    have 2: "?a ⊑m (λx. Some (eq T x ?a))"
    proof -
      from mono_eq and ‹(λx. Some (eq T x ?a)) ⊑m ?a›
      have "(λx. Some (eq T x (λx. Some (eq T x ?a)))) ⊑m (λx. Some (eq T x ?a))"
        unfolding map_le_def is_mono_eq_def mlup_def by simp
      then show ?thesis using map_inf_lower by auto
    qed
    from 1 and 2 show ?thesis
      unfolding map_le_def map_inf_def mlup_def option_inf_def
      using order_antisym by fastforce
  qed
  then have "∀x. eq T x ?a = mlup ?a x"
    unfolding mlup_def
    by (metis (no_types, lifting) option.simps(5))
  then show ?thesis
    using map_inf_lower
    unfolding mlup_def map_le_def by auto
qed

lemma least_is_least_post_solution:
  "least_post_solution (⨅m {u. (λx. Some (eq T x u)) ⊑m u})"
proof -
  let ?H = "{u. (λx. Some (eq T x u)) ⊑m u}"
  let ?a = "⨅m?H"
  have "least_solution ?a" using solution_exists by auto
  moreover have "∀σ'. post_solution σ' ⟶ ?a ⊑m σ'"
    using map_inf_lower[of _ ?H] unfolding map_le_def mlup_def by simp
  ultimately show ?thesis by auto
qed


subsubsection ‹Exactness Proof›

lemma exact_on_partial_solution:
  assumes "part_solution T x sigma vars"
    and "∀σl. solution σl ⟶ sigma ⊑m σl"
  shows "∃σl. least_solution σl ∧ (∀y ∈ vars. mlup sigma y = mlup σl y)"
proof -
  let ?σl = "⨅m {u. (λx. Some (eq T x u)) ⊑m u}"
  have least_sol: "least_solution ?σl" using solution_exists by blast

  let ?σe = "λy. if y ∈ vars then sigma y else Some ⊤"
  have ext_post: "post_solution ?σe"
  proof
    fix y
    show "eq T y ?σe ≤ mlup ?σe y"
    proof (cases "y ∈ vars")
      case True
      then have "eq T y ?σe = eq T y sigma"
        using solution_sufficient assms(1)
        unfolding mlup_def by (smt (verit) subsetD)
      also have "... = mlup sigma y" using True partial_correctness assms(1,2) by blast
      finally have "eq T y ?σe = mlup ?σe y" by (simp add: True mlup_def)
      then show ?thesis by auto
    next
      case False
      then have "eq T y ?σe ≤ ⊤" by simp
      then have "eq T y ?σe ≤ mlup ?σe y" by (simp add: False mlup_def)
      then show ?thesis by blast
    qed
  qed

  have "∀y ∈ vars. mlup sigma y = mlup ?σl y"
  proof -
    have "?σl ⊑m ?σe"
    proof -
      have least_post: "least_post_solution ?σl"
        using least_is_least_post_solution by auto
      then show ?thesis using ext_post by blast
    qed
    moreover have "∀u∈vars. mlup ?σe u ≤ mlup ?σl u"
    proof -
      have "sigma ⊑m ?σl" using assms(2) least_sol by blast
      then show ?thesis unfolding map_le_def mlup_def by auto
    qed
    ultimately have "∀u∈vars. mlup ?σe u = mlup ?σl u"
      using order_antisym_conv map_le_def by fastforce
    then show ?thesis by (simp add: mlup_def)
  qed

  then show ?thesis using least_sol by auto
qed

theorem exactness:
  assumes "solve_dom x"
    and "solve x = sigma"
  shows "∃σl. least_solution σl ∧ (∀y ∈ reach T sigma x. mlup sigma y = mlup σl y)"
  (* TODO only mlup same or actual entry same? *)
proof -
  have least: "∀σl. solution σl ⟶ sigma ⊑m σl"
    using less_than_solution[OF assms] reach_empty_capped
    unfolding solve_dom_def invariant_def map_le_def mlup_def
    by simp
  then show ?thesis
    using exact_on_partial_solution[of x "reach T sigma x" sigma] assms partial_correctness
    by fast
qed

end ― ‹end of locale for @{term TD_plain_mono}›


subsection ‹Termination›

locale TD_plain_term = TD_plain_mono T
  for T :: "'x ⇒ ('x, 'd :: complete_lattice) strategy_tree" +
  assumes acc: "wf {(y::'d,x::'d). x < y}"
      and finite_vars: "finite (UNIV :: 'x set)"
begin

subsubsection‹Construction of Wellfounded Relation for Termination Proof›

type_synonym ('a,'b) query_in = "'a × 'a × ('a, 'b) state"
type_synonym ('a,'b) iterate_in = "'a × ('a, 'b) state"
type_synonym ('a,'b) eval_in = "'a × ('a, 'b) strategy_tree × ('a, 'b) state"

text‹For finding an appropriate measure for the mutual recursive iterate, query and eval functions
  consider the following:
  - query terminates either immediately (when x is in called) or calls iterate with a called set
    increased by one -> measure |U - c|
  - for iterate to terminate the evalaluation of x's right-hand side (with unchanged @{term c} and
    @{term σ}) must terminate. If no fixpoint is found, iterate is called with a greater sigma
    (according to @{const sigma_increasing} relation).
  - eval terminates immediately for Answer nodes and for Query nodes if the query call to the
    associated unknown (with unchanged c and @{term σ}) terminates and the following eval call with
    smaller strategy tree terminates $\rightarrow$ measure depth, i.e., number of Query nodes in t
›

text‹As wellfounded relation over input parameters used for proving termination we will use the
  lexicographic product of unknowns not in called, an ordering between the mutual recursive
  fuctions, the subtree relation constructed above and the relation of sigmas based on point-wise
  comparisons of entries according to the lattice order.
›

definition prod_relation :: "(('x set × nat × ('x, 'd) strategy_tree × ('x, 'd) map)
    × ('x set × nat × ('x, 'd) strategy_tree × ('x, 'd) map)) set" where
  "prod_relation = lex_prod c_relation (lex_prod (measure (λi. i)) (lex_prod strict_subt_relation
    sigma_increasing))"

lemma wf_prod_relation: "wf prod_relation"
  unfolding  prod_relation_def c_relation_def
  using wf_sigma_increasing[OF acc finite_vars] wf_rhs_relation by auto

definition term_relation :: "((('x,'d) query_in + ('x,'d) iterate_in + ('x,'d) eval_in) ×
   (('x,'d) query_in + ('x,'d) iterate_in + ('x,'d) eval_in)) set"
  where
  "term_relation = inv_image prod_relation (λst. case st of
      Inl (x,y,state) ⇒ (called state, 1, T x, σ state)
    | Inr (Inl (x,state)) ⇒ (called state, 3, T x, σ state)
    | Inr (Inr (x,t,state)) ⇒ (called state, 2, t, σ state))"

lemma wf_term_relation: "wf term_relation"
  unfolding term_relation_def
  by (simp add: wf_prod_relation)


subsubsection‹Auxiliary Lemmas›

lemma "x = y ⟹ mlup (σ1(x ↦ xd_new)) y = xd_new"
  by (simp add: mlup_def)

text‹In the following helper lemma we proof that for every step the computed @{term σ'} is greater
  than the one provided as parameter, given that every entry in the starting @{term σ} is smaller
  or equal than the result of the corresponding evaluation of the right-hand side.›

lemma sigma_only_increasing:
  shows "query_dom y x state
    ⟹ (xd, state') = query y x state
    ⟹ (∀ξ. mlup (σ state) ξ ≤ eq T ξ (σ state))
    ⟹ σ state ⊑m σ state'
      ∧ (∀ξ. mlup (σ state') ξ ≤ eq T ξ (σ state'))"
    and "iterate_dom x state
    ⟹ (xd, state') = iterate x state
    ⟹ x ∈ called state
    ⟹ (∀ξ. mlup (σ state) ξ ≤ eq T ξ (σ state))
    ⟹ σ state ⊑m σ state'
      ∧ (∀ξ. mlup (σ state') ξ ≤ eq T ξ (σ state'))"
    and "eval_dom x t state
    ⟹ (xd, state') = eval x t state
    ⟹ (∀ξ. mlup (σ state) ξ ≤ eq T ξ (σ state))
    ⟹ σ state ⊑m σ state'
      ∧ (∀ξ. mlup (σ state') ξ ≤ eq T ξ (σ state'))"
proof(induction arbitrary: xd state' and xd state' and xd state' rule: query_iterate_eval_pinduct)
  case (Query y x state)
  show ?case
    using Query.IH(1) Query.prems(1)
  proof (cases rule: query_iterate_lookup_cases)
    case Iterate
    then show ?thesis using Query.IH(2)[OF Iterate(4,2)] Query.prems(2) by simp
  next
    case Lookup
    then show ?thesis using Query.prems by auto
  qed
next
  case (Iterate x state)
  show ?case
    using Iterate.IH(1) Iterate.prems(1)
  proof(cases rule: iterate_continue_fixpoint_cases)
    case (Fixpoint state1)
    show ?thesis using Iterate.IH(2)[OF Fixpoint(2)] Iterate.prems(3) Fixpoint(4) by auto
  next
    case (Continue state1 xd_new)
    have IH1: "σ state ⊑m σ state1" and IH2: "mlup (σ state1) ξ ≤ eq T ξ (σ state1)" for ξ
      using Iterate.IH(2)[OF Continue(2)] Iterate.prems(2,3) sigma_increasing_def by auto
    moreover have "(σ state1)(x ↦ xd_new) ⊑m σ state'"
      and "∀ξ. mlup (σ state') ξ ≤ eq T ξ (σ state')"
    proof -
      have "eq T x (σ state1) = xd_new"
        using partial_correctness_ind(3)[OF Continue(1,2)] unfolding invariant_def by auto
      then have "eq T ξ (σ state1) ≤ eq T ξ ((σ state1)(x ↦ xd_new))" for ξ
        using IH2 mono_eq unfolding is_mono_eq_def mlup_def by auto
      then have "mlup ((σ state1)(x ↦ xd_new)) ξ ≤ eq T ξ ((σ state1)(x ↦ xd_new))" for ξ
        using order.trans[OF IH2]
        by (auto simp add: mlup_def simp flip: ‹eq T x (σ state1) = xd_new›)
      then show "(σ state1)(x ↦ xd_new) ⊑m σ state'"
          and "∀ξ. mlup (σ state') ξ ≤ eq T ξ (σ state')"
        using Iterate.IH(3)[OF Continue(2) refl Continue(3,5)] Iterate.prems(2)
          c_same(3)[OF Continue(1,2)]
        unfolding invariant_def sigma_increasing_def
        by (auto simp del: fun_upd_apply)
    qed
    moreover have "σ state1 ⊏m (σ state1)(x ↦ xd_new)"
    proof -
      have "mlup (σ state1) x ≤ xd_new"
      proof -
        have "σ state ⊑m σ state1" using ‹σ state ⊑m σ state1› finite_vars by auto
        then have "eq T x (σ state) ≤ eq T x (σ state1)"
          using mono_eq unfolding is_mono_eq_def by simp
        moreover have "mlup (σ state1) x ≤ eq T x (σ state1)" using IH2 by blast
        moreover have "eq T x (σ state1) = xd_new"
          using Continue(1,2) Iterate.prems(2) partial_correctness_ind(3)[where ?s = "{}"]
          by (metis empty_Diff empty_iff eq_def invariant_def)
        ultimately show ?thesis by auto
      qed
      then have "mlup (σ state1) x < xd_new" using Continue(3) by auto
      then have "mlup (σ state1) x < mlup ((σ state1)(x ↦ xd_new)) x" by (simp add: mlup_def)
      moreover have "σ state1 ⊑m (σ state1)(x ↦ xd_new)"
      proof -
        have "mlup (σ state1) y ≤ mlup ((σ state1)(x ↦ xd_new)) y" if "y ∈ UNIV" for y
        proof (cases "y = x")
          case True
          then show ?thesis using ‹mlup (σ state1) x < xd_new› True by (simp add: mlup_def)
        next
          case False
          then show ?thesis by (simp add: mlup_def)
        qed
        then show ?thesis by auto
      qed
      then show ?thesis
        using ‹mlup (σ state1) x < mlup ((σ state1)(x ↦ xd_new)) x› finite_vars by blast
    qed
    ultimately show ?thesis by (meson order.trans)
  qed
next
  case (Eval x t state)
  show ?case
    using Eval.IH(1) Eval.prems(1)
  proof(cases rule: eval_query_answer_cases)
    case (Query y g yd state1)
    have "σ state ⊑m σ state1" and "∀ξ. mlup (σ state1) ξ ≤ eq T ξ (σ state1)"
      using Eval.IH(2)[OF Query(1,3)] Eval.prems(2) by auto
    moreover have "σ state1 ⊑m σ state'" and "∀ξ. mlup (σ state') ξ ≤ eq T ξ (σ state')"
      using Eval.IH(3)[OF Query(1) Query(3) _ Query(5)] invariant_def
      using calculation(2) by blast+
    ultimately have "σ state ⊑m σ state'" using order.trans by blast
    then show ?thesis using Query(1) ‹∀ξ. mlup (σ state') ξ ≤ eq T ξ (σ state')› by auto
  next
    case Answer
    then show ?thesis using Eval by blast
  qed
qed

subsubsection‹Termination Proof›

text ‹We proof termination conditionally for initial @{term σ} that are smaller than the result of
  a right-hand side evaluation with @{term σ}.›

abbreviation smaller_sigma where
  "smaller_sigma st ≡ (let sigma = case st of Inl (x, y, state) ⇒ σ state
    | Inr (Inl (x, state)) ⇒ σ state | Inr (Inr (x, t, state)) ⇒ σ state in
    ∀ξ. mlup sigma ξ ≤ eq T ξ sigma)"

lemma cond_termination: "query_iterate_eval_dom st" if "smaller_sigma st"
  using wf_term_relation that
proof (induction st rule: wf_induct_rule[where ?r = term_relation])
  case (less st)
  then
  consider (Query) x y state where "st = Inl (y, x, state)"
    | (Iterate) x state where "st = Inr (Inl (x, state))"
    | (Eval) x t state where "st = Inr (Inr (x, t, state))"
    by (metis sumE surj_pair)
  then show ?case
  proof cases
    case Query
    show ?thesis
    proof (cases "x ∈ called state")
      case True
      then show ?thesis using Query query_iterate_eval.domintros(1) by auto
    next
      case False
      then have "card (UNIV - insert x (called state)) < card (UNIV - called state)"
        using finite_vars finite_vars
        by (metis DiffI Diff_insert card_Diff1_less_iff finite_Diff iso_tuple_UNIV_I)
      then have "(insert x (called state), called state) ∈ c_relation"
        unfolding c_relation_def by (meson in_measure)
      then have "(Inr (Inl (x, state⦇called := insert x (called state)⦈)), st) ∈ term_relation"
        unfolding term_relation_def prod_relation_def Query by force
      then show ?thesis using Query less by (simp add: query_iterate_eval.domintros(1))
      qed
  next
    case Iterate
    then have "(Inr (Inr (x, T x, state)), st) ∈ term_relation"
      unfolding term_relation_def prod_relation_def by simp
    then have eval_dom: "eval_dom x (T x) state" using less.IH less.prems Iterate by simp
    then obtain state' xd where eval: "(xd, state') = eval x (T x) state" by (metis surj_pair)
    note c_same = c_same(3)[OF eval_dom eval]
    show ?thesis
    proof (cases "xd = mlup (σ state') x")
      case True
      then show ?thesis using eval_dom eval query_iterate_eval.domintros(2) Iterate eval_dom_def
        by (metis Pair_inject)
    next
      case False
      have "iterate_dom x (state'⦇σ := (σ state')(x ↦ xd)⦈)"
      proof -
        have "σ state ⊑m σ state'" and "(∀ξ. mlup (σ state') ξ ≤ eq T ξ (σ state'))"
          using sigma_only_increasing(3)[OF eval_dom eval] less.prems Iterate
          by (auto simp add: Let_def)
        have "σ state ⊏m (σ state')(x ↦ xd)" and
          " mlup ((σ state')(x ↦ xd)) ξ ≤ eq T ξ ((σ state')(x ↦ xd))" for ξ
        proof -
          have "invariant {} state" unfolding invariant_def by auto
          then have "invariant (reach_cap_tree T (σ state') (called state') (T x)) state'" and
              "traverse_rhs (T x) (σ state') = xd"
            using partial_correctness_ind(3)[OF eval_dom eval, where ?s = "{}"]
            by auto
          then show "mlup ((σ state')(x ↦ xd)) ξ ≤ eq T ξ ((σ state')(x ↦ xd))"
            using ‹∀ξ. mlup (σ state') ξ ≤ eq T ξ (σ state')› mono_eq
            unfolding is_mono_eq_def eq_def mlup_def
            by (smt (verit, ccfv_SIG) dual_order.order_iff_strict fun_upd_other fun_upd_same
                option.simps(5) order.trans)
          have "mlup (σ state') x ≤ eq T x (σ state')"
            using ‹∀ξ. mlup (σ state') ξ ≤ eq T ξ (σ state')› by auto
          then have "mlup (σ state') x ≤ xd" by (simp add: ‹traverse_rhs (T x) (σ state') = xd›)
          then show "σ state ⊏m (σ state')(x ↦ xd)"
            using ‹σ state ⊑m σ state'› False finite_vars order_le_neq_trans order_le_less_trans
            unfolding mlup_def
            by fastforce
        qed
        then have "(Inr (Inl (x, state'⦇σ := (σ state')(x ↦ xd)⦈)), st) ∈ term_relation"
          unfolding term_relation_def prod_relation_def sigma_increasing_def
          by (simp add: Iterate c_same)
        then show ?thesis
          unfolding iterate_dom_def
          using less.IH ‹⋀ξ. mlup ((σ state')(x ↦ xd)) ξ ≤ eq T ξ ((σ state')(x ↦ xd))›
          by (auto simp add: Let_def simp del: fun_upd_apply)
      qed
      then show ?thesis
        using Iterate query_iterate_eval.domintros(2)[of x state] eval eval_dom False
        unfolding eval_dom_def iterate_dom_def
        by (metis Pair_inject)
    qed
  next
    case Eval
    show ?thesis
    proof (cases t)
      case (Answer d)
      then show ?thesis using Eval TD_plain.query_iterate_eval.domintros(3) by fastforce
    next
      case (Query y g)
      then have "(Inl (x, y, state), st) ∈ term_relation"
        unfolding term_relation_def prod_relation_def Eval by simp
      then have query_dom: "query_dom x y state" using less.IH less.prems Eval by simp
      then obtain state' d' where query: "(d', state') = query x y state" by (metis surj_pair)
      note c_same = c_same(1)[OF query_dom query]
      have "eval_dom x (g d') state'"
      proof -
        have "d' = mlup (σ state') y" using query_fmlookup query_dom_def query query_dom by metis
        then have "(g d', t) ∈ strict_subt_relation" using Query direct_subt.intros by fastforce
        then have "(Inr (Inr (x, g d', state')), st) ∈ term_relation"
          using Eval c_same unfolding term_relation_def prod_relation_def by simp
        moreover have "smaller_sigma (Inr (Inr (x, g d', state')))"
          using sigma_only_increasing(1)[OF query_dom query] less.prems Eval
          by (auto simp add: Let_def)
        ultimately show ?thesis using less.IH eval_dom_def by blast
      qed
      then show ?thesis
        using query_iterate_eval.domintros(3)[of t x state] Query Eval query query_dom
        unfolding query_dom_def eval_dom_def
        by (smt (verit, best) Pair_inject strategy_tree.inject(2))
    qed
  qed
qed

corollary terminating: "solve_dom x"
  unfolding solve_dom_def
  using cond_termination[of "Inr (Inl (x, ⦇called = {x}, σ = Map.empty⦈))"]
  by (auto simp add: Let_def mlup_def)

end ― ‹end of locale for termination proof›

end