Theory Finite_Foundation_Checker

theory Finite_Foundation_Checker
  imports Foundation_Separations "HOL-Library.Transitive_Closure_Table"
begin

section ‹Executable Finite Models›

text ‹
  A finite model is represented by a vertex list and an edge list.  The
  declarative abstraction uses the set of listed vertices and carrier-restricts
  the edge relation.  Duplicated vertices do not change the abstraction, while
  well-formed clients may additionally require @{term "distinct (fst M)"}.
›

type_synonym finite_dependency_model = "nat list × (nat × nat) list"

definition finite_vertices :: "finite_dependency_model ⇒ nat list"
where
  "finite_vertices M = fst M"

definition finite_edges :: "finite_dependency_model ⇒ (nat × nat) list"
where
  "finite_edges M = snd M"

definition finite_model_immediate ::
  "finite_dependency_model ⇒ nat ⇒ nat ⇒ bool"
where
  "finite_model_immediate M x y ⟷
     (x, y) ∈ set (finite_edges M)"

definition finite_model_relation ::
  "finite_dependency_model ⇒ nat ⇒ nat ⇒ bool"
where
  "finite_model_relation M x y ⟷
     dependency_on (set (finite_vertices M))
       (finite_model_immediate M) x y"

definition finite_model_carrier ::
  "finite_dependency_model ⇒ nat set"
where
  "finite_model_carrier M = set (finite_vertices M)"

fun finite_list_all :: "('a ⇒ bool) ⇒ 'a list ⇒ bool"
where
  "finite_list_all P [] = True"
| "finite_list_all P (x # xs) = (P x ∧ finite_list_all P xs)"

fun finite_list_ex :: "('a ⇒ bool) ⇒ 'a list ⇒ bool"
where
  "finite_list_ex P [] = False"
| "finite_list_ex P (x # xs) = (P x ∨ finite_list_ex P xs)"

fun finite_first_failure ::
  "('a ⇒ bool) ⇒ 'a list ⇒ 'a option"
where
  "finite_first_failure P [] = None"
| "finite_first_failure P (x # xs) =
     (if P x then Some x else finite_first_failure P xs)"

lemma finite_list_all_iff:
  "finite_list_all P xs ⟷ (∀x ∈ set xs. P x)"
  by (induction xs) simp_all

lemma finite_list_ex_iff:
  "finite_list_ex P xs ⟷ (∃x ∈ set xs. P x)"
  by (induction xs) simp_all

lemma finite_list_exI:
  assumes "x ∈ set xs" and "P x"
  shows "finite_list_ex P xs"
  using assms
  by (induction xs) auto

fun finite_reach ::
  "nat ⇒ 'a list ⇒ ('a ⇒ 'a ⇒ bool) ⇒
   'a ⇒ 'a ⇒ bool"
where
  "finite_reach 0 V R x y = (x = y)"
| "finite_reach (Suc n) V R x y =
     (x = y ∨
       finite_list_ex
         (λz. R x z ∧ finite_reach n V R z y) V)"

lemma finite_reach_imp_rtranclp:
  "finite_reach n V R x y ⟹ R** x y"
proof (induction n arbitrary: x y)
  case 0
  then show ?case
    by simp
next
  case (Suc n)
  then show ?case
    by (auto simp: finite_list_ex_iff
      intro: r_into_rtranclp converse_rtranclp_into_rtranclp)
qed

lemma finite_reach_mono:
  assumes "n ≤ m" and "finite_reach n V R x y"
  shows "finite_reach m V R x y"
using assms
proof (induction m arbitrary: n x y)
  case 0
  then show ?case
    by simp
