Theory CDCL_W_Termination

theory CDCL_W_Termination
imports CDCL_W
theory CDCL_W_Termination
imports CDCL_W
begin

context conflict_driven_clause_learningW
begin

subsection ‹Termination›

subsubsection ‹No Relearning of a clause›

text ‹
  Because of the conflict minimisation, this version is less clear than the version without:
  instead of extracting the clause from the conflicting clause, we must take it from the clause
  used to backjump; i.e., the annotation of the first literal of the trail.

  We also prove below that no learned clause is subsumed by a (smaller) clause in the clause set.
›
lemma cdclW_stgy_no_relearned_clause:
  assumes
    cdcl: ‹backtrack S T› and
    inv: ‹cdclW_all_struct_inv S› and
    smaller: ‹no_smaller_propa S›
  shows
    ‹mark_of (hd_trail T) ∉# clauses S›
proof (rule ccontr)
  assume n_dist: ‹¬ ?thesis›
  obtain K L :: "'v literal" and
    M1 M2 :: "('v, 'v clause) ann_lit list" and i :: nat and D D' where
    confl_S: "conflicting S = Some (add_mset L D)" and
    decomp: "(Decided K # M1, M2) ∈ set (get_all_ann_decomposition (trail S))" and
    lev_L: "get_level (trail S) L = backtrack_lvl S" and
    max_D_L: "get_level (trail S) L = get_maximum_level (trail S) (add_mset L D')" and
    i: "get_maximum_level (trail S) D' ≡ i" and
    lev_K: "get_level (trail S) K = i + 1" and
    T: "T ∼ cons_trail (Propagated L (add_mset L D'))
        (reduce_trail_to M1
          (add_learned_cls (add_mset L D')
            (update_conflicting None S)))" and
    D_D': ‹D' ⊆# D› and
    ‹clauses S ⊨pm add_mset L D'›
    using cdcl by (auto elim!: rulesE)

  obtain M2' where M2': ‹trail S = (M2' @ M2) @ Decided K # M1›
    using decomp by auto
  have inv_T: ‹cdclW_all_struct_inv T›
    using cdcl cdclW_stgy_cdclW_all_struct_inv inv W_other backtrack bj
      cdclW_all_struct_inv_inv cdclW_cdclW_restart by blast

  have M1_D': ‹M1 ⊨as CNot D'›
    using backtrack_M1_CNot_D'[of S D' ‹i› K M1 M2 L ‹add_mset L D› T
        ‹Propagated L (add_mset L D')›] inv confl_S decomp i T D_D' lev_K lev_L max_D_L
    unfolding cdclW_all_struct_inv_def cdclW_conflicting_def cdclW_M_level_inv_def
    by (auto simp: subset_mset_trans_add_mset)
  have ‹undefined_lit M1 L›
    using inv_T T decomp unfolding cdclW_all_struct_inv_def cdclW_M_level_inv_def
    by (auto simp: defined_lit_map)
  moreover have ‹D' + {#L#} ∈# clauses S›
    using n_dist T by (auto simp: clauses_def)
  ultimately show False
    using smaller M1_D' unfolding no_smaller_propa_def M2' by blast
qed

lemma cdclW_stgy_no_relearned_larger_clause:
  assumes
    cdcl: ‹backtrack S T› and
    inv: ‹cdclW_all_struct_inv S› and
    smaller: ‹no_smaller_propa S› and
    smaller_conf: ‹no_smaller_confl S› and
    E_subset: ‹E ⊂# mark_of (hd_trail T)›
  shows ‹E ∉# clauses S›
proof (rule ccontr)
  assume n_dist: ‹¬ ?thesis›
  obtain K L :: "'v literal" and
    M1 M2 :: "('v, 'v clause) ann_lit list" and i :: nat and D D' where
    confl_S: "conflicting S = Some (add_mset L D)" and
    decomp: "(Decided K # M1, M2) ∈ set (get_all_ann_decomposition (trail S))" and
    lev_L: "get_level (trail S) L = backtrack_lvl S" and
    max_D_L: "get_level (trail S) L = get_maximum_level (trail S) (add_mset L D')" and
    i: "get_maximum_level (trail S) D' ≡ i" and
    lev_K: "get_level (trail S) K = i + 1" and
    T: "T ∼ cons_trail (Propagated L (add_mset L D'))
        (reduce_trail_to M1
          (add_learned_cls (add_mset L D')
            (update_conflicting None S)))" and
    D_D': ‹D' ⊆# D› and
    ‹clauses S ⊨pm add_mset L D'›
    using cdcl by (auto elim!: rulesE)

  obtain M2' where M2': ‹trail S = (M2' @ M2) @ Decided K # M1›
    using decomp by auto
  have inv_T: ‹cdclW_all_struct_inv T›
    using cdcl cdclW_stgy_cdclW_all_struct_inv inv W_other backtrack bj
      cdclW_all_struct_inv_inv cdclW_cdclW_restart by blast
  have ‹distinct_mset (add_mset L D')›
    using inv_T T unfolding cdclW_all_struct_inv_def distinct_cdclW_state_def
    by auto
  then have dist_E: ‹distinct_mset E›
    using distinct_mset_mono_strict[OF E_subset] T by auto

  have M1_D': ‹M1 ⊨as CNot D'›
    using backtrack_M1_CNot_D'[of S D' ‹i› K M1 M2 L ‹add_mset L D› T
        ‹Propagated L (add_mset L D')›] inv confl_S decomp i T D_D' lev_K lev_L max_D_L
    unfolding cdclW_all_struct_inv_def cdclW_conflicting_def cdclW_M_level_inv_def
    by (auto simp: subset_mset_trans_add_mset)
  have undef_L: ‹undefined_lit M1 L›
    using inv_T T decomp unfolding cdclW_all_struct_inv_def cdclW_M_level_inv_def
    by (auto simp: defined_lit_map)

  show False
  proof (cases ‹L ∈# E›)
    case True
    then obtain E' where
      E: ‹E = add_mset L E'›
      by (auto dest: multi_member_split)
    then have ‹distinct_mset E'› and ‹L ∉# E'› and E'_E': ‹E' ⊆# D'›
      using dist_E T E_subset by auto
    then have M1_E': ‹M1 ⊨as CNot E'›
      using M1_D' T unfolding true_annots_true_cls_def_iff_negation_in_model
      by (auto dest: multi_member_split[of _ E] mset_subset_eq_insertD)
    have propa:  ‹⋀M' K M L D. trail S = M' @ Decided K # M ⟹
      D + {#L#} ∈# clauses S ⟹ undefined_lit M L ⟹ ¬ M ⊨as CNot D›
      using smaller unfolding no_smaller_propa_def by blast
    show False
      using M1_E' propa[of ‹M2' @ M2› K M1 E', OF M2' _ undef_L] n_dist unfolding E
      by auto
  next
    case False
    then have ‹E ⊆# D'›
      using E_subset T by (auto simp: subset_add_mset_notin_subset)
    then have M1_E: ‹M1 ⊨as CNot E›
      using M1_D' T dist_E E_subset unfolding  true_annots_true_cls_def_iff_negation_in_model
      by (auto dest: multi_member_split[of _ E] mset_subset_eq_insertD)
    have confl:  ‹⋀M' K M L D. trail S = M' @ Decided K # M ⟹
      D ∈# clauses S ⟹ ¬ M ⊨as CNot D›
      using smaller_conf unfolding no_smaller_confl_def by blast
    show False
      using confl[of ‹M2' @ M2› K M1 E, OF M2'] n_dist M1_E
      by auto
  qed
qed

lemma cdclW_stgy_no_relearned_highest_subres_clause:
  assumes
    cdcl: ‹backtrack S T› and
    inv: ‹cdclW_all_struct_inv S› and
    smaller: ‹no_smaller_propa S› and
    smaller_conf: ‹no_smaller_confl S› and
    E_subset: ‹mark_of (hd_trail T) = add_mset (lit_of (hd_trail T)) E›
  shows ‹add_mset (- lit_of (hd_trail T)) E ∉# clauses S›
proof (rule ccontr)
  assume n_dist: ‹¬ ?thesis›
  obtain K L :: "'v literal" and
    M1 M2 :: "('v, 'v clause) ann_lit list" and i :: nat and D D' where
    confl_S: "conflicting S = Some (add_mset L D)" and
    decomp: "(Decided K # M1, M2) ∈ set (get_all_ann_decomposition (trail S))" and
    lev_L: "get_level (trail S) L = backtrack_lvl S" and
    max_D_L: "get_level (trail S) L = get_maximum_level (trail S) (add_mset L D')" and
    i: "get_maximum_level (trail S) D' ≡ i" and
    lev_K: "get_level (trail S) K = i + 1" and
    T: "T ∼ cons_trail (Propagated L (add_mset L D'))
        (reduce_trail_to M1
          (add_learned_cls (add_mset L D')
            (update_conflicting None S)))" and
    D_D': ‹D' ⊆# D› and
    ‹clauses S ⊨pm add_mset L D'›
    using cdcl by (auto elim!: rulesE)

  obtain M2' where M2': ‹trail S = (M2' @ M2) @ Decided K # M1›
    using decomp by auto
  have inv_T: ‹cdclW_all_struct_inv T›
    using cdcl cdclW_stgy_cdclW_all_struct_inv inv W_other backtrack bj
      cdclW_all_struct_inv_inv cdclW_cdclW_restart by blast
  have ‹distinct_mset (add_mset L D')›
    using inv_T T unfolding cdclW_all_struct_inv_def distinct_cdclW_state_def
    by auto

  have M1_D': ‹M1 ⊨as CNot D'›
    using backtrack_M1_CNot_D'[of S D' ‹i› K M1 M2 L ‹add_mset L D› T
        ‹Propagated L (add_mset L D')›] inv confl_S decomp i T D_D' lev_K lev_L max_D_L
    unfolding cdclW_all_struct_inv_def cdclW_conflicting_def cdclW_M_level_inv_def
    by (auto simp: subset_mset_trans_add_mset)
  have undef_L: ‹undefined_lit M1 L›
    using inv_T T decomp unfolding cdclW_all_struct_inv_def cdclW_M_level_inv_def
    by (auto simp: defined_lit_map)
  then have undef_uL: ‹undefined_lit M1 (-L)›
    by auto
  have propa:  ‹⋀M' K M L D. trail S = M' @ Decided K # M ⟹
    D + {#L#} ∈# clauses S ⟹ undefined_lit M L ⟹ ¬ M ⊨as CNot D›
    using smaller unfolding no_smaller_propa_def by blast
  have E[simp]: ‹E = D'›
    using E_subset T by (auto dest: multi_member_split)
  have propa:  ‹⋀M' K M L D. trail S = M' @ Decided K # M ⟹
    D + {#L#} ∈# clauses S ⟹ undefined_lit M L ⟹ ¬ M ⊨as CNot D›
    using smaller unfolding no_smaller_propa_def by blast
  show False
    using T M1_D' propa[of ‹M2' @ M2› K M1 D', OF M2' _ undef_uL] n_dist unfolding E
    by auto
qed


lemma cdclW_stgy_distinct_mset:
  assumes
    cdcl: ‹cdclW_stgy S T› and
    inv: "cdclW_all_struct_inv S" and
    smaller: ‹no_smaller_propa S› and
    dist: ‹distinct_mset (clauses S)›
  shows
    ‹distinct_mset (clauses T)›
proof (rule ccontr)
  assume n_dist: ‹¬ distinct_mset (clauses T)›
  then have ‹backtrack S T›
    using cdcl dist by (auto simp: cdclW_stgy.simps cdclW_o.simps cdclW_bj.simps
        elim: propagateE conflictE decideE skipE resolveE)
  then show False
    using n_dist cdclW_stgy_no_relearned_clause[of S T] dist
    by (auto simp: inv smaller elim!: rulesE)
qed

text ‹
  This is a more restrictive version of the previous theorem, but is a better bound for an
  implementation that does not do duplication removal (esp. as part of preprocessing).
›
lemma cdclW_stgy_learned_distinct_mset:
  assumes
    cdcl: ‹cdclW_stgy S T› and
    inv: "cdclW_all_struct_inv S" and
    smaller: ‹no_smaller_propa S› and
    dist: ‹distinct_mset (learned_clss S + remdups_mset (init_clss S))›
  shows
    ‹distinct_mset (learned_clss T + remdups_mset (init_clss T))›
proof (rule ccontr)
  assume n_dist: ‹¬ ?thesis›
  then have ‹backtrack S T›
    using cdcl dist by (auto simp: cdclW_stgy.simps cdclW_o.simps cdclW_bj.simps
        elim: propagateE conflictE decideE skipE resolveE)
  then show False
    using n_dist cdclW_stgy_no_relearned_clause[of S T] dist
    by (auto simp: inv smaller clauses_def elim!: rulesE)
qed

lemma rtranclp_cdclW_stgy_distinct_mset_clauses:
  assumes
    st: "cdclW_stgy** R S" and
    invR: "cdclW_all_struct_inv R" and
    dist: "distinct_mset (clauses R)" and
    no_smaller: ‹no_smaller_propa R›
  shows "distinct_mset (clauses S)"
  using assms by (induction rule: rtranclp_induct)
    (auto simp: cdclW_stgy_distinct_mset rtranclp_cdclW_stgy_no_smaller_propa
      rtranclp_cdclW_stgy_cdclW_all_struct_inv)


lemma rtranclp_cdclW_stgy_distinct_mset_learned_clauses:
  assumes
    st: "cdclW_stgy** R S" and
    invR: "cdclW_all_struct_inv R" and
    dist: "distinct_mset (learned_clss R + remdups_mset (init_clss R))" and
    no_smaller: ‹no_smaller_propa R›
  shows "distinct_mset (learned_clss S + remdups_mset (init_clss S))"
  using assms by (induction rule: rtranclp_induct)
    (auto simp: cdclW_stgy_learned_distinct_mset rtranclp_cdclW_stgy_no_smaller_propa
      rtranclp_cdclW_stgy_cdclW_all_struct_inv)

lemma cdclW_stgy_distinct_mset_clauses:
  assumes
    st: "cdclW_stgy** (init_state N) S" and
    no_duplicate_clause: "distinct_mset N" and
    no_duplicate_in_clause: "distinct_mset_mset N"
  shows "distinct_mset (clauses S)"
  using rtranclp_cdclW_stgy_distinct_mset_clauses[OF st] assms
  by (auto simp: cdclW_all_struct_inv_def distinct_cdclW_state_def no_smaller_propa_def)

lemma cdclW_stgy_learned_distinct_mset_new:
  assumes
    cdcl: ‹cdclW_stgy S T› and
    inv: "cdclW_all_struct_inv S" and
    smaller: ‹no_smaller_propa S› and
    dist: ‹distinct_mset (learned_clss S - A)›
  shows ‹distinct_mset (learned_clss T - A)›
proof (rule ccontr)
  have [iff]: ‹distinct_mset (add_mset C (learned_clss S) - A) ⟷
     C ∉# (learned_clss S) - A› for C
    using dist distinct_mset_add_mset[of C ‹learned_clss S - A›]
  proof -
    have f1: "learned_clss S - A = remove1_mset C (add_mset C (learned_clss S) - A)"
      by (metis Multiset.diff_right_commute add_mset_remove_trivial)
    have "remove1_mset C (add_mset C (learned_clss S) - A) = add_mset C (learned_clss S) - A ⟶
        distinct_mset (add_mset C (learned_clss S) - A)"
      by (metis (no_types) Multiset.diff_right_commute add_mset_remove_trivial dist)
    then have "¬ distinct_mset (add_mset C (learned_clss S - A)) ∨
        distinct_mset (add_mset C (learned_clss S) - A) ≠ (C ∈# learned_clss S - A)"
      by (metis (full_types) Multiset.diff_right_commute
          distinct_mset_add_mset[of C ‹learned_clss S - A›] add_mset_remove_trivial
          diff_single_trivial insert_DiffM)
    then show ?thesis
      using f1 by (metis (full_types) distinct_mset_add_mset[of C ‹learned_clss S - A›]
          diff_single_trivial dist insert_DiffM)
  qed

  assume n_dist: ‹¬ ?thesis›
  then have ‹backtrack S T›
    using cdcl dist by (auto simp: cdclW_stgy.simps cdclW_o.simps cdclW_bj.simps
        elim: propagateE conflictE decideE skipE resolveE)
  then show False
    using n_dist cdclW_stgy_no_relearned_clause[of S T]
    by (auto simp: inv smaller clauses_def elim!: rulesE
        dest!: in_diffD)
qed

lemma rtranclp_cdclW_stgy_distinct_mset_clauses_new_abs:
  assumes
    st: "cdclW_stgy** R S" and
    invR: "cdclW_all_struct_inv R" and
    no_smaller: ‹no_smaller_propa R› and
    ‹distinct_mset (learned_clss R - A)›
  shows "distinct_mset (learned_clss S - A)"
  using assms by (induction rule: rtranclp_induct)
    (auto simp: cdclW_stgy_distinct_mset rtranclp_cdclW_stgy_no_smaller_propa
      rtranclp_cdclW_stgy_cdclW_all_struct_inv
      cdclW_stgy_learned_distinct_mset_new)

lemma rtranclp_cdclW_stgy_distinct_mset_clauses_new:
  assumes
    st: "cdclW_stgy** R S" and
    invR: "cdclW_all_struct_inv R" and
    no_smaller: ‹no_smaller_propa R›
  shows "distinct_mset (learned_clss S - learned_clss R)"
  using assms by (rule rtranclp_cdclW_stgy_distinct_mset_clauses_new_abs) auto


subsubsection ‹Decrease of a Measure›

fun cdclW_restart_measure where
"cdclW_restart_measure S =
  [(3::nat) ^ (card (atms_of_mm (init_clss S))) - card (set_mset (learned_clss S)),
    if conflicting S = None then 1 else 0,
    if conflicting S = None then card (atms_of_mm (init_clss S)) - length (trail S)
    else length (trail S)
    ]"

lemma length_model_le_vars:
  assumes
    "no_strange_atm S" and
    no_d: "no_dup (trail S)" and
    "finite (atms_of_mm (init_clss S))"
  shows "length (trail S) ≤ card (atms_of_mm (init_clss S))"
proof -
  obtain M N U k D where S: "state S = (M, N, U, k, D)" by (cases "state S", auto)
  have "finite (atm_of ` lits_of_l (trail S))"
    using assms(1,3) unfolding S by (auto simp add: finite_subset)
  have "length (trail S) = card (atm_of ` lits_of_l (trail S))"
    using no_dup_length_eq_card_atm_of_lits_of_l no_d by blast
  then show ?thesis using assms(1) unfolding no_strange_atm_def
  by (auto simp add: assms(3) card_mono)
qed

lemma length_model_le_vars_all_inv:
  assumes "cdclW_all_struct_inv S"
  shows "length (trail S) ≤ card (atms_of_mm (init_clss S))"
  using assms length_model_le_vars[of S] unfolding cdclW_all_struct_inv_def
  by (auto simp: cdclW_M_level_inv_decomp)

lemma learned_clss_less_upper_bound:
  fixes S :: 'st
  assumes
    "distinct_cdclW_state S" and
    "∀s ∈# learned_clss S. ¬tautology s"
  shows "card(set_mset (learned_clss S)) ≤ 3 ^ card (atms_of_mm (learned_clss S))"
proof -
  have "set_mset (learned_clss S) ⊆ simple_clss (atms_of_mm (learned_clss S))"
    apply (rule simplified_in_simple_clss)
    using assms unfolding distinct_cdclW_state_def by auto
  then have "card(set_mset (learned_clss S))
    ≤ card (simple_clss (atms_of_mm (learned_clss S)))"
    by (simp add: simple_clss_finite card_mono)
  then show ?thesis
    by (meson atms_of_ms_finite simple_clss_card finite_set_mset order_trans)
qed


lemma cdclW_restart_measure_decreasing:
  fixes S :: 'st
  assumes
    "cdclW_restart S S'" and
    no_restart:
      "¬(learned_clss S ⊆# learned_clss S' ∧ [] = trail S' ∧ conflicting S' = None)"
    (*no restart*) and
    no_forget: "learned_clss S ⊆# learned_clss S'" (*no forget*) and
    no_relearn: "⋀S'. backtrack S S' ⟹ mark_of (hd_trail S') ∉# learned_clss S"
      and
    alien: "no_strange_atm S" and
    M_level: "cdclW_M_level_inv S" and
    no_taut: "∀s ∈# learned_clss S. ¬tautology s" and
    no_dup: "distinct_cdclW_state S" and
    confl: "cdclW_conflicting S"
  shows "(cdclW_restart_measure S', cdclW_restart_measure S) ∈ lexn less_than 3"
  using assms(1) assms(2,3)
proof (induct rule: cdclW_restart_all_induct)
  case (propagate C L) note conf = this(1) and undef = this(5) and T = this(6)
  have propa: "propagate S (cons_trail (Propagated L C) S)"
    using propagate_rule[OF propagate.hyps(1,2)] propagate.hyps by auto
  then have no_dup': "no_dup (Propagated L C # trail S)"
    using M_level cdclW_M_level_inv_decomp(2) undef defined_lit_map by auto

  let ?N = "init_clss S"
  have "no_strange_atm (cons_trail (Propagated L C) S)"
    using alien cdclW_restart.propagate cdclW_restart_no_strange_atm_inv propa M_level by blast
  then have "atm_of ` lits_of_l (Propagated L C # trail S)
    ⊆ atms_of_mm (init_clss S)"
    using undef unfolding no_strange_atm_def by auto
  then have "card (atm_of ` lits_of_l (Propagated L C # trail S))
    ≤ card (atms_of_mm (init_clss S))"
    by (meson atms_of_ms_finite card_mono finite_set_mset)
  then have "length (Propagated L C # trail S) ≤ card (atms_of_mm ?N)"
    using no_dup_length_eq_card_atm_of_lits_of_l no_dup' by fastforce
  then have H: "card (atms_of_mm (init_clss S)) - length (trail S)
    = Suc (card (atms_of_mm (init_clss S)) - Suc (length (trail S)))"
    by simp
  show ?case using conf T undef by (auto simp: H lexn3_conv)
next
  case (decide L) note conf = this(1) and undef = this(2) and T = this(4)
  moreover {
    have dec: "decide S (cons_trail (Decided L) S)"
      using decide_rule decide.hyps by force
    then have "cdclW_restart S (cons_trail (Decided L) S)"
      using cdclW_restart.simps cdclW_o.intros by blast } note cdclW_restart = this
  moreover {
    have lev: "cdclW_M_level_inv (cons_trail (Decided L) S)"
      using cdclW_restart M_level cdclW_restart_consistent_inv[OF cdclW_restart] by auto
    then have no_dup: "no_dup (Decided L # trail S)"
      using undef unfolding cdclW_M_level_inv_def by auto
    have "no_strange_atm (cons_trail (Decided L) S)"
      using M_level alien calculation(4) cdclW_restart_no_strange_atm_inv by blast
    then have "length (Decided L # (trail S))
      ≤ card (atms_of_mm (init_clss S))"
      using no_dup undef
      length_model_le_vars[of "cons_trail (Decided L) S"]
      by fastforce }
  ultimately show ?case using conf by (simp add: lexn3_conv)
next
  case (skip L C' M D) note tr = this(1) and conf = this(2) and T = this(5)
  show ?case using conf T by (simp add: tr lexn3_conv)
next
  case conflict
  then show ?case by (simp add: lexn3_conv)
next
  case resolve
  then show ?case using finite by (simp add: lexn3_conv)
next
  case (backtrack L D K i M1 M2 T D') note conf = this(1) and decomp = this(3) and D_D' = this(7)
    and T = this(9)
  let ?D' = ‹add_mset L D'›
  have bt: "backtrack S T"
    using backtrack_rule[OF backtrack.hyps] by auto
  have "?D' ∉# learned_clss S"
    using no_relearn[OF bt] conf T by auto
  then have card_T:
    "card (set_mset ({#?D'#} + learned_clss S)) = Suc (card (set_mset (learned_clss S)))"
    by simp
  have "distinct_cdclW_state T"
    using bt M_level distinct_cdclW_state_inv no_dup other cdclW_o.intros cdclW_bj.intros by blast
  moreover have "∀s∈#learned_clss T. ¬ tautology s"
    using learned_clss_are_not_tautologies[OF cdclW_restart.other[OF cdclW_o.bj[OF
      cdclW_bj.backtrack[OF bt]]]] M_level no_taut confl by auto
  ultimately have "card (set_mset (learned_clss T)) ≤ 3 ^ card (atms_of_mm (learned_clss T))"
      by (auto simp: learned_clss_less_upper_bound)
    then have H: "card (set_mset ({#?D'#} + learned_clss S))
      ≤ 3 ^ card (atms_of_mm ({#?D'#} + learned_clss S))"
      using T decomp M_level by (simp add: cdclW_M_level_inv_decomp)
  moreover
    have "atms_of_mm ({#?D'#} + learned_clss S) ⊆ atms_of_mm (init_clss S)"
      using alien conf atms_of_subset_mset_mono[OF D_D'] unfolding no_strange_atm_def
      by auto
    then have card_f: "card (atms_of_mm ({#?D'#} + learned_clss S))
      ≤ card (atms_of_mm (init_clss S))"
      by (meson atms_of_ms_finite card_mono finite_set_mset)
    then have "(3::nat) ^ card (atms_of_mm ({#?D'#} + learned_clss S))
      ≤ 3 ^ card (atms_of_mm (init_clss S))" by simp
  ultimately have "(3::nat) ^ card (atms_of_mm (init_clss S))
    ≥ card (set_mset ({#?D'#} + learned_clss S))"
    using le_trans by blast
  then show ?case using decomp diff_less_mono2 card_T T M_level
    by (auto simp: cdclW_M_level_inv_decomp lexn3_conv)
next
  case restart
  then show ?case using alien by auto
next
  case (forget C T) note no_forget = this(9)
  then have "C ∈# learned_clss S" and "C ∉# learned_clss T"
    using forget.hyps by auto
  then have "¬ learned_clss S ⊆# learned_clss T"
     by (auto simp add: mset_subset_eqD)
  then show ?case using no_forget by blast
qed

lemma cdclW_stgy_step_decreasing:
  fixes S T :: 'st
  assumes
    cdcl: ‹cdclW_stgy S T› and
    struct_inv: ‹cdclW_all_struct_inv S› and
    smaller: ‹no_smaller_propa S›
  shows "(cdclW_restart_measure T, cdclW_restart_measure S) ∈ lexn less_than 3"
proof (rule cdclW_restart_measure_decreasing)
  show ‹cdclW_restart S T›
    using cdcl cdclW_cdclW_restart cdclW_stgy_cdclW by blast
  show ‹¬ (learned_clss S ⊆# learned_clss T ∧ [] = trail T ∧ conflicting T = None)›
    using cdcl by (cases rule: cdclW_stgy_cases) (auto elim!: rulesE)
  show ‹learned_clss S ⊆# learned_clss T›
    using cdcl by (cases rule: cdclW_stgy_cases) (auto elim!: rulesE)
  show ‹mark_of (hd_trail S') ∉# learned_clss S› if ‹backtrack S S'› for S'
    using cdclW_stgy_no_relearned_clause[of S S'] cdclW_stgy_no_smaller_propa[of S S']
      cdcl struct_inv smaller that unfolding clauses_def
    by (auto elim!: rulesE)
  show ‹no_strange_atm S› and ‹cdclW_M_level_inv S› and ‹distinct_cdclW_state S› and
    ‹cdclW_conflicting S› and ‹∀s∈#learned_clss S. ¬ tautology s›
    using struct_inv unfolding cdclW_all_struct_inv_def by blast+
qed

lemma empty_trail_no_smaller_propa: ‹trail R = [] ⟹ no_smaller_propa R›
  by (simp add: no_smaller_propa_def)

text ‹Roughly corresponds to \cwref{theo:prop:cdcltermlc}{theorem 2.9.15 page 100}
  but using a different bound (the bound is below)›
lemma tranclp_cdclW_stgy_decreasing:
  fixes R S T :: 'st
  assumes "cdclW_stgy++ R S" and
  tr: "trail R = []" and
  "cdclW_all_struct_inv R"
  shows "(cdclW_restart_measure S, cdclW_restart_measure R) ∈ lexn less_than 3"
  using assms
  apply induction
   using empty_trail_no_smaller_propa cdclW_stgy_no_relearned_clause cdclW_stgy_step_decreasing
    apply blast
  using tranclp_into_rtranclp[of cdclW_stgy R] lexn_transI[OF trans_less_than, of 3]
    rtranclp_cdclW_stgy_no_smaller_propa unfolding trans_def
  by (meson cdclW_stgy_step_decreasing empty_trail_no_smaller_propa
      rtranclp_cdclW_stgy_cdclW_all_struct_inv)

lemma tranclp_cdclW_stgy_S0_decreasing:
  fixes R S T :: 'st
  assumes
    pl: "cdclW_stgy++ (init_state N) S" and
    no_dup: "distinct_mset_mset N"
  shows "(cdclW_restart_measure S, cdclW_restart_measure (init_state N)) ∈ lexn less_than 3"
proof -
  have "cdclW_all_struct_inv (init_state N)"
    using no_dup unfolding cdclW_all_struct_inv_def by auto
  then show ?thesis using pl tranclp_cdclW_stgy_decreasing init_state_trail by blast
qed

lemma wf_tranclp_cdclW_stgy:
  "wf {(S::'st, init_state N)| S N. distinct_mset_mset N ∧ cdclW_stgy++ (init_state N) S}"
  apply (rule wf_wf_if_measure'_notation2[of "lexn less_than 3" _ _ cdclW_restart_measure])
   apply (simp add: wf wf_lexn)
  using tranclp_cdclW_stgy_S0_decreasing by blast

text ‹The following theorems is deeply linked with the strategy: It shows that a decision alone
cannot lead to a conflict. This is obvious but I expect this to be a major part of the proof that
the number of learnt clause cannot be larger that \<^term>‹2^n›.›
lemma no_conflict_after_decide:
  assumes
    dec: ‹decide S T› and
    inv: ‹cdclW_all_struct_inv T› and
    smaller: ‹no_smaller_propa T› and
    smaller_confl: ‹no_smaller_confl T›
  shows ‹¬conflict T U›
proof (rule ccontr)
  assume ‹¬ ?thesis›
  then obtain D where
    D: ‹D ∈# clauses T› and
    confl: ‹trail T ⊨as CNot D›
    by (auto simp: conflict.simps)
  obtain L where
    ‹conflicting S = None› and
    undef: ‹undefined_lit (trail S) L› and
    ‹atm_of L ∈ atms_of_mm (init_clss S)› and
    T: ‹T ∼ cons_trail (Decided L) S›
    using dec by (auto simp: decide.simps)
  have dist: ‹distinct_mset D›
    using inv D unfolding cdclW_all_struct_inv_def distinct_cdclW_state_def
    by (auto dest!: multi_member_split simp: clauses_def)
  have L_D: ‹L ∉# D›
    using confl undef T
    by (auto dest!: multi_member_split simp: Decided_Propagated_in_iff_in_lits_of_l)

  show False
  proof (cases ‹-L ∈# D›)
    case True
    have H: ‹trail T = M' @ Decided K # M ⟹
      D + {#L#} ∈# clauses T ⟹ undefined_lit M L ⟹ ¬ M ⊨as CNot D›
      for M K M' D L
      using smaller unfolding no_smaller_propa_def
      by auto
    have ‹trail S ⊨as CNot (remove1_mset (-L) D)›
      using true_annots_CNot_lit_of_notin_skip[of ‹Decided L› ‹trail S› ‹remove1_mset (-L) D›] T True
        dist confl L_D
      by (auto dest: multi_member_split)
    then show False
      using True H[of ‹Nil› L ‹trail S› ‹remove1_mset (-L) D› ‹-L›] T D confl undef
      by auto
  next
    case False
    have H: ‹trail T = M' @ Decided K # M ⟹
      D ∈# clauses T ⟹ ¬ M ⊨as CNot D›
      for M K M' D
      using smaller_confl unfolding no_smaller_confl_def
      by auto
    have ‹trail S ⊨as CNot D›
      using true_annots_CNot_lit_of_notin_skip[of ‹Decided L› ‹trail S› D] T False
        dist confl L_D
      by (auto dest: multi_member_split)
    then show False
      using False H[of ‹Nil› L ‹trail S› D] T D confl undef
      by auto
  qed
qed

abbreviation list_weight_propa_trail :: ‹ ('v literal, 'v literal, 'v literal multiset) annotated_lit list ⇒ bool list› where
‹list_weight_propa_trail M ≡ map is_proped M›

definition comp_list_weight_propa_trail :: ‹nat ⇒  ('v literal, 'v literal, 'v literal multiset) annotated_lit list ⇒ bool list› where
‹comp_list_weight_propa_trail b M ≡ replicate (b - length M) False @ list_weight_propa_trail M›

lemma comp_list_weight_propa_trail_append[simp]:
  ‹comp_list_weight_propa_trail b (M @ M') =
     comp_list_weight_propa_trail (b - length M') M @ list_weight_propa_trail M'›
  by (auto simp: comp_list_weight_propa_trail_def)

lemma comp_list_weight_propa_trail_append_single[simp]:
  ‹comp_list_weight_propa_trail b (M @ [K]) =
    comp_list_weight_propa_trail (b - 1) M @ [is_proped K]›
  by (auto simp: comp_list_weight_propa_trail_def)

lemma comp_list_weight_propa_trail_cons[simp]:
  ‹comp_list_weight_propa_trail b (K # M') =
    comp_list_weight_propa_trail (b - Suc (length M')) [] @ is_proped K # list_weight_propa_trail M'›
  by (auto simp: comp_list_weight_propa_trail_def)

fun of_list_weight :: ‹bool list ⇒ nat› where
  ‹of_list_weight [] = 0›
| ‹of_list_weight (b # xs) = (if b then 1 else 0) + 2 * of_list_weight xs›

lemma of_list_weight_append[simp]:
  ‹of_list_weight (a @ b) = of_list_weight a + 2^(length a) * of_list_weight b›
  by (induction a) auto

lemma of_list_weight_append_single[simp]:
  ‹of_list_weight (a @ [b]) = of_list_weight a + 2^(length a) * (if b then 1 else 0)›
  using of_list_weight_append[of ‹a› ‹[b]›]
  by (auto simp del: of_list_weight_append)

lemma of_list_weight_replicate_False[simp]: ‹of_list_weight (replicate n False) = 0›
  by (induction n) auto

lemma of_list_weight_replicate_True[simp]: ‹of_list_weight (replicate n True) = 2^n - 1›
  apply (induction n)
  subgoal by auto
  subgoal for m
    using power_gt1_lemma[of ‹2 :: nat›]
    by (auto simp add: algebra_simps Suc_diff_Suc)
  done

lemma of_list_weight_le: ‹of_list_weight xs ≤ 2^(length xs) - 1›
proof -
  have ‹of_list_weight xs ≤ of_list_weight (replicate (length xs) True)›
    by (induction xs) auto
  then show ‹?thesis›
    by auto
qed

lemma of_list_weight_lt: ‹of_list_weight xs < 2^(length xs)›
  using of_list_weight_le[of xs]  by (metis One_nat_def Suc_le_lessD
    Suc_le_mono Suc_pred of_list_weight_le zero_less_numeral zero_less_power)

lemma [simp]: ‹of_list_weight (comp_list_weight_propa_trail n []) = 0›
  by (auto simp: comp_list_weight_propa_trail_def)

abbreviation propa_weight
  :: ‹nat ⇒ ('v literal, 'v literal, 'v literal multiset) annotated_lit list ⇒ nat›
where
  ‹propa_weight n M ≡ of_list_weight (comp_list_weight_propa_trail n M)›

lemma length_comp_list_weight_propa_trail[simp]: ‹length (comp_list_weight_propa_trail a M) = max (length M) a›
  by (auto simp: comp_list_weight_propa_trail_def)

lemma (in -)pow2_times_n:
  ‹Suc a ≤ n ⟹ 2 * 2^(n - Suc a) = (2::nat)^ (n - a)›
  ‹Suc a ≤ n ⟹ 2^(n - Suc a) * 2 = (2::nat)^ (n - a)›
  ‹Suc a ≤ n ⟹ 2^(n - Suc a) * b * 2 = (2::nat)^ (n - a) * b›
  ‹Suc a ≤ n ⟹ 2^(n - Suc a) * (b * 2) = (2::nat)^ (n - a) * b›
  ‹Suc a ≤ n ⟹ 2^(n - Suc a) * (2 * b) = (2::nat)^ (n - a) * b›
  ‹Suc a ≤ n ⟹ 2 * b * 2^(n - Suc a) = (2::nat)^ (n - a) * b›
  ‹Suc a ≤ n ⟹ 2 * (b * 2^(n - Suc a)) = (2::nat)^ (n - a) * b›
  apply (simp_all add: Suc_diff_Suc semiring_normalization_rules(27))
  using Suc_diff_le by fastforce+

lemma decide_propa_weight:
  ‹decide S T ⟹ n ≥ length (trail T) ⟹ propa_weight n (trail S) ≤ propa_weight n (trail T)›
  by (auto elim!: decideE simp: comp_list_weight_propa_trail_def
    algebra_simps pow2_times_n)

lemma propagate_propa_weight:
  ‹propagate S T ⟹ n ≥ length (trail T) ⟹ propa_weight n (trail S) < propa_weight n (trail T)›
  by (auto elim!: propagateE simp: comp_list_weight_propa_trail_def
    algebra_simps pow2_times_n)

text ‹
  The theorem below corresponds the bound of \cwref{theo:prop:cdcltermlc}{theorem 2.9.15 page 100}.
  In the current version there is no proof of the bound.

  The following proof contains an immense amount of stupid bookkeeping. The proof itself
  is rather easy and Isabelle makes it extra-complicated.

  Let's consider the sequence \<^text>‹S → ... → T›.
  The bookkeping part:
    ▸ We decompose it into its components \<^text>‹f 0 → f 1 → ... → f n›.
    ▸ Then we extract the backjumps out of it, which are at position \<^text>‹nth_nj 0›,
      \<^text>‹nth_nj 1›, ...
    ▸ Then we extract the conflicts out of it, which are at position \<^text>‹nth_confl 0›,
      \<^text>‹nth_confl 1›, ...

  Then the simple part:
    ▸ each backtrack increases \<^term>‹propa_weight›
    ▸ but \<^term>‹propa_weight› is bounded by \<^term>‹2 ^ (card (atms_of_mm (init_clss S)))›
  Therefore, we get the bound.

  Comments on the proof:
  ▪ The main problem of the proof is the number of inductions in the bookkeeping part.
  ▪ The proof is actually by contradiction to make sure that enough backtrack step exists. This could
    probably be avoided, but without change in the proof.

  Comments on the bound:
  ▪ The proof is very very crude: Any propagation also decreases the bound. The lemma
    @{thm no_conflict_after_decide} above shows that a decision cannot lead immediately to a
    conflict.
  ▪ TODO: can a backtrack could be immediately followed by another conflict
    (if there are several conflicts for the initial backtrack)? If not the bound can be divided by
    two.
›
lemma cdcl_pow2_n_learned_clauses:
  assumes
    cdcl: ‹cdclW** S T› and
    confl: ‹conflicting S = None› and
    inv: ‹cdclW_all_struct_inv S›
  shows ‹size (learned_clss T) ≤ size (learned_clss S) + 2 ^ (card (atms_of_mm (init_clss S)))›
    (is ‹_ ≤ _ + ?b›)
proof (rule ccontr)
  assume ge: ‹¬ ?thesis›
  let ?m = ‹card (atms_of_mm (init_clss S))›
  obtain n :: nat where
    n: ‹(cdclW^^n) S T›
    using cdcl unfolding rtranclp_power by fast
  then obtain f :: ‹nat ⇒ 'st› where
    f: ‹⋀i. i < n ⟹ cdclW (f i) (f (Suc i))› and
    [simp]: ‹f 0 = S› and
    [simp]: ‹f n = T›
    using power_ex_decomp[OF n]
    by auto

  have cdcl_st_k: ‹cdclW** S (f k)› if ‹k ≤ n› for k
    using that
    apply (induction k)
    subgoal by auto
    subgoal for k using f[of k] by (auto)
    done
  let ?g = ‹λi. size (learned_clss (f i))›
  have ‹?g 0 = size (learned_clss S)›
    by auto
  have g_n: ‹?g n > ?g 0 + 2 ^ (card (atms_of_mm (init_clss S)))›
    using ge by auto
  have g: ‹?g (Suc i) = ?g i ∨ (?g (Suc i) = Suc (?g i) ∧ backtrack (f i) (f (Suc i)))› if ‹i < n›
    for i
    using f[OF that]
    by (cases rule: cdclW.cases)
      (auto elim: propagateE conflictE decideE backtrackE skipE resolveE
        simp: cdclW_o.simps cdclW_bj.simps)
  have g_le: ‹?g i ≤ i + ?g 0› if ‹i ≤ n› for i
    using that
    apply (induction i)
    subgoal by auto
    subgoal for i
      using g[of i]
      by auto
    done
  from this[of n] have n_ge_m: ‹n > ?b›
    using g_n ge by auto
  then have n0: ‹n > 0›
    using not_add_less1 by fastforce
  define nth_bj where
    ‹nth_bj = rec_nat 0 (λ_ j. (LEAST i. i > j ∧ i < n ∧ backtrack (f i) (f (Suc i))))›
  have [simp]: ‹nth_bj 0 = 0›
    by (auto simp: nth_bj_def)
  have nth_bj_Suc: ‹nth_bj (Suc i) = (LEAST x. nth_bj i < x ∧ x < n ∧ backtrack (f x) (f (Suc x)))›
    for i
    by (auto simp: nth_bj_def)

  have between_nth_bj_not_bt:
    ‹¬backtrack (f k) (f (Suc k))›
    if ‹k < n› ‹k > nth_bj i› ‹k < nth_bj (Suc i) › for k i
    using not_less_Least[of k ‹λx. nth_bj i < x ∧ x < n ∧ backtrack (f x) (f (Suc x))›] that
    unfolding nth_bj_Suc[symmetric]
    by auto

  have g_nth_bj_eq:
    ‹?g (Suc k) = ?g k›
    if ‹k < n› ‹k > nth_bj i› ‹k < nth_bj (Suc i)› for k i
    using between_nth_bj_not_bt[OF that(1-3)] f[of k, OF that(1)]
    by (auto elim: propagateE conflictE decideE backtrackE skipE resolveE
        simp: cdclW_o.simps cdclW_bj.simps cdclW.simps)
  have g_nth_bj_eq2:
    ‹?g (Suc k) = ?g (Suc (nth_bj i))›
    if ‹k < n› ‹k > nth_bj i› ‹k < nth_bj (Suc i)› for k i
    using that
    apply (induction k)
    subgoal by blast
    subgoal for k
      using g_nth_bj_eq less_antisym by fastforce
    done
  have [simp]: ‹?g (Suc 0) = ?g 0›
    using confl f[of 0] n0
    by (auto elim: propagateE conflictE decideE backtrackE skipE resolveE
        simp: cdclW_o.simps cdclW_bj.simps cdclW.simps)
  have ‹(?g (nth_bj i) = size (learned_clss S) + (i - 1)) ∧
    nth_bj i < n ∧
    nth_bj i ≥ i ∧
    (i > 0 ⟶ backtrack (f (nth_bj i)) (f (Suc (nth_bj i)))) ∧
    (i > 0 ⟶ ?g (Suc (nth_bj i)) = size (learned_clss S) + i) ∧
    (i > 0 ⟶ nth_bj i > nth_bj (i-1))›
    if ‹i ≤ ?b+1›
    for i
    using that
  proof (induction i)
    case 0
    then show ?case using n0 by auto
  next
    case (Suc i)
    then have IH: ‹?g (nth_bj i) = size (learned_clss S) + (i - 1)›
        ‹0 < i ⟹ backtrack (f (nth_bj i)) (f (Suc (nth_bj i)))›
	‹0 < i ⟹ ?g (Suc (nth_bj i))  = size (learned_clss S) + i› and
      i_le_m: ‹Suc i ≤ ?b+1› and
      le_n: ‹nth_bj i < n› and
      gei: ‹nth_bj i ≥ i›
      by auto
    have ex_larger: ‹∃x>nth_bj i. x < n ∧ backtrack (f x) (f (Suc x))›
    proof (rule ccontr)
      assume ‹¬ ?thesis›
      then have [simp]: ‹n>x ⟹ x>nth_bj i ⟹ ?g (Suc x) = ?g x› for x
        using g[of x] n_ge_m
	by auto
      have eq1: ‹nth_bj i < Suc x ⟹ ¬ nth_bj i < x ⟹ x = nth_bj i› and
         eq2: ‹nth_bj i < x ⟹ ¬ nth_bj i < x - Suc 0 ⟹ nth_bj i = x - Suc 0›
	 for x
        by simp_all
      have ex_larger: ‹n>x ⟹ x>nth_bj i ⟹ ?g (Suc x) = ?g (Suc (nth_bj i))› for x
        apply (induction x)
	subgoal by auto
	subgoal for x
	  by (cases ‹nth_bj i < x›) (auto dest: eq1)
	done
      from this[of ‹n-1›] have g_n_nth_bj: ‹?g n = ?g (Suc (nth_bj i))›
        using n_ge_m i_le_m le_n
	by (cases ‹nth_bj i < n - Suc 0›)
          (auto dest: eq2)
      then have ‹size (learned_clss (f (Suc (nth_bj i)))) < size (learned_clss T)›
        using g_n i_le_m n_ge_m g_le[of ‹Suc (nth_bj i)›] le_n ge
	   ‹?g (nth_bj i) = size (learned_clss S) + (i - 1)›
	using Suc.IH by auto
      then show False
        using g_n i_le_m n_ge_m g_le[of ‹Suc (nth_bj i)›] g_n_nth_bj by auto
    qed

    from LeastI_ex[OF ex_larger]
    have bt: ‹backtrack (f (nth_bj (Suc i))) (f (Suc (nth_bj (Suc i))))› and
      le: ‹nth_bj (Suc i) < n› and
      nth_mono: ‹nth_bj i < nth_bj (Suc i)›
      unfolding nth_bj_Suc[symmetric]
      by auto

    have g_nth_Suc_g_Suc_nth: ‹?g (nth_bj (Suc i)) = ?g (Suc (nth_bj i))›
      using g_nth_bj_eq2[of ‹nth_bj (Suc i) - 1› i] le nth_mono
      apply auto (*TODO proof*)
      by (metis Suc_pred gr0I less_Suc0 less_Suc_eq less_imp_diff_less)
    have H1: ‹size (learned_clss (f (Suc (nth_bj (Suc i))))) =
       1 + size (learned_clss (f (nth_bj (Suc i))))› if ‹i = 0›
      using bt unfolding that
      by (auto simp: that elim: backtrackE)
    have ?case if ‹i > 0›
      using IH that nth_mono le bt gei
      by (auto elim: backtrackE simp: g_nth_Suc_g_Suc_nth)
    moreover have ?case if ‹i = 0›
      using le bt gei nth_mono IH g_nth_bj_eq2[of ‹nth_bj (Suc i) - 1› i]
        g_nth_Suc_g_Suc_nth
      apply (intro conjI)
      subgoal by (simp add: that)
      subgoal by (auto simp: that elim: backtrackE)
      subgoal by (auto simp: that elim: backtrackE)
      subgoal Hk by (auto simp: that elim: backtrackE)
      subgoal using H1 by (auto simp: that elim: backtrackE)
      subgoal using nth_mono by auto
      done
    ultimately show ?case by blast
  qed
  then have
    ‹(?g (nth_bj i) = size (learned_clss S) + (i - 1))› and
    nth_bj_le: ‹nth_bj i < n› and
    nth_bj_ge: ‹nth_bj i ≥ i› and
    bt_nth_bj: ‹i > 0 ⟹ backtrack (f (nth_bj i)) (f (Suc (nth_bj i)))› and
    ‹i > 0 ⟹ ?g (Suc (nth_bj i)) = size (learned_clss S) + i› and
    nth_bj_mono: ‹i > 0 ⟹ nth_bj (i - 1) < nth_bj i›
    if ‹i ≤ ?b+1›
    for i
    using that by blast+
  have
    confl_None: ‹conflicting (f (Suc (nth_bj i))) = None› and
    confl_nth_bj: ‹conflicting (f (nth_bj i)) ≠ None›
    if ‹i ≤ ?b+1› ‹i > 0›
    for i
    using bt_nth_bj[OF that] by (auto simp: backtrack.simps)

  have conflicting_still_conflicting:
    ‹conflicting (f k) ≠ None ⟶ conflicting (f (Suc k)) ≠ None›
    if ‹k < n› ‹k > nth_bj i› ‹k < nth_bj (Suc i)› for k i
    using between_nth_bj_not_bt[OF that] f[OF that(1)]
    by (auto elim: propagateE conflictE decideE backtrackE skipE resolveE
        simp: cdclW_o.simps cdclW_bj.simps cdclW.simps)

  define nth_confl where
    ‹nth_confl n ≡ LEAST i. i > nth_bj n ∧ i < nth_bj (Suc n) ∧ conflict (f i) (f (Suc i))› for n
  have ‹∃i>nth_bj a. i < nth_bj (Suc a) ∧ conflict (f i) (f (Suc i))›
    if a_n: ‹a ≤ ?b› ‹a > 0›
    for a
  proof (rule ccontr)
    assume H: ‹¬ ?thesis›
    have ‹conflicting (f (nth_bj a + Suc i)) = None›
      if ‹nth_bj a + Suc i ≤ nth_bj (Suc a)› for i :: nat
      using that
      apply (induction i)
      subgoal
        using confl_None[of a] a_n n_ge_m by auto
      subgoal for i
        apply (cases ‹Suc (nth_bj a + i) < n›)
        using f[of ‹nth_bj a + Suc i›] H
        apply (auto elim: propagateE conflictE decideE backtrackE skipE resolveE
          simp: cdclW_o.simps cdclW_bj.simps cdclW.simps)[]
	using nth_bj_le[of ‹Suc a›] a_n(1) by auto
      done
    from this[of ‹nth_bj (Suc a) - 1 - nth_bj a›] a_n
    show False
      using nth_bj_mono[of ‹Suc a›] a_n nth_bj_le[of ‹Suc a›] confl_nth_bj[of ‹Suc a›]
      by auto
  qed
  from LeastI_ex[OF this] have nth_bj_le_nth_confl: ‹nth_bj a < nth_confl a› and
    nth_confl: ‹conflict (f (nth_confl a)) (f (Suc (nth_confl a)))› and
    nth_confl_le_nth_bj_Suc: ‹nth_confl a < nth_bj (Suc a)›
    if a_n: ‹a ≤ ?b› ‹a > 0›
    for a
    using that unfolding nth_confl_def[symmetric]
    by blast+
  have nth_confl_conflicting: ‹conflicting (f (Suc (nth_confl a))) ≠ None›
    if a_n: ‹a ≤ ?b› ‹a > 0›
    for a
    using nth_confl[OF a_n]
    by (auto simp: conflict.simps)
  have no_conflict_before_nth_confl: ‹¬conflict (f k) (f (Suc k))›
    if ‹k > nth_bj a› and
      ‹k < nth_confl a› and
      a_n: ‹a ≤ ?b› ‹a > 0›
    for k a
    using not_less_Least[of k ‹λi. i > nth_bj a ∧ i < nth_bj (Suc a) ∧ conflict (f i) (f (Suc i))›] that
    nth_confl_le_nth_bj_Suc[of a]
    unfolding nth_confl_def[symmetric]
    by auto
  have conflicting_after_nth_confl: ‹conflicting (f (Suc (nth_confl a) + k)) ≠ None›
    if a_n: ‹a ≤ ?b› ‹a > 0› and
      k: ‹Suc (nth_confl a) + k < nth_bj (Suc a)›
    for a k
    using k
    apply (induction k)
    subgoal using nth_confl_conflicting[OF a_n] by simp
    subgoal for k
      using conflicting_still_conflicting[of ‹Suc (nth_confl a + k)› a] a_n
        nth_bj_le[of a] nth_bj_le_nth_confl[of a]
      apply (cases ‹Suc (nth_confl a + k) < n›)
      apply auto
       by (metis (no_types, lifting) Suc_le_lessD add.commute le_less less_trans_Suc nth_bj_le
         plus_1_eq_Suc)
    done
  have conflicting_before_nth_confl: ‹conflicting (f (Suc (nth_bj a) + k)) = None›
    if a_n: ‹a ≤ ?b› ‹a > 0› and
      k: ‹Suc (nth_bj a) + k < nth_confl a›
    for a k
    using k
    apply (induction k)
    subgoal using confl_None[of a] a_n by simp
    subgoal for k
      using f[of ‹Suc (nth_bj a) + k›] no_conflict_before_nth_confl[of a ‹Suc (nth_bj a) + k›] a_n
        nth_confl_le_nth_bj_Suc[of a] nth_bj_le[of ‹Suc a›]
      apply (cases ‹Suc (nth_bj a + k) < n›)
      apply (auto elim!: propagateE conflictE decideE backtrackE skipE resolveE
          simp: cdclW_o.simps cdclW_bj.simps cdclW.simps)[]
      by linarith
    done
  have
    ex_trail_decomp: ‹∃M. trail (f (Suc (nth_confl a))) = M @ trail (f (Suc (nth_confl a + k)))›
    if a_n: ‹a ≤ ?b› ‹a > 0› and
      k: ‹Suc (nth_confl a) + k ≤ nth_bj (Suc a)›
    for a k
    using k
  proof (induction k)
    case 0
    then show ‹?case› by auto
  next
    case (Suc k)
    moreover have ‹nth_confl a + k < n›
      proof -
	have "nth_bj (Suc a) < n"
	  by (rule nth_bj_le) (use a_n(1) in simp)
	then show ?thesis
	  using Suc.prems by linarith
      qed
    moreover have ‹∃Ma. M @ trail (f (Suc (nth_confl a + k))) =
            Ma @ tl (trail (f (Suc (nth_confl a + k))))› for M
      by (cases ‹trail (f (Suc (nth_confl a + k)))›) auto
    ultimately show ?case
      using f[of ‹Suc (nth_confl a + k)›] conflicting_after_nth_confl[of a ‹k›, OF a_n] Suc
        between_nth_bj_not_bt[of ‹Suc (nth_confl a + k)› ‹a›]
	nth_bj_le_nth_confl[of a, OF a_n]
      apply (cases ‹Suc (nth_confl a + k) < n›)
      subgoal
        by (auto elim!: propagateE conflictE decideE skipE resolveE
          simp: cdclW_o.simps cdclW_bj.simps cdclW.simps)[]
      subgoal
        by (metis (no_types, lifting) Suc_leD Suc_lessI a_n(1) add.commute add_Suc
	  add_mono_thms_linordered_semiring(1) le_numeral_extra(4) not_le nth_bj_le plus_1_eq_Suc)
      done
  qed
  have propa_weight_decreasing_confl:
    ‹propa_weight n (trail (f (Suc (nth_bj (Suc a))))) > propa_weight n (trail (f (nth_confl a)))›
    if a_n: ‹a ≤ ?b› ‹a > 0› and
      n: ‹n ≥ length (trail (f (nth_confl a)))›
    for a n
  proof -
    have pw0: ‹propa_weight n (trail (f (Suc (nth_confl a)))) =
      propa_weight n (trail (f (nth_confl a)))› and
      [simp]: ‹trail (f (Suc (nth_confl a))) = trail (f (nth_confl a))›
      using nth_confl[OF a_n] by (auto elim!: conflictE)
    have H: ‹nth_bj (Suc a) = Suc (nth_confl a) ∨ nth_bj (Suc a) ≥ Suc (Suc (nth_confl a))›
      using nth_bj_le_nth_confl[of a, OF a_n]
      using a_n(1) nth_confl_le_nth_bj_Suc that(2) by force

    from ex_trail_decomp[of a ‹nth_bj (Suc a) - (1+nth_confl a)›, OF a_n]
    obtain M where
      M: ‹trail (f (Suc (nth_confl a))) = M @ trail (f (nth_bj (Suc a)))›
      apply -
      apply (rule disjE[OF H])
      subgoal
        by auto
      subgoal
        using nth_bj_le_nth_confl[of a, OF a_n] nth_bj_ge[of ‹Suc a›] a_n
	by (auto simp add: numeral_2_eq_2)
      done
    obtain K M1 M2 D D' L where
      decomp: ‹(Decided K # M1, M2)
         ∈ set (get_all_ann_decomposition (trail (f (nth_bj (Suc a)))))› and
      ‹get_maximum_level (trail (f (nth_bj (Suc a)))) (add_mset L D') =
       backtrack_lvl (f (nth_bj (Suc a)))› and
      ‹get_level (trail (f (nth_bj (Suc a)))) L = backtrack_lvl (f (nth_bj (Suc a)))› and
      ‹get_level (trail (f (nth_bj (Suc a)))) K =
       Suc (get_maximum_level (trail (f (nth_bj (Suc a)))) D')› and
      ‹D' ⊆# D› and
      ‹clauses (f (nth_bj (Suc a))) ⊨pm add_mset L D'› and
      st_Suc: ‹f (Suc (nth_bj (Suc a))) ∼
       cons_trail (Propagated L (add_mset L D'))
        (reduce_trail_to M1
          (add_learned_cls (add_mset L D')
            (update_conflicting None (f (nth_bj (Suc a))))))›
      using bt_nth_bj[of ‹Suc a›] a_n
      by (auto elim!: backtrackE)
    obtain M3 where
      tr: ‹trail (f (nth_bj (Suc a))) = M3 @ M2 @ Decided K # M1›
      using decomp by auto
    define M2' where
      ‹M2' = M3 @ M2›
    then have
      tr: ‹trail (f (nth_bj (Suc a))) = M2' @ Decided K # M1›
      using tr by auto
    define M' where
      ‹M' = M @ M2'›
    then have tr2: ‹trail (f (nth_confl a)) = M' @ Decided K # M1›
      using tr M n
      by auto
    have [simp]: ‹max (length M) (n - Suc (length M1 + (length M2')))
      = (n - Suc (length M1 + (length M2')))›
      using tr M st_Suc n by auto
    have [simp]: ‹2 *
      (of_list_weight (list_weight_propa_trail M1) *
       (2 ^ length M2' *
        (2 ^ (n - Suc (length M1 + length M2'))))) =
	 of_list_weight (list_weight_propa_trail M1) * 2 ^ (n - length M1)›
	 using tr M n by (auto simp: algebra_simps field_simps pow2_times_n
	   comm_semiring_1_class.semiring_normalization_rules(26))
    have n_ge: ‹Suc (length M + (length M2' + length M1)) ≤ n›
      using n st_Suc tr M by auto
    have WTF: ‹a < b ⟹ b ≤ c ⟹ a < c› and
      WTF': ‹a ≤ b ⟹ b < c ⟹ a < c› for a b c :: nat
      by auto

    have 3: ‹propa_weight (n - Suc (length M1 + (length M2'))) M
      ≤ 2^(n - Suc (length M1 + length M2')) - 1›
      using of_list_weight_le
      apply auto
      by (metis ‹max (length M) (n - Suc (length M1 + (length M2'))) = n - Suc (length M1 + (length M2'))›
        length_comp_list_weight_propa_trail)
    have 1: ‹of_list_weight (list_weight_propa_trail M2') *
      2 ^ (n - Suc (length M1 + length M2')) < Suc (if M2' = [] then 0
        else 2 ^ (n - Suc (length M1)) - 2 ^ (n - Suc (length M1 + length M2')))›
      apply (cases ‹M2' = []›)
      subgoal by auto
      subgoal
	apply (rule WTF')
	  apply (rule Nat.mult_le_mono1[of ‹of_list_weight (list_weight_propa_trail M2')›,
	  OF of_list_weight_le[of ‹(list_weight_propa_trail M2')›]])
	using of_list_weight_le[of ‹(list_weight_propa_trail M2')›] n M tr
	by (auto simp add: comm_semiring_1_class.semiring_normalization_rules(26)
	  algebra_simps)
      done
    have WTF2:
      ‹a ≤ a' ⟹ b < b' ⟹ a + b < a' + b'› for a b c a' b' c' :: nat
      by auto

    have ‹propa_weight (n - Suc (length M1 + length M2')) M +
    of_list_weight (list_weight_propa_trail M2') *
    2 ^ (n - Suc (length M1 + length M2'))
    < 2 ^ (n - Suc (length M1))›
      apply (rule WTF)
      apply (rule WTF2[OF 3 1])
      using n_ge[unfolded nat_le_iff_add] by (auto simp: ac_simps algebra_simps)
    then have ‹propa_weight n (trail (f (nth_confl a))) < propa_weight n (trail (f (Suc (nth_bj (Suc a)))))›
      using tr2 M st_Suc n tr
      by (auto simp: pow2_times_n algebra_simps
        comm_semiring_1_class.semiring_normalization_rules(26))
    then show ‹?thesis›
      using pw0 by auto
  qed
  have length_trail_le_m: ‹length (trail (f k)) < ?m + 1›
    if ‹k ≤ n›
    for k
  proof -
    have ‹cdclW_all_struct_inv (f k)›
      using rtranclp_cdclW_cdclW_restart[OF cdcl_st_k[OF that]] inv
      rtranclp_cdclW_all_struct_inv_inv by blast
    then have ‹cdclW_M_level_inv (f k)› and ‹no_strange_atm (f k)›
      unfolding cdclW_all_struct_inv_def by blast+
    then have ‹no_dup (trail (f k))› and
      incl: ‹atm_of ` lits_of_l (trail (f k)) ⊆ atms_of_mm (init_clss (f k))›
      unfolding cdclW_M_level_inv_def no_strange_atm_def
      by auto
    have eq: ‹(atms_of_mm (init_clss (f k))) = (atms_of_mm (init_clss S))›
      using rtranclp_cdclW_restart_init_clss[OF rtranclp_cdclW_cdclW_restart[OF cdcl_st_k[OF that]]]
      by auto
    have ‹length (trail (f k)) = card (atm_of ` lits_of_l (trail (f k)))›
      using ‹no_dup (trail (f k))› no_dup_length_eq_card_atm_of_lits_of_l by blast
    also have ‹card (atm_of ` lits_of_l (trail (f k))) ≤ ?m›
      using card_mono[OF _ incl] eq by auto
    finally show ?thesis
      by linarith
  qed
  have propa_weight_decreasing_propa:
    ‹propa_weight ?m (trail (f (nth_confl a))) ≥ propa_weight ?m (trail (f (Suc (nth_bj a))))›
    if a_n: ‹a ≤ ?b› ‹a > 0›
    for a
  proof -
    have ppa: ‹propa_weight ?m (trail (f (Suc (nth_bj a) + Suc k)))
      ≥ propa_weight ?m (trail (f (Suc (nth_bj a) + k)))›
      if ‹k < nth_confl a - Suc (nth_bj a)›
      for k
    proof -
      have ‹Suc (nth_bj a + k) < n› and ‹Suc (nth_bj a + k) < nth_confl a›
        using that nth_bj_le_nth_confl[OF a_n] nth_confl_le_nth_bj_Suc[OF a_n]
	  nth_bj_le[of ‹Suc a›] a_n
	by auto
      then show ?thesis
        using f[of ‹(Suc (nth_bj a) + k)›] conflicting_before_nth_confl[OF a_n, of ‹k›]
	no_conflict_before_nth_confl[OF _ _ a_n, of ‹Suc (nth_bj a) + k›] that
	length_trail_le_m[of ‹Suc (Suc (nth_bj a) + k)›]
        by (auto elim!: skipE resolveE backtrackE
            simp: cdclW_o.simps cdclW_bj.simps cdclW.simps
	    dest!: propagate_propa_weight[of _ _ ?m]
	      decide_propa_weight[of _ _ ?m])
    qed
    have WTF3: ‹(Suc (nth_bj a + (nth_confl a - Suc (nth_bj a)))) = nth_confl a›
      using a_n(1) nth_bj_le_nth_confl that(2) by fastforce
    have ‹propa_weight ?m (trail (f (Suc (nth_bj a) + k)))
      ≥ propa_weight ?m (trail (f (Suc (nth_bj a))))›
      if ‹k ≤ nth_confl a - Suc (nth_bj a)›
      for k
      using that
      apply (induction k)
      subgoal by auto
      subgoal for k using ppa[of k]
        apply (cases ‹k < nth_confl a - Suc (nth_bj a)›)
	subgoal by auto
	subgoal by linarith
      done
      done
    from this[of ‹nth_confl a - (Suc (nth_bj a))›]
    show ?thesis
      by (auto simp: WTF3)
  qed
  have propa_weight_decreasing_confl:
    ‹propa_weight ?m (trail (f (Suc (nth_bj a))))
      < propa_weight ?m (trail (f (Suc (nth_bj (Suc a)))))›
    if a_n: ‹a ≤ ?b› ‹a > 0›
    for a
  proof -
    have WTF: ‹b < c ⟹ a ≤ b ⟹ a < c› for a b c  :: nat by linarith
    have ‹nth_confl a < n›
      by (metis Suc_le_mono a_n(1) add.commute add_lessD1 less_imp_le nat_le_iff_add
        nth_bj_le nth_confl_le_nth_bj_Suc plus_1_eq_Suc that(2))

    show ?thesis
      apply (rule WTF)
        apply (rule propa_weight_decreasing_confl[OF a_n, of ?m])
	subgoal using length_trail_le_m[of ‹nth_confl a›] ‹nth_confl a < n› by auto
       apply (rule propa_weight_decreasing_propa[OF a_n])
      done
  qed
  have weight1: ‹propa_weight ?m (trail (f (Suc (nth_bj 1)))) ≥ 1›
    using bt_nth_bj[of 1]
    by (auto simp: elim!: backtrackE intro!: trans_le_add1)
  have ‹propa_weight ?m (trail (f (Suc (nth_bj (Suc a))))) ≥
       propa_weight ?m (trail (f (Suc (nth_bj 1)))) + a›
    if a_n: ‹a ≤ ?b›
    for a :: nat
    using that
    apply (induction a)
    subgoal by auto
    subgoal for a
      using propa_weight_decreasing_confl[of ‹Suc a›]
      by auto
    done
  from this[of ‹?b›] have ‹propa_weight ?m (trail (f (Suc (nth_bj (Suc (?b)))))) ≥ 1 + ?b›
    using weight1 by auto
  moreover have
    ‹max (length (trail (f (Suc (nth_bj (Suc ?b)))))) ?m = ?m›
    using length_trail_le_m[of ‹(Suc (nth_bj (Suc ?b)))›] Suc_leI nth_bj_le
    nth_bj_le[of ‹Suc (?b)›] by (auto simp: max_def)
  ultimately show ‹False›
    using of_list_weight_le[of ‹comp_list_weight_propa_trail ?m (trail (f (Suc (nth_bj (Suc ?b)))))›]
    by (simp del: state_eq_init_clss state_eq_trail)
qed

text ‹Application of the previous theorem to an initial state:›
corollary cdcl_pow2_n_learned_clauses2:
  assumes
    cdcl: ‹cdclW** (init_state N) T› and
    inv: ‹cdclW_all_struct_inv (init_state N)›
  shows ‹size (learned_clss T) ≤ 2 ^ (card (atms_of_mm N))›
  using assms cdcl_pow2_n_learned_clauses[of ‹init_state N› T]
  by auto

end

end