Theory DPLL_W

theory DPLL_W
imports Partial_Annotated_Herbrand_Interpretation Wellfounded_More
theory DPLL_W
imports
  Entailment_Definition.Partial_Herbrand_Interpretation
  Entailment_Definition.Partial_Annotated_Herbrand_Interpretation
  Weidenbach_Book_Base.Wellfounded_More
begin

section ‹Weidenbach's DPLL›

subsection ‹Rules›

type_synonym 'a dpllW_ann_lit = "('a, unit) ann_lit"
type_synonym 'a dpllW_ann_lits = "('a, unit) ann_lits"
type_synonym 'v dpllW_state = "'v dpllW_ann_lits × 'v clauses"

abbreviation trail :: "'v dpllW_state ⇒ 'v dpllW_ann_lits" where
"trail ≡ fst"
abbreviation clauses :: "'v dpllW_state ⇒ 'v clauses" where
"clauses ≡ snd"

inductive dpllW :: "'v dpllW_state ⇒ 'v dpllW_state ⇒ bool" where
propagate: "add_mset L C ∈# clauses S ⟹ trail S ⊨as CNot C ⟹ undefined_lit (trail S) L
  ⟹ dpllW S (Propagated L () # trail S, clauses S)" |
decided: "undefined_lit (trail S) L ⟹ atm_of L ∈ atms_of_mm (clauses S)
  ⟹ dpllW S (Decided L # trail S, clauses S)" |
backtrack: "backtrack_split (trail S) = (M', L # M) ⟹ is_decided L ⟹ D ∈# clauses S
  ⟹ trail S ⊨as CNot D ⟹ dpllW S (Propagated (- (lit_of L)) () # M, clauses S)"


subsection ‹Invariants›

lemma dpllW_distinct_inv:
  assumes "dpllW S S'"
  and "no_dup (trail S)"
  shows "no_dup (trail S')"
  using assms
proof (induct rule: dpllW.induct)
  case (decided L S)
  then show ?case using defined_lit_map by force
next
  case (propagate C L S)
  then show ?case using defined_lit_map by force
next
  case (backtrack S M' L M D) note extracted = this(1) and no_dup = this(5)
  show ?case
    using no_dup backtrack_split_list_eq[of "trail S", symmetric] unfolding extracted
    by (auto dest: no_dup_appendD)
qed

lemma dpllW_consistent_interp_inv:
  assumes "dpllW S S'"
  and "consistent_interp (lits_of_l (trail S))"
  and "no_dup (trail S)"
  shows "consistent_interp (lits_of_l (trail S'))"
  using assms
proof (induct rule: dpllW.induct)
  case (backtrack S M' L M D) note extracted = this(1) and decided = this(2) and D = this(4) and
    cons = this(5) and no_dup = this(6)
  have no_dup': "no_dup M"
    by (metis (no_types) backtrack_split_list_eq distinct.simps(2) distinct_append extracted
      list.simps(9) map_append no_dup snd_conv no_dup_def)
  then have "insert (lit_of L) (lits_of_l M) ⊆ lits_of_l (trail S)"
    using backtrack_split_list_eq[of "trail S", symmetric] unfolding extracted by auto
  then have cons: "consistent_interp (insert (lit_of L) (lits_of_l M))"
    using consistent_interp_subset cons by blast
  moreover have undef: "undefined_lit M (lit_of L)"
      using no_dup backtrack_split_list_eq[of "trail S", symmetric] unfolding extracted by force
  moreover have "lit_of L ∉ lits_of_l M"
      using undef by (auto simp: Decided_Propagated_in_iff_in_lits_of_l)
  ultimately show ?case by simp
qed (auto intro: consistent_add_undefined_lit_consistent)

lemma dpllW_vars_in_snd_inv:
  assumes "dpllW S S'"
  and "atm_of ` (lits_of_l (trail S)) ⊆ atms_of_mm (clauses S)"
  shows "atm_of ` (lits_of_l (trail S')) ⊆ atms_of_mm (clauses S')"
  using assms
proof (induct rule: dpllW.induct)
  case (backtrack S M' L M D)
  then have "atm_of (lit_of L) ∈ atms_of_mm (clauses S)"
    using backtrack_split_list_eq[of "trail S", symmetric] by auto
  moreover
    have "atm_of ` lits_of_l (trail S) ⊆ atms_of_mm (clauses S)"
      using backtrack(5)  by simp
    then have "⋀xb. xb ∈ set M ⟹ atm_of (lit_of xb) ∈ atms_of_mm (clauses S)"
      using backtrack_split_list_eq[symmetric, of "trail S"] backtrack.hyps(1)
      unfolding lits_of_def by auto
  ultimately show ?case by (auto simp : lits_of_def)
qed (auto simp: in_plus_implies_atm_of_on_atms_of_ms)

lemma atms_of_ms_lit_of_atms_of: "atms_of_ms (unmark ` c) = atm_of ` lit_of ` c"
  unfolding atms_of_ms_def using image_iff by force

text ‹\cwref{dpll:sound:model}{theorem 2.8.3 page 86}›
lemma dpllW_propagate_is_conclusion:
  assumes "dpllW S S'"
  and "all_decomposition_implies_m (clauses S) (get_all_ann_decomposition (trail S))"
  and "atm_of ` lits_of_l (trail S) ⊆ atms_of_mm (clauses S)"
  shows "all_decomposition_implies_m (clauses S') (get_all_ann_decomposition (trail S'))"
  using assms
proof (induct rule: dpllW.induct)
  case (decided L S)
  then show ?case unfolding all_decomposition_implies_def by simp
next
  case (propagate L C S) note inS = this(1) and cnot = this(2) and IH = this(4) and undef = this(3) and atms_incl = this(5)
  let ?I = "set (map unmark (trail S)) ∪ set_mset (clauses S)"
  have "?I ⊨p add_mset L C" by (auto simp add: inS)
  moreover have "?I ⊨ps CNot C" using true_annots_true_clss_cls cnot by fastforce
  ultimately have "?I ⊨p {#L#}" using true_clss_cls_plus_CNot[of ?I L C] inS by blast
  {
    assume "get_all_ann_decomposition (trail S) = []"
    then have ?case by blast
  }
  moreover {
    assume n: "get_all_ann_decomposition (trail S) ≠ []"
    have 1: "⋀a b. (a, b) ∈ set (tl (get_all_ann_decomposition (trail S)))
      ⟹ (unmark_l a ∪ set_mset (clauses S)) ⊨ps unmark_l b"
      using IH unfolding all_decomposition_implies_def by (fastforce simp add: list.set_sel(2) n)
    moreover have 2: "⋀a c. hd (get_all_ann_decomposition (trail S)) = (a, c)
      ⟹ (unmark_l a ∪ set_mset (clauses S)) ⊨ps (unmark_l c)"
      by (metis IH all_decomposition_implies_cons_pair all_decomposition_implies_single
        list.collapse n)
    moreover have 3: "⋀a c. hd (get_all_ann_decomposition (trail S)) = (a, c)
      ⟹ (unmark_l a ∪ set_mset (clauses S)) ⊨p {#L#}"
      proof -
        fix a c
        assume h: "hd (get_all_ann_decomposition (trail S)) = (a, c)"
        have h': "trail S = c @ a" using get_all_ann_decomposition_decomp h by blast
        have I: "set (map unmark a) ∪ set_mset (clauses S)
          ∪ unmark_l c ⊨ps CNot C"
          using ‹?I ⊨ps CNot C› unfolding h' by (simp add: Un_commute Un_left_commute)
        have
          "atms_of_ms (CNot C) ⊆ atms_of_ms (set (map unmark a) ∪ set_mset (clauses S))"
            and
          "atms_of_ms (unmark_l c) ⊆ atms_of_ms (set (map unmark a)
            ∪ set_mset (clauses S))"
           using atms_incl cnot
           apply (auto simp: atms_of_def dest!: true_annots_CNot_all_atms_defined; fail)[]
          using inS atms_of_atms_of_ms_mono atms_incl by (fastforce simp: h')

        then have "unmark_l a ∪ set_mset (clauses S) ⊨ps CNot C"
          using true_clss_clss_left_right[OF _ I] h "2" by auto
        then show "unmark_l a ∪ set_mset (clauses S) ⊨p {#L#}"
          using inS true_clss_cls_plus_CNot true_clss_clss_in_imp_true_clss_cls union_trus_clss_clss
          by blast
      qed
    ultimately have ?case
      by (cases "hd (get_all_ann_decomposition (trail S))")
         (auto simp: all_decomposition_implies_def)
  }
  ultimately show ?case by auto
next
  case (backtrack S M' L M D) note extracted = this(1) and decided = this(2) and D = this(3) and
    cnot = this(4) and cons = this(4) and IH = this(5) and atms_incl = this(6)
  have S: "trail S = M' @ L # M"
    using backtrack_split_list_eq[of "trail S"] unfolding extracted by auto
  have M': "∀l ∈ set M'. ¬is_decided l"
    using extracted backtrack_split_fst_not_decided[of _ "trail S"] by simp
  have n: "get_all_ann_decomposition (trail S) ≠ []" by auto
  then have "all_decomposition_implies_m (clauses S) ((L # M, M')
           # tl (get_all_ann_decomposition (trail S)))"
    by (metis (no_types) IH extracted get_all_ann_decomposition_backtrack_split list.exhaust_sel)
  then have 1: "unmark_l (L # M) ∪ set_mset (clauses S) ⊨ps(λa.{#lit_of a#}) ` set M'"
    by simp
  moreover
    have "unmark_l (L # M) ∪ unmark_l M' ⊨ps CNot D"
      by (metis (mono_tags, lifting) S Un_commute cons image_Un set_append
        true_annots_true_clss_clss)
    then have 2: "unmark_l (L # M) ∪ set_mset (clauses S) ∪ unmark_l M'
        ⊨ps CNot D"
      by (metis (no_types, lifting) Un_assoc Un_left_commute true_clss_clss_union_l_r)
  ultimately
    have "set (map unmark (L # M)) ∪ set_mset (clauses S) ⊨ps CNot D"
      using true_clss_clss_left_right by fastforce
    then have "set (map unmark (L # M)) ∪ set_mset (clauses S) ⊨p {#}"
      by (metis (mono_tags, lifting) D Un_def mem_Collect_eq
        true_clss_clss_contradiction_true_clss_cls_false)
    then have IL: "unmark_l M ∪ set_mset (clauses S) ⊨p {#-lit_of L#}"
      using true_clss_clss_false_left_right by auto
  show ?case unfolding S all_decomposition_implies_def
    proof
      fix x P level
      assume x: "x ∈ set (get_all_ann_decomposition
        (fst (Propagated (- lit_of L) P # M, clauses S)))"
      let ?M' = "Propagated (- lit_of L) P # M"
      let ?hd = "hd (get_all_ann_decomposition ?M')"
      let ?tl = "tl (get_all_ann_decomposition ?M')"
      have "x = ?hd ∨ x ∈ set ?tl"
        using x
        by (cases "get_all_ann_decomposition ?M'")
           auto
      moreover {
        assume x': "x ∈ set ?tl"
        have L': "Decided (lit_of L) = L" using decided by (cases L, auto)
        have "x ∈ set (get_all_ann_decomposition (M' @ L # M))"
          using x' get_all_ann_decomposition_except_last_choice_equal[of M' "lit_of L" P M]
          L' by (metis (no_types) M' list.set_sel(2) tl_Nil)
        then have "case x of (Ls, seen) ⇒ unmark_l Ls ∪ set_mset (clauses S)
          ⊨ps unmark_l seen"
          using decided IH by (cases L) (auto simp add: S all_decomposition_implies_def)
      }
      moreover {
        assume x': "x = ?hd"
        have tl: "tl (get_all_ann_decomposition (M' @ L # M)) ≠ []"
        proof -
          have f1: "⋀ms. length (get_all_ann_decomposition (M' @ ms))
              = length (get_all_ann_decomposition ms)"
            by (simp add: M' get_all_ann_decomposition_remove_undecided_length)
          have "Suc (length (get_all_ann_decomposition M)) ≠ Suc 0"
            by blast
          then show ?thesis
            using f1[of ‹L # M›] decided by (cases ‹get_all_ann_decomposition
               (M' @ L # M)›; cases L) auto
        qed
        obtain M0' M0 where
          L0: "hd (tl (get_all_ann_decomposition (M' @ L # M))) = (M0, M0')"
          by (cases "hd (tl (get_all_ann_decomposition (M' @ L # M)))")
        have x'': "x = (M0, Propagated (-lit_of L) P # M0')"
          unfolding x' using get_all_ann_decomposition_last_choice tl M' L0
          by (smt is_decided_ex_Decided lit_of.simps(1) local.decided old.unit.exhaust)
        obtain l_get_all_ann_decomposition where
          "get_all_ann_decomposition (trail S) = (L # M, M') # (M0, M0') #
            l_get_all_ann_decomposition"
          using get_all_ann_decomposition_backtrack_split extracted by (metis (no_types) L0 S
            hd_Cons_tl n tl)
        then have "M = M0' @ M0" using get_all_ann_decomposition_hd_hd by fastforce
        then have IL':  "unmark_l M0 ∪ set_mset (clauses S)
          ∪ unmark_l M0' ⊨ps {{#- lit_of L#}}"
          using IL by (simp add: Un_commute Un_left_commute image_Un)
        moreover have H: "unmark_l M0 ∪ set_mset (clauses S)
          ⊨ps unmark_l M0'"
          using IH x'' unfolding all_decomposition_implies_def by (metis (no_types, lifting) L0 S
            list.set_sel(1) list.set_sel(2) old.prod.case tl tl_Nil)
        ultimately have "case x of (Ls, seen) ⇒ unmark_l Ls ∪ set_mset (clauses S)
          ⊨ps unmark_l seen"
          using true_clss_clss_left_right unfolding x'' by auto
      }
      ultimately show "case x of (Ls, seen) ⇒
        unmark_l Ls ∪ set_mset (snd (?M', clauses S))
          ⊨ps unmark_l seen"
        unfolding snd_conv by blast
    qed
qed

text ‹\cwref{dpll:sound:propLits:valuation}{theorem 2.8.4 page 86}›
theorem dpllW_propagate_is_conclusion_of_decided:
  assumes "dpllW S S'"
  and "all_decomposition_implies_m (clauses S) (get_all_ann_decomposition (trail S))"
  and "atm_of ` lits_of_l (trail S) ⊆ atms_of_mm (clauses S)"
  shows "set_mset (clauses S') ∪ {{#lit_of L#} |L. is_decided L ∧ L ∈ set (trail S')}
    ⊨ps unmark ` ⋃(set ` snd ` set (get_all_ann_decomposition (trail S')))"
  using all_decomposition_implies_trail_is_implied[OF dpllW_propagate_is_conclusion[OF assms]] .

text ‹\cwref{dpll:sound:propLits:unsat}{theorem 2.8.5 page 86}›
lemma only_propagated_vars_unsat:
  assumes decided: "∀x ∈ set M. ¬ is_decided x"
  and DN: "D ∈ N" and D: "M ⊨as CNot D"
  and inv: "all_decomposition_implies N (get_all_ann_decomposition M)"
  and atm_incl: "atm_of ` lits_of_l M ⊆ atms_of_ms N"
  shows "unsatisfiable N"
proof (rule ccontr)
  assume "¬ unsatisfiable N"
  then obtain I where
    I: "I ⊨s N" and
    cons: "consistent_interp I" and
    tot: "total_over_m I N"
    unfolding satisfiable_def by auto
  then have I_D: "I ⊨ D"
    using DN unfolding true_clss_def by auto

  have l0: "{{#lit_of L#} |L. is_decided L ∧ L ∈ set M} = {}" using decided by auto
  have "atms_of_ms (N ∪ unmark_l M) = atms_of_ms N"
    using atm_incl unfolding atms_of_ms_def lits_of_def by auto

  then have "total_over_m I (N ∪ unmark ` (set M))"
    using tot unfolding total_over_m_def by auto
  then have "I ⊨s unmark ` (set M)"
    using all_decomposition_implies_propagated_lits_are_implied[OF inv] cons I
    unfolding true_clss_clss_def l0 by auto
  then have IM: "I ⊨s unmark_l M" by auto
  {
    fix K
    assume "K ∈# D"
    then have "-K ∈ lits_of_l M"
      by (auto split: if_split_asm
        intro: allE[OF D[unfolded true_annots_def Ball_def], of "{#-K#}"])
    then have "-K ∈ I" using IM true_clss_singleton_lit_of_implies_incl by fastforce
  }
  then have "¬ I ⊨ D" using cons unfolding true_cls_def consistent_interp_def by auto
  then show False using I_D by blast
qed

lemma dpllW_same_clauses:
  assumes "dpllW S S'"
  shows "clauses S = clauses S'"
  using assms by (induct rule: dpllW.induct, auto)

lemma rtranclp_dpllW_inv:
  assumes "rtranclp dpllW S S'"
  and inv: "all_decomposition_implies_m (clauses S) (get_all_ann_decomposition (trail S))"
  and atm_incl: "atm_of ` lits_of_l (trail S) ⊆ atms_of_mm (clauses S)"
  and "consistent_interp (lits_of_l (trail S))"
  and "no_dup (trail S)"
  shows "all_decomposition_implies_m (clauses S') (get_all_ann_decomposition (trail S'))"
  and "atm_of ` lits_of_l (trail S')  ⊆ atms_of_mm (clauses S')"
  and "clauses S = clauses S'"
  and "consistent_interp (lits_of_l (trail S'))"
  and "no_dup (trail S')"
  using assms
proof (induct rule: rtranclp_induct)
  case base
  show
    "all_decomposition_implies_m (clauses S) (get_all_ann_decomposition (trail S))" and
    "atm_of ` lits_of_l (trail S) ⊆ atms_of_mm (clauses S)" and
    "clauses S = clauses S" and
    "consistent_interp (lits_of_l (trail S))" and
    "no_dup (trail S)" using assms by auto
next
  case (step S' S'') note dpllWStar = this(1) and IH = this(3,4,5,6,7) and
    dpllW = this(2)
  moreover
    assume
      inv: "all_decomposition_implies_m (clauses S) (get_all_ann_decomposition (trail S))" and
      atm_incl: "atm_of ` lits_of_l (trail S)  ⊆ atms_of_mm (clauses S)" and
      cons: "consistent_interp (lits_of_l (trail S))" and
      "no_dup (trail S)"
  ultimately have decomp: "all_decomposition_implies_m (clauses S')
    (get_all_ann_decomposition (trail S'))" and
    atm_incl': "atm_of ` lits_of_l (trail S') ⊆ atms_of_mm (clauses S')" and
    snd: "clauses S = clauses S'" and
    cons': "consistent_interp (lits_of_l (trail S'))" and
    no_dup': "no_dup (trail S')" by blast+
  show "clauses S = clauses S''" using dpllW_same_clauses[OF dpllW] snd by metis

  show "all_decomposition_implies_m (clauses S'') (get_all_ann_decomposition (trail S''))"
    using dpllW_propagate_is_conclusion[OF dpllW] decomp atm_incl' by auto
  show "atm_of ` lits_of_l (trail S'') ⊆ atms_of_mm (clauses S'')"
    using dpllW_vars_in_snd_inv[OF dpllW]  atm_incl atm_incl' by auto
  show "no_dup (trail S'')" using dpllW_distinct_inv[OF dpllW] no_dup' dpllW by auto
  show "consistent_interp (lits_of_l (trail S''))"
    using cons' no_dup' dpllW_consistent_interp_inv[OF dpllW] by auto
qed

definition "dpllW_all_inv S ≡
  (all_decomposition_implies_m (clauses S) (get_all_ann_decomposition (trail S))
  ∧ atm_of ` lits_of_l (trail S)  ⊆ atms_of_mm (clauses S)
  ∧ consistent_interp (lits_of_l (trail S))
  ∧ no_dup (trail S))"

lemma dpllW_all_inv_dest[dest]:
  assumes "dpllW_all_inv S"
  shows "all_decomposition_implies_m (clauses S) (get_all_ann_decomposition (trail S))"
  and "atm_of ` lits_of_l (trail S)  ⊆ atms_of_mm (clauses S)"
  and "consistent_interp (lits_of_l (trail S)) ∧ no_dup (trail S)"
  using assms unfolding dpllW_all_inv_def lits_of_def by auto

lemma rtranclp_dpllW_all_inv:
  assumes "rtranclp dpllW S S'"
  and "dpllW_all_inv S"
  shows "dpllW_all_inv S'"
  using assms rtranclp_dpllW_inv[OF assms(1)] unfolding dpllW_all_inv_def lits_of_def by blast

lemma dpllW_all_inv:
  assumes "dpllW S S'"
  and "dpllW_all_inv S"
  shows "dpllW_all_inv S'"
  using assms rtranclp_dpllW_all_inv by blast

lemma rtranclp_dpllW_inv_starting_from_0:
  assumes "rtranclp dpllW S S'"
  and inv: "trail S = []"
  shows "dpllW_all_inv S'"
proof -
  have "dpllW_all_inv S"
    using assms unfolding all_decomposition_implies_def dpllW_all_inv_def by auto
  then show ?thesis using rtranclp_dpllW_all_inv[OF assms(1)] by blast
qed

lemma dpllW_can_do_step:
  assumes "consistent_interp (set M)"
  and "distinct M"
  and "atm_of ` (set M) ⊆ atms_of_mm N"
  shows "rtranclp dpllW ([], N) (map Decided M, N)"
  using assms
proof (induct M)
  case Nil
  then show ?case by auto
next
  case (Cons L M)
  then have "undefined_lit (map Decided M) L"
    unfolding defined_lit_def consistent_interp_def by auto
  moreover have "atm_of L ∈ atms_of_mm N" using Cons.prems(3) by auto
  ultimately have "dpllW (map Decided M, N) (map Decided (L # M), N)"
    using dpllW.decided by auto
  moreover have "consistent_interp (set M)" and "distinct M" and "atm_of ` set M ⊆ atms_of_mm N"
    using Cons.prems unfolding consistent_interp_def by auto
  ultimately show ?case using Cons.hyps by auto
qed

definition "conclusive_dpllW_state (S:: 'v dpllW_state) ⟷
  (trail S ⊨asm clauses S ∨ ((∀L ∈ set (trail S). ¬is_decided L)
  ∧ (∃C ∈# clauses S. trail S ⊨as CNot C)))"

text ‹\cwref{prop:prop:dpllcomplete}{theorem 2.8.7 page 87}›
lemma dpllW_strong_completeness:
  assumes "set M ⊨sm N"
  and "consistent_interp (set M)"
  and "distinct M"
  and "atm_of ` (set M) ⊆ atms_of_mm N"
  shows "dpllW** ([], N) (map Decided M, N)"
  and "conclusive_dpllW_state (map Decided M, N)"
proof -
  show "rtranclp dpllW ([], N) (map Decided M, N)" using dpllW_can_do_step assms by auto
  have "map Decided M ⊨asm N" using assms(1) true_annots_decided_true_cls by auto
  then show "conclusive_dpllW_state (map Decided M, N)"
    unfolding conclusive_dpllW_state_def by auto
qed

text ‹\cwref{prop:prop:dpllsound}{theorem 2.8.6 page 86}›
lemma dpllW_sound:
  assumes
    "rtranclp dpllW ([], N) (M, N)" and
    "∀S. ¬dpllW (M, N) S"
  shows "M ⊨asm N ⟷ satisfiable (set_mset N)" (is "?A ⟷ ?B")
proof
  let ?M'= "lits_of_l M"
  assume ?A
  then have "?M' ⊨sm N" by (simp add: true_annots_true_cls)
  moreover have "consistent_interp ?M'"
    using rtranclp_dpllW_inv_starting_from_0[OF assms(1)] by auto
  ultimately show ?B by auto
next
  assume ?B
  show ?A
    proof (rule ccontr)
      assume n: "¬ ?A"
      have "(∃L. undefined_lit M L ∧ atm_of L ∈ atms_of_mm N) ∨ (∃D∈#N. M ⊨as CNot D)"
        proof -
          obtain D :: "'a clause" where D: "D ∈# N" and "¬ M ⊨a D"
            using n unfolding true_annots_def Ball_def by auto
          then have "(∃L. undefined_lit M L ∧ atm_of L ∈ atms_of D) ∨ M ⊨as CNot D"
             unfolding true_annots_def Ball_def CNot_def true_annot_def
             using atm_of_lit_in_atms_of true_annot_iff_decided_or_true_lit true_cls_def
             by (smt mem_Collect_eq union_single_eq_member)
          then show ?thesis
            by (metis Bex_def D atms_of_atms_of_ms_mono rev_subsetD)
        qed
      moreover {
        assume "∃L. undefined_lit M L ∧ atm_of L ∈ atms_of_mm N"
        then have False using assms(2) decided by fastforce
      }
      moreover {
        assume "∃D∈#N. M ⊨as CNot D"
        then obtain D where DN: "D ∈# N" and MD: "M ⊨as CNot D" by auto
        {
          assume "∀l ∈ set M. ¬ is_decided l"
          moreover have "dpllW_all_inv ([], N)"
            using assms unfolding all_decomposition_implies_def dpllW_all_inv_def by auto
          ultimately have "unsatisfiable (set_mset N)"
            using only_propagated_vars_unsat[of M D "set_mset N"] DN MD
            rtranclp_dpllW_all_inv[OF assms(1)] by force
          then have False using ‹?B› by blast
        }
        moreover {
          assume l: "∃l ∈ set M. is_decided l"
          then have False
            using backtrack[of "(M, N)" _ _ _ D ] DN MD assms(2)
              backtrack_split_some_is_decided_then_snd_has_hd[OF l]
            by (metis backtrack_split_snd_hd_decided fst_conv list.distinct(1) list.sel(1) snd_conv)
        }
        ultimately have False by blast
      }
      ultimately show False by blast
     qed
qed


subsection ‹Termination›

definition "dpllW_mes M n =
  map (λl. if is_decided l then 2 else (1::nat)) (rev M) @ replicate (n - length M) 3"

lemma length_dpllW_mes:
  assumes "length M ≤ n"
  shows "length (dpllW_mes M n) = n"
  using assms unfolding dpllW_mes_def by auto

lemma distinctcard_atm_of_lit_of_eq_length:
  assumes "no_dup S"
  shows "card (atm_of ` lits_of_l S) = length S"
  using assms by (induct S) (auto simp add: image_image lits_of_def no_dup_def)

lemma Cons_lexn_iff:
  shows ‹(x # xs, y # ys) ∈ lexn R n ⟷ (length (x # xs) = n ∧ length (y # ys) = n ∧
         ((x,y) ∈ R ∨ (x = y ∧ (xs, ys) ∈ lexn R (n - 1))))›
  unfolding lexn_conv apply (rule iffI; clarify)
  subgoal for xys xa ya xs' ys'
    by (cases xys) (auto simp: lexn_conv)
  subgoal by (auto 5 5 simp: lexn_conv simp del: append_Cons simp: append_Cons[symmetric])
  done
declare append_same_lexn[simp] prepend_same_lexn[simp] Cons_lexn_iff[simp]
declare lexn.simps(2)[simp del]

lemma dpllW_card_decrease:
  assumes
    dpll: "dpllW S S'" and
    [simp]: "length (trail S') ≤ card vars" and
    "length (trail S) ≤ card vars"
  shows
    "(dpllW_mes (trail S') (card vars), dpllW_mes (trail S) (card vars)) ∈ lexn less_than (card vars)"
  using assms
proof (induct rule: dpllW.induct)
  case (propagate C L S)
  then have m: "card vars - length (trail S) = Suc (card vars - Suc (length (trail S)))"
    by fastforce
  then show ‹(dpllW_mes (trail (Propagated C () # trail S, clauses S)) (card vars),
         dpllW_mes (trail S) (card vars)) ∈ lexn less_than (card vars)›
     unfolding dpllW_mes_def by auto
next
  case (decided S L)
  have m: "card vars - length (trail S) = Suc (card vars - Suc (length (trail S)))"
    using decided.prems[simplified] using Suc_diff_le by fastforce
  then show ‹(dpllW_mes (trail (Decided L # trail S, clauses S)) (card vars),
         dpllW_mes (trail S) (card vars)) ∈ lexn less_than (card vars)›
     unfolding dpllW_mes_def by auto
next
  case (backtrack S M' L M D)
  moreover have S: "trail S = M' @ L # M"
    using backtrack.hyps(1) backtrack_split_list_eq[of "trail S"] by auto
  ultimately show ‹(dpllW_mes (trail (Propagated (- lit_of L) () # M, clauses S)) (card vars),
         dpllW_mes (trail S) (card vars)) ∈ lexn less_than (card vars)›
    using backtrack_split_list_eq[of "trail S"] unfolding dpllW_mes_def by fastforce
qed

text ‹\cwref{prop:prop:dpllterminating}{theorem 2.8.8 page 87}›
lemma dpllW_card_decrease':
  assumes dpll: "dpllW S S'"
  and atm_incl: "atm_of ` lits_of_l (trail S) ⊆ atms_of_mm (clauses S)"
  and no_dup: "no_dup (trail S)"
  shows "(dpllW_mes (trail S') (card (atms_of_mm (clauses S'))),
          dpllW_mes (trail S) (card (atms_of_mm (clauses S)))) ∈ lex less_than"
proof -
  have "finite (atms_of_mm (clauses S))" unfolding atms_of_ms_def by auto
  then have 1: "length (trail S) ≤ card (atms_of_mm (clauses S))"
    using distinctcard_atm_of_lit_of_eq_length[OF no_dup] atm_incl card_mono by metis

  moreover {
    have no_dup': "no_dup (trail S')" using dpll dpllW_distinct_inv no_dup by blast
    have SS': "clauses S' = clauses S" using dpll by (auto dest!: dpllW_same_clauses)
    have atm_incl': "atm_of ` lits_of_l (trail S') ⊆ atms_of_mm (clauses S')"
      using atm_incl dpll dpllW_vars_in_snd_inv[OF dpll] by force
    have "finite (atms_of_mm (clauses S'))"
      unfolding atms_of_ms_def by auto
    then have 2: "length (trail S') ≤ card (atms_of_mm (clauses S))"
      using distinctcard_atm_of_lit_of_eq_length[OF no_dup'] atm_incl' card_mono SS' by metis }

  ultimately have "(dpllW_mes (trail S') (card (atms_of_mm (clauses S))),
      dpllW_mes (trail S) (card (atms_of_mm (clauses S))))
    ∈ lexn less_than (card (atms_of_mm (clauses S)))"
    using dpllW_card_decrease[OF assms(1), of "atms_of_mm (clauses S)"] by blast
  then have "(dpllW_mes (trail S') (card (atms_of_mm (clauses S))),
          dpllW_mes (trail S) (card (atms_of_mm (clauses S)))) ∈ lex less_than"
    unfolding lex_def by auto
  then show "(dpllW_mes (trail S') (card (atms_of_mm (clauses S'))),
         dpllW_mes (trail S) (card (atms_of_mm (clauses S)))) ∈ lex less_than"
    using dpllW_same_clauses[OF assms(1)]  by auto
qed

lemma wf_lexn: "wf (lexn {(a, b). (a::nat) < b} (card (atms_of_mm (clauses S))))"
proof -
  have m: "{(a, b). a < b} = measure id" by auto
  show ?thesis apply (rule wf_lexn) unfolding m by auto
qed

lemma wf_dpllW: 
  "wf {(S', S). dpllW_all_inv S ∧ dpllW S S'}"
  apply (rule wf_wf_if_measure'[OF wf_lex_less, of _ _
          "λS. dpllW_mes (trail S) (card (atms_of_mm (clauses S)))"])
  using dpllW_card_decrease' by fast


lemma dpllW_tranclp_star_commute:
  "{(S', S). dpllW_all_inv S ∧ dpllW S S'}+ = {(S', S). dpllW_all_inv S ∧ tranclp dpllW S S'}"
  (is "?A = ?B")
proof
  { fix S S'
    assume "(S, S') ∈ ?A"
    then have "(S, S') ∈ ?B"
      by (induct rule: trancl.induct, auto)
  }
  then show "?A ⊆ ?B" by blast
  { fix S S'
    assume "(S, S') ∈ ?B"
    then have "dpllW++ S' S" and "dpllW_all_inv S'" by auto
    then have "(S, S') ∈ ?A"
    proof (induct rule: tranclp.induct)
      case r_into_trancl
      then show ?case by (simp_all add: r_into_trancl')
    next
      case (trancl_into_trancl S S' S'')
      then have "(S', S) ∈ {a. case a of (S', S) ⇒ dpllW_all_inv S ∧ dpllW S S'}+" by blast
      moreover have "dpllW_all_inv S'"
        using rtranclp_dpllW_all_inv[OF tranclp_into_rtranclp[OF trancl_into_trancl.hyps(1)]]
          trancl_into_trancl.prems by auto
      ultimately have "(S'', S') ∈ {(pa, p). dpllW_all_inv p ∧ dpllW p pa}+"
        using ‹dpllW_all_inv S'› trancl_into_trancl.hyps(3) by blast
      then show ?case
        using ‹(S', S) ∈ {a. case a of (S', S) ⇒ dpllW_all_inv S ∧ dpllW S S'}+ by auto
    qed
  }
  then show "?B ⊆ ?A" by blast
qed

lemma wf_dpllW_tranclp: "wf {(S', S). dpllW_all_inv S ∧ dpllW++ S S'}"
  unfolding dpllW_tranclp_star_commute[symmetric] by (simp add: wf_dpllW wf_trancl)

lemma wf_dpllW_plus:
  "wf {(S', ([], N))| S'. dpllW++ ([], N) S'}" (is "wf ?P")
  apply (rule wf_subset[OF wf_dpllW_tranclp, of ?P])
  unfolding dpllW_all_inv_def by auto


subsection ‹Final States›

text ‹Proposition 2.8.1: final states are the normal forms of @{term dpllW}›
lemma dpllW_no_more_step_is_a_conclusive_state:
  assumes "∀S'. ¬dpllW S S'"
  shows "conclusive_dpllW_state S"
proof -
  have vars: "∀s ∈ atms_of_mm (clauses S). s ∈ atm_of ` lits_of_l (trail S)"
    proof (rule ccontr)
      assume "¬ (∀s∈atms_of_mm (clauses S). s ∈ atm_of ` lits_of_l (trail S))"
      then obtain L where
        L_in_atms: "L ∈ atms_of_mm (clauses S)" and
        L_notin_trail: "L ∉ atm_of ` lits_of_l (trail S)" by metis
      obtain L' where L': "atm_of L' = L" by (meson literal.sel(2))
      then have "undefined_lit (trail S) L'"
        unfolding Decided_Propagated_in_iff_in_lits_of_l by (metis L_notin_trail atm_of_uminus imageI)
      then show False using dpllW.decided assms(1) L_in_atms L' by blast
    qed
  show ?thesis
    proof (rule ccontr)
      assume not_final: "¬ ?thesis"
      then have
        "¬ trail S ⊨asm clauses S" and
        "(∃L∈set (trail S). is_decided L) ∨ (∀C∈#clauses S. ¬trail S ⊨as CNot C)"
        unfolding conclusive_dpllW_state_def by auto
      moreover {
        assume "∃L∈set (trail S). is_decided L"
        then obtain L M' M where L: "backtrack_split (trail S) = (M', L # M)"
          using backtrack_split_some_is_decided_then_snd_has_hd by blast
        obtain D where "D ∈# clauses S" and "¬ trail S ⊨a D"
          using ‹¬ trail S ⊨asm clauses S› unfolding true_annots_def by auto
        then have "∀s∈atms_of_ms {D}. s ∈ atm_of ` lits_of_l (trail S)"
          using vars unfolding atms_of_ms_def by auto
        then have "trail S ⊨as CNot D"
          using all_variables_defined_not_imply_cnot[of D] ‹¬ trail S ⊨a D› by auto
        moreover have "is_decided L"
          using L by (metis backtrack_split_snd_hd_decided list.distinct(1) list.sel(1) snd_conv)
        ultimately have False
          using assms(1) dpllW.backtrack L ‹D ∈# clauses S› ‹trail S ⊨as CNot D› by blast
      }
      moreover {
        assume tr: "∀C∈#clauses S. ¬trail S ⊨as CNot C"
        obtain C where C_in_cls: "C ∈# clauses S" and trC: "¬ trail S ⊨a C"
          using ‹¬ trail S ⊨asm clauses S› unfolding true_annots_def by auto
        have "∀s∈atms_of_ms {C}. s ∈ atm_of ` lits_of_l (trail S)"
          using vars ‹C ∈# clauses S› unfolding atms_of_ms_def by auto
        then have "trail S ⊨as CNot C"
          by (meson C_in_cls tr trC all_variables_defined_not_imply_cnot)
        then have False using tr C_in_cls by auto
      }
      ultimately show False by blast
    qed
qed

lemma dpllW_conclusive_state_correct: 
  assumes "dpllW** ([], N) (M, N)" and "conclusive_dpllW_state (M, N)"
  shows "M ⊨asm N ⟷ satisfiable (set_mset N)" (is "?A ⟷ ?B")
proof
  let ?M'= "lits_of_l M"
  assume ?A
  then have "?M' ⊨sm N" by (simp add: true_annots_true_cls)
  moreover have "consistent_interp ?M'"
    using rtranclp_dpllW_inv_starting_from_0[OF assms(1)] by auto
  ultimately show ?B by auto
next
  assume ?B
  show ?A
  proof (rule ccontr)
    assume n: "¬ ?A"
    have no_mark: "∀L∈set M. ¬ is_decided L" "∃C ∈# N. M ⊨as CNot C"
      using n assms(2) unfolding conclusive_dpllW_state_def by auto
    moreover obtain D where DN: "D ∈# N" and MD: "M ⊨as CNot D" using no_mark by auto
    ultimately have "unsatisfiable (set_mset N)"
      using only_propagated_vars_unsat rtranclp_dpllW_all_inv[OF assms(1)]
      unfolding dpllW_all_inv_def by force
    then show False using ‹?B› by blast
  qed
qed


lemma dpllW_trail_after_step1:
  assumes ‹dpllW S T›
  shows
    ‹∃K' M1 M2' M2''.
      (rev (trail T) = rev (trail S) @ M2' ∧ M2' ≠ []) ∨
      (rev (trail S) = M1 @ Decided (-K') # M2' ∧
        rev (trail T) = M1 @ Propagated K' () # M2'' ∧
       Suc (length M1) ≤ length (trail S))›
  using assms
  apply (induction S T rule: dpllW.induct)
  subgoal for L C T
    by auto
  subgoal
    by auto
  subgoal for S M' L M D
    using backtrack_split_snd_hd_decided[of ‹trail S›]
      backtrack_split_list_eq[of ‹trail S›, symmetric]
    apply - apply (rule exI[of _ ‹-lit_of L›], rule exI[of _ ‹rev M›], rule exI[of _ ‹rev M'›], rule exI[of _ ‹[]›])
    by (cases L)
      auto
  done

lemma tranclp_dpllW_trail_after_step:
  assumes ‹dpllW++ S T›
  shows
    ‹∃K' M1 M2' M2''.
      (rev (trail T) = rev (trail S) @ M2' ∧ M2' ≠ []) ∨
      (rev (trail S) = M1 @ Decided (-K') # M2' ∧
        rev (trail T) = M1 @ Propagated K' () # M2'' ∧ Suc (length M1) ≤ length (trail S))›
  using assms(1)
proof (induction rule: tranclp_induct)
  case (base y)
  then show ?case by (auto dest!: dpllW_trail_after_step1)
next
  case (step y z)
  then consider
    (1) M2' where
      ‹rev (DPLL_W.trail y) = rev (DPLL_W.trail S) @ M2'› ‹M2' ≠ []› |
    (2) K' M1 M2' M2'' where ‹rev (DPLL_W.trail S) = M1 @ Decided (- K') # M2'›
       ‹rev (DPLL_W.trail y) = M1 @ Propagated K' () # M2''› and ‹Suc (length M1) ≤ length (trail S)›
    by blast
  then show ?case
  proof cases
    case (1 M2')
    consider
      (a) M2' where
        ‹rev (DPLL_W.trail z) = rev (DPLL_W.trail y) @ M2'› ‹M2' ≠ []› |
      (b) K'' M1' M2'' M2''' where ‹rev (DPLL_W.trail y) = M1' @ Decided (- K'') # M2''›
         ‹rev (DPLL_W.trail z) = M1' @ Propagated K'' () # M2'''› and
        ‹Suc (length M1') ≤ length (trail y)›
      using dpllW_trail_after_step1[OF step(2)]
      by blast
    then show ?thesis
    proof cases
      case a
      then show ?thesis using 1 by auto
    next
      case b
      have H: ‹rev (DPLL_W.trail S) @ M2' = M1' @ Decided (- K'') # M2'' ⟹
           length M1' ≠ length (DPLL_W.trail S) ⟹
           length M1' < Suc (length (DPLL_W.trail S)) ⟹ rev (DPLL_W.trail S) =
           M1' @ Decided (- K'') # drop (Suc (length M1')) (rev (DPLL_W.trail S))›
        apply (drule arg_cong[of _ _ ‹take (length (trail S))›])
        by (auto simp: take_Cons')
      show ?thesis using b 1 apply -
        apply (rule exI[of _ ‹K''›])
        apply (rule exI[of _ ‹M1'›])
        apply (rule exI[of _ ‹if length (trail S) ≤ length M1' then drop (length (DPLL_W.trail S)) (rev (DPLL_W.trail z)) else
              drop (Suc (length M1')) (rev (DPLL_W.trail S))›])
        apply (cases ‹length (trail S) < length M1'›)
        subgoal
          apply auto
          by (simp add: append_eq_append_conv_if)
        apply (cases ‹length M1' = length (trail S)›)
        subgoal by auto
        subgoal
          using H
          apply (clarsimp simp: )
          done
        done
      qed
    next
      case (2 K'' M1' M2'' M2''')
      consider
        (a) M2' where
          ‹rev (DPLL_W.trail z) = rev (DPLL_W.trail y) @ M2'› ‹M2' ≠ []› |
        (b) K'' M1' M2'' M2''' where ‹rev (DPLL_W.trail y) = M1' @ Decided (- K'') # M2''›
           ‹rev (DPLL_W.trail z) = M1' @ Propagated K'' () # M2'''› and
          ‹Suc (length M1') ≤ length (trail y)›
        using dpllW_trail_after_step1[OF step(2)]
        by blast
      then show ?thesis
      proof cases
        case a
        then show ?thesis using 2 by auto
      next
        case (b K''' M1'' M2'''' M2''''')
        have [iff]: ‹M1' @ Propagated K'' () # M2''' = M1'' @ Decided (- K''') # M2'''' ⟷
          (∃N1''. M1'' = M1' @ Propagated K'' () # N1'' ∧ M2''' = N1'' @ Decided (- K''') # M2'''')› if ‹length M1' < length M1''›
          using that apply (auto simp: append_eq_append_conv_if)
          by (metis (no_types, lifting) Cons_eq_append_conv append_take_drop_id drop_eq_Nil leD)
        have [iff]: ‹M1' @ Propagated K'' () # M2''' = M1'' @ Decided (- K''') # M2'''' ⟷
          (∃N1''. M1' = M1'' @ Decided (- K''') # N1'' ∧ M2'''' = N1'' @ Propagated K'' () # M2''')› if ‹¬length M1' < length M1''›
          using that apply (auto simp: append_eq_append_conv_if)
          by (metis (no_types, lifting) Cons_eq_append_conv append_take_drop_id drop_eq_Nil le_eq_less_or_eq)

        show ?thesis using b 2 apply -
          apply (rule exI[of _ ‹if length M1' < length M1'' then K'' else K'''›])
          apply (rule exI[of _ ‹if length M1' < length M1'' then M1' else M1''›])
          apply (cases ‹length (trail S) < min (length M1') (length M1'')›)
          subgoal
            by auto
          apply (cases ‹min (length M1') (length M1'') = length (trail S)›)
          subgoal by auto
          subgoal
            by (auto simp: )
          done
       qed
   qed
qed

text ‹
  This theorem is an important (although rather obvious) property: the model
  induced by trails are not repeated.
›
lemma tranclp_dpllW_no_dup_trail:
  assumes ‹dpllW++ S T› and ‹dpllW_all_inv S›
  shows ‹set (trail S) ≠ set (trail T)›
proof -
  have [simp]: ‹A = B ∪ A ⟷ B ⊆ A› for A B
    by auto
  have [simp]: ‹rev (trail U) = xs ⟷trail U = rev xs› for xs U
    by auto
  have ‹dpllW_all_inv T›
    by (metis assms(1) assms(2) reflclp_tranclp rtranclp_dpllW_all_inv sup2CI)
  then have n_d: ‹no_dup (trail S)› ‹no_dup (trail T)›
    using assms unfolding dpllW_all_inv_def by (auto dest: no_dup_imp_distinct)
  have [simp]: ‹no_dup (rev M2' @ DPLL_W.trail S) ⟹
          dpllW_all_inv S ⟹
          set M2' ⊆ set (DPLL_W.trail S) ⟷ M2' = []› for M2'
    by (cases M2' rule: rev_cases)
      (auto simp: undefined_notin)
  show ?thesis
    using n_d tranclp_dpllW_trail_after_step[OF assms(1)] assms(2) apply auto
    by (metis (no_types, lifting) Un_insert_right insertI1 list.simps(15) lit_of.simps(1,2)
      n_d(1) no_dup_cannot_not_lit_and_uminus set_append set_rev)
qed


end