Theory IsaSAT_Literals

theory IsaSAT_Literals
imports Watched_Literals_Watch_List Bits_Natural
theory IsaSAT_Literals
  imports Watched_Literals.WB_More_Refinement "HOL-Word.More_Word"
     Watched_Literals.Watched_Literals_Watch_List
     Entailment_Definition.Partial_Herbrand_Interpretation
     Isabelle_LLVM.Bits_Natural (*Watched_Literals.WB_Word*)
begin


chapter ‹Refinement of Literals›


section ‹Literals as Natural Numbers›

subsection ‹Definition›

lemma Pos_div2_iff:
  ‹Pos ((bb :: nat) div 2) = b ⟷ is_pos b ∧ (bb = 2 * atm_of b ∨ bb = 2 * atm_of b + 1)›
  by (cases b) auto
lemma Neg_div2_iff:
  ‹Neg ((bb :: nat) div 2) = b ⟷ is_neg b ∧ (bb = 2 * atm_of b ∨ bb = 2 * atm_of b + 1)›
  by (cases b) auto

text ‹
  Modeling \<^typ>‹nat literal› via the transformation associating \<^term>‹2*n› or \<^term>‹2*n+1›
  has some advantages over the transformation to positive or negative integers: 0 is not an issue.
  It is also a bit faster according to Armin Biere.
›
fun nat_of_lit :: ‹nat literal ⇒ nat› where
  ‹nat_of_lit (Pos L) = 2*L›
| ‹nat_of_lit (Neg L) = 2*L + 1›

lemma nat_of_lit_def: ‹nat_of_lit L = (if is_pos L then 2 * atm_of L else 2 * atm_of L + 1)›
  by (cases L) auto

fun literal_of_nat :: ‹nat ⇒ nat literal› where
  ‹literal_of_nat n = (if even n then Pos (n div 2) else Neg (n div 2))›

lemma lit_of_nat_nat_of_lit[simp]: ‹literal_of_nat (nat_of_lit L) = L›
  by (cases L) auto

lemma nat_of_lit_lit_of_nat[simp]: ‹nat_of_lit (literal_of_nat n) = n›
  by auto

lemma atm_of_lit_of_nat: ‹atm_of (literal_of_nat n) = n div 2›
  by auto

text ‹There is probably a more ``closed'' form from the following theorem, but it is unclear if
  that is useful or not.›
lemma uminus_lit_of_nat:
  ‹- (literal_of_nat n) = (if even n then literal_of_nat (n+1) else literal_of_nat (n-1))›
  by (auto elim!: oddE)

lemma literal_of_nat_literal_of_nat_eq[iff]: ‹literal_of_nat x = literal_of_nat xa ⟷ x = xa›
  by auto presburger+

definition nat_lit_rel :: ‹(nat × nat literal) set› where
  ‹nat_lit_rel =  br literal_of_nat (λ_. True)›

lemma ex_literal_of_nat: ‹∃bb. b = literal_of_nat bb›
  by (cases b)
    (auto simp: nat_of_lit_def split: if_splits; presburger; fail)+


subsection ‹Lifting to annotated literals›

fun pair_of_ann_lit :: ‹('a, 'b) ann_lit ⇒ 'a literal × 'b option› where
  ‹pair_of_ann_lit (Propagated L D) = (L, Some D)›
| ‹pair_of_ann_lit (Decided L) = (L, None)›

fun ann_lit_of_pair :: ‹'a literal × 'b option ⇒ ('a, 'b) ann_lit› where
  ‹ann_lit_of_pair (L, Some D) = Propagated L D›
| ‹ann_lit_of_pair (L, None) = Decided L›

lemma ann_lit_of_pair_alt_def:
  ‹ann_lit_of_pair (L, D) = (if D = None then Decided L else Propagated L (the D))›
  by (cases D) auto

lemma ann_lit_of_pair_pair_of_ann_lit: ‹ann_lit_of_pair (pair_of_ann_lit L) = L›
  by (cases L) auto

lemma pair_of_ann_lit_ann_lit_of_pair: ‹pair_of_ann_lit (ann_lit_of_pair L) = L›
  by (cases L; cases ‹snd L›) auto