next
  case (Suc m)
  show ?case
  proof (cases n)
    case 0
    then show ?thesis
      using Suc.prems(2) by simp
  next
    case (Suc n)
    have "n ≤ m"
      using Suc.prems(1) Suc by simp
    have "x = y ∨
        finite_list_ex
          (λz. R x z ∧ finite_reach n V R z y) V"
      using Suc.prems(2) Suc by simp
    then show ?thesis
    proof
      assume "x = y"
      then show ?thesis by simp
    next
      assume ex:
        "finite_list_ex
          (λz. R x z ∧ finite_reach n V R z y) V"
      obtain z where zV: "z ∈ set V"
        and R_xz: "R x z"
        and reach: "finite_reach n V R z y"
        using ex by (auto simp: finite_list_ex_iff)
      have reach_m: "finite_reach m V R z y"
        using Suc.IH[OF ‹n ≤ m› reach] .
      have ex_m:
          "finite_list_ex
            (λw. R x w ∧ finite_reach m V R w y) V"
        using finite_list_exI[of z V
          "λw. R x w ∧ finite_reach m V R w y"]
          zV R_xz reach_m
        by blast
      show ?thesis
        using ex_m by (simp add: finite_reach.simps)
    qed
  qed
qed

lemma rtrancl_path_vertices:
  assumes path: "rtrancl_path R x xs y"
    and xV: "x ∈ set V"
    and closed: "⋀u v. R u v ⟹ u ∈ set V ∧ v ∈ set V"
  shows "set xs ⊆ set V ∧ y ∈ set V"
  using path xV
proof (induction rule: rtrancl_path.induct)
  case base
  then show ?case by simp
next
  case (step x z ys y)
  have zV: "z ∈ set V"
    using closed step.hyps(1) by blast
  have tail: "set ys ⊆ set V ∧ y ∈ set V"
    using step.IH zV by blast
  then show ?case
    using zV by simp
qed

lemma finite_reach_path:
  assumes path: "rtrancl_path R x xs y"
    and xV: "x ∈ set V"
    and closed: "⋀u v. R u v ⟹ u ∈ set V ∧ v ∈ set V"
  shows "finite_reach (length xs) V R x y"
  using path xV
proof (induction rule: rtrancl_path.induct)
  case base
  then show ?case by simp
next
  case (step x z ys y)
  have zV: "z ∈ set V"
    using closed step.hyps(1) by blast
  have tail: "finite_reach (length ys) V R z y"
    using step.IH zV by blast
  have ex:
      "finite_list_ex
        (λw. R x w ∧ finite_reach (length ys) V R w y) V"
    using finite_list_exI[of z V
      "λw. R x w ∧ finite_reach (length ys) V R w y"]
      zV step.hyps(1) tail
    by blast
  show ?case
    using ex by (simp add: finite_reach.simps)
qed

lemma rtranclp_imp_finite_reach:
  assumes reach: "R** x y"
    and xV: "x ∈ set V"
    and closed: "⋀u v. R u v ⟹ u ∈ set V ∧ v ∈ set V"
  shows "finite_reach (length V) V R x y"
