Theory TD

section ‹The Top-Down Solver›

text ‹\label{sec:td}›

text‹In this theory we proof the partial correctness of the original TD by establishing its
  equivalence with the TD\_plain. Compared to the TD\_plain, it additionally tracks a set of
  currently stable unknowns @{term stabl}, and a map @{term infl} collecting for each unknown
  @{term x} a list of unknowns influenced by it. This allows for the optimization that skips the
  re-evaluation of unknowns which are already stable. It does, however, also require a
  destabilization mechanism triggering re-evaluation of all unknowns possibly affected by an unknown
  whose value has changed.
›

theory TD
  imports Basics Destabilization TD_plain
begin

declare fun_upd_apply[simp del]


subsection‹Definition of the Solver Algorithm›

record ('x,'d) state = "('x,'d) state" +
  infl :: "('x, 'x list) fmap"
  stabl :: "'x set"

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

text‹Apart from passing the additional arguments for the solver state, the @{term iterate} function
  contains, compared to the TD\_plain, an additional check to skip iteration of already stable
  unknowns. Furthermore, the helper function @{term destabilize} is called whenever the newly
  evalauated value of an unknown changed compared to the value tracked in @{term σ}. Lastly, a
  dependency is recorded whenever returning from a @{term query} call for unknown  @{term x} within
  the evaluation of right-hand side of unknown @{term y}.›

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 = (
    let (xd, state) =
      if x ∈ called state then
        (mlup (σ state) x, state)
      else
        iterate x (state⦇called := (insert x (called state))⦈)
    in (xd, state⦇infl := fminsert (infl state) x y⦈))"
| "iterate x state = (
    if x ∉ stabl state then
      let (d_new, state) = eval x (T x) (state⦇stabl := insert x (stabl state)⦈) in
      if mlup (σ state) x = d_new then
        (d_new, state⦇called := called state - {x}⦈)
      else
        let (i, s) = destab x (infl state) (stabl state) in
        iterate x (state⦇infl := i, stabl := s, σ := (σ state)(x ↦ d_new)⦈)
    else
      (mlup (σ state) x, state⦇called := called state - {x}⦈))"
| "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 set × ('x, 'd) map" where
  "solve x = (
    let (_, state) = iterate x ⦇called = {x}, σ = Map.empty, infl = fmempty, stabl = {}⦈
    in (stabl state, σ 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, infl = fmempty, stabl = {}⦈"

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 such that the rule is easier to instantiate and gives comprehensible
names to the cases.›

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
    ⟹ x ∉ stabl state ⟹ (eval_dom x (T x) (state⦇stabl := insert x (stabl state)⦈) ∧
        ((xd_new, state1) = eval x (T x) (state⦇stabl := insert x (stabl state)⦈)
          ⟶ mlup (σ state1) x ≠ xd_new ⟶ (infl2, stabl2) = destab x (infl state1) (stabl state1)
          ⟶ iterate_dom x (state1⦇infl := infl2, stabl := stabl2, σ := (σ state1)(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 state1 infl2 stabl2 and y g yd state'
    rule: query_iterate_eval_pinduct)
  case (Query y x state)
  then show ?case using query_iterate_eval.domintros(2) by fastforce
next
  case (Iterate x state)
  then show ?case
    using query_iterate_eval.domintros(2)[folded dom_defs, of x
          "state1⦇infl := infl2, stabl := stabl2, σ := (σ state1)(x ↦ xd_new)⦈"]
        query_iterate_eval.domintros(3)
    by auto
next
  case (Eval x 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 (Stable) "state' = state⦇called := called state - {x}⦈"
    and "mlup (σ state) x = xd"
    and "x ∈ stabl state"
  | (Fixpoint) state1
  where "eval_dom x (T x) (state⦇stabl := insert x (stabl state)⦈)"
    and "(xd, state1) = eval x (T x) (state⦇stabl := insert x (stabl state)⦈)"
    and "mlup (σ state') x = xd"
    and "state' = state1⦇called := called state1 - {x}⦈"
    and "x ∉ stabl state"
  | (Continue) state1 xd_new stabl2 infl2
  where "eval_dom x (T x) (state⦇stabl := insert x (stabl state)⦈)"
    and "(xd_new, state1) = eval x (T x) (state⦇stabl := insert x (stabl state)⦈)"
    and "mlup (σ state1) x ≠ xd_new"
    and "(infl2, stabl2) = destab x (infl state1) (stabl state1)"
    and "iterate_dom x (state1⦇infl := infl2, stabl := stabl2, σ := (σ state1)(x ↦ xd_new)⦈)"
    and "(xd, state') =
         iterate x (state1⦇infl := infl2, stabl := stabl2, σ := (σ state1)(x ↦ xd_new)⦈)"
    and "x ∉ stabl state"
proof(cases "x ∈ stabl state" rule: case_split[case_names Stable Unstable])
  case Stable
  then show ?thesis using that(1) assms by auto
next
  case Unstable
  then have sldom: "eval_dom x (T x) (state⦇stabl := insert x (stabl state)⦈)"
    using assms(1) dom_backwards_pinduct(2)
    by simp
  then obtain xd_new state1
    where slapp: "eval x (T x) (state⦇stabl := insert x (stabl state)⦈) = (xd_new, state1)"
    by (cases "eval x (T x) (state⦇stabl := insert x (stabl state)⦈)") auto
  show ?thesis
  proof (cases "mlup (σ state1) x = xd_new")
    case True
    then show ?thesis
      using Unstable sldom slapp assms that(2) by auto
  next
    case False
    then obtain infl2 stabl2 where
      destab: "destab x (infl state1) (stabl state1) = (infl2, stabl2)"
      by (cases "destab x (infl state1) (stabl state1)")
    let ?state2 = "(state1⦇infl := infl2, stabl := stabl2, σ := (σ state1)(x ↦ xd_new)⦈)"
    have dom: "iterate_dom x ?state2"
      and "iterate x state = iterate x ?state2"
      and app: "iterate x ?state2 = (xd, state')"
      using Unstable False slapp assms(1-2) dom_backwards_pinduct(2) destab
      by auto
    then show ?thesis using sldom slapp Unstable False destab that(3) by auto
  qed
qed

lemma query_iterate_lookup_cases_basic [consumes 2]:
  assumes "query_dom y x state"
    and "(xd, state') = query y x state"
  obtains (Iterate) state1
  where "iterate_dom x (state⦇called := insert x (called state)⦈)"
    and "(xd, state1) = iterate x (state⦇called := insert x (called state)⦈)"
    and "state' = state1⦇infl := fminsert (infl state1) x y⦈"
    and "x ∉ called state"
  | (Lookup) "mlup (σ state) x = xd"
    and "state' = state⦇infl := fminsert (infl state) x y⦈"
    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 x y state)
  show ?case using Query(1,3)
  proof (cases rule: query_iterate_lookup_cases_basic)
    case (Iterate state1)
    then show ?thesis using Query(2)[of xd state1] 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 Stable
    then show ?thesis by simp
  next
    case Fixpoint
    then show ?thesis using Iterate.IH(2) by simp
  next
    case (Continue state1)
    then show ?thesis
      using Iterate.IH(2)[OF Continue(7,2)]
        Iterate.IH(3)[OF Continue(7,2) _ _ Continue(4) _ Continue(6)]
      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 stabl2 infl2)
    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 y x state]
  by (auto split: prod.splits if_splits)

lemma query_iterate_lookup_cases [consumes 2]:
  assumes "query_dom y x state"
    and "(xd, state') = query y x state"
  obtains (Iterate) state1
  where "iterate_dom x (state⦇called := insert x (called state)⦈)"
    and "(xd, state1) = iterate x (state⦇called := insert x (called state)⦈)"
    and "state' = state1⦇infl := fminsert (infl state1) x y⦈"
    and "mlup (σ state') x = xd"
    and "x ∉ called state"
  | (Lookup) "mlup (σ state) x = xd"
    and "state' = state⦇infl := fminsert (infl state) x y⦈"
    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‹To derive executable code for the TD, we do a program refinement and define an
  equivalent solve function based on partial\_function with options that can be used for the code
  generation.›

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) ⇒ Option.bind
      (if x ∈ called state then
        Some (mlup (σ state) x, state)
      else
        solve_rec_c (I (x, state⦇called := insert x (called state)⦈)))
      (λ (xd, state). Some (xd, state⦇infl := fminsert (infl state) x y⦈))
  | I (x,state) ⇒
      if x ∉ stabl state then Option.bind (
        solve_rec_c (E (x, (T x), state⦇stabl := insert x (stabl state)⦈))) (λ(d_new, state).
        if mlup (σ state) x = d_new then
          Some (d_new, state⦇called := called state - {x}⦈)
        else
          let (infl, stabl) = destab x (infl state) (stabl state) in
          solve_rec_c (I (x, state⦇infl := infl, stabl := stabl, σ := (σ state)(x ↦ d_new)⦈)))
      else
        Some (mlup (σ state) x, state⦇called := called state - {x}⦈)
  | 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))))))"

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

declare destab.simps[code]
declare destab_iter.simps[code]
declare solve_rec_c.simps[simp,code]

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

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