lemma literal_of_neq_eq_nat_of_lit_eq_iff: ‹literal_of_nat b = L ⟷ b = nat_of_lit L›
  by (auto simp del: literal_of_nat.simps)

lemma nat_of_lit_eq_iff[iff]: ‹nat_of_lit xa = nat_of_lit x ⟷ x = xa›
  apply (cases x; cases xa) by auto presburger+

definition ann_lit_rel:: ‹('a × nat) set ⇒ ('b × nat option) set ⇒
    (('a × 'b) × (nat, nat) ann_lit) set› where
  ann_lit_rel_internal_def:
  ‹ann_lit_rel R R' = {(a, b). ∃c d. (fst a, c) ∈ R ∧ (snd a, d) ∈ R' ∧
      b = ann_lit_of_pair (literal_of_nat c, d)}›


section ‹Conflict Clause›

definition the_is_empty where
  ‹the_is_empty D = Multiset.is_empty (the D)›


section ‹Atoms with bound›

definition uint32_max :: nat where
  ‹uint32_max ≡ 2^32-1›

definition uint64_max :: nat where
  ‹uint64_max ≡ 2^64-1›

definition sint32_max :: nat where
  ‹sint32_max ≡ 2^31-1›

definition sint64_max :: nat where
  ‹sint64_max ≡ 2^63-1›


lemma uint64_max_uint_def: ‹unat (-1 :: 64 Word.word) = uint64_max›
proof -
  have ‹unat (-1 :: 64 Word.word) = unat (- Numeral1 :: 64 Word.word)›
    unfolding numeral.numeral_One ..
  also have ‹… = uint64_max›
    unfolding unat_bintrunc_neg
    apply (simp add: uint64_max_def)
    apply (subst numeral_eq_Suc; subst bintrunc.Suc; simp)+
    done
  finally show ?thesis .
qed


section ‹Operations with set of atoms.›

context
  fixes 𝒜in :: ‹nat multiset›
begin