proof -
  have path_exists: "∃xs. rtrancl_path R x xs y"
    using reach by (simp add: rtranclp_eq_rtrancl_path)
  obtain xs where path: "rtrancl_path R x xs y"
    using path_exists by blast
  obtain xs' where path': "rtrancl_path R x xs' y"
    and distinct: "distinct (x # xs')"
    and subset: "set xs' ⊆ set xs"
    using rtrancl_path_distinct[OF path] by blast
  have path_V: "set xs ⊆ set V ∧ y ∈ set V"
    using rtrancl_path_vertices[OF path xV closed] .
  have xs_V: "set xs ⊆ set V"
    using path_V by blast
  have xs'_V: "set xs' ⊆ set V"
    using subset xs_V by blast
  have len_xs': "length xs' ≤ length V"
  proof -
    have card_xs': "card (set xs') = length xs'"
      using distinct by (simp add: distinct_card)
    have "card (set xs') ≤ card (set V)"
      using card_mono xs'_V by blast
    moreover have "card (set V) ≤ length V"
      by (rule card_length)
    ultimately show ?thesis
      using card_xs' by linarith
  qed
  have finite_path: "finite_reach (length xs') V R x y"
    using finite_reach_path[OF path' xV closed] .
  have monotone:
      "finite_reach (length V) V R x y"
    using finite_reach_mono[
      where n = "length xs'" and m = "length V"
        and V = V and R = R and x = x and y = y]
      len_xs' finite_path .
  show "finite_reach (length V) V R x y"
    using monotone by blast
qed

lemma finite_first_failure_sound:
  assumes "finite_first_failure P xs = Some x"
  shows "x ∈ set xs" "P x"
  using assms
  by (induction xs) (auto split: if_splits)

lemma finite_first_failure_complete:
  assumes "∃x ∈ set xs. P x"
  obtains x where "finite_first_failure P xs = Some x"
    and "x ∈ set xs" and "P x"
proof -
  have exists:
      "∃x. finite_first_failure P xs = Some x ∧
        x ∈ set xs ∧ P x"
    using assms
    by (induction xs) (auto split: if_splits)
  then obtain x where "finite_first_failure P xs = Some x"
    and "x ∈ set xs" and "P x"
    by blast
  then show thesis
    by (rule that)
qed

section ‹Finite Foundation Predicate and Checker›

definition finite_model_independent ::
  "finite_dependency_model ⇒ nat ⇒ bool"
where
  "finite_model_independent M x ⟷
     x ∈ finite_model_carrier M ∧
     ¬ finite_list_ex
       (λy. dependency_on (finite_model_carrier M)
         (finite_model_immediate M) y x) (finite_vertices M)"

definition finite_model_grounded ::
  "finite_dependency_model ⇒ nat ⇒ bool"
where
  "finite_model_grounded M x ⟷
     finite_list_ex
       (λy. dependency_on (finite_model_carrier M)
         (finite_model_immediate M) y x) (finite_vertices M)"

definition finite_model_supports ::
  "finite_dependency_model ⇒ nat ⇒ bool"
where
  "finite_model_supports M x ⟷
     finite_list_ex
       (λf. finite_model_independent M f ∧
         finite_reach (length (finite_vertices M))
           (finite_vertices M)
           (dependency_on (finite_model_carrier M)
             (finite_model_immediate M)) f x)
       (finite_vertices M)"

definition finite_model_bad ::
  "finite_dependency_model ⇒ nat ⇒ bool"
where
  "finite_model_bad M x ⟷
     finite_model_grounded M x ∧ ¬ finite_model_supports M x"

definition finite_foundation_check ::
  "finite_dependency_model ⇒ bool"
where
  "finite_foundation_check M ⟷
     finite_list_all (λx. ¬ finite_model_bad M x)
       (finite_vertices M)"

definition finite_failure_witness ::
  "finite_dependency_model ⇒ nat"
where
  "finite_failure_witness M =
     (case finite_first_failure (finite_model_bad M) (finite_vertices M) of
        None ⇒ 0
      | Some x ⇒ x)"

text ‹
  The total executable helper has a harmless default for the impossible
  empty-failure case.  The specification theorem below proves
  that whenever the checker takes its failure branch, the returned natural
  number is a genuine carrier vertex and a genuine bad vertex.
›

datatype finite_foundation_result =
    Foundation_Certificate "nat list"
  | Missing_Foundation_Certificate nat

definition finite_foundation_result ::
  "finite_dependency_model ⇒ finite_foundation_result"
where
  "finite_foundation_result M =
     (if finite_foundation_check M then
        Foundation_Certificate
          (filter (finite_model_independent M) (finite_vertices M))
      else
        Missing_Foundation_Certificate (finite_failure_witness M))"

lemma finite_model_relation_eq:
  "finite_model_relation M =
    dependency_on (finite_model_carrier M) (finite_model_immediate M)"
  by (rule ext, rule ext)
    (simp add: finite_model_relation_def finite_model_carrier_def)

lemma finite_model_immediate_eq:
  "finite_model_immediate M =
    (λx y. (x, y) ∈ set (finite_edges M))"
  by (rule ext, rule ext)
    (simp add: finite_model_immediate_def)

lemma finite_model_grounded_iff:
  assumes "x ∈ finite_model_carrier M"
  shows "finite_model_grounded M x ⟷
    grounded_in (finite_model_carrier M) (finite_model_immediate M) x"
  using assms
  unfolding finite_model_grounded_def
  by (auto simp: finite_list_ex_iff
    finite_model_relation_def
    finite_model_carrier_def finite_vertices_def
    finite_model_immediate_eq finite_model_immediate_def
    finite_edges_def grounded_in_def
    intro: dependency_onI dest: dependency_onD)

lemma finite_model_independent_iff:
  assumes "x ∈ finite_model_carrier M"
  shows "finite_model_independent M x ⟷
    independent_in (finite_model_carrier M) (finite_model_immediate M) x"
  using assms
  unfolding finite_model_independent_def
  by (auto simp: finite_list_ex_iff
    finite_model_relation_def
    finite_model_carrier_def finite_vertices_def
    finite_model_immediate_eq finite_model_immediate_def
    finite_edges_def independent_in_def
    intro: dependency_onI dest: dependency_onD)

lemma finite_model_supports_iff:
  assumes "x ∈ finite_model_carrier M"
  shows "finite_model_supports M x ⟷
    (∃f. first_member_in (finite_model_carrier M)
      (finite_model_immediate M) f x)"
proof -
  have closed:
      "⋀u v. dependency_on (finite_model_carrier M)
        (finite_model_immediate M) u v ⟹
        u ∈ finite_model_carrier M ∧
        v ∈ finite_model_carrier M"
    by (auto simp: dependency_on_def)
  have closedV:
      "⋀u v. dependency_on (finite_model_carrier M)
        (finite_model_immediate M) u v ⟹
        u ∈ set (finite_vertices M) ∧
        v ∈ set (finite_vertices M)"
    using closed by (auto simp: finite_model_carrier_def)
  have reach_iff:
      "⋀f. f ∈ finite_model_carrier M ⟹
        finite_reach (length (finite_vertices M))
          (finite_vertices M)
          (dependency_on (finite_model_carrier M)
            (finite_model_immediate M)) f x
        ⟷
        dependency_ancestor (finite_model_carrier M)
          (finite_model_immediate M) f x"
  proof -
    fix f
    assume fC: "f ∈ finite_model_carrier M"
    show "finite_reach (length (finite_vertices M))
          (finite_vertices M)
          (dependency_on (finite_model_carrier M)
            (finite_model_immediate M)) f x
        ⟷
        dependency_ancestor (finite_model_carrier M)
          (finite_model_immediate M) f x"
    proof (rule iffI)
      assume bounded:
        "finite_reach (length (finite_vertices M))
          (finite_vertices M)
          (dependency_on (finite_model_carrier M)
            (finite_model_immediate M)) f x"
      have rtrancl:
          "(dependency_on (finite_model_carrier M)
            (finite_model_immediate M))** f x"
        using finite_reach_imp_rtranclp[OF bounded] .
      show "dependency_ancestor (finite_model_carrier M)
          (finite_model_immediate M) f x"
        using fC assms rtrancl
        by (simp add: dependency_ancestor_def)
    next
      assume ancestor:
        "dependency_ancestor (finite_model_carrier M)
          (finite_model_immediate M) f x"
      obtain rtrancl:
          "(dependency_on (finite_model_carrier M)
            (finite_model_immediate M))** f x"
        using ancestor by (auto simp: dependency_ancestor_def)
      show "finite_reach (length (finite_vertices M))
          (finite_vertices M)
          (dependency_on (finite_model_carrier M)
            (finite_model_immediate M)) f x"
      proof -
        have fV: "f ∈ set (finite_vertices M)"
          using fC by (simp add: finite_model_carrier_def)
        show "finite_reach (length (finite_vertices M))
            (finite_vertices M)
            (dependency_on (finite_model_carrier M)
              (finite_model_immediate M)) f x"
          using rtranclp_imp_finite_reach[
            where R = "dependency_on (finite_model_carrier M)
              (finite_model_immediate M)"
              and x = f and y = x and V = "finite_vertices M"]
            rtrancl fV closedV .
      qed
    qed
  qed
  show ?thesis
    unfolding finite_model_supports_def
  proof (rule iffI)
    assume support:
      "finite_list_ex
        (λf. finite_model_independent M f ∧
          finite_reach (length (finite_vertices M))
            (finite_vertices M)
            (dependency_on (finite_model_carrier M)
              (finite_model_immediate M)) f x)
        (finite_vertices M)"
    obtain f where fV: "f ∈ set (finite_vertices M)"
        and f_ind: "finite_model_independent M f"
        and bounded:
          "finite_reach (length (finite_vertices M))
            (finite_vertices M)
            (dependency_on (finite_model_carrier M)
              (finite_model_immediate M)) f x"
      using support by (auto simp: finite_list_ex_iff)
    have fC: "f ∈ finite_model_carrier M"
      using fV by (simp add: finite_model_carrier_def)
    have independent:
          "independent_in (finite_model_carrier M)
            (finite_model_immediate M) f"
      using finite_model_independent_iff[OF fC] f_ind by blast
    have ancestor:
          "dependency_ancestor (finite_model_carrier M)
            (finite_model_immediate M) f x"
      using reach_iff[OF fC] bounded by blast
    show "∃f. first_member_in (finite_model_carrier M)
          (finite_model_immediate M) f x"
      using ancestor independent
      by (intro exI) (simp add: first_member_in_def)
  next
    assume first:
      "∃f. first_member_in (finite_model_carrier M)
        (finite_model_immediate M) f x"
    then obtain f where first_f:
        "first_member_in (finite_model_carrier M)
          (finite_model_immediate M) f x"
      by blast
    have ancestor:
          "dependency_ancestor (finite_model_carrier M)
            (finite_model_immediate M) f x"
      using first_f by (rule first_member_inD)
    have independent:
          "independent_in (finite_model_carrier M)
            (finite_model_immediate M) f"
      using first_f by (rule first_member_inD)
    have fC: "f ∈ finite_model_carrier M"
      using independent by (rule independent_inD)
    have f_ind: "finite_model_independent M f"
      using finite_model_independent_iff[OF fC] independent by blast
    have bounded:
          "finite_reach (length (finite_vertices M))
            (finite_vertices M)
            (dependency_on (finite_model_carrier M)
              (finite_model_immediate M)) f x"
      using reach_iff[OF fC] ancestor by blast
    have fV: "f ∈ set (finite_vertices M)"
      using fC by (simp add: finite_model_carrier_def)
    have Pf:
        "finite_model_independent M f ∧
          finite_reach (length (finite_vertices M))
            (finite_vertices M)
            (dependency_on (finite_model_carrier M)
              (finite_model_immediate M)) f x"
      using f_ind bounded by blast
    show "finite_list_ex
        (λf. finite_model_independent M f ∧
          finite_reach (length (finite_vertices M))
            (finite_vertices M)
            (dependency_on (finite_model_carrier M)
              (finite_model_immediate M)) f x)
        (finite_vertices M)"
      using fV Pf
      by (rule finite_list_exI)
  qed