text‹We prove the equivalence of 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⦇infl := fminsert (infl state) x y⦈)"
      by simp
    moreover have "query y x state = (mlup (σ state) x, state⦇infl := fminsert (infl state) x y⦈)"
      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
    obtain d1 state1 where
        I: "iterate x (state⦇called := insert x (called state)⦈) = (d1, state1)"
      by fastforce
    then have J: "query y x state = (d1, state1⦇infl := fminsert (infl state1) x y⦈)"
      using False Query.IH(1) query.pelims[folded dom_defs] by fastforce
    then have "solve_rec_c (I (x, state⦇called := insert x (called state)⦈)) = Some (d1, state1)"
      using Query(2) False I by (simp add: solve_rec_c_dom_def)
    then have "solve_rec_c (Q (y, x, state)) =
        Some (d1, state1⦇infl := fminsert (infl state1) x y⦈)"
      using False by simp
    moreover have "solve_rec_c_dom (Q (y, x, state))"
      using Query(2) False unfolding solve_rec_c_dom_def by fastforce
    ultimately show ?thesis using Query J unfolding solve_rec_c_dom_def by auto
  qed
next
  case (Iterate x state)
  show ?case
  proof (cases "x ∈ stabl state")
    case True
    have "iterate_dom x state ∧
        iterate x state = (mlup (σ state) x, state⦇called := (called state) - {x}⦈)"
      using True iterate.psimps query_iterate_eval.domintros(2)
      unfolding iterate_dom_def
      by fastforce
    then show ?thesis using True unfolding solve_rec_c_dom_def by auto
  next
    case False
    obtain d1 state1 where
        eval: "eval x (T x) (state⦇stabl := insert x (stabl state)⦈) = (d1, state1)"
        "solve_rec_c (E (x, T x, state⦇stabl := insert x (stabl state)⦈)) = Some (d1, state1)"
      using Iterate(2) False unfolding solve_rec_c_dom_def by force
    show ?thesis
    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 False by simp
      moreover have "solve_rec_c (I (x, state)) = Some (d1, state1⦇called := called state1 - {x}⦈)"
        using eval False True by simp
      ultimately show ?thesis unfolding solve_rec_c_dom_def by simp
    next
      case False
      obtain infl2 stabl2 where destab: "(infl2, stabl2) = destab x (infl state1) (stabl state1)"
        by (cases "destab x (infl state1) (stabl state1)") auto
      have
        "solve_rec_c_dom (I (x, state1⦇infl := infl2, stabl := stabl2, σ := (σ state1)(x ↦ d1)⦈))"
        and "iterate x (state1⦇infl := infl2, stabl := stabl2, σ := (σ state1)(x ↦ d1)⦈) =
        the (solve_rec_c (I (x, state1⦇infl := infl2, stabl := stabl2, σ := (σ state1)(x ↦ d1)⦈)))"
        using Iterate(3)[OF ‹x ∉ stabl state› eval(1)[symmetric] refl False destab refl]
        by blast+
      moreover have "iterate x state =
          iterate x (state1⦇infl := infl2, stabl := stabl2, σ := (σ state1)(x ↦ d1)⦈)"
        using eval iterate.psimps[folded dom_defs, OF Iterate(1)] False ‹x ∉ stabl state› destab
        by (auto split: prod.split)
      moreover have "solve_rec_c (I (x, state))
          = solve_rec_c (I (x, state1⦇infl := infl2, stabl := stabl2, σ := (σ state1)(x ↦ d1)⦈))"
        using ‹x ∉ stabl state› False eval(2) destab[symmetric] by simp
      ultimately show ?thesis unfolding solve_rec_c_dom_def by auto
    qed
  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
  next
    case (Query y g)
    then obtain d1 state1 where
        query: "solve_rec_c (Q (x, y, state)) = Some (d1, state1)"
          "query x y state = (d1, state1)"
      using Query Eval(2) unfolding solve_rec_c_dom_def by auto
    then have "solve_rec_c_dom (E (x, g d1, state1))"
        "eval x (g d1) state1 = the (solve_rec_c (E (x, g d1, state1)))"
      using Eval(3)[OF Query] by auto
    moreover have "eval x t state = eval x (g d1) state1"
      using Eval.IH(1) Query eval.psimps query unfolding eval_dom_def
      by fastforce
    moreover have "solve_rec_c (E (x, t, state))
        = solve_rec_c (E (x, g d1, state1))"
      using Query query solve_rec_c.simps[of "E (x,t,state)"]
      by (simp del: solve_rec_c.simps)
    ultimately show ?thesis unfolding solve_rec_c_dom_def by force
  qed
qed

lemma solve_rec_c_query_iterate_eval_equiv:
  shows "solve_rec_c s = Some r ⟹ (case s of
        Q (x,y,state) ⇒ query_dom x y state ∧ query x y 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
    by (smt (verit) ccpo.admissibleI chain_fun curry_def flat_lub_in_chain fun_lub_def
        mem_Collect_eq option.simps(3))
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⦇infl := fminsert (infl state) x y⦈) = Some r"
        using 3(2) Q ‹a = (y, x, state)› by simp
      then show ?thesis using query.psimps[folded query_dom_def, of y x state]
            query_iterate_eval.domintros(1)[folded query_dom_def, of x state] True by simp
    next
      case False
      then have "Option.bind (S (I (x, state⦇called := insert x (called state)⦈))) (λ(d,state).
          Some (d, state⦇infl := fminsert (infl state) x y⦈)) = Some r"
        using 3(2) Q ‹a = (y, x, state)› by simp
      then obtain d1 state1
        where "S (I (x, state⦇called := insert x (called state)⦈)) = Some (d1 ,state1)"
        and "(d1, state1⦇infl := fminsert (infl state1) x y⦈) = r"
        by (cases "S (I (x, state⦇called := insert x (called state)⦈))") auto
      then have "iterate_dom x (state⦇called := insert x (called state)⦈)
          ∧ iterate x (state⦇called := insert x (called state)⦈) = (d1, state1)"
        using 3(1) unfolding iterate_dom_def by fastforce
      then show ?thesis using False ‹(d1, state1⦇infl := fminsert (infl state1) x y⦈) = r›
        by (simp add: query_iterate_eval.domintros(1) False)
    qed
    then show ?thesis using Q ‹a = (y, x, state)› by simp
  next
    case (I a)
    obtain x state where "a = (x, state)" by fastforce
    show ?thesis
    proof(cases "x ∈ stabl state")
      case True
      then have "(mlup (σ state) x, state⦇called := called state - {x}⦈) = r"
        using I ‹a = (x, state)› 3(2) by simp
      moreover have "iterate_dom x state
          ∧ iterate x state = (mlup (σ state) x, state⦇called := called state - {x}⦈)"
        using True query_iterate_eval.domintros(2) iterate.psimps dom_defs by fastforce
      ultimately show ?thesis using I ‹a = (x, state)› by simp
    next
      case False
      then have IH1: "Option.bind (S (E (x, T x, state⦇stabl := insert x (stabl state)⦈)))
         (λ(d_new, state).
           if mlup (σ state) x = d_new then Some (d_new, state⦇called := (called state) - {x}⦈)
           else let (infl, stabl) = destab x (infl state) (stabl state) in
            S (I (x, state⦇infl := infl, stabl := stabl, σ := (σ state)(x ↦ d_new)⦈))) = Some r"
        using 3(2) I ‹a = (x, state)› by simp
      then obtain d_new state1
        where eval_some: "S (E (x, T x, state⦇stabl := insert x (stabl state)⦈))
          = Some (d_new, state1)"
        using 3(2) I
        by (cases "S (E (x, T x, state⦇stabl := insert x (stabl state)⦈))") auto
      then have eval: "eval_dom x (T x) (state⦇stabl := insert x (stabl state)⦈)
          ∧ eval x (T x) (state⦇stabl := insert x (stabl state)⦈) = (d_new, state1)"
        using 3(1) unfolding TD_plain.eval_dom_def by force
      have "iterate_dom x state ∧ iterate x state = r"
      proof (cases "mlup (σ state1) x = d_new")
        case True
        then have "(d_new, state1⦇called := called state1 - {x}⦈) = r" using IH1 eval_some by simp
        moreover have "iterate_dom x state"
          using query_iterate_eval.domintros(2)[folded dom_defs] False True eval by fastforce
        ultimately show ?thesis
          using iterate.psimps[folded dom_defs] False True eval by fastforce
      next
        case False
        obtain infl2 stabl2 where destab: "(infl2, stabl2) = destab x (infl state1) (stabl state1)"
          by (cases "destab x (infl state1) (stabl state1)") auto
        then have "S (I (x, state1⦇infl := infl2, stabl := stabl2, σ := (σ state1)(x ↦ d_new)⦈)) =
            Some r"
          using IH1 False eval_some by (auto split: prod.splits)
        then have iter_cont:
            "iterate_dom x (state1⦇infl := infl2, stabl := stabl2, σ := (σ state1)(x ↦ d_new)⦈)
            ∧ iterate x (state1⦇infl := infl2, stabl := stabl2, σ := (σ state1)(x ↦ d_new)⦈) = r"
          using 3(1) unfolding iterate_dom_def by fastforce
        then have "iterate_dom x state"
          using query_iterate_eval.domintros(2)[folded dom_defs destab.simps, of x state] eval
            ‹x ∉ stabl state› False destab
          by (cases "destab x (infl state1) (stabl state1)") auto
        then show ?thesis
          using iterate.psimps[folded dom_defs, of x state] ‹x ∉ stabl state› destab eval
            False iter_cont
          by (cases "destab x (infl state1) (stabl state1)") auto
      qed
      then show ?thesis
        using I ‹a = (x, state)› by simp
    qed
  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 eval.psimps[folded dom_defs, OF calculation] by auto
      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 TD_plain.dom_defs by force+
      then show ?thesis
        using query_iterate_eval.domintros(3)[folded dom_defs] eval.psimps[folded dom_defs] Query
        by fastforce
    qed
    then show ?thesis
      using E ‹a = (x, t, state)› by simp
  qed
