# Theory SatChecker

```(*  Title:      HOL/Imperative_HOL/ex/SatChecker.thy
Author:     Lukas Bulwahn, TU Muenchen
*)

section ‹An efficient checker for proofs from a SAT solver›

theory SatChecker
imports "HOL-Library.RBT_Impl" Sorted_List "../Imperative_HOL"
begin

section‹General settings and functions for our representation of clauses›

subsection‹Types for Literals, Clauses and ProofSteps›
text ‹We encode Literals as integers and Clauses as sorted Lists.›

type_synonym ClauseId = nat
type_synonym Lit = int
type_synonym Clause = "Lit list"

text ‹This resembles exactly to Isat's Proof Steps›

type_synonym Resolvants = "ClauseId * (Lit * ClauseId) list"
datatype ProofStep =
ProofDone bool
| Root ClauseId Clause
| Conflict ClauseId Resolvants
| Delete ClauseId
| Xstep ClauseId ClauseId

subsection‹Interpretation of Literals, Clauses, and an array of Clauses›
text ‹Specific definitions for Literals as integers›

definition compl :: "Lit ⇒ Lit"
where
"compl a = -a"

definition interpLit :: "(nat ⇒ bool) ⇒ Lit ⇒ bool"
where
"interpLit assgnmt lit = (if lit > 0 then assgnmt (nat lit) else (if (lit < 0) then ¬ (assgnmt (nat (- lit))) else undefined))"

lemma interpLit_compl[simp]:
assumes lit_not_zero: "lit ≠ 0"
shows "interpLit a (compl lit) = (¬ interpLit a lit)"
unfolding interpLit_def compl_def using lit_not_zero by auto

lemma compl_compl_is_id[simp]:
"compl (compl x) = x"
unfolding compl_def by simp

lemma compl_not_zero[simp]:
"(compl x ≠ 0) = (x ≠ 0)"
unfolding compl_def by simp

lemma compl_exists: "∃l'. l = compl l'"
unfolding compl_def by arith

text ‹Specific definitions for Clauses as sorted lists›

definition interpClause :: "(nat ⇒ bool) ⇒ Clause ⇒ bool"
where
"interpClause assgnmt cl = (∃ l ∈ set cl. interpLit assgnmt l)"

lemma interpClause_empty[simp]:
"interpClause a [] = False"
unfolding interpClause_def by simp

lemma interpClause_sort[simp]: "interpClause a (sort clause) = interpClause a clause"
unfolding interpClause_def
by simp

lemma interpClause_remdups[simp]: "interpClause a (remdups clause) = interpClause a clause"
unfolding interpClause_def
by simp

definition
"inconsistent cs = (∀a.∃c∈set cs. ¬ interpClause a c)"

lemma interpClause_resolvants':
assumes lit_not_zero: "lit ≠ 0"
assumes resolv_clauses: "lit ∈ cli" "compl lit ∈ clj"
assumes interp: "∃x ∈ cli. interpLit a x" "∃x ∈ clj. interpLit a x"
shows "∃x ∈ (cli - {lit} ∪ (clj - {compl lit})). interpLit a x"
proof -
from resolv_clauses interp have "(∃l ∈ cli - {lit}. interpLit a l) ∨ interpLit a lit"
"(∃l ∈ clj - {compl lit}. interpLit a l) ∨ interpLit a (compl lit)" by auto
with lit_not_zero show ?thesis by (fastforce simp add: bex_Un)
qed

lemma interpClause_resolvants:
assumes lit_not_zero: "lit ≠ 0"
assumes sorted_and_distinct: "sorted cli" "distinct cli" "sorted clj" "distinct clj"
assumes resolv_clauses: "lit ∈ set cli" "compl lit ∈ set clj"
assumes interp: "interpClause a cli" "interpClause a clj"
shows "interpClause a (merge (remove lit cli) (remove (compl lit) clj))"
proof -
from lit_not_zero resolv_clauses interp sorted_and_distinct show ?thesis
unfolding interpClause_def
using interpClause_resolvants' by simp
qed

definition correctClause :: "Clause list ⇒ Clause ⇒ bool"
where
"correctClause rootcls cl = (∀a. (∀rcl ∈ set rootcls. interpClause a rcl) ⟶ (interpClause a cl))"

lemma correctClause_resolvants:
assumes lit_not_zero: "lit ≠ 0"
assumes sorted_and_distinct: "sorted cli" "distinct cli" "sorted clj" "distinct clj"
assumes resolv_clauses: "lit ∈ set cli" "compl lit ∈ set clj"
assumes correct: "correctClause r cli" "correctClause r clj"
shows "correctClause r (merge (remove lit cli) (remove (compl lit) clj))"
using assms interpClause_resolvants unfolding correctClause_def by auto

lemma implies_empty_inconsistent:
"correctClause cs [] ⟹ inconsistent cs"
unfolding inconsistent_def correctClause_def
by auto

text ‹Specific definition for derived clauses in the Array›

definition
array_ran :: "('a::heap) option array ⇒ heap ⇒ 'a set" where
"array_ran a h = {e. Some e ∈ set (Array.get h a)}"
― ‹FIXME›

lemma array_ranI: "⟦ Some b = Array.get h a ! i; i < Array.length h a ⟧ ⟹ b ∈ array_ran a h"
unfolding array_ran_def Array.length_def by simp

lemma array_ran_upd_array_Some:
assumes "cl ∈ array_ran a (Array.update a i (Some b) h)"
shows "cl ∈ array_ran a h ∨ cl = b"
proof -
have "set ((Array.get h a)[i := Some b]) ⊆ insert (Some b) (set (Array.get h a))" by (rule set_update_subset_insert)
with assms show ?thesis
unfolding array_ran_def Array.update_def by fastforce
qed

lemma array_ran_upd_array_None:
assumes "cl ∈ array_ran a (Array.update a i None h)"
shows "cl ∈ array_ran a h"
proof -
have "set ((Array.get h a)[i := None]) ⊆ insert None (set (Array.get h a))" by (rule set_update_subset_insert)
with assms show ?thesis
unfolding array_ran_def Array.update_def by auto
qed

definition correctArray :: "Clause list ⇒ Clause option array ⇒ heap ⇒ bool"
where
"correctArray rootcls a h =
(∀cl ∈ array_ran a h. correctClause rootcls cl ∧ sorted cl ∧ distinct cl)"

lemma correctArray_update:
assumes "correctArray rcs a h"
assumes "correctClause rcs c" "sorted c" "distinct c"
shows "correctArray rcs a (Array.update a i (Some c) h)"
using assms
unfolding correctArray_def
by (auto dest:array_ran_upd_array_Some)

lemma correctClause_mono:
assumes "correctClause rcs c"
assumes "set rcs ⊆ set rcs'"
shows "correctClause rcs' c"
using assms unfolding correctClause_def
by auto

section‹Improved version of SatChecker›

text‹This version just uses a single list traversal.›

subsection‹Function definitions›

primrec res_mem :: "Lit ⇒ Clause ⇒ Clause Heap"
where
"res_mem l [] = raise STR ''MiniSatChecked.res_thm: Cannot find literal''"
| "res_mem l (x#xs) = (if (x = l) then return xs else do { v ← res_mem l xs; return (x # v) })"

fun resolve1 :: "Lit ⇒ Clause ⇒ Clause ⇒ Clause Heap"
where
"resolve1 l (x#xs) (y#ys) =
(if (x = l) then return (merge xs (y#ys))
else (if (x < y) then do { v ← resolve1 l xs (y#ys); return (x # v) }
else (if (x > y) then do { v ← resolve1 l (x#xs) ys; return (y # v) }
else do { v ← resolve1 l xs ys; return (x # v) })))"
| "resolve1 l [] ys = raise STR ''MiniSatChecked.res_thm: Cannot find literal''"
| "resolve1 l xs [] = res_mem l xs"

fun resolve2 :: "Lit ⇒ Clause ⇒ Clause ⇒ Clause Heap"
where
"resolve2 l (x#xs) (y#ys) =
(if (y = l) then return (merge (x#xs) ys)
else (if (x < y) then do { v ← resolve2 l xs (y#ys); return (x # v) }
else (if (x > y) then do { v ← resolve2 l (x#xs) ys; return (y # v) }
else do { v ← resolve2 l xs ys; return (x # v) })))"
| "resolve2 l xs [] = raise STR ''MiniSatChecked.res_thm: Cannot find literal''"
| "resolve2 l [] ys = res_mem l ys"

fun res_thm' :: "Lit ⇒ Clause ⇒ Clause ⇒ Clause Heap"
where
"res_thm' l (x#xs) (y#ys) =
(if (x = l ∨ x = compl l) then resolve2 (compl x) xs (y#ys)
else (if (y = l ∨ y = compl l) then resolve1 (compl y) (x#xs) ys
else (if (x < y) then (res_thm' l xs (y#ys)) ⤜ (λv. return (x # v))
else (if (x > y) then (res_thm' l (x#xs) ys) ⤜ (λv. return (y # v))
else (res_thm' l xs ys) ⤜ (λv. return (x # v))))))"
| "res_thm' l [] ys = raise STR ''MiniSatChecked.res_thm: Cannot find literal''"
| "res_thm' l xs [] = raise STR ''MiniSatChecked.res_thm: Cannot find literal''"

declare res_mem.simps [simp del] resolve1.simps [simp del] resolve2.simps [simp del] res_thm'.simps [simp del]

lemma res_mem:
assumes "effect (res_mem l xs) h h' r"
shows "l ∈ set xs ∧ r = remove1 l xs"
using assms
proof (induct xs arbitrary: r)
case Nil
thus ?case unfolding res_mem.simps by (auto elim: effect_raiseE)
next
case (Cons x xs')
thus ?case
unfolding res_mem.simps
by (elim effect_raiseE effect_returnE effect_ifE effect_bindE) auto
qed

lemma resolve1_Inv:
assumes "effect (resolve1 l xs ys) h h' r"
shows "l ∈ set xs ∧ r = merge (remove1 l xs) ys"
using assms
proof (induct xs ys arbitrary: r rule: resolve1.induct)
case (1 l x xs y ys r)
thus ?case
unfolding resolve1.simps
by (elim effect_bindE effect_ifE effect_returnE) auto
next
case (2 l ys r)
thus ?case
unfolding resolve1.simps
by (elim effect_raiseE) auto
next
case (3 l v va r)
thus ?case
unfolding resolve1.simps
by (fastforce dest!: res_mem)
qed

lemma resolve2_Inv:
assumes "effect (resolve2 l xs ys) h h' r"
shows "l ∈ set ys ∧ r = merge xs (remove1 l ys)"
using assms
proof (induct xs ys arbitrary: r rule: resolve2.induct)
case (1 l x xs y ys r)
thus ?case
unfolding resolve2.simps
by (elim effect_bindE effect_ifE effect_returnE) auto
next
case (2 l ys r)
thus ?case
unfolding resolve2.simps
by (elim effect_raiseE) auto
next
case (3 l v va r)
thus ?case
unfolding resolve2.simps
by (fastforce dest!: res_mem)
qed

lemma res_thm'_Inv:
assumes "effect (res_thm' l xs ys) h h' r"
shows "∃l'. (l' ∈ set xs ∧ compl l' ∈ set ys ∧ (l' = compl l ∨ l' = l)) ∧ r = merge (remove1 l' xs) (remove1 (compl l') ys)"
using assms
proof (induct xs ys arbitrary: r rule: res_thm'.induct)
case (1 l x xs y ys r)
(* There are five cases for res_thm: We will consider them one after another: *)
{
assume cond: "x = l ∨ x = compl l"
assume resolve2: "effect (resolve2 (compl x) xs (y # ys)) h h' r"
from resolve2_Inv [OF resolve2] cond have ?case
apply -
by (rule exI[of _ "x"]) fastforce
} moreover
{
assume cond: "¬ (x = l ∨ x = compl l)" "y = l ∨ y = compl l"
assume resolve1: "effect (resolve1 (compl y) (x # xs) ys) h h' r"
from resolve1_Inv [OF resolve1] cond have ?case
apply -
by (rule exI[of _ "compl y"]) fastforce
} moreover
{
fix r'
assume cond: "¬ (x = l ∨ x = compl l)" "¬ (y = l ∨ y = compl l)" "x < y"
assume res_thm: "effect (res_thm' l xs (y # ys)) h h' r'"
assume return: "r = x # r'"
from "1.hyps"(1) [OF cond res_thm] cond return have ?case by auto
} moreover
{
fix r'
assume cond: "¬ (x = l ∨ x = compl l)" "¬ (y = l ∨ y = compl l)" "¬ x < y" "y < x"
assume res_thm: "effect (res_thm' l (x # xs) ys) h h' r'"
assume return: "r = y # r'"
from "1.hyps"(2) [OF cond res_thm] cond return have ?case by auto
} moreover
{
fix r'
assume cond: "¬ (x = l ∨ x = compl l)" "¬ (y = l ∨ y = compl l)" "¬ x < y" "¬ y < x"
assume res_thm: "effect (res_thm' l xs ys) h h' r'"
assume return: "r = x # r'"
from "1.hyps"(3) [OF cond res_thm] cond return have ?case by auto
} moreover
note "1.prems"
ultimately show ?case
unfolding res_thm'.simps
apply (elim effect_bindE effect_ifE effect_returnE)
apply simp
apply simp
apply simp
apply simp
apply fastforce
done
next
case (2 l ys r)
thus ?case
unfolding res_thm'.simps
by (elim effect_raiseE) auto
next
case (3 l v va r)
thus ?case
unfolding res_thm'.simps
by (elim effect_raiseE) auto
qed

lemma res_mem_no_heap:
assumes "effect (res_mem l xs) h h' r"
shows "h = h'"
using assms
apply (induct xs arbitrary: r)
unfolding res_mem.simps
apply (elim effect_raiseE)
apply auto
apply (elim effect_ifE effect_bindE effect_raiseE effect_returnE)
apply auto
done

lemma resolve1_no_heap:
assumes "effect (resolve1 l xs ys) h h' r"
shows "h = h'"
using assms
apply (induct xs ys arbitrary: r rule: resolve1.induct)
unfolding resolve1.simps
apply (elim effect_bindE effect_ifE effect_returnE effect_raiseE)
by (elim effect_raiseE) auto

lemma resolve2_no_heap:
assumes "effect (resolve2 l xs ys) h h' r"
shows "h = h'"
using assms
apply (induct xs ys arbitrary: r rule: resolve2.induct)
unfolding resolve2.simps
apply (elim effect_bindE effect_ifE effect_returnE effect_raiseE)
by (elim effect_raiseE) auto

lemma res_thm'_no_heap:
assumes "effect (res_thm' l xs ys) h h' r"
shows "h = h'"
using assms
proof (induct xs ys arbitrary: r rule: res_thm'.induct)
case (1 l x xs y ys r)
thus ?thesis
unfolding res_thm'.simps
by (elim effect_bindE effect_ifE effect_returnE)
next
case (2 l ys r)
thus ?case
unfolding res_thm'.simps
by (elim effect_raiseE) auto
next
case (3 l v va r)
thus ?case
unfolding res_thm'.simps
by (elim effect_raiseE) auto
qed

lemma res_thm'_Inv2:
assumes res_thm: "effect (res_thm' l xs ys) h h' rcl"
assumes l_not_null: "l ≠ 0"
assumes ys: "correctClause r ys ∧ sorted ys ∧ distinct ys"
assumes xs: "correctClause r xs ∧ sorted xs ∧ distinct xs"
shows "correctClause r rcl ∧ sorted rcl ∧ distinct rcl"
proof -
from res_thm'_Inv [OF res_thm] xs ys l_not_null show ?thesis
unfolding correctClause_def
prefer 2
apply (rule interpClause_resolvants)
apply simp_all
apply (insert compl_exists [of l])
apply auto
apply (rule interpClause_resolvants)
apply simp_all
done
qed

subsection ‹res_thm and doProofStep›

definition get_clause :: "Clause option array ⇒ ClauseId ⇒ Clause Heap"
where
"get_clause a i =
do { c ← Array.nth a i;
| Some x ⇒ return x)
}"

primrec res_thm2 :: "Clause option array ⇒ (Lit * ClauseId) ⇒ Clause ⇒ Clause Heap"
where
"res_thm2 a (l, j) cli =
( if l = 0 then raise STR ''Illegal literal''
else
do { clj ← get_clause a j;
res_thm' l cli clj
})"

primrec
foldM :: "('a ⇒ 'b ⇒ 'b Heap) ⇒ 'a list ⇒ 'b ⇒ 'b Heap"
where
"foldM f [] s = return s"
| "foldM f (x#xs) s = f x s ⤜ foldM f xs"

fun doProofStep2 :: "Clause option array ⇒ ProofStep ⇒ Clause list ⇒ Clause list Heap"
where
"doProofStep2 a (Conflict saveTo (i, rs)) rcs =
do {
cli ← get_clause a i;
result ← foldM (res_thm2 a) rs cli;
Array.upd saveTo (Some result) a;
return rcs
}"
| "doProofStep2 a (Delete cid) rcs = do { Array.upd cid None a; return rcs }"
| "doProofStep2 a (Root cid clause) rcs = do { Array.upd cid (Some (remdups (sort clause))) a; return (clause # rcs) }"
| "doProofStep2 a (Xstep cid1 cid2) rcs = raise STR ''MiniSatChecked.doProofStep: Xstep constructor found.''"
| "doProofStep2 a (ProofDone b) rcs = raise STR ''MiniSatChecked.doProofStep: ProofDone constructor found.''"

definition checker :: "nat ⇒ ProofStep list ⇒ nat ⇒ Clause list Heap"
where
"checker n p i =
do {
a ← Array.new n None;
rcs ← foldM (doProofStep2 a) p [];
ec ← Array.nth a i;
(if ec = Some [] then return rcs
else raise STR ''No empty clause'')
}"

lemma effect_case_option:
assumes "effect (case x of None ⇒ n | Some y ⇒ s y) h h' r"
obtains "x = None" "effect n h h' r"
| y where "x = Some y" "effect (s y) h h' r"
using assms unfolding effect_def by (auto split: option.splits)

lemma res_thm2_Inv:
assumes res_thm: "effect (res_thm2 a (l, j) cli) h h' rs"
assumes correct_a: "correctArray r a h"
assumes correct_cli: "correctClause r cli ∧ sorted cli ∧ distinct cli"
shows "h = h' ∧ correctClause r rs ∧ sorted rs ∧ distinct rs"
proof -
from res_thm have l_not_zero: "l ≠ 0"
by (auto elim: effect_raiseE)
{
fix clj
let ?rs = "merge (remove l cli) (remove (compl l) clj)"
let ?rs' = "merge (remove (compl l) cli) (remove l clj)"
assume "h = h'" "Some clj = Array.get h' a ! j" "j < Array.length h' a"
with correct_a have clj: "correctClause r clj" "sorted clj" "distinct clj"
unfolding correctArray_def by (auto intro: array_ranI)
with clj l_not_zero correct_cli
have "(l ∈ set cli ∧ compl l ∈ set clj
⟶ correctClause r ?rs ∧ sorted ?rs ∧ distinct ?rs) ∧
(compl l ∈ set cli ∧ l ∈ set clj
⟶ correctClause r ?rs' ∧ sorted ?rs' ∧ distinct ?rs')"
apply (auto intro!: correctClause_resolvants)
apply (insert compl_exists [of l])
by (auto intro!: correctClause_resolvants)
}
{
fix v clj
assume "Some clj = Array.get h a ! j" "j < Array.length h a"
with correct_a have clj: "correctClause r clj ∧ sorted clj ∧ distinct clj"
unfolding correctArray_def by (auto intro: array_ranI)
assume "effect (res_thm' l cli clj) h h' rs"
from res_thm'_no_heap[OF this] res_thm'_Inv2[OF this l_not_zero clj correct_cli]
have "h = h' ∧ correctClause r rs ∧ sorted rs ∧ distinct rs" by simp
}
with assms show ?thesis
unfolding res_thm2.simps get_clause_def
by (elim effect_bindE effect_ifE effect_nthE effect_raiseE effect_returnE effect_case_option) auto
qed

lemma foldM_Inv2:
assumes "effect (foldM (res_thm2 a) rs cli) h h' rcl"
assumes correct_a: "correctArray r a h"
assumes correct_cli: "correctClause r cli ∧ sorted cli ∧ distinct cli"
shows "h = h' ∧ correctClause r rcl ∧ sorted rcl ∧ distinct rcl"
using assms
proof (induct rs arbitrary: h h' cli)
case Nil thus ?case
unfolding foldM.simps
by (elim effect_returnE) auto
next
case (Cons x xs)
{
fix h1 ret
obtain l j where x_is: "x = (l, j)" by fastforce
assume res_thm2: "effect (res_thm2 a x cli) h h1 ret"
with x_is have res_thm2': "effect (res_thm2 a (l, j) cli) h h1 ret" by simp
note step = res_thm2_Inv [OF res_thm2' Cons.prems(2) Cons.prems(3)]
from step have ret: "correctClause r ret ∧ sorted ret ∧ distinct ret" by simp
from step Cons.prems(2) have correct_a: "correctArray r a h1"  by simp
assume foldM: "effect (foldM (res_thm2 a) xs ret) h1 h' rcl"
from step Cons.hyps [OF foldM correct_a ret] have
"h = h' ∧ correctClause r rcl ∧ sorted rcl ∧ distinct rcl" by auto
}
with Cons show ?case
unfolding foldM.simps
by (elim effect_bindE) auto
qed

lemma step_correct2:
assumes effect: "effect (doProofStep2 a step rcs) h h' res"
assumes correctArray: "correctArray rcs a h"
shows "correctArray res a h'"
proof (cases "(a,step,rcs)" rule: doProofStep2.cases)
case (1 a saveTo i rs rcs)
with effect correctArray
show ?thesis
apply auto
apply (auto simp: get_clause_def elim!: effect_bindE effect_nthE)
apply (auto elim!: effect_bindE effect_nthE effect_case_option effect_raiseE
effect_returnE effect_updE)
apply (frule foldM_Inv2)
apply assumption
apply (drule_tac x="y" in bspec)
apply (rule array_ranI[where i=i])
by (auto intro: correctArray_update)
next
case (2 a cid rcs)
with effect correctArray
show ?thesis
by (auto simp: correctArray_def elim!: effect_bindE effect_updE effect_returnE
dest: array_ran_upd_array_None)
next
case (3 a cid c rcs)
with effect correctArray
show ?thesis
apply (auto elim!: effect_bindE effect_updE effect_returnE)
apply (auto simp: correctArray_def dest!: array_ran_upd_array_Some)
apply (auto intro: correctClause_mono)
by (auto simp: correctClause_def)
next
case 4
with effect correctArray
show ?thesis by (auto elim: effect_raiseE)
next
case 5
with effect correctArray
show ?thesis by (auto elim: effect_raiseE)
qed

theorem fold_steps_correct:
assumes "effect (foldM (doProofStep2 a) steps rcs) h h' res"
assumes "correctArray rcs a h"
shows "correctArray res a h'"
using assms
by (induct steps arbitrary: rcs h h' res)
(auto elim!: effect_bindE effect_returnE dest:step_correct2)

theorem checker_soundness:
assumes "effect (checker n p i) h h' cs"
shows "inconsistent cs"
using assms unfolding checker_def
apply (elim effect_bindE effect_nthE effect_ifE effect_returnE effect_raiseE effect_newE)
prefer 2 apply simp
apply auto
apply (drule fold_steps_correct)
apply (rule implies_empty_inconsistent)
apply (drule bspec)
by (rule array_ranI, auto)

section ‹Functional version with Lists as Array›

subsection ‹List specific definitions›

definition list_ran :: "'a option list ⇒ 'a set"
where
"list_ran xs = {e. Some e ∈ set xs }"

lemma list_ranI: "⟦ i < List.length xs; xs ! i = Some b ⟧ ⟹ b ∈ list_ran xs"
unfolding list_ran_def by (drule sym, simp)

lemma list_ran_update_Some:
"cl ∈ list_ran (xs[i := (Some b)]) ⟹ cl ∈ list_ran xs ∨ cl = b"
proof -
assume assms: "cl ∈ list_ran (xs[i := (Some b)])"
have "set (xs[i := Some b]) ⊆ insert (Some b) (set xs)"
by (simp only: set_update_subset_insert)
with assms have "Some cl ∈ insert (Some b) (set xs)"
unfolding list_ran_def by fastforce
thus ?thesis
unfolding list_ran_def by auto
qed

lemma list_ran_update_None:
"cl ∈ list_ran (xs[i := None]) ⟹ cl ∈ list_ran xs"
proof -
assume assms: "cl ∈ list_ran (xs[i := None])"
have "set (xs[i := None]) ⊆ insert None (set xs)"
by (simp only: set_update_subset_insert)
with assms show ?thesis
unfolding list_ran_def by auto
qed

definition correctList :: "Clause list ⇒ Clause option list ⇒ bool"
where
"correctList rootcls xs =
(∀cl ∈ list_ran xs. correctClause rootcls cl ∧ sorted cl ∧ distinct cl)"

subsection ‹Checker functions›

primrec lres_thm :: "Clause option list ⇒ (Lit * ClauseId) ⇒ Clause ⇒ Clause Heap"
where
"lres_thm xs (l, j) cli = (if (j < List.length xs) then (case (xs ! j) of
None ⇒ raise STR ''MiniSatChecked.res_thm: No resolvant clause in thms array for Conflict step.''
| Some clj ⇒ res_thm' l cli clj
) else raise STR ''Error'')"

fun ldoProofStep :: " ProofStep ⇒ (Clause option list  * Clause list) ⇒ (Clause option list * Clause list) Heap"
where
"ldoProofStep (Conflict saveTo (i, rs)) (xs, rcl) =
(case (xs ! i) of
None ⇒ raise STR ''MiniSatChecked.doProofStep: No starting clause in thms array for Conflict step.''
| Some cli ⇒ do {
result ← foldM (lres_thm xs) rs cli ;
return ((xs[saveTo:=Some result]), rcl)
})"
| "ldoProofStep (Delete cid) (xs, rcl) = return (xs[cid:=None], rcl)"
| "ldoProofStep (Root cid clause) (xs, rcl) = return (xs[cid:=Some (sort clause)], (remdups(sort clause)) # rcl)"
| "ldoProofStep (Xstep cid1 cid2) (xs, rcl) = raise STR ''MiniSatChecked.doProofStep: Xstep constructor found.''"
| "ldoProofStep (ProofDone b) (xs, rcl) = raise STR ''MiniSatChecked.doProofStep: ProofDone constructor found.''"

definition lchecker :: "nat ⇒ ProofStep list ⇒ nat ⇒ Clause list Heap"
where
"lchecker n p i =
do {
rcs ← foldM (ldoProofStep) p ([], []);
(if (fst rcs ! i) = Some [] then return (snd rcs)
else raise STR ''No empty clause'')
}"

section ‹Functional version with RedBlackTrees›

primrec tres_thm :: "(ClauseId, Clause) RBT_Impl.rbt ⇒ Lit × ClauseId ⇒ Clause ⇒ Clause Heap"
where
"tres_thm t (l, j) cli =
(case (rbt_lookup t j) of
None ⇒ raise STR ''MiniSatChecked.res_thm: No resolvant clause in thms array for Conflict step.''
| Some clj ⇒ res_thm' l cli clj)"

fun tdoProofStep :: " ProofStep ⇒ ((ClauseId, Clause) RBT_Impl.rbt * Clause list) ⇒ ((ClauseId, Clause) RBT_Impl.rbt * Clause list) Heap"
where
"tdoProofStep (Conflict saveTo (i, rs)) (t, rcl) =
(case (rbt_lookup t i) of
None ⇒ raise STR ''MiniSatChecked.doProofStep: No starting clause in thms array for Conflict step.''
| Some cli ⇒ do {
result ← foldM (tres_thm t) rs cli;
return ((rbt_insert saveTo result t), rcl)
})"
| "tdoProofStep (Delete cid) (t, rcl) = return ((rbt_delete cid t), rcl)"
| "tdoProofStep (Root cid clause) (t, rcl) = return (rbt_insert cid (sort clause) t, (remdups(sort clause)) # rcl)"
| "tdoProofStep (Xstep cid1 cid2) (t, rcl) = raise STR ''MiniSatChecked.doProofStep: Xstep constructor found.''"
| "tdoProofStep (ProofDone b) (t, rcl) = raise STR ''MiniSatChecked.doProofStep: ProofDone constructor found.''"

definition tchecker :: "nat ⇒ ProofStep list ⇒ nat ⇒ Clause list Heap"
where
"tchecker n p i =
do {
rcs ← foldM (tdoProofStep) p (RBT_Impl.Empty, []);
(if (rbt_lookup (fst rcs) i) = Some [] then return (snd rcs)
else raise STR ''No empty clause'')
}"

export_code checker tchecker lchecker checking SML

end

```