qed

lemma finite_model_bad_iff:
  assumes "x ∈ finite_model_carrier M"
  shows "finite_model_bad M x ⟷
    grounded_in (finite_model_carrier M) (finite_model_immediate M) x ∧
    ¬ (∃f. first_member_in (finite_model_carrier M)
      (finite_model_immediate M) f x)"
  using assms finite_model_grounded_iff[OF assms]
    finite_model_supports_iff[OF assms]
  by (auto simp: finite_model_bad_def)

theorem finite_foundation_check_iff:
  "finite_foundation_check M ⟷
   local_foundation_on (finite_model_carrier M)
     (finite_model_immediate M) (finite_model_carrier M)"
proof -
  show ?thesis
    unfolding finite_foundation_check_def local_foundation_on_def
  proof (rule iffI)
    assume check:
      "finite_list_all (λx. ¬ finite_model_bad M x)
        (finite_vertices M)"
    show "∀a ∈ finite_model_carrier M.
      grounded_in (finite_model_carrier M) (finite_model_immediate M) a
      ⟶ (∃f. first_member_in (finite_model_carrier M)
        (finite_model_immediate M) f a)"
    proof (intro ballI impI)
      fix a
      assume aC: "a ∈ finite_model_carrier M"
        and grounded:
          "grounded_in (finite_model_carrier M)
            (finite_model_immediate M) a"
      have not_bad: "¬ finite_model_bad M a"
        using check aC
        by (simp add: finite_list_all_iff finite_model_carrier_def)
      have bad_iff: "finite_model_bad M a ⟷
          grounded_in (finite_model_carrier M)
            (finite_model_immediate M) a ∧
          ¬ (∃f. first_member_in (finite_model_carrier M)
            (finite_model_immediate M) f a)"
        by (rule finite_model_bad_iff[OF aC])
      show "∃f. first_member_in (finite_model_carrier M)
        (finite_model_immediate M) f a"
        using not_bad grounded bad_iff by blast
    qed
  next
    assume foundation:
      "∀a ∈ finite_model_carrier M.
        grounded_in (finite_model_carrier M)
          (finite_model_immediate M) a
        ⟶ (∃f. first_member_in (finite_model_carrier M)
          (finite_model_immediate M) f a)"
    have all_bad:
        "∀a ∈ set (finite_vertices M). ¬ finite_model_bad M a"
    proof (intro ballI)
      fix a
      assume a_list: "a ∈ set (finite_vertices M)"
      have aC: "a ∈ finite_model_carrier M"
        using a_list by (simp add: finite_model_carrier_def)
      have bad_iff: "finite_model_bad M a ⟷
          grounded_in (finite_model_carrier M)
            (finite_model_immediate M) a ∧
          ¬ (∃f. first_member_in (finite_model_carrier M)
            (finite_model_immediate M) f a)"
        by (rule finite_model_bad_iff[OF aC])
      show "¬ finite_model_bad M a"
        using foundation aC bad_iff by blast
    qed
    show "finite_list_all (λx. ¬ finite_model_bad M x)
      (finite_vertices M)"
      using all_bad by (simp add: finite_list_all_iff)
  qed