qed

theorem term_equivalence: "solve_dom x ⟷ solve_c_dom x"
  using solve_rec_c_query_iterate_eval_equiv[of
      "I (x, ⦇called = {x}, σ = λx. None, infl = fmempty, stabl = {}⦈)"]
    query_iterate_eval_solve_rec_c_equiv(2)[of x
      "⦇called = {x}, σ = λx. None, infl = fmempty, stabl = {}⦈"]
  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, infl = fmempty, stabl = {}⦈))")
    fastforce+

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, infl = fmempty, stabl = {}⦈)) = Some r
    ∧ iterate x (⦇called = {x}, σ = λx. None, infl = fmempty, stabl = {}⦈) = r"
    using query_iterate_eval_solve_rec_c_equiv(2)[OF 1[unfolded solve_dom_def]]
    unfolding solve_rec_c_dom_def solve_dom_def
    by fastforce
  then show ?case unfolding solve_c_def solve_def by (auto split: prod.split)
qed

text‹With the equivalence of the refined version and the initial version proven, we can specify a
  the code equation.›

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_c_def solve_def by fastforce
next
  case False
  then have "solve_c x = None" using term_equivalence
    unfolding solve_c_dom_def by (simp split: option.splits) fast
  then show ?thesis by auto
qed

declare solve_rec_c.simps[simp del]

end

text‹Finally, we use a dedicated rewrite rule for the code generation of the solver locale.›

global_interpretation TD_Interp: TD T for T
  defines
    TD_Interp_solve = TD_Interp.solve
  done


subsection ‹Equivalence with TD\_plain›

context TD
begin

subsubsection ‹Predicate for Valid Input States›

text‹For the TD, we extend the predicate of valid solver states of the TD\_plain, to also covers the
  additional data structures @{term stabl} and @{term infl}:›

abbreviation "eq_same sigma vars ≡ ∀u ∈ vars. eq T u sigma = mlup sigma u"

definition invariant where
  "invariant state ≡
    called state ⊆ stabl state
    ∧ eq_same (σ state) (stabl state - called state)
    ∧ fset (fmdom (infl state)) ⊆ stabl state
    ∧ (∀y∈stabl state - called state. ∀x ∈ dep T (σ state) y. y ∈ slookup (infl state) x)"

text‹To carry out the induction proof, we complement the valid solver state invariant, with a
  second predicate @{term update_rel}, that describes the relation between output and input solver
  states.›

