Theory SatChecker

theory SatChecker
imports RBT_Impl Sorted_List Imperative_HOL
(*  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]

subsection ‹Proofs about these functions›

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)
apply (auto simp add: res_mem_no_heap)
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)
apply (auto simp add: res_mem_no_heap)
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)
  (auto simp add: resolve1_no_heap resolve2_no_heap)
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
    apply (auto simp add: sorted_remove1)
    unfolding correctClause_def
    apply (auto simp add: remove1_eq_remove)
    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;
           (case c of None ⇒ raise STR ''Clause not found''
                    | 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 (simp add: correctArray_def)
    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 (simp add: correctArray_def array_ran_def)
apply (rule implies_empty_inconsistent)
apply (simp add:correctArray_def)
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