qed

lemma finite_failure_witness_spec:
  assumes "¬ finite_foundation_check M"
  shows "finite_failure_witness M ∈ finite_model_carrier M"
    and "finite_model_bad M (finite_failure_witness M)"
proof -
  have bad_exists:
      "∃x ∈ set (finite_vertices M). finite_model_bad M x"
    using assms
    by (simp add: finite_foundation_check_def finite_list_all_iff)
  obtain x where failure:
      "finite_first_failure (finite_model_bad M) (finite_vertices M) =
        Some x"
    and x_list: "x ∈ set (finite_vertices M)"
    and bad: "finite_model_bad M x"
    using finite_first_failure_complete[OF bad_exists] .
  have witness_eq: "finite_failure_witness M = x"
    using failure by (simp add: finite_failure_witness_def)
  have xC: "x ∈ finite_model_carrier M"
    using x_list by (simp add: finite_model_carrier_def)
  show "finite_failure_witness M ∈ finite_model_carrier M"
    using witness_eq xC by simp
  show "finite_model_bad M (finite_failure_witness M)"
    using witness_eq bad by simp
qed

lemma finite_foundation_result_success:
  assumes "finite_foundation_result M =
    Foundation_Certificate roots"
  shows "local_foundation_on (finite_model_carrier M)
    (finite_model_immediate M) (finite_model_carrier M)"