abbreviation "update_rel x state state' ≡
    stabl state ⊆ stabl state' ∧
    (∀u ∈ stabl state. slookup (infl state) u ⊆ slookup (infl state') u) ∧
    (∀u. (slookup (infl state') u - slookup (infl state) u) ∩ (stabl state - {x}) = {})"

lemma invariant_simp_c_stabl:
  assumes "x ∈ called state"
    and "invariant (state⦇called := called state - {x}⦈)"
  shows "invariant (state⦇stabl := insert x (stabl state)⦈)"
  using assms
proof -
  have "called state - {x} ⊆ stabl state ≡ called state ⊆ insert x (stabl state)"
    using assms(1)
    by (simp add: subset_insert_iff)
  moreover have "stabl state - (called state - {x}) ⊇ insert x (stabl state) - called state"
    using assms(1)
    by auto
  ultimately show ?thesis
    using assms(2)
    unfolding invariant_def
    by auto
qed


subsubsection ‹Preservation of the Invariant›

text‹In this section, we prove that the destabilization of some unknown that is currently being
  iterated, will preserve the valid solver state invariant.›

lemma destab_upd_preserves_part_sol:
  assumes "(infl2, stabl2) = destab x (infl state1) (stabl state1)"
    and "eq_same (σ state1) (stabl state1 - called state1)"
    and "∀y∈stabl state1 - (called state1 - {x}). ∀x∈dep T (σ state1) y. y ∈ slookup (infl state1) x"
    and "traverse_rhs (T x) (σ state1) = xd'"
  shows "eq_same ((σ state1)(x ↦ xd')) (stabl2 - (called state1 - {x}))"
proof (intro ballI, goal_cases)
  case (1 y)
  have stabl2_def: "stabl2 = stabl state1 - influenced_by (infl state1) x"
    using destab_infl_stabl_relation[OF assms(1)] by auto
  have x_no_dep: "∀y ∈ stabl2 - (called state1 - {x}). x ∉ dep T (σ state1) y"
    using destab_x_no_dep[OF assms(1,3)] by simp
  have eq_y_upd: "eq T y ((σ state1)(x ↦ xd')) = eq T y (σ state1)"
    using 1 eq_mupd_no_dep[of x T "σ state1" y] x_no_dep
    by auto
  show ?case
  proof (cases "y = x")
    case True
    then show ?thesis using assms(4) eq_y_upd unfolding mlup_def by (simp add: fun_upd_same)
  next
    case False
    then have "y ∈ stabl state1 - called state1"
      using 1 stabl2_def by force
    then have "eq T y (σ state1) = mlup (σ state1) y"
      using assms(2) by blast
    then show ?thesis using False eq_y_upd unfolding mlup_def by (simp add: fun_upd_other)
  qed
qed


subsubsection ‹Auxiliary Lemmas for the Equivalence Induction Proof›

lemma stabl_infl_empty:
  assumes "x ∉ s"
    and "fset (fmdom i) ⊆ s"
  shows "slookup i x = {}"
proof (rule ccontr, goal_cases)
  case 1
  then have "x ∈ fset (fmdom i)"
    unfolding fmlookup_default_def by force
  then show ?case using assms by blast
qed

lemma dep_closed_implies_reach_cap_tree_closed:
  assumes "x ∈ s"
    and "∀ξ∈s - (c - {x}). dep T sigma ξ ⊆ s"
  shows "reach_cap T sigma (c - {x}) x ⊆ s"
proof (intro subsetI, goal_cases)
  case (1 y)
  then show ?case using assms
  proof(cases "x = y")
    case False
    then have "y ∈ reach_cap_tree T sigma (c - {x}) (T x)"
      using 1 reach_cap_tree_simp2[of x "c - {x}" T sigma] by auto
    then show ?thesis using assms
    proof(induction)
      case (base y)
      then show ?case using base.hyps unfolding dep_def by auto
    next
      case (step y z)
      then show ?case by (metis (no_types, lifting) Diff_iff insert_subset mk_disjoint_insert)
    qed
  qed simp
qed

lemma dep_subset_stable:
  assumes "fset (fmdom i) ⊆ s"
    and "(∀y∈s - c. ∀x ∈ dep T sigma y. y ∈ slookup i x)"
  shows "(∀ξ∈s - c. dep T sigma ξ ⊆ s)"
  using assms stabl_infl_empty[of _ s]
  by (metis DiffD2 Diff_empty subsetI)

lemma new_lookup_to_infl_not_stabl:
  assumes "∀ξ. (slookup (infl state1) ξ - slookup (infl state) ξ) ∩ stabl state = {}"
    and "x ∉ stabl state"
    and "fset (fmdom (infl state)) ⊆ stabl state"
  shows "influenced_by (infl state1) x ∩ stabl state = {}"
proof -
  have "u ∉ stabl state" if "u ∈ influenced_by (infl state1) x" for u
    using that
  proof (induction rule: influenced_by.induct)
    case (base ys y)
    have "slookup (infl state) x = {}" using stabl_infl_empty[OF assms(2,3)] by auto
    then have "y ∈ slookup (infl state1) x - slookup (infl state) x"
      using base.hyps(1,2) by auto
    then show ?case using base.hyps(1) assms(1,3) by force
  next
    case (step y zs z)
    have "slookup (infl state) y = {}"
      by (meson assms(3) stabl_infl_empty step.IH)
    then have "z ∈ slookup (infl state1) y - slookup (infl state) y"
      by (simp add: step.hyps(2,3))
    then show ?case using assms(1) stabl_infl_empty[OF _ assms(3)] by fastforce
  qed
  then show ?thesis by auto
qed

lemma infl_upd_diff:
  assumes "∀ξ. (slookup i' ξ - slookup i ξ) ∩ s = {}"
  shows "∀ξ. (slookup (fminsert i' x y) ξ - slookup i ξ) ∩ (s - {y}) = {}"
proof(intro allI, goal_cases)
  case (1 ξ)
  show ?case
    using assms
    unfolding fminsert_def fmlookup_default_def
    by (cases "x = ξ") auto
qed

lemma infl_diff_eval_step:
  assumes "s ⊆ s1"
    and "∀ξ. (slookup i' ξ - slookup i1 ξ) ∩ (s1 - {x}) = {}"
    and "∀ξ. (slookup i1 ξ - slookup i ξ) ∩ (s - {x}) = {}"
  shows "∀ξ. (slookup i' ξ - slookup i ξ) ∩ (s - {x}) = {}"
proof(intro allI, goal_cases)
  case (1 ξ)
  have "((slookup i' ξ - slookup i1 ξ) ∪ (slookup i1 ξ - slookup i ξ)) ∩ (s - {x}) = {}"
    using assms by auto
  then show ?case by blast
qed

declare TD_plain.dom_defs[of T,simp]
declare TD_plain.query.psimps[of T,simp]
    and TD_plain.iterate.psimps[of T,simp]
    and TD_plain.eval.psimps[of T,simp]

text‹In the equivalence proof of the TD and the TD\_plain, we need to show that when the TD
  trivially terminates because the queried unknown is already stable and its value is only looked
  up, the evaluation of this unknown @{term x} with TD\_plain also terminates. For this, we exploit
  that the set of stable unknowns is always finite during a terminating solver's run and provide
  the following lemma:›

lemma td_plain_terminates_for_stabl:
  assumes "x ∈ s"
    and "TD_plain.invariant T s (state⦇called := called state - {x}⦈)"
    and "mlup (σ state) x = xd"
    and "finite s"
    and "x ∈ called state"
  shows "TD_plain.iterate_dom T x state"
    and "TD_plain.iterate T x state = (xd, state⦇called := called state - {x}⦈)"
proof(goal_cases)
  have "reach_cap T (σ state) (called state - {x}) x ⊆ s"
    using assms(1,2) dep_closed_implies_reach_cap_tree_closed
    unfolding TD_plain.invariant_def by simp
  from finite_subset[OF this]
  have "finite (reach_cap T (σ state) (called state - {x}) x - (called state - {x}))"
    using assms(4) by simp+
  then have goal: "TD_plain.iterate_dom T x state ∧
      TD_plain.iterate T x state = (xd, state⦇called := called state - {x}⦈)"
    using assms(1-3,5)
  proof(induction "reach_cap T (σ state) (called state - {x}) x - (called state - {x})"
      arbitrary: x state xd rule: finite_psubset_induct)
    case psubset
    have "TD_plain.eval_dom T x t state
      ∧ (traverse_rhs t (σ state), state) = TD_plain.eval T x t state"
      if "t ∈ subt T (σ state) x" for t
      using that
    proof(induction t)
      case (Answer _)
      then show ?case
        using TD_plain.query_iterate_eval.domintros(3)
        by fastforce
    next
      case (Query y g)
      have "reach_cap_tree T (σ state) (insert x (called state - {x})) (T x) ⊆ s"
        using dep_closed_implies_reach_cap_tree_closed[OF psubset.prems(1)]
          psubset.prems(2)[unfolded TD_plain.invariant_def]
        by auto
      then have y_stable: "y ∈ s"
        using dep_subset_reach_cap_tree[where ?T=T]
          subt_implies_dep[OF Query(2)[unfolded subt_def]]
        by blast
      show ?case
      proof(cases "y ∈ called state" rule: case_split[case_names called not_called])
        case called
        then have dom: "TD_plain.query_dom T x y state"
          using TD_plain.query_iterate_eval.domintros(1)[where ?T=T]
          by auto
        moreover have query_val: "(mlup (σ state) y, state) = TD_plain.query T x y state"
          using called
          by (metis TD_plain.query.psimps TD_plain.query_iterate_eval.domintros(1))
        ultimately have "TD_plain.eval_dom T x (Query y g) state"
          using Query.IH[of "g (mlup (σ state) y)"]
            TD_plain.query_iterate_eval.domintros(3)[of "Query y g" T x state] Query.prems
            subt_aux.step subt_def
          by fastforce
        have "g (mlup (σ state) y) ∈ subt_aux (σ state) (T x)"
          using Query.prems subt_aux.step unfolding subt_def by fast
        then have "TD_plain.eval_dom T x (g (mlup (σ state) y)) state"
            and "(traverse_rhs (g (mlup (σ state) y)) (σ state), state) =
            TD_plain.eval T x (g (mlup (σ state) y)) state"
          using Query.IH unfolding subt_def by auto
        then show ?thesis
          using ‹TD_plain.eval_dom T x (Query y g) state› query_val
          by (auto split: strategy_tree.split prod.split)
      next
        case not_called
        then obtain yd where lupy: "mlup (σ state) y = yd" and eqy: "eq T y (σ state) = yd"
          using y_stable psubset.prems(2) unfolding TD_plain.invariant_def by auto
        have ih: "TD_plain.eval_dom T x (g (mlup (σ state) y)) state"
            and "(traverse_rhs (g (mlup (σ state) y)) (σ state), state) =
            TD_plain.eval T x (g (mlup (σ state) y)) state"
          using Query.IH[of "g (mlup (σ state) y)"] Query.prems subt_aux.step subt_def by fast+
        moreover have "reach_cap T (σ state) (called state) y ⊆
            reach_cap T (σ state) (called state - {x}) x"
          using not_called psubset.prems(4) reach_cap_tree_step[of "σ state" y yd T _ g, OF lupy]
            reach_cap_tree_subset_subt[of "Query y g" _ "T x" T "called state", folded subt_def,
            OF Query.prems]
          by (simp add: insert_absorb subset_insertI2)
        then have f_def: "reach_cap T (σ state) (called state) y - called state ⊂
            reach_cap T (σ state) (called state - {x}) x - (called state - {x})"
          using psubset.prems(4)
          by blast
        have "TD_plain.invariant T s state"
          using psubset.prems(1,4) TD_plain.invariant_simp[OF _ psubset.prems(2)]
          by (simp add: insert_absorb)
        then have IH: "TD_plain.iterate_dom T y (state⦇called := insert y (called state)⦈) ∧
            TD_plain.iterate T y (state⦇called := insert y (called state)⦈) =
            (yd, state⦇called := called state - {y}⦈)"
          using f_def y_stable not_called lupy
            psubset.hyps(2)[of y "state⦇called := insert y (called state)⦈"]
          by simp
        then have "TD_plain.query_dom T x y state ∧
            (mlup (σ state) y, state) = TD_plain.query T x y state"
          using not_called lupy TD_plain.query_iterate_eval.domintros(1)[of y _ T]
          by simp
        ultimately show ?thesis
          using TD_plain.query_iterate_eval.domintros(3)[of "Query y g" T x state]
          by fastforce
      qed
    qed
    note IH = this[of "T x", folded eq_def, OF subt_aux.base[of "T x" "σ state", folded subt_def]]
    moreover have "eq T x (σ state) = mlup (σ state) x"
      using psubset.prems(1,2) unfolding TD_plain.invariant_def by auto
    moreover have "TD_plain.iterate_dom T x state"
      using TD_plain.query_iterate_eval.domintros(2)[folded TD_plain.dom_defs, of T x state] IH
        ‹eq T x (σ state) = mlup (σ state) x›
      by (metis prod.inject)
    ultimately show ?case
      using TD_plain.iterate.psimps[folded dom_defs, of T x state] psubset.prems(3)
      by (cases "TD_plain.eval T x (T x) state") auto
  qed
  case 1 show ?case using goal ..
  case 2 show ?case using goal ..
qed


subsubsection ‹TD\_plain ‹→› TD›

text‹Finally, we can prove the equivalence of TD and TD\_plain. We split this proof into two parts:
  first we show that whenever the TD\_plain terminates the TD terminates as well and returns the
  same result, and second we show the other direction, i.e., whenever the TD terminates, the
  TD\_plain terminates as well and returns the same result.›

lemma TD_plain_TD_equivalence_ind:
  shows "TD_plain.query_dom T y x state_plain
    ⟹ (xd, state_plain') = TD_plain.query T y x state_plain 
    ⟹ invariant state
    ⟹ TD_plain.state.truncate state = state_plain
    ⟹ query_dom y x state
        ∧ (∃state'. (xd, state') = query y x state
        ∧ invariant state'
        ∧ y ∈ slookup (infl state') x
        ∧ update_rel y state state'
        ∧ TD_plain.state.truncate state' = state_plain')"
    and "TD_plain.iterate_dom T x state_plain
    ⟹ (xd, state_plain') = TD_plain.iterate T x state_plain
    ⟹ x ∈ called state
    ⟹ invariant (state⦇called := called state - {x}⦈)
    ⟹ TD_plain.state.truncate state = state_plain
    ⟹ iterate_dom x state
        ∧ (∃state'. (xd, state') = iterate x state
        ∧ invariant (state'⦇called := called state' - {x}⦈)
        ∧ x ∈ stabl state'
        ∧ update_rel x state state'
        ∧ TD_plain.state.truncate  state' = state_plain')"
    and "TD_plain.eval_dom T x t state_plain
    ⟹ (xd, state_plain') = TD_plain.eval T x t state_plain
    ⟹ invariant state
    ⟹ x ∈ stabl state
    ⟹ TD_plain.state.truncate state = state_plain
    ⟹ eval_dom x t state
        ∧ (∃state'. (xd, state') = eval x t state
        ∧ invariant state'
        ∧ traverse_rhs t (σ state') = xd
        ∧ (∀y∈dep_aux (σ state') t. x ∈ slookup (infl state') y)
        ∧ update_rel x state state'
        ∧ TD_plain.state.truncate  state' = state_plain')"
proof(induction y x state_plain and x state_plain and x t state_plain
    arbitrary: xd state state_plain' and xd state state_plain' and xd state state_plain'
    rule: TD_plain.query_iterate_eval_pinduct[of T, consumes 1, case_names Query Iterate Eval])
  case (Query y x state_plain)
  show ?case using Query.IH(1) Query.prems(1)
  proof (cases rule:
      TD_plain.query_iterate_lookup_cases[consumes 2, case_names Iterate Lookup])
    case Iterate
    obtain state' where IH: "iterate_dom x (state⦇called := insert x (called state)⦈) ∧
        (xd, state') = iterate x (state⦇called := insert x (called state)⦈) ∧
        invariant (state'⦇called := called state' - {x}⦈) ∧
        x ∈ stabl state' ∧
        update_rel x state state' ∧
        TD_plain.state.truncate state' = state_plain'"
      using Query.IH(2)[OF Iterate(4,2), where ?state1="state⦇called := insert x (called state)⦈"]
        Query.prems(2,3) Iterate(4)
      unfolding TD_plain.state.truncate_def
      by auto
    then show ?thesis
    proof (intro conjI, goal_cases)
      case 1 then show dom: ?case using query_iterate_eval.domintros(1)[folded dom_defs] by auto
      case 2 then show ?case
      proof (intro exI[of _ "state'⦇infl := fminsert (infl state') x y⦈"], intro conjI, goal_cases)
        case 1 then show ?case
          using dom Query.prems(3) Iterate(4) unfolding TD_plain.state.truncate_def
          by (auto split: prod.split)
      next
        case 2 then show ?case
          unfolding invariant_def by (auto simp add: fminsert_def fmlookup_default_def)
      next
        case 6
        then have "∀ξ. (slookup (infl state') ξ - slookup (infl state) ξ) ∩ stabl state = {}"
          by (cases "x ∈ stabl state") auto
        then show ?case
          using infl_upd_diff[of "infl state'" "infl state" _ x y] by auto
      next
        case 7
        then show ?case unfolding TD_plain.state.truncate_def by simp
      qed (auto simp add: fminsert_def fmlookup_default_def)
    qed
  next
    case Lookup
    then show ?thesis using Query.prems(1,2,3)
    proof (intro conjI, goal_cases)
      case 1 then show dom: ?case
        using query_iterate_eval.domintros(1)[of x state]
        unfolding TD_plain.state.truncate_def by auto
      case 2 then show ?case
      proof (intro exI[of _ "state⦇infl := fminsert (infl state) x y⦈"], intro conjI, goal_cases)
        case 1 then show ?case using dom unfolding TD_plain.state.truncate_def by auto
      next
        case 2 then show ?case
          unfolding invariant_def unfolding TD_plain.state.truncate_def
          by (auto simp add: fminsert_def fmlookup_default_def)
      next
        case 6 then show ?case
          using infl_upd_diff[of "infl state" "infl state" _ x] by auto
      qed (auto simp add: fminsert_def fmlookup_default_def TD_plain.state.truncate_def)
    qed
  qed
next
  case (Iterate x state_plain)
  have inv: "invariant (state⦇stabl := insert x (stabl state)⦈)"
    using Iterate.prems(2,3) invariant_simp_c_stabl by auto
  have dep_in_stabl: "∀ξ∈stabl state - (called state - {x}). dep T (σ state) ξ ⊆ stabl state"
    using Iterate.prems(3) dep_subset_stable[of "infl state" "stabl state"] unfolding invariant_def
    by auto
  show ?case
  proof(cases "x ∈ stabl state" rule: case_split[case_names Stable Unstable])
    case Stable
    then show ?thesis
    proof(intro conjI, goal_cases)
      case 1 then show dom: ?case using query_iterate_eval.domintros(2)[of x state] by simp
      case 2 moreover have "σ state = σ state_plain'"
        using Iterate.prems(3) TD_plain.already_solution(2)[OF Iterate.IH(1) Iterate.prems(1) _ 2]
          dep_in_stabl Iterate.prems(2,4)
        unfolding TD_plain.invariant_def invariant_def TD_plain.state.truncate_def by auto
      ultimately show ?case
      proof (intro exI[of _ "state⦇called := called state - {x}⦈"] conjI, goal_cases)
        case 1
        then show ?case
          using dom TD_plain.iterate_fmlookup[OF Iterate.IH(1) Iterate.prems(1)] Iterate.prems(4)
          unfolding TD_plain.state.truncate_def
          by simp
        next
          case 2 then show ?case using Iterate.prems(3) by auto
        next
          case 7 then show ?case
            using Iterate.prems(4) TD_plain.c_same(2)[OF Iterate.IH(1) Iterate.prems(1)]
            unfolding TD_plain.state.truncate_def by fastforce
      qed (auto simp add: TD_plain.state.truncate_def)
    qed
  next
    case Unstable
    show ?thesis using Iterate.IH(1) Iterate.prems(1)
    proof(cases rule:
        TD_plain.iterate_continue_fixpoint_cases[of T, consumes 3, case_names Fixpoint Continue])
      case (Fixpoint state_plain1)
      moreover obtain state1 where
        IH: "eval_dom x (T x) (state⦇stabl := insert x (stabl state)⦈) ∧
        (xd, state1) = eval x (T x) (state⦇stabl := insert x (stabl state)⦈) ∧
        invariant state1 ∧
        eq T x (σ state1) = xd ∧
        (∀y∈dep T (σ state1) x. x ∈ slookup (infl state1) y) ∧
        update_rel x (state⦇stabl := insert x (stabl state)⦈) state1 ∧
        TD_plain.state.truncate state1 = state_plain1"
        using Iterate.IH(2)[OF Fixpoint(2) inv] Iterate.prems(4)
        unfolding TD_plain.state.truncate_def dep_def by auto
      ultimately show ?thesis using Unstable
      proof(intro conjI, goal_cases)
        case 1
        then show dom: ?case
          using query_iterate_eval.domintros(2)[of x state]
          unfolding TD_plain.state.truncate_def
          by (cases "eval x (T x) (state⦇stabl := insert x (stabl state)⦈)"; auto)
        case 2 then show ?case
        proof (intro exI[of _ "state1⦇called := called state1 - {x}⦈"] conjI, goal_cases)
          case 1
          then show ?case
            using dom unfolding TD_plain.state.truncate_def by (auto split: prod.splits)
        next
          case 2 then show ?case unfolding invariant_def TD_plain.state.truncate_def by auto
        next
          case 3 then show ?case using Iterate.prems(2) invariant_def by fastforce
        qed (auto simp add: TD_plain.state.truncate_def)
      qed
    next
      case (Continue state_plain1 xd')
      obtain state1 where IH: "eval_dom x (T x) (state⦇stabl := insert x (stabl state)⦈)"
        "(xd', state1) = eval x (T x) (state⦇stabl := insert x (stabl state)⦈)"
        "invariant state1"
        "eq T x (σ state1) = xd'"
        "(∀y∈dep T (σ state1) x. x ∈ slookup (infl state1) y)"
        "update_rel x (state⦇stabl := insert x (stabl state)⦈) state1"
        "TD_plain.state.truncate state1 = state_plain1"
        using Iterate.IH(2)[OF Continue(2) inv, folded dep_def] Iterate.prems(4)
        unfolding TD_plain.state.truncate_def by auto
      obtain infl2 stabl2 where destab: "(infl2, stabl2) = destab x (infl state1) (stabl state1)"
        by (cases "destab x (infl state1) (stabl state1)"; auto)
      then have
        infl2_def: "infl2 = fmdrop_set (insert x (influenced_by (infl state1) x)) (infl state1)"
        and stabl2_def: "stabl2 = stabl state1 - influenced_by (infl state1) x"
        using destab_infl_stabl_relation[of infl2 stabl2 x "infl state1" "stabl state1"] by auto
      let ?state2 = "state1⦇infl := infl2, stabl := stabl2, σ := (σ state1)(x ↦ xd')⦈"
      have infl_diff: "∀ξ. (slookup (infl state1) ξ - slookup (infl state) ξ) ∩ stabl state = {}"
        using Unstable Iterate.prems(3) IH
        unfolding invariant_def by auto
      have infl_closed: "∀x∈stabl state1 - (called state1 - {x}). ∀y∈dep T (σ state1) x.
          x ∈ slookup (infl state1) y"
        using IH unfolding dep_def invariant_def by auto
      have stabl_inc: "stabl state ⊆ stabl2"
        using IH Iterate.prems(3) new_lookup_to_infl_not_stabl[OF infl_diff Unstable]
        unfolding invariant_def stabl2_def by auto

      have inv2: "invariant (?state2⦇called := called ?state2 - {x}⦈)"
        using IH unfolding invariant_def
      proof(elim conjE, intro conjI, goal_cases)
        case 1
        show ?case
          using Iterate.prems(3) IH(3) stabl_inc
          unfolding invariant_def
          by (auto intro: subset_trans[of _ "stabl state"] simp add: c_same(3)[OF IH(1,2)])
      next
        case 2 then show ?case using destab_upd_preserves_part_sol[OF destab _ infl_closed] by auto
      next
        case 3 then show ?case using destab_preserves_infl_dom_stabl[OF destab] by auto
      next
        case 4 show ?case
        proof(intro ballI, goal_cases)
          case (1 y z)
          have x_no_dep: "x ∉ dep T (σ state1) y" if "y ∈ stabl2 - (called state1 - {x})" for y
            using that infl_closed destab_x_no_dep[OF destab] by blast
          then have "x ∉ dep T (σ state1) y" using 1(1) by simp
          then have "dep T (σ state1) y = dep T ((σ state1)(x ↦ xd')) y"
            using dep_eq[of T "σ state1" _ "(σ state1)(x ↦ xd')"]
            unfolding mlup_def by (simp add: fun_upd_apply)
          then show ?case
            using 1 destab_and_upd_preserves_dep_closed_in_infl[OF destab infl_closed] by auto
        qed
      qed
      obtain state' where ih: "iterate_dom x ?state2"
          "(xd, state') = iterate x ?state2"
          "invariant (state'⦇called := called state' - {x}⦈)"
          "x ∈ stabl state'"
          "update_rel x ?state2 state'"
          "TD_plain.state.truncate state' = state_plain'"
        using Iterate.IH(3)[OF Continue(2) refl Continue(3) Continue(5) _ inv2, folded dom_defs]
          Continue(2,3,5) Iterate.prems(2,4) inv2 IH(7)
        unfolding TD_plain.state.truncate_def
        by (auto simp add: c_same(3)[OF IH(1,2)])
      show ?thesis
      proof (intro conjI, goal_cases)
        case 1 show dom: ?case
          using query_iterate_eval.domintros(2)[of x state] Unstable IH(1,2,7) Continue(3) destab
            ih(1)
          unfolding TD_plain.state.truncate_def
          by (cases "eval x (T x) (state⦇stabl := insert x (stabl state)⦈)";
              cases "destab x (infl state1) (stabl state1)"; auto)
        case 2 then show ?case
        proof (intro exI[of _ state'] conjI, goal_cases)
          case 1
          show ?case
            using dom Unstable IH(2,7) Continue(3) destab ih(2)
            unfolding TD_plain.state.truncate_def by (auto split: prod.splits)
        next
          case 4
          show ?case
            using stabl_inc ih(5) by auto
        next
          case 5  show ?case
          proof(intro ballI subsetI, goal_cases)
            case (1 ξ u)
            have "ξ ∉ insert x (influenced_by (infl state1) x)"
              using 1(1) stabl2_def stabl_inc Unstable by blast
            then have "slookup infl2 ξ = slookup (infl state1) ξ"
              using infl2_def unfolding fmlookup_default_def by auto
            then show ?case using stabl_inc 1 IH(6) ih(5) by force
          qed
        next
          case 6 show ?case
          proof(intro allI, goal_cases)
            case (1 ξ)
            have "slookup infl2 ξ ⊆ slookup (infl state1) ξ" using infl2_def
              unfolding fmlookup_default_def by auto
            moreover have "(slookup (infl state') ξ - slookup infl2 ξ) ∩ (stabl state - {x}) = {}"
              using stabl_inc ih(5) by fastforce
            moreover have "(slookup (infl state1) ξ - slookup (infl state) ξ) ∩
                (stabl state - {x}) = {}"
              using infl_diff stabl_infl_empty[of ξ "stabl state1" "infl state1"]
              by (cases "ξ ∈ stabl state1"; auto)
            ultimately show ?case unfolding stabl2_def by auto
          qed
        qed (auto simp add: ih)
      qed
    qed
  qed
next
  case (Eval x t state_plain)
  show ?case using Eval.IH(1) Eval.prems(1)
  proof(cases rule: TD_plain.eval_query_answer_cases[consumes 2, case_names Query Answer])
    case (Query y g yd state_plain1)
    obtain state1 where IH: "query_dom x y state"
        "(yd, state1) = query x y state"
        "invariant state1"
        "x ∈ slookup (infl state1) y"
        "update_rel x state state1"
        "TD_plain.state.truncate state1 = state_plain1"
      using Eval.IH(2)[OF Query(1,3) Eval.prems(2,4)] by metis
    obtain state' where ih: "eval_dom x (g yd) state1"
      "(xd, state') = eval x (g yd) state1"
      "invariant state'"
      "traverse_rhs (g yd) (σ state') = xd"
      "(∀y∈dep_aux (σ state') (g yd). x ∈ slookup (infl state') y)"
      "update_rel x state1 state'"
      "TD_plain.state.truncate state' = state_plain'"
      using Eval.prems(3) Eval.IH(3)[OF Query(1,3) refl Query(5) IH(3) _ IH(6)] IH(5) by blast
    have td1_inv: "TD_plain.invariant T (stabl state) state_plain"
      using Eval.prems(2,4) dep_subset_stable
      unfolding TD_plain.invariant_def invariant_def TD_plain.state.truncate_def by force
    have td1_inv2: "TD_plain.invariant T (stabl state ∪ reach_cap T (σ state_plain1)
        (called state_plain1) y) state_plain1"
      using TD_plain.partial_correctness_ind(1)[OF Query(2,3) td1_inv] by auto
    have mlup: "mlup (σ state') y = yd"
      using TD_plain.partial_correctness_ind(3)[OF Query(4,5) td1_inv2] Query(6) ih(7)
      unfolding TD_plain.state.truncate_def by auto

    show ?thesis using IH ih
    proof (elim conjE, intro conjI, goal_cases)
      case 1
      show dom: ?case
        using IH(1,2) ih(1) Query(1) query_iterate_eval.domintros(3)[of t x state]
        by (cases "query x y state"; fastforce)
      case 2
      then show ?case
      proof (intro exI[of _ state'] conjI, goal_cases)
        case 1 show ?case using IH(2) ih(2) dom Query(1) by (auto split:prod.splits)
      next
        case 3 then show ?case using Query(1) mlup by auto
      next
        case 4 show ?case
          using IH(3,4) ih(5,6) Query(1) mlup stabl_infl_empty[of y "stabl state1" "infl state1"]
          unfolding invariant_def by auto
      next
        case 6 then show ?case by blast
      next
        case 7 show ?case
          using infl_diff_eval_step[of "stabl state" "stabl state1" "infl state'" _ x] IH(5) ih(6)
          by auto
      qed auto
    qed
  next
    case Answer
    then show ?thesis using Eval.prems(2)
    proof (intro conjI, goal_cases)
      case 1 then show dom: ?case using query_iterate_eval.domintros(3)[of t] by auto
      case 2 then show ?case
      proof (intro exI[of _ state] conjI, goal_cases)
        case 1 then show ?case using dom by auto
        case 8 then show ?case using Eval.prems(4) by auto
      qed auto
    qed
  qed
qed

corollary TD_plain_TD_equivalence:
  assumes "TD_plain.solve_dom T x"
    and "TD_plain.solve T x = sigma"
  shows "∃stabl. solve_dom x ∧ solve x = (stabl, sigma)"
proof -
  let ?init_state = "⦇called = {x}, σ = Map.empty, infl = fmempty, stabl = {}⦈"
  obtain xd state_plain where "sigma = σ state_plain" and
    iter: "(xd, state_plain) = TD_plain.iterate T x ⦇called = {x}, σ = Map.empty⦈"
    using assms(2) unfolding TD_plain.solve_def by (auto split: prod.splits)
  have inv: "invariant ⦇called = {}, σ = Map.empty, infl = fmempty, stabl = {}⦈"
    unfolding invariant_def by fastforce
  have "TD_plain.state.truncate ?init_state = ⦇called = {x}, σ = Map.empty⦈"
    unfolding TD_plain.state.truncate_def by simp
  then obtain state where "iterate_dom x ⦇called = {x}, σ = Map.empty, infl = fmempty, stabl = {}⦈"
      and "iterate x ⦇called = {x}, σ = Map.empty, infl = fmempty, stabl = {}⦈ = (xd, state)"
      and "TD_plain.state.truncate state = state_plain"
    using TD_plain_TD_equivalence_ind(2)[OF assms(1)[unfolded TD_plain.solve_dom_def] iter,
        of ?init_state] inv
    by force
  then show ?thesis
    using ‹sigma = σ state_plain› unfolding solve_dom_def solve_def TD_plain.state.truncate_def
    by auto
qed


subsubsection ‹TD ‹→› TD\_plain›

lemmas TD_plain_dom_defs =
    TD_plain.query_dom_def
    TD_plain.iterate_dom_def
    TD_plain.eval_dom_def

lemma TD_TD_plain_equivalence_ind:
  shows "query_dom y x state
    ⟹ (xd, state') = query y x state
    ⟹ invariant state
    ⟹ finite (stabl state)
    ⟹ invariant state'
      ∧ TD_plain.query_dom T y x (TD_plain.state.truncate state)
      ∧ (xd, TD_plain.state.truncate state') = TD_plain.query T y x (TD_plain.state.truncate state)
      ∧ finite (stabl state')
      ∧ y ∈ slookup (infl state') x
      ∧ update_rel y state state'"
    and "iterate_dom x state
    ⟹ (xd, state') = iterate x state
    ⟹ x ∈ called state
    ⟹ invariant (state⦇called := called state - {x}⦈)
    ⟹ finite (stabl state)
    ⟹ invariant (state'⦇called := called state' - {x}⦈)
      ∧ TD_plain.iterate_dom T x (TD_plain.state.truncate state)
      ∧ (xd, TD_plain.state.truncate state') = TD_plain.iterate T x (TD_plain.state.truncate state)
      ∧ finite (stabl state')
      ∧ x ∈ stabl state'
      ∧ update_rel x state state'"
    and "eval_dom x t state
    ⟹ (xd, state') = eval x t state
    ⟹ invariant state
    ⟹ x ∈ stabl state
    ⟹ finite (stabl state)
    ⟹ invariant state'
      ∧ TD_plain.eval_dom T x t (TD_plain.state.truncate state)
      ∧ (xd, TD_plain.state.truncate state') = TD_plain.eval T x t (TD_plain.state.truncate state)
      ∧ finite (stabl state')
      ∧ traverse_rhs t (σ state') = xd
      ∧ (∀y∈dep_aux (σ state') t. x ∈ slookup (infl state') y)
      ∧ update_rel x state state'"
proof(induction y x state and y 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.IH(1) Query.prems(1)
  proof(cases rule: query_iterate_lookup_cases)
    case (Iterate state1)
    moreover
    note IH = Query.IH(2)[simplified, folded TD_plain_dom_defs, OF Iterate(5,2) Query.prems(2,3)]
    ultimately show ?thesis
    proof(intro conjI, goal_cases)
      case 1 then show ?case unfolding invariant_def
        by (auto simp add: fminsert_def fmlookup_default_def)
    next
      case 2 then show dom: ?case
        using TD_plain.query_iterate_eval.domintros(1)[of x "TD_plain.state.truncate state"]
        unfolding TD_plain.state.truncate_def
        by auto
      case 3 then show ?case using dom unfolding TD_plain.state.truncate_def by auto
    next case 8
      then have "∀ξ. (slookup (infl state1) ξ - slookup (infl state) ξ) ∩ stabl state = {}"
        using Query.prems(3)[unfolded invariant_def]
        by (cases "x ∈ stabl state"; simp)
      then show ?case
        using 8 infl_upd_diff[of "infl state1" "infl state" _ x] Query.prems(2) by auto
    qed (auto simp add: fminsert_def fmlookup_default_def)
  next
    case Lookup
    then show ?thesis using Query.prems(2,3)
    proof(intro conjI, goal_cases)
      case 1 then show ?case unfolding invariant_def
        by (auto simp add: fminsert_def fmlookup_default_def)
    next
      case 2 then show dom: ?case
        using TD_plain.query_iterate_eval.domintros(1)[of x "TD_plain.state.truncate state"]
        unfolding TD_plain.state.truncate_def
        by auto
      case 3 then show ?case using dom unfolding TD_plain.state.truncate_def by auto
    next case 8 then show ?case
      using infl_upd_diff[of "infl state" "infl state" _ x] Query.prems(2) by auto
    qed (auto simp add: fminsert_def fmlookup_default_def)
  qed
next
  case (Iterate x state)
  then have inv: "invariant (state⦇stabl := insert x (stabl state)⦈)"
    using invariant_simp_c_stabl by metis
  have xstabl: "x ∈ stabl (state⦇stabl := insert x (stabl state)⦈)" by simp
  have stablfinite: "finite (stabl (state⦇stabl := insert x (stabl state)⦈))"
    using Iterate.prems(4) by auto
  show ?case using Iterate.IH(1) Iterate.prems(1)
  proof(cases rule: iterate_continue_fixpoint_cases)
    case Stable
    let ?state_plain = "TD_plain.state.truncate state"
    have inv: "TD_plain.invariant T (stabl state) (?state_plain⦇called := called ?state_plain - {x}⦈)"
      using Iterate.prems(3) dep_subset_stable[of "infl state" "stabl state"]
      unfolding invariant_def TD_plain.invariant_def[of T] TD_plain.state.truncate_def
      by auto
    then have "TD_plain.iterate_dom T x ?state_plain"
      and "TD_plain.iterate T x ?state_plain =
        (xd, (?state_plain⦇called := called ?state_plain - {x}⦈))"
      using Stable(2,3) Iterate.prems(2,4) td_plain_terminates_for_stabl[OF Stable(3) inv]
      unfolding TD_plain.state.truncate_def
      by auto
    then show ?thesis
      using Stable(3) Iterate.prems(1,3,4) Iterate.IH(1)
      unfolding TD_plain.state.truncate_def
      by auto
  next
    case (Fixpoint state1)
    note IH = Iterate.IH(2)[OF Fixpoint(5,2) inv xstabl stablfinite, folded eq_def dep_def]
    then show ?thesis
    proof(intro conjI, goal_cases)
      case 1 then show ?case unfolding invariant_def
      proof(intro conjI, goal_cases)
        case 1 then have "eq_same (σ state') (stabl state' - (called state' - {x}))"
          using Fixpoint(3,4) unfolding eq_def invariant_def by auto
        then show ?case using IH Fixpoint(4) unfolding invariant_def by auto
      next
        case 2
        then show ?case using Fixpoint(3,4) by auto
      next
        case 3 then show ?case using Iterate.prems(2) Fixpoint(4) by (simp add: insert_absorb)
      qed (auto simp add: Fixpoint(4))
    next
      case 2
      show dom: ?case
        apply (rule TD_plain.query_iterate_eval.domintros(2)[folded TD_plain_dom_defs])
        using 2 Fixpoint(3,4)
        unfolding TD_plain.state.truncate_def
        by (cases "TD_plain_Interp.eval T x (T x) ⦇called = called state, σ = σ state⦈"; simp)+
      case 3
      then show ?case
        using dom Fixpoint(3,4) unfolding TD_plain.state.truncate_def
        by (cases "TD_plain_Interp.eval T x (T x) ⦇called = called state, σ = σ state⦈") auto
    next
      case 6 then show ?case
        using Fixpoint(4) by simp
    next case 8
      have "x ∉ fset (fmdom (infl state))"
        using Iterate.prems(3) Fixpoint(5)
        unfolding invariant_def
        by auto
      then have "slookup (infl state) x = {}"
        unfolding fmlookup_default_def
        by (simp add: fmdom_notD)
      then show ?case
        using Fixpoint(4) IH lookup_in_influenced
        by auto
    qed (auto simp add: Fixpoint(4))
  next
    case (Continue state1 xd' stabl2 infl2)
    have infl2_def: "infl2 = fmdrop_set (insert x (influenced_by (infl state1) x)) (infl state1)"
      and stabl2_def: "stabl2 = stabl state1 - influenced_by (infl state1) x"
      using destab_infl_stabl_relation[of infl2 stabl2 x] Continue(4) by auto
    note IH = Iterate.IH(2)[OF Continue(7,2) inv xstabl stablfinite]

    have "(slookup (infl state1) ξ - slookup (infl state) ξ) ∩ stabl state = {}" for ξ
      using Iterate.prems(3) Continue(7) IH
      unfolding invariant_def
      by auto
    then have stabl_inc: "stabl state ⊆ stabl2"
      using Iterate.prems(3) Continue(4,7) new_lookup_to_infl_not_stabl[of state1 state]
        destab_infl_stabl_relation[of infl2 stabl2] IH
      unfolding invariant_def
      by auto

    have infl_closed: "(∀x∈stabl state1 - (called state1 - {x}).
        ∀y∈dep T (σ state1) x. x ∈ slookup (infl state1) y)"
      using IH[unfolded invariant_def, folded dep_def] by auto

    have x_no_dep: "x ∉ dep T (σ state1) y" if "y ∈ stabl2 - (called state1 - {x})" for y
      using that Continue(4) destab_infl_stabl_relation destab_x_no_dep[OF _ infl_closed]
        by fastforce

    have inv: "invariant (state1⦇infl := infl2, stabl := stabl2, σ := (σ state1)(x ↦ xd'),
        called := called state1 - {x}⦈)"
      using IH Iterate.prems(2,3) Continue(4,7)
      unfolding invariant_def
    proof(elim conjE, intro conjI, goal_cases)
      case 1
      let ?σ2 = "(σ state1)(x ↦ xd')"
      show ?case using 1(4) stabl_inc c_same(3)[OF Continue(1,2)] by simp
      case 2
      show ?case using destab_upd_preserves_part_sol[OF 2(2) 2(8) infl_closed 2(15)] by simp
      case 3
      show ?case using destab_preserves_infl_dom_stabl[OF 3(2,12)] by auto
      case 4
      show ?case
      proof(intro ballI, goal_cases)
        case (1 y z)
        have "dep T (σ state1) y = dep T ((σ state1)(x ↦ xd')) y"
          apply (rule dep_eq)
          using x_no_dep[of y] 1(1)
          by (simp add: mlup_def fun_upd_apply)
        then show ?case
          using 1 destab_and_upd_preserves_dep_closed_in_infl[OF 4(2) infl_closed] by auto
      qed
    qed
    have x_called1: "x ∈ called state1" using Iterate.prems(2) c_same(3)[OF Continue(1,2)] by simp
    have stabl2_finite: "finite stabl2"
      by (metis Continue(4) Diff_subset IH destab_infl_stabl_relation infinite_super)
    note ih = Iterate.IH(3)[OF Continue(7,2) refl Continue(3,4) refl Continue(6), simplified,
              OF x_called1 inv stabl2_finite, folded TD_plain_dom_defs]
    then show ?thesis
    proof(intro conjI, goal_cases)
      case 2
      show dom: ?case
        apply (rule TD_plain.query_iterate_eval.domintros(2)[folded TD_plain_dom_defs])
        using IH ih
        unfolding TD_plain.state.truncate_def
        by (cases "TD_plain_Interp.eval T x (T x) ⦇called = called state, σ = σ state⦈"; simp)+
      case 3
      then show ?case
        using dom Continue(3) IH ih
        unfolding TD_plain.state.truncate_def
        by (auto split: prod.split)
    next case 6 then show ?case
        using stabl_inc by auto
    next case 7
      then show ?case unfolding invariant_def
      proof(elim conjE, intro ballI subsetI, goal_cases)
        case (1 ξ u)
        have "ξ ∉ insert x (influenced_by (infl state1) x)"
          using 1(13) Continue(7) stabl2_def stabl_inc by blast
        then show ?case
          using stabl_inc infl2_def 1(10,13,14) IH fmlookup_default_drop_set[of
              "insert x (influenced_by (infl state1) x)" "infl state1"]
          by fastforce
      qed
    next case 8
      then show ?case unfolding invariant_def
      proof(intro allI, goal_cases)
        case (1 ξ)
        have "slookup infl2 ξ ⊆ slookup (infl state1) ξ"
          using infl2_def unfolding fmlookup_default_def by auto
        moreover have "(slookup (infl state') ξ - slookup infl2 ξ) ∩ stabl state = {}"
        proof (cases "x ∈ stabl2")
          case True
          then show ?thesis using Continue(5,6) by auto
        next
          case False
          then show ?thesis
            using 1(1) inv[unfolded invariant_def] stabl_inc
            by fastforce
        qed
        moreover have "(slookup (infl state1) ξ - slookup (infl state) ξ) ∩ stabl state = {}"
          using Continue(7) Iterate.prems(3) IH stabl_infl_empty[of x "stabl state" "infl state"]
          unfolding invariant_def by auto
        ultimately show ?case using infl2_def stabl2_def by blast
      qed
    qed 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,4)]
    have inv: "invariant state1"
        "TD_plain.invariant T (stabl state1) (TD_plain.state.truncate state1)"
      using conjunct1[OF IH] Eval.prems(3) dep_subset_stable[of "infl state1" "stabl state1"]
      unfolding invariant_def TD_plain.invariant_def TD_plain.state.truncate_def
      by auto
    have B: "finite (stabl state1)" using IH by simp
    have C: "x ∈ stabl state1" using IH Eval.prems(3) by blast
    note ih = Eval.IH(3)[OF Query(1,3) refl Query(5) inv(1) C B, folded TD_plain_dom_defs]

    have "y ∈ stabl state1"
      using IH stabl_infl_empty[of y "stabl state1" "infl state1"]
      unfolding invariant_def
      by fastforce
    then have "mlup (σ state1) y = mlup (σ state') y"
      using TD_plain.partial_correctness_ind(3)[OF _ _ inv(2)] ih
      unfolding TD_plain.state.truncate_def
      by auto
    then have mlup: "mlup (σ state') y = yd"
      using Query(6) by auto

    show ?thesis using ih
    proof(intro conjI, goal_cases)
      case 2
      then show dom: ?case
        using IH ih Query(1)
        apply (intro TD_plain.query_iterate_eval.domintros(3)[of t T, folded TD_plain_dom_defs])
        unfolding TD_plain.state.truncate_def
        by (cases "TD_plain.query T x y ⦇called = called state, σ = σ state⦈", simp)+
      case 3
      then show ?case
        using dom IH Query(1)
          TD_plain.query_iterate_eval.domintros(3)[of t T, folded TD_plain_dom_defs]
        unfolding TD_plain.state.truncate_def
        by (cases "TD_plain.query T x y ⦇called = called state, σ = σ state⦈", simp)+
    next
      case 5
      then show ?case using Query IH mlup unfolding invariant_def by auto
    next
      case 6
      then show ?case using 6 Query IH mlup ‹y ∈ stabl state1› unfolding invariant_def by auto
    next
      case 7
      then show ?case using IH by auto
    next
      case 8
      then show ?case using IH by blast
    next
      case 9
      then show ?case
        using infl_diff_eval_step[of "stabl state" "stabl state1" "infl state'" "infl state1"] IH
          ih Eval.prems(2,3) by auto
    qed auto
  next
    case Answer
    then show ?thesis using Answer TD_plain.query_iterate_eval.domintros(3) Eval.prems(2-3,4)
      by fastforce
  qed
qed

corollary TD_TD_plain_equivalence:
  assumes "solve_dom x"
    and "solve x = (vars, sigma)"
  shows "TD_plain.solve_dom T x ∧ TD_plain.solve T x = sigma"
proof -
  let ?init_state = "⦇called = {x}, σ = Map.empty, infl = fmempty, stabl = {}⦈  :: ('x,'d) state"
  let ?init_state_plain = "⦇called = {x}, σ = Map.empty⦈ :: ('x,'d) TD_plain.state"
  obtain xd state where "σ state = sigma" and iter: "(xd, state) = iterate x ?init_state"
    using assms(2) unfolding solve_def by (auto split: prod.splits)
  have inv: "invariant (?init_state⦇called := called ?init_state - {x}⦈)"
    unfolding invariant_def by simp
  have "TD_plain.iterate_dom T x ?init_state_plain ∧
      (xd, TD_plain.state.truncate state) = TD_plain.iterate T x ?init_state_plain"
    using TD_TD_plain_equivalence_ind(2)[OF assms(1)[unfolded solve_dom_def] iter _ inv]
    unfolding TD_plain.state.truncate_def
    by simp
  moreover have "σ (TD_plain.state.truncate state) = sigma"
    using ‹σ state = sigma› unfolding TD_plain.state.truncate_def by simp
  ultimately show ?thesis
    unfolding TD_plain.solve_dom_def TD_plain.solve_def by (auto split: prod.splits)
qed


subsection ‹Partial Correctness of the TD›

text‹From the equivalence of the TD and TD\_plain and the partial correctness proof of the TD\_plain
  we can now conclude partial correctness also for the TD.›

corollary partial_correctness:
  assumes "solve_dom x"
    and "solve x = (vars, sigma)"
  shows "part_solution T x sigma vars"
proof -
  let ?init_state = "⦇called = {x}, σ = Map.empty, infl = fmempty, stabl = {}⦈  :: ('x,'d) state"
  note it_dom = assms(1)[unfolded solve_dom_def]
  obtain state xd where "sigma = σ state" "vars = stabl state"
    and it: "(xd, state) = iterate x ?init_state"
    using assms unfolding solve_def by (cases "iterate x ?init_state") auto
  then show ?thesis
    using TD_TD_plain_equivalence_ind(2)[OF it_dom it] c_same(2)[OF it_dom it]
      TD.dep_subset_stable[of "infl state" "stabl state" "{}" T "σ state"]
    unfolding invariant_def
    by simp
qed

end ―‹end context TD for equivalence proof›


subsection ‹Exactness›

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

theorem exactness:
  assumes "solve_dom x"
    and "solve x = (vars, sigma)"
  shows "∃σl. TD_plain_mono.least_solution T σl ∧ (∀y ∈ vars. mlup sigma y = mlup σl y)"
  (* TODO only mlup same or actual entry same? *)
proof (rule TD_plain_mono.exact_on_partial_solution[of T x], goal_cases)
  case 1
  then show ?case using mono_eq TD_plain_mono.intro by auto
next
  case 2
  show ?case using dep_subset_stable stabl_infl_empty partial_correctness[OF assms] by fastforce
next
  case 3
  have "TD_plain.solve_dom T x" and "TD_plain.solve T x = sigma"
    using TD_TD_plain_equivalence assms by auto
  then show ?case
    using TD_plain_mono.less_than_solution[of T x sigma] mono_eq TD_plain_mono.intro by auto
qed

end ― ‹end of exactness/monotonic right-hand side locale›


subsection ‹Termination of TD›

text ‹We prove the termination of TD under the assumptions that (i) the set of unknowns is finite,
  (ii) the domain fulfills the ascending chain condition, and (iii) the equation systems has
  monotonic right-hand sides.›

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

lemma termination_equiv: "solve_dom x = TD_plain.solve_dom T x"
proof
  let ?init_state = "⦇called = {x}, σ = Map.empty, infl = fmempty, stabl = {}⦈  :: ('x,'d) state"
  show "solve_dom x ⟹ TD_plain.solve_dom T x"
    using TD_TD_plain_equivalence_ind(2)[of x ?init_state]
    unfolding solve_dom_def TD_plain.solve_dom_def invariant_def TD_plain.state.truncate_def
    by (cases "iterate x ⦇called = {x}, σ = Map.empty, infl = fmempty, stabl = {}⦈") auto
next
  show "TD_plain.solve_dom T x ⟹ solve_dom x"
    using TD_plain_TD_equivalence_ind(2)
    unfolding solve_dom_def TD_plain.solve_dom_def invariant_def TD_plain.state.truncate_def
    by (cases "TD_plain.iterate T x ⦇called = {x}, σ = Map.empty⦈") auto
qed 

theorem terminating: "solve_dom x"
  using termination_equiv TD_plain_term.terminating[of T] mono_eq acc finite_vars
  unfolding TD_plain_term_def TD_plain_term_axioms_def TD_plain_mono_def
  by auto

end ―‹end of termination locale›

end