abbreviation D0 :: ‹(nat × nat literal) set› where
  ‹D0 ≡ (λL. (nat_of_lit L, L)) ` set_mset (ℒall 𝒜in)›

definition length_ll_f where
  ‹length_ll_f W L = length (W L)›

text ‹
  The following lemma was necessary at some point to prove the existence of some
  list.
›
lemma ex_list_watched:
  fixes W :: ‹nat literal ⇒ 'a list›
  shows ‹∃aa. ∀x∈#ℒall 𝒜in. nat_of_lit x < length aa ∧ aa ! nat_of_lit x = W x›
  (is ‹∃aa. ?P aa›)
proof -
  define D' where ‹D' = D0
  define all' where ‹ℒall' = ℒall
  define D'' where ‹D'' = mset_set (snd ` D')›
  let ?f = ‹(λL a. a[nat_of_lit L:= W L])›
  interpret comp_fun_commute ?f
    apply standard
    apply (case_tac ‹y = x›)
     apply (solves simp)
    apply (intro ext)
    apply (subst (asm) lit_of_nat_nat_of_lit[symmetric])
    apply (subst (asm)(3) lit_of_nat_nat_of_lit[symmetric])
    apply (clarsimp simp only: comp_def intro!: list_update_swap)
    done
  define aa where
    ‹aa ≡ fold_mset ?f (replicate (1+Max (nat_of_lit ` snd ` D')) []) (mset_set (snd ` D'))›
  have length_fold: ‹length (fold_mset (λL a. a[nat_of_lit L := W L]) l M) = length l› for l M
    by (induction M) auto
  have length_aa: ‹length aa = Suc (Max (nat_of_lit ` snd ` D'))›
    unfolding aa_def D''_def[symmetric] by (simp add: length_fold)
  have H: ‹x ∈# ℒall' ⟹
      length l ≥ Suc (Max (nat_of_lit ` set_mset (ℒall'))) ⟹
      fold_mset (λL a. a[nat_of_lit L := W L]) l (remdups_mset (ℒall')) ! nat_of_lit x = W x›
    for x l all'
    unfolding all'_def[symmetric]
    apply (induction all' arbitrary: l)
    subgoal by simp
    subgoal for xa Ls l
      apply (case_tac ‹(nat_of_lit ` set_mset Ls) = {}›)
       apply (solves simp)
      apply (auto simp: less_Suc_eq_le length_fold)
      done
    done
  have H': ‹aa ! nat_of_lit x = W x› if ‹x ∈# ℒall 𝒜in for x
    using that unfolding aa_def D'_def
    by (auto simp: D'_def image_image remdups_mset_def[symmetric]
        less_Suc_eq_le intro!: H)
  have ‹?P aa›
    by (auto simp: D'_def image_image remdups_mset_def[symmetric]
        less_Suc_eq_le length_aa H')
  then show ?thesis
    by blast
qed

definition isasat_input_bounded where
  [simp]: ‹isasat_input_bounded = (∀L ∈# ℒall 𝒜in. nat_of_lit L ≤ uint32_max)›

definition isasat_input_nempty where
  [simp]: ‹isasat_input_nempty = (set_mset 𝒜in ≠ {})›

definition isasat_input_bounded_nempty where
  ‹isasat_input_bounded_nempty = (isasat_input_bounded ∧ isasat_input_nempty)›


section ‹Set of atoms with bound›

context
  assumes in_ℒall_less_uint32_max: ‹isasat_input_bounded›
begin

lemma in_ℒall_less_uint32_max': ‹L ∈# ℒall 𝒜in ⟹ nat_of_lit L ≤ uint32_max›
  using in_ℒall_less_uint32_max by auto

lemma in_𝒜in_less_than_uint32_max_div_2:
  ‹L ∈# 𝒜in ⟹ L ≤ uint32_max div 2›
  using in_ℒall_less_uint32_max'[of ‹Neg L›]
  unfolding Ball_def atms_of_ℒall_𝒜in in_ℒall_atm_of_in_atms_of_iff
  by (auto simp: uint32_max_def)

lemma simple_clss_size_upper_div2':
  assumes
    lits: ‹literals_are_in_ℒin  𝒜in C› and
    dist: ‹distinct_mset C› and
    tauto: ‹¬tautology C› and
    in_ℒall_less_uint32_max: ‹∀L ∈# ℒall 𝒜in. nat_of_lit L < uint32_max - 1›
  shows ‹size C ≤ uint32_max div 2›
proof -
  let ?C = ‹atm_of `# C›
  have ‹distinct_mset ?C›
  proof (rule ccontr)
    assume ‹¬ ?thesis›
    then obtain K where ‹¬count (atm_of `# C) K ≤ Suc 0›
      unfolding distinct_mset_count_less_1
      by auto
    then have ‹count (atm_of `# C) K ≥ 2›
      by auto
    then obtain L L' C' where
      C: ‹C = {#L, L'#} + C'› and L_L': ‹atm_of L = atm_of L'›
      by (auto dest!: count_image_mset_multi_member_split_2)
    then show False
      using dist tauto by (auto simp: atm_of_eq_atm_of tautology_add_mset)
  qed
  then have card: ‹size ?C = card (set_mset ?C)›
    using distinct_mset_size_eq_card by blast
  have size: ‹size ?C = size C›
    using dist tauto
    by (induction C) (auto simp: tautology_add_mset)
  have m: ‹set_mset ?C ⊆ {0..<uint32_max div 2}›
  proof
    fix L
    assume ‹L ∈ set_mset ?C›
    then have ‹L ∈ atms_of (ℒall 𝒜in)›
    using lits by (auto simp: literals_are_in_ℒin_def atm_of_lit_in_atms_of
        in_all_lits_of_m_ain_atms_of_iff subset_iff)
    then have ‹Pos L ∈# (ℒall 𝒜in)›
      using lits by (auto simp: in_ℒall_atm_of_in_atms_of_iff)
    then have ‹nat_of_lit (Pos L) < uint32_max - 1›
      using in_ℒall_less_uint32_max by (auto simp: atm_of_lit_in_atms_of
        in_all_lits_of_m_ain_atms_of_iff subset_iff)
    then have ‹L < uint32_max div 2›
       by (auto simp: atm_of_lit_in_atms_of
        in_all_lits_of_m_ain_atms_of_iff subset_iff uint32_max_def)
    then show ‹L ∈ {0..<uint32_max div 2}›
       by (auto simp: atm_of_lit_in_atms_of uint32_max_def
        in_all_lits_of_m_ain_atms_of_iff subset_iff)
  qed
  moreover have ‹card … =  uint32_max div 2›
    by auto
  ultimately have ‹card (set_mset ?C) ≤ uint32_max div 2›
    using card_mono[OF _ m] by auto
  then show ?thesis
    unfolding card[symmetric] size .
qed


lemma simple_clss_size_upper_div2:
  assumes
   lits: ‹literals_are_in_ℒin  𝒜in C› and
   dist: ‹distinct_mset C› and
   tauto: ‹¬tautology C›
  shows ‹size C ≤ 1 + uint32_max div 2›
proof -
  let ?C = ‹atm_of `# C›
  have ‹distinct_mset ?C›
  proof (rule ccontr)
    assume ‹¬ ?thesis›
    then obtain K where ‹¬count (atm_of `# C) K ≤ Suc 0›
      unfolding distinct_mset_count_less_1
      by auto
    then have ‹count (atm_of `# C) K ≥ 2›
      by auto
    then obtain L L' C' where
      C: ‹C = {#L, L'#} + C'› and L_L': ‹atm_of L = atm_of L'›
      by (auto dest!: count_image_mset_multi_member_split_2)
    then show False
      using dist tauto by (auto simp: atm_of_eq_atm_of tautology_add_mset)
  qed
  then have card: ‹size ?C = card (set_mset ?C)›
    using distinct_mset_size_eq_card by blast
  have size: ‹size ?C = size C›
    using dist tauto
    by (induction C) (auto simp: tautology_add_mset)
  have m: ‹set_mset ?C ⊆ {0..uint32_max div 2}›
  proof
    fix L
    assume ‹L ∈ set_mset ?C›
    then have ‹L ∈ atms_of (ℒall  𝒜in)›
    using lits by (auto simp: literals_are_in_ℒin_def atm_of_lit_in_atms_of
        in_all_lits_of_m_ain_atms_of_iff subset_iff)
    then have ‹Neg L ∈# (ℒall  𝒜in)›
      using lits by (auto simp: in_ℒall_atm_of_in_atms_of_iff)
    then have ‹nat_of_lit (Neg L) ≤ uint32_max›
      using in_ℒall_less_uint32_max by (auto simp: atm_of_lit_in_atms_of
        in_all_lits_of_m_ain_atms_of_iff subset_iff)
    then have ‹L ≤ uint32_max div 2›
       by (auto simp: atm_of_lit_in_atms_of
        in_all_lits_of_m_ain_atms_of_iff subset_iff uint32_max_def)
    then show ‹L ∈ {0 .. uint32_max div 2}›
       by (auto simp: atm_of_lit_in_atms_of uint32_max_def
        in_all_lits_of_m_ain_atms_of_iff subset_iff)
  qed
  moreover have ‹card … =  1 + uint32_max div 2›
    by auto
  ultimately have ‹card (set_mset ?C) ≤ 1 + uint32_max div 2›
    using card_mono[OF _ m] by auto
  then show ?thesis
    unfolding card[symmetric] size .
qed

lemma clss_size_uint32_max:
  assumes
   lits: ‹literals_are_in_ℒin 𝒜in C› and
   dist: ‹distinct_mset C›
  shows ‹size C ≤ uint32_max + 2›
proof -
  let ?posC = ‹filter_mset is_pos C›
  let ?negC = ‹filter_mset is_neg C›
  have C: ‹C = ?posC + ?negC›
    apply (subst multiset_partition[of _ is_pos])
    by auto
  have ‹literals_are_in_ℒin 𝒜in ?posC›
    by (rule literals_are_in_ℒin_mono[OF lits]) auto
  moreover have ‹distinct_mset ?posC›
    by (rule distinct_mset_mono[OF _dist]) auto
  ultimately have pos: ‹size ?posC ≤ 1 + uint32_max div 2›
    by (rule simple_clss_size_upper_div2) (auto simp: tautology_decomp)

  have ‹literals_are_in_ℒin 𝒜in ?negC›
    by (rule literals_are_in_ℒin_mono[OF lits]) auto
  moreover have ‹distinct_mset ?negC›
    by (rule distinct_mset_mono[OF _dist]) auto
  ultimately have neg: ‹size ?negC ≤ 1 + uint32_max div 2›
    by (rule simple_clss_size_upper_div2) (auto simp: tautology_decomp)

  show ?thesis
    apply (subst C)
    apply (subst size_union)
    using pos neg by linarith
qed

lemma clss_size_upper:
  assumes
   lits: ‹literals_are_in_ℒin 𝒜in C› and
   dist: ‹distinct_mset C› and
   in_ℒall_less_uint32_max: ‹∀L ∈# ℒall 𝒜in. nat_of_lit L < uint32_max - 1›
 shows ‹size C ≤ uint32_max›
proof -
  let ?A = ‹remdups_mset (atm_of `# C)›
  have [simp]: ‹distinct_mset (poss ?A)› ‹distinct_mset (negs ?A)›
    by (simp_all add: distinct_image_mset_inj inj_on_def)

  have ‹C ⊆# poss ?A + negs ?A›
    apply (rule distinct_subseteq_iff[THEN iffD1])
    subgoal by (auto simp: dist distinct_mset_add disjunct_not_in)
    subgoal by (auto simp: dist distinct_mset_add disjunct_not_in)
    subgoal
      apply rule
      using literal.exhaust_sel by (auto simp: image_iff)
    done
  have [simp]: ‹literals_are_in_ℒin 𝒜in (poss ?A)› ‹literals_are_in_ℒin 𝒜in (negs ?A)›
    using lits
    by (auto simp: literals_are_in_ℒin_negs_remdups_mset literals_are_in_ℒin_poss_remdups_mset)

  have ‹¬ tautology (poss ?A)› ‹¬ tautology (negs ?A)›
    by (auto simp: tautology_decomp)
  then have ‹size (poss ?A) ≤ uint32_max div 2› and ‹size (negs ?A) ≤ uint32_max div 2›
    using simple_clss_size_upper_div2'[of ‹poss ?A›]
      simple_clss_size_upper_div2'[of ‹negs ?A›] in_ℒall_less_uint32_max
    by auto
  then have ‹size C ≤ uint32_max div 2 + uint32_max div 2›
    using ‹C ⊆# poss (remdups_mset (atm_of `# C)) + negs (remdups_mset (atm_of `# C))›
      size_mset_mono by fastforce
  then show ?thesis by (auto simp: uint32_max_def)
qed

lemma
  assumes
    lits: ‹literals_are_in_ℒin_trail 𝒜in M› and
    n_d: ‹no_dup M›
  shows
    literals_are_in_ℒin_trail_length_le_uint32_max:
      ‹length M ≤ Suc (uint32_max div 2)› and
    literals_are_in_ℒin_trail_count_decided_uint32_max:
      ‹count_decided M ≤ Suc (uint32_max div 2)› and
    literals_are_in_ℒin_trail_get_level_uint32_max:
      ‹get_level M L ≤ Suc (uint32_max div 2)›
proof -
  have ‹length M = card (atm_of ` lits_of_l M)›
    using no_dup_length_eq_card_atm_of_lits_of_l[OF n_d] .
  moreover have ‹atm_of ` lits_of_l M ⊆ set_mset 𝒜in
    using lits unfolding literals_are_in_ℒin_trail_atm_of by auto
  ultimately have ‹length M ≤ card (set_mset 𝒜in)›
    by (simp add: card_mono)
  moreover {
    have ‹set_mset 𝒜in ⊆ {0 ..< (uint32_max div 2) + 1}›
      using in_𝒜in_less_than_uint32_max_div_2 by (fastforce simp: in_ℒall_atm_of_in_atms_of_iff
          Ball_def atms_of_ℒall_𝒜in uint32_max_def)
    from subset_eq_atLeast0_lessThan_card[OF this] have ‹card (set_mset 𝒜in) ≤ uint32_max div 2 + 1›
      .
  }
  ultimately show ‹length M ≤ Suc (uint32_max div 2)›
    by linarith
  moreover have ‹count_decided M ≤ length M›
    unfolding count_decided_def by auto
  ultimately show ‹count_decided M ≤ Suc (uint32_max div 2)› by simp
  then show ‹get_level M L ≤ Suc (uint32_max div 2)›
    using count_decided_ge_get_level[of M L]
    by simp
qed

lemma length_trail_uint32_max_div2:
  fixes M :: ‹(nat, 'b) ann_lits›
  assumes
    M_ℒall: ‹∀L∈set M. lit_of L ∈# ℒall 𝒜in and
    n_d: ‹no_dup M›
  shows ‹length M ≤ uint32_max div 2 + 1›
proof -
  have dist_atm_M: ‹distinct_mset {#atm_of (lit_of x). x ∈# mset M#}›
    using n_d by (metis distinct_mset_mset_distinct mset_map no_dup_def)
  have incl: ‹atm_of `# lit_of `# mset M ⊆# remdups_mset (atm_of `# ℒall 𝒜in)›
    apply (subst distinct_subseteq_iff[THEN iffD1])
    using assms dist_atm_M
    by (auto 5 5 simp: Decided_Propagated_in_iff_in_lits_of_l lits_of_def no_dup_distinct
        atm_of_eq_atm_of)
  have inj_on: ‹inj_on nat_of_lit (set_mset (remdups_mset (ℒall 𝒜in)))›
    by (auto simp: inj_on_def)
  have H: ‹xa ∈# ℒall 𝒜in ⟹ atm_of xa ≤ uint32_max div 2› for xa
    using in_ℒall_less_uint32_max
    by (cases xa) (auto simp: uint32_max_def)
  have ‹remdups_mset (atm_of `# ℒall 𝒜in) ⊆# mset [0..< 1 + (uint32_max div 2)]›
    apply (subst distinct_subseteq_iff[THEN iffD1])
    using H distinct_image_mset_inj[OF inj_on]
    by (force simp del: literal_of_nat.simps simp: distinct_mset_mset_set
        dest: le_neq_implies_less)+
  note _ = size_mset_mono[OF this]
  moreover have ‹size (nat_of_lit `# remdups_mset (ℒall 𝒜in)) = size (remdups_mset (ℒall 𝒜in))›
    by simp
  ultimately have 2: ‹size (remdups_mset (atm_of `# (ℒall 𝒜in))) ≤ 1 + uint32_max div 2›
    by auto
  from size_mset_mono[OF incl] have 1: ‹length M ≤ size (remdups_mset (atm_of `# (ℒall 𝒜in)))›
    unfolding uint32_max_def count_decided_def
    by (auto simp del: length_filter_le)
  with 2 show ?thesis
    by (auto simp: uint32_max_def)
qed

end

end


section ‹Instantion for code generation›
instantiation literal :: (default) default
begin

definition default_literal where
‹default_literal = Pos default›
instance by standard

end

instantiation fmap :: (type, type) default
begin

definition default_fmap where
‹default_fmap = fmempty›
instance by standard

end


subsection ‹Literals as Natural Numbers›

definition propagated where
  ‹propagated L C = (L, Some C)›

definition decided where
  ‹decided L = (L, None)›

definition uminus_lit_imp :: ‹nat ⇒ nat› where
  ‹uminus_lit_imp L = bitXOR L 1›

lemma uminus_lit_imp_uminus:
  ‹(RETURN o uminus_lit_imp, RETURN o uminus) ∈
     nat_lit_rel →f ⟨nat_lit_rel⟩nres_rel›
  unfolding bitXOR_1_if_mod_2 uminus_lit_imp_def
  by (intro frefI nres_relI) (auto simp: uminus_lit_imp_def case_prod_beta p2rel_def
      br_def nat_lit_rel_def split: option.splits, presburger)


subsection ‹State Conversion›

subsubsection ‹Functions and Types:›


subsubsection ‹More Operations›

subsection ‹Code Generation›

subsubsection ‹More Operations›

definition literals_to_update_wl_empty :: ‹nat twl_st_wl ⇒ bool›  where
  ‹literals_to_update_wl_empty = (λ(M, N, D, NE, UE, Q, W). Q = {#})›

lemma in_nat_list_rel_list_all2_in_set_iff:
    ‹(a, aa) ∈ nat_lit_rel ⟹
       list_all2 (λx x'. (x, x') ∈ nat_lit_rel) b ba ⟹
       a ∈ set b ⟷ aa ∈ set ba›
  apply (subgoal_tac ‹length b = length ba›)
  subgoal
    apply (rotate_tac 2)
    apply (induction b ba rule: list_induct2)
     apply (solves simp)
    apply (auto simp: p2rel_def nat_lit_rel_def br_def, presburger)[]
    done
  subgoal using list_all2_lengthD by auto
  done

definition is_decided_wl where
  ‹is_decided_wl L ⟷ snd L = None›

lemma ann_lit_of_pair_if:
  ‹ann_lit_of_pair (L, D) = (if D = None then Decided L else Propagated L (the D))›
  by (cases D) auto

definition get_maximum_level_remove where
  ‹get_maximum_level_remove M D L =  get_maximum_level M (remove1_mset L D)›

lemma in_list_all2_ex_in: ‹a ∈ set xs ⟹ list_all2 R xs ys ⟹ ∃b ∈ set ys. R a b›
  apply (subgoal_tac ‹length xs = length ys›)
   apply (rotate_tac 2)
   apply (induction xs ys rule: list_induct2)
    apply ((solves auto)+)[2]
  using list_all2_lengthD by blast

(*
lemma
  nat_lit_assn_right_unique:
    ‹CONSTRAINT (IS_PURE IS_RIGHT_UNIQUE) nat_lit_assn› and
  nat_lit_assn_left_unique:
    ‹CONSTRAINT (IS_PURE IS_LEFT_UNIQUE) nat_lit_assn›
   by (auto simp: IS_PURE_def single_valued_def p2rel_def IS_LEFT_UNIQUE_def nat_lit_rel_def
      br_def, presburger) *)

definition find_decomp_wl_imp :: ‹(nat, nat) ann_lits ⇒ nat clause ⇒ nat literal ⇒ (nat, nat) ann_lits nres› where
  ‹find_decomp_wl_imp = (λM0 D L. do {
    let lev = get_maximum_level M0 (remove1_mset (-L) D);
    let k = count_decided M0;
    (_, M) ←
       WHILETλ(j, M). j = count_decided M ∧ j ≥ lev ∧
           (M = [] ⟶ j = lev) ∧
           (∃M'. M0 = M' @ M ∧ (j = lev ⟶ M' ≠ [] ∧ is_decided (last M')))
         (λ(j, M). j > lev)
         (λ(j, M). do {
            ASSERT(M ≠ []);
            if is_decided (hd M)
            then RETURN (j-1, tl M)
            else RETURN (j, tl M)}
         )
         (k, M0);
    RETURN M
  })›

lemma ex_decomp_get_ann_decomposition_iff:
  ‹(∃M2. (Decided K # M1, M2) ∈ set (get_all_ann_decomposition M)) ⟷
    (∃M2. M = M2 @ Decided K # M1)›
  using get_all_ann_decomposition_ex by fastforce

lemma count_decided_tl_if:
  ‹M ≠ [] ⟹ count_decided (tl M) = (if is_decided (hd M) then count_decided M - 1 else count_decided M)›
  by (cases M) auto

lemma count_decided_butlast:
  ‹count_decided (butlast xs) = (if is_decided (last xs) then count_decided xs - 1 else count_decided xs)›
  by (cases xs rule: rev_cases) (auto simp: count_decided_def)

definition find_decomp_wl' where
  ‹find_decomp_wl' =
     (λ(M::(nat, nat) ann_lits) (D::nat clause) (L::nat literal).
        SPEC(λM1. ∃K M2. (Decided K # M1, M2) ∈ set (get_all_ann_decomposition M) ∧
          get_level M K = get_maximum_level M (D - {#-L#}) + 1))›

definition get_conflict_wl_is_None :: ‹nat twl_st_wl ⇒ bool› where
  ‹get_conflict_wl_is_None = (λ(M, N, D, NE, UE, Q, W). is_None D)›

lemma get_conflict_wl_is_None: ‹get_conflict_wl S = None ⟷ get_conflict_wl_is_None S›
  by (cases S) (auto simp: get_conflict_wl_is_None_def split: option.splits)

lemma watched_by_nth_watched_app':
  ‹watched_by S K = ((snd o snd o snd o snd o snd o snd o snd o snd) S) K›
  by (cases S) (auto)

lemma hd_decided_count_decided_ge_1:
  ‹x ≠ [] ⟹ is_decided (hd x) ⟹ Suc 0 ≤ count_decided x›
  by (cases x) auto

definition (in -) find_decomp_wl_imp' :: ‹(nat, nat) ann_lits ⇒ nat clause_l list ⇒ nat ⇒
    nat clause ⇒ nat clauses ⇒ nat clauses ⇒ nat lit_queue_wl ⇒
    (nat literal ⇒ nat watched) ⇒ _ ⇒ (nat, nat) ann_lits nres› where
  ‹find_decomp_wl_imp' = (λM N U D NE UE W Q L. find_decomp_wl_imp M D L)›

definition is_decided_hd_trail_wl where
  ‹is_decided_hd_trail_wl S = is_decided (hd (get_trail_wl S))›

definition is_decided_hd_trail_wll :: ‹nat twl_st_wl ⇒ bool nres› where
  ‹is_decided_hd_trail_wll = (λ(M, N, D, NE, UE, Q, W).
     RETURN (is_decided (hd M))
   )›

lemma Propagated_eq_ann_lit_of_pair_iff:
  ‹Propagated x21 x22 = ann_lit_of_pair (a, b) ⟷ x21 = a ∧ b = Some x22›
  by (cases b) auto

lemma set_mset_all_lits_of_mm_atms_of_ms_iff:
  ‹set_mset (all_lits_of_mm A) = set_mset (ℒall 𝒜) ⟷ atms_of_ms (set_mset A) = atms_of (ℒall 𝒜)›
  by (force simp add:  atms_of_s_def in_all_lits_of_mm_ain_atms_of_iff atms_of_ms_def
      atms_of_ℒall_𝒜in atms_of_def atm_of_eq_atm_of uminus_𝒜in_iff
       eq_commute[of ‹set_mset (all_lits_of_mm _)› ‹set_mset (ℒall _)›]
      dest: multi_member_split)

definition card_max_lvl where
  ‹card_max_lvl M C ≡ size (filter_mset (λL. get_level M L = count_decided M) C)›

lemma card_max_lvl_add_mset: ‹card_max_lvl M (add_mset L C) =
  (if get_level M L = count_decided M then 1 else 0) +
    card_max_lvl M C›
  by (auto simp: card_max_lvl_def)

lemma card_max_lvl_empty[simp]: ‹card_max_lvl M {#} = 0›
  by (auto simp: card_max_lvl_def)

lemma card_max_lvl_all_poss:
   ‹card_max_lvl M C = card_max_lvl M (poss (atm_of `# C))›
  unfolding card_max_lvl_def
  apply (induction C)
  subgoal by auto
  subgoal for L C
    using get_level_uminus[of M L]
    by (cases L) (auto)
  done

lemma card_max_lvl_distinct_cong:
  assumes
    ‹⋀L. get_level M (Pos L) = count_decided M ⟹ (L ∈ atms_of C) ⟹ (L ∈ atms_of C')› and
    ‹⋀L. get_level M (Pos L) = count_decided M ⟹ (L ∈ atms_of C') ⟹ (L ∈ atms_of C)› and
    ‹distinct_mset C› ‹¬tautology C› and
    ‹distinct_mset C'› ‹¬tautology C'›
  shows ‹card_max_lvl M C = card_max_lvl M C'›
proof -
  have [simp]: ‹NO_MATCH (Pos x) L ⟹ get_level M L = get_level M (Pos (atm_of L))› for x L
    by (simp add: get_level_def)
  have [simp]: ‹atm_of L ∉ atms_of C' ⟷ L ∉# C' ∧ -L ∉# C'› for L C'
    by (cases L) (auto simp: atm_iff_pos_or_neg_lit)
  then have [iff]: ‹atm_of L ∈ atms_of C' ⟷ L ∈# C' ∨ -L ∈# C'› for L C'
    by blast
  have H: ‹distinct_mset {#L ∈# poss (atm_of `# C). get_level M L = count_decided M#}›
    if ‹distinct_mset C› ‹¬tautology C› for C
    using that by (induction C) (auto simp: tautology_add_mset atm_of_eq_atm_of)
  show ?thesis
    apply (subst card_max_lvl_all_poss)
    apply (subst (2) card_max_lvl_all_poss)
    unfolding card_max_lvl_def
    apply (rule arg_cong[of _ _ size])
    apply (rule distinct_set_mset_eq)
    subgoal by (rule H) (use assms in fast)+
    subgoal by (rule H) (use assms in fast)+
    subgoal using assms by (auto simp: atms_of_def imageI image_iff) blast+
    done
qed


end