proof -
  have "finite_foundation_check M"
    using assms
    by (simp add: finite_foundation_result_def split: if_splits)
  then show ?thesis
    using finite_foundation_check_iff by blast
qed

theorem finite_foundation_result_roots:
  assumes "finite_foundation_result M =
    Foundation_Certificate roots"
  shows "set roots =
    {x ∈ finite_model_carrier M.
      independent_in (finite_model_carrier M)
        (finite_model_immediate M) x}"
proof -
  have check: "finite_foundation_check M"
    using assms
    by (simp add: finite_foundation_result_def split: if_splits)
  have roots_eq:
      "roots = filter (finite_model_independent M) (finite_vertices M)"
    using assms check
    by (simp add: finite_foundation_result_def)
  have model_roots:
      "set roots =
        {x ∈ finite_model_carrier M. finite_model_independent M x}"
    using roots_eq
    by (simp add: finite_model_carrier_def)
  have root_independent:
      "{x ∈ finite_model_carrier M. finite_model_independent M x} =
       {x ∈ finite_model_carrier M.
         independent_in (finite_model_carrier M)
           (finite_model_immediate M) x}"
  proof (rule set_eqI)
    fix x
    show "x ∈ {x ∈ finite_model_carrier M.
          finite_model_independent M x} ⟷
        x ∈ {x ∈ finite_model_carrier M.
          independent_in (finite_model_carrier M)
            (finite_model_immediate M) x}"
    proof (rule iffI)
      assume left:
        "x ∈ {x ∈ finite_model_carrier M.
          finite_model_independent M x}"
      have xC: "x ∈ finite_model_carrier M"
        using left by simp
      have finite_independent: "finite_model_independent M x"
        using left by simp
      have independent:
          "independent_in (finite_model_carrier M)
            (finite_model_immediate M) x"
        using finite_model_independent_iff[OF xC] finite_independent
        by blast
      show "x ∈ {x ∈ finite_model_carrier M.
          independent_in (finite_model_carrier M)
            (finite_model_immediate M) x}"
        using xC independent by simp
    next
      assume right:
        "x ∈ {x ∈ finite_model_carrier M.
          independent_in (finite_model_carrier M)
            (finite_model_immediate M) x}"
      have xC: "x ∈ finite_model_carrier M"
        using right by simp
      have independent:
          "independent_in (finite_model_carrier M)
            (finite_model_immediate M) x"
        using right by simp
      have finite_independent: "finite_model_independent M x"
        using finite_model_independent_iff[OF xC] independent
        by blast
      show "x ∈ {x ∈ finite_model_carrier M.
          finite_model_independent M x}"
        using xC finite_independent by simp
    qed
  qed
  then show ?thesis
    using model_roots by simp
qed

theorem finite_foundation_result_certificate:
  assumes "finite_foundation_result M =
    Foundation_Certificate roots"
  shows "foundation_set_for (finite_model_carrier M)
      (finite_model_immediate M) (finite_model_carrier M) (set roots)"
proof -
  have root_set:
      "set roots =
        {x ∈ finite_model_carrier M.
          independent_in (finite_model_carrier M)
            (finite_model_immediate M) x}"
    using finite_foundation_result_roots[OF assms] .
  have roots_carrier:
      "set roots ⊆ finite_model_carrier M"
    using root_set by auto
  have roots_independent:
      "∀f ∈ set roots.
        independent_in (finite_model_carrier M)
          (finite_model_immediate M) f"
    using root_set by auto
  have local:
      "∀a ∈ finite_model_carrier M.
        grounded_in (finite_model_carrier M)
          (finite_model_immediate M) a ⟶
        (∃f. first_member_in (finite_model_carrier M)
          (finite_model_immediate M) f a)"
    using finite_foundation_result_success[OF assms]
    by (simp add: local_foundation_on_def)
  have supports:
      "∀a ∈ finite_model_carrier M.
        grounded_in (finite_model_carrier M)
          (finite_model_immediate M) a ⟶
        (∃f ∈ set roots.
          dependency_ancestor (finite_model_carrier M)
            (finite_model_immediate M) f a)"
  proof (intro ballI impI)
    fix a
    assume aC: "a ∈ finite_model_carrier M"
      and grounded:
        "grounded_in (finite_model_carrier M)
          (finite_model_immediate M) a"
    obtain f where first: "first_member_in (finite_model_carrier M)
        (finite_model_immediate M) f a"
      using local aC grounded by blast
    have f_independent:
        "independent_in (finite_model_carrier M)
          (finite_model_immediate M) f"
      using first by (rule first_member_inD)
    have f_ancestor:
        "dependency_ancestor (finite_model_carrier M)
          (finite_model_immediate M) f a"
      using first by (rule first_member_inD)
    have fC: "f ∈ finite_model_carrier M"
      using f_independent by (rule independent_inD)
    have f_root: "f ∈ set roots"
      using root_set fC f_independent by auto
    then show "∃f ∈ set roots.
        dependency_ancestor (finite_model_carrier M)
          (finite_model_immediate M) f a"
      using f_ancestor by blast
  qed
  show ?thesis
    unfolding foundation_set_for_def
    using roots_carrier roots_independent supports by blast
qed

lemma finite_foundation_result_failure:
  assumes "finite_foundation_result M =
    Missing_Foundation_Certificate x"
  shows "x ∈ finite_model_carrier M" and "finite_model_bad M x"
proof -
  have not_check: "¬ finite_foundation_check M"
    using assms
    by (simp add: finite_foundation_result_def split: if_splits)
  have witness:
      "finite_failure_witness M ∈ finite_model_carrier M"
      "finite_model_bad M (finite_failure_witness M)"
    using finite_failure_witness_spec[OF not_check] .
  have x_eq: "x = finite_failure_witness M"
    using assms not_check
    by (simp add: finite_foundation_result_def)
  show "x ∈ finite_model_carrier M"
    using x_eq witness(1) by simp
  show "finite_model_bad M x"
    using x_eq witness(2) by simp
qed

theorem finite_foundation_result_complete:
  assumes "¬ local_foundation_on (finite_model_carrier M)
    (finite_model_immediate M) (finite_model_carrier M)"
  obtains x where
    "finite_foundation_result M =
      Missing_Foundation_Certificate x"
    and "x ∈ finite_model_carrier M"
    and "finite_model_bad M x"
proof -
  have not_check: "¬ finite_foundation_check M"
    using assms finite_foundation_check_iff by blast
  have witness:
      "finite_failure_witness M ∈ finite_model_carrier M"
      "finite_model_bad M (finite_failure_witness M)"
    using finite_failure_witness_spec[OF not_check] .
  have result:
      "finite_foundation_result M =
        Missing_Foundation_Certificate (finite_failure_witness M)"
    using not_check
    by (simp add: finite_foundation_result_def)
  show thesis
    using result witness by (rule that)
qed

theorem finite_foundation_result_correct:
  "(∃roots. finite_foundation_result M =
      Foundation_Certificate roots) ⟷
   local_foundation_on (finite_model_carrier M)
     (finite_model_immediate M) (finite_model_carrier M)"
proof
  assume "∃roots. finite_foundation_result M =
    Foundation_Certificate roots"
  then obtain roots where result:
      "finite_foundation_result M = Foundation_Certificate roots"
    by blast
  then show "local_foundation_on (finite_model_carrier M)
      (finite_model_immediate M) (finite_model_carrier M)"
    by (rule finite_foundation_result_success)
next
  assume foundation:
    "local_foundation_on (finite_model_carrier M)
      (finite_model_immediate M) (finite_model_carrier M)"
  have "finite_foundation_check M"
    using foundation finite_foundation_check_iff by blast
  then show "∃roots. finite_foundation_result M =
      Foundation_Certificate roots"
    by (auto simp: finite_foundation_result_def)
qed

section ‹Executable Regression Models›

definition finite_chain_model :: finite_dependency_model
where
  "finite_chain_model = ([0, 1, 2], [(0, 1), (1, 2)])"

definition finite_cycle_model :: finite_dependency_model
where
  "finite_cycle_model = ([0, 1], [(0, 1), (1, 0)])"

value "finite_foundation_result finite_chain_model"
value "finite_foundation_result finite_cycle_model"

end