Theory CDCL_Conflict_Minimisation

theory CDCL_Conflict_Minimisation
  imports
    Watched_Literals_Watch_List
    More_Sepref.WB_More_Refinement
    More_Sepref.WB_More_Refinement_List "List-Index.List_Index"
begin

chapter Conflict Minimisation

text We implement the conflict minimisation as presented by Sörensson and Biere
(``Minimizing Learned Clauses''').

We refer to the paper for further details, but the general idea is to produce a series of resolution
steps such that eventually (i.e., after enough resolution steps) no new literals has been introduced
in the conflict clause.

The resolution steps are only done with the reasons of the of literals
appearing in the trail. Hence these steps are terminating: we are ``shortening'' the trail we have
to consider with each resolution step. Remark that the shortening refers to the length of the trail
we have to consider, not the levels.

The concrete proof was harder than we initially expected. Our first proof try was to certify the
resolution steps. While this worked out, adding caching on top of that turned to be rather hard,
since it is not obvious how to add resolution steps in the middle of the current proof if the
literal has already been removed (basically we would have to prove termination and confluence of the
rewriting system).
Therefore, we worked instead directly on the entailment of the literals of the conflict clause
(up to the point in the trail we currently considering, which is also the termination measure).
The previous try is still present in our formalisation (see termminimize_conflict_support, which
we however only use for the termination proof).

The algorithm presented above does not distinguish between literals propagated at the same level:
we cannot reuse information about failures to cut branches. There is a variant of the algorithm
presented above that is able to do so (Van Gelder, ``Improved Conflict-Clause Minimization
Leads to Improved Propositional Proof Traces''). The algorithm is however more complicated and has
only be implemented in very few solvers (at least lingeling and cadical) and is especially not part
of glucose nor cryptominisat. Therefore, we have decided to not implement it: It is probably not
worth it and requires some additional data structures.

declare cdclW_restart_mset_state[simp]

type_synonym out_learned = nat clause_l


text The data structure contains the (unique) literal of highest at position one. This is useful
since this is what we want to have at the end (propagation clause) and we can skip the first
literal when minimising the clause.

definition out_learned :: (nat, nat) ann_lits  nat clause option  out_learned  bool where
  out_learned M D out 
     out  [] 
     (D = None  length out = 1) 
     (D  None  mset (tl out) = filter_mset (λL. get_level M L < count_decided M) (the D))

definition out_learned_confl :: (nat, nat) ann_lits  nat clause option  out_learned  bool where
  out_learned_confl M D out 
     out  []  (D  None  mset out = the D)

lemma out_learned_Cons_None[simp]:
  out_learned (L # aa) None ao  out_learned aa None ao
  by (auto simp: out_learned_def)

lemma out_learned_tl_None[simp]:
  out_learned (tl aa) None ao  out_learned aa None ao
  by (auto simp: out_learned_def)

definition index_in_trail :: ('v, 'a) ann_lits  'v literal  nat where
  index_in_trail M L = index (map (atm_of o lit_of) (rev M)) (atm_of L)

lemma Propagated_in_trail_entailed:
  assumes
    invs: cdclW_restart_mset.cdclW_all_struct_inv (M, N, U, D) and
    in_trail: Propagated L C  set M
  shows
    M ⊨as CNot (remove1_mset L C) and L ∈# C and N + U ⊨pm C and
    K ∈# remove1_mset L C  index_in_trail M K < index_in_trail M L and
    ¬tautology C and distinct_mset C
proof -
  obtain M2 M1 where
    M: M = M2 @ Propagated L C # M1
    using split_list[OF in_trail] by metis
  have a @ Propagated L mark # b = trail (M, N, U, D) 
       b ⊨as CNot (remove1_mset L mark)  L ∈# mark and
    dist: cdclW_restart_mset.distinct_cdclW_state (M, N, U, D)
    for L mark a b
    using invs
    unfolding cdclW_restart_mset.cdclW_all_struct_inv_def
      cdclW_restart_mset.cdclW_conflicting_def
    by fast+
  then have L_E: L ∈# C and M1_E: M1 ⊨as CNot (remove1_mset L C)
    unfolding M by force+
  then have M_E: M ⊨as CNot (remove1_mset L C)
    unfolding M by (simp add: true_annots_append_l)
  show M ⊨as CNot (remove1_mset L C) and L ∈# C
    using L_E M_E by fast+
  have set (get_all_mark_of_propagated (trail (M, N, U, D)))
     set_mset (cdclW_restart_mset.clauses (M, N, U, D))
    using invs
    unfolding cdclW_restart_mset.cdclW_all_struct_inv_def
       cdclW_restart_mset.cdclW_learned_clause_alt_def
    by fast
  then have C ∈# N + U
    using in_trail cdclW_restart_mset.in_get_all_mark_of_propagated_in_trail[of C M]
    by (auto simp: clauses_def)
  then show N + U ⊨pm C by auto

  have n_d: no_dup M
    using invs
    unfolding cdclW_restart_mset.cdclW_all_struct_inv_def
      cdclW_restart_mset.cdclW_M_level_inv_def
    by auto
  show index_in_trail M K < index_in_trail M L if K_C: K ∈# remove1_mset L C
  proof -
    have
      KL: atm_of K  atm_of L and
      uK_M1: -K  lits_of_l M1 and
      L: L  lit_of ` (set M2  set M1) -L  lit_of ` (set M2  set M1)
      using M1_E K_C n_d unfolding M true_annots_true_cls_def_iff_negation_in_model
      by (auto dest!: multi_member_split simp: atm_of_eq_atm_of lits_of_def uminus_lit_swap
          Decided_Propagated_in_iff_in_lits_of_l)
    have L_M1: atm_of L  (atm_of  lit_of) ` set M1
      using L by (auto simp: image_Un atm_of_eq_atm_of)
    have K_M1: atm_of K  (atm_of  lit_of) ` set M1
      using uK_M1 by (auto simp: lits_of_def image_image comp_def uminus_lit_swap)
    show ?thesis
      using KL L_M1 K_M1 unfolding index_in_trail_def M by (auto simp: index_append)
  qed
  have ¬tautology(remove1_mset L C)
    by (rule consistent_CNot_not_tautology[of lits_of_l M1])
     (use n_d M1_E in auto dest: distinct_consistent_interp no_dup_appendD
       simp: true_annots_true_cls M)
  then show ¬tautology C
    using multi_member_split[OF L_E] M1_E n_d
    by (auto simp: tautology_add_mset true_annots_true_cls_def_iff_negation_in_model M
        dest!: multi_member_split in_lits_of_l_defined_litD)
  show distinct_mset (C)
    using dist C ∈# N + U unfolding cdclW_restart_mset.distinct_cdclW_state_def
    by (auto dest: multi_member_split)
qed

text This predicate corresponds to one resolution step.
inductive minimize_conflict_support :: ('v, 'v clause) ann_lits  'v clause  'v clause  bool
  for M where
resolve_propa:
  minimize_conflict_support M (add_mset (-L) C) (C + remove1_mset L E)
  if Propagated L E  set M |
remdups: minimize_conflict_support M (add_mset L C) C


lemma index_in_trail_uminus[simp]: index_in_trail M (-L) = index_in_trail M L
  by (auto simp: index_in_trail_def)

text This is the termination argument of the conflict minimisation: the multiset of the levels
decreases (for the multiset ordering).
definition minimize_conflict_support_mes :: ('v, 'v clause) ann_lits  'v clause  nat multiset
where
  minimize_conflict_support_mes M C = index_in_trail M `# C

context
  fixes M :: ('v, 'v clause) ann_lits and N U :: 'v clauses and
    D :: 'v clause option
  assumes invs: cdclW_restart_mset.cdclW_all_struct_inv (M, N, U, D)
begin

private lemma
   no_dup: no_dup M and
   consistent: consistent_interp (lits_of_l M)
  using invs unfolding cdclW_restart_mset.cdclW_all_struct_inv_def
  cdclW_restart_mset.cdclW_M_level_inv_def
  by simp_all

lemma minimize_conflict_support_entailed_trail:
  assumes minimize_conflict_support M C E and M ⊨as CNot C
  shows M ⊨as CNot E
  using assms
proof (induction rule: minimize_conflict_support.induct)
  case (resolve_propa L E C) note in_trail = this(1) and M_C = this(2)
  then show ?case
    using Propagated_in_trail_entailed[OF invs in_trail] by (auto dest!: multi_member_split)
next
  case (remdups L C)
  then show ?case
    by auto
qed

lemma rtranclp_minimize_conflict_support_entailed_trail:
  assumes (minimize_conflict_support M)** C E and M ⊨as CNot C
  shows M ⊨as CNot E
  using assms apply (induction rule: rtranclp_induct)
  subgoal by fast
  subgoal using minimize_conflict_support_entailed_trail by fast
  done

lemma minimize_conflict_support_mes:
  assumes minimize_conflict_support M C E
  shows minimize_conflict_support_mes M E < minimize_conflict_support_mes M C
  using assms unfolding minimize_conflict_support_mes_def
proof (induction rule: minimize_conflict_support.induct)
  case (resolve_propa L E C) note in_trail = this
  let ?f = λxa. index (map (λa. atm_of (lit_of a)) (rev M)) xa
  have ?f (atm_of x) < ?f (atm_of L) if x: x ∈# remove1_mset L E for x
  proof -
    obtain M2 M1 where
      M: M = M2 @ Propagated L E # M1
      using split_list[OF in_trail] by metis
    have a @ Propagated L mark # b = trail (M, N, U, D) 
       b ⊨as CNot (remove1_mset L mark)  L ∈# mark for L mark a b
      using invs
      unfolding cdclW_restart_mset.cdclW_all_struct_inv_def
        cdclW_restart_mset.cdclW_conflicting_def
      by fast
    then have L_E: L ∈# E and M_E: M1 ⊨as CNot (remove1_mset L E)
      unfolding M by force+
    then have -x  lits_of_l M1
      using x unfolding true_annots_true_cls_def_iff_negation_in_model by auto
    then have ?f (atm_of x) < length M1
      using no_dup
      by (auto simp: M lits_of_def index_append Decided_Propagated_in_iff_in_lits_of_l
          uminus_lit_swap)
    moreover have ?f (atm_of L) = length M1
      using no_dup unfolding M by (auto simp: index_append Decided_Propagated_in_iff_in_lits_of_l
          atm_of_eq_atm_of lits_of_def)
    ultimately show ?thesis by auto
  qed

  then show ?case by (auto simp: comp_def index_in_trail_def)
next
  case (remdups L C)
  then show ?case by auto
qed

lemma wf_minimize_conflict_support:
  shows wf {(C', C). minimize_conflict_support M C C'}
  apply (rule wf_if_measure_in_wf[of {(C', C). C' < C} _ minimize_conflict_support_mes M])
  subgoal using wf .
  subgoal using minimize_conflict_support_mes by auto
  done
end

lemma conflict_minimize_step:
  assumes
    NU ⊨p add_mset L C and
    NU ⊨p add_mset (-L) D and
    K'. K' ∈# C  NU ⊨p add_mset (-K') D
  shows NU ⊨p D
proof -
  have NU ⊨p D + C
    using assms(1,2) true_clss_cls_or_true_clss_cls_or_not_true_clss_cls_or by blast
  then show ?thesis
    using assms(3)
  proof (induction C)
    case empty
    then show ?case
      using true_clss_cls_in true_clss_cls_or_true_clss_cls_or_not_true_clss_cls_or by fastforce
  next
    case (add x C) note IH =this(1) and NU_DC = this(2) and entailed = this(3)
    have NU ⊨p D + C + D
      using entailed[of x] NU_DC
        true_clss_cls_or_true_clss_cls_or_not_true_clss_cls_or[of NU -x D + C D]
      by auto
    then have NU ⊨p D + C
      by (smt (verit) total_over_m_sum total_over_m_union true_cls_union true_clss_cls_def)
    from IH[OF this] entailed show ?case by auto
  qed
qed

text This function filters the clause by the levels up the level of the given literal. This is
the part the conflict clause that is considered when testing if the given literal is redundant.
definition filter_to_poslev where
  filter_to_poslev M L D = filter_mset (λK. index_in_trail M K < index_in_trail M L) D

lemma filter_to_poslev_uminus[simp]:
  filter_to_poslev M (-L) D = filter_to_poslev M L D
  by (auto simp: filter_to_poslev_def)

lemma filter_to_poslev_empty[simp]:
  filter_to_poslev M L {#} = {#}
  by (auto simp: filter_to_poslev_def)

lemma filter_to_poslev_mono:
  index_in_trail M K'  index_in_trail M L 
   filter_to_poslev M K' D ⊆# filter_to_poslev M L D
  unfolding filter_to_poslev_def
  by (auto simp: multiset_filter_mono2)

lemma filter_to_poslev_mono_entailement:
  index_in_trail M K'  index_in_trail M L 
   NU ⊨p filter_to_poslev M K' D  NU ⊨p filter_to_poslev M L D
  by (metis (full_types) filter_to_poslev_mono subset_mset.le_iff_add true_clss_cls_mono_r)

lemma filter_to_poslev_mono_entailement_add_mset:
  index_in_trail M K'  index_in_trail M L 
   NU ⊨p add_mset J (filter_to_poslev M K' D)  NU ⊨p add_mset J (filter_to_poslev M L D)
  by (metis filter_to_poslev_mono mset_subset_eq_add_mset_cancel subset_mset.le_iff_add
      true_clss_cls_mono_r)

lemma conflict_minimize_intermediate_step:
  assumes
    NU ⊨p add_mset L C and
    K'_C: K'. K' ∈# C  NU ⊨p add_mset (-K') D  K' ∈# D
  shows NU ⊨p add_mset L D
proof -
  have NU ⊨p add_mset L C + D
    using assms(1) true_clss_cls_mono_r by blast
  then show ?thesis
    using assms(2)
  proof (induction C)
    case empty
    then show ?case
      using true_clss_cls_in true_clss_cls_or_true_clss_cls_or_not_true_clss_cls_or by fastforce
  next
    case (add x C) note IH =this(1) and NU_DC = this(2) and entailed = this(3)

    have 1: NU ⊨p add_mset x (add_mset L (D + C))
      using NU_DC by (auto simp: add_mset_commute ac_simps)
    moreover have 2: remdups_mset (add_mset L (D + C + D)) = remdups_mset (add_mset L (C + D))
      by (auto simp: remdups_mset_def)
    moreover have 3: remdups_mset (D + C + D) = remdups_mset (D + C)
      by (auto simp: remdups_mset_def)
    moreover have x ∈# D  NU ⊨p add_mset L (D + C + D)
      using 1
      apply (subst (asm) true_clss_cls_remdups_mset[symmetric])
      apply (subst true_clss_cls_remdups_mset[symmetric])
      by (auto simp: 2 3)
    ultimately have NU ⊨p add_mset L (D + C + D)
      using entailed[of x] NU_DC
        true_clss_cls_or_true_clss_cls_or_not_true_clss_cls_or[of NU -x add_mset L D + C D]
      by auto
    moreover have remdups_mset (D + (C + D)) = remdups_mset (D + C)
      by (auto simp: remdups_mset_def)
    ultimately have NU ⊨p add_mset L C + D
      apply (subst true_clss_cls_remdups_mset[symmetric])
      apply (subst (asm) true_clss_cls_remdups_mset[symmetric])
      by (auto simp add: 3 2 add.commute simp del: true_clss_cls_remdups_mset)
    from IH[OF this] entailed show ?case by auto
  qed
qed

lemma conflict_minimize_intermediate_step_filter_to_poslev:
  assumes
    lev_K_L: K'. K' ∈# C  index_in_trail M K' < index_in_trail M L and
    NU_LC: NU ⊨p add_mset L C and
    K'_C: K'. K' ∈# C  NU ⊨p add_mset (-K') (filter_to_poslev M L D) 
     K' ∈# filter_to_poslev M L D
  shows NU ⊨p add_mset L (filter_to_poslev M L D)
proof -
  have C_entailed: K' ∈# C  NU ⊨p add_mset (-K') (filter_to_poslev M L D) 
   K' ∈# filter_to_poslev M L D for K'
    using filter_to_poslev_mono[of M K' L D] lev_K_L[of K'] K'_C[of K']
      true_clss_cls_mono_r[of _  add_mset (- K') (filter_to_poslev M K' D) ]
    by (auto simp: mset_subset_eq_exists_conv)
  show ?thesis
    using conflict_minimize_intermediate_step[OF NU_LC C_entailed]  by fast
qed

datatype minimize_status = SEEN_FAILED | SEEN_REMOVABLE | SEEN_UNKNOWN

instantiation minimize_status :: default
begin
  definition default_minimize_status where
    default_minimize_status = SEEN_UNKNOWN

instance by standard
end

type_synonym 'v conflict_min_analyse = ('v literal × 'v clause) list
type_synonym 'v conflict_min_cach = 'v  minimize_status

definition get_literal_and_remove_of_analyse
   :: 'v conflict_min_analyse  ('v literal × 'v conflict_min_analyse) nres where
  get_literal_and_remove_of_analyse analyse =
    SPEC(λ(L, ana). L ∈# snd (hd analyse)  tl ana = tl analyse  ana  [] 
         hd ana = (fst (hd analyse), snd (hd (analyse)) - {#L#}))

definition mark_failed_lits
  :: _  'v conflict_min_analyse  'v conflict_min_cach  'v conflict_min_cach nres
where
  mark_failed_lits NU analyse cach = SPEC(λcach'.
     (L. cach' L = SEEN_REMOVABLE  cach L = SEEN_REMOVABLE))


definition conflict_min_analysis_inv
  :: ('v, 'a) ann_lits  'v conflict_min_cach  'v clauses  'v clause  bool
where
  conflict_min_analysis_inv M cach NU D 
    (L. L  lits_of_l M  cach (atm_of L) = SEEN_REMOVABLE 
      set_mset NU ⊨p add_mset L (filter_to_poslev M L D))

lemma conflict_min_analysis_inv_alt_def:
  conflict_min_analysis_inv M cach NU D 
    (L. -L  lits_of_l M  cach (atm_of L) = SEEN_REMOVABLE 
      set_mset NU ⊨p add_mset (-L) (filter_to_poslev M L D))
  unfolding conflict_min_analysis_inv_def filter_to_poslev_def index_in_trail_def
  apply (auto dest: spec[of - _] intro: filter_mset_cong2 simp: atm_of_eq_atm_of)
  by (metis (no_types, lifting) atm_of_eq_atm_of atm_of_uminus filter_mset_cong2)

lemma conflict_min_analysis_inv_update_removable:
  no_dup M  -L  lits_of_l M 
      conflict_min_analysis_inv M (cach(atm_of L := SEEN_REMOVABLE)) NU D 
      conflict_min_analysis_inv M cach NU D  set_mset NU ⊨p add_mset (-L) (filter_to_poslev M L D)
  by (auto simp: conflict_min_analysis_inv_alt_def atm_of_eq_atm_of dest: no_dup_consistentD)


lemma conflict_min_analysis_inv_update_failed:
  conflict_min_analysis_inv M cach NU D 
   conflict_min_analysis_inv M (cach(L := SEEN_FAILED)) NU D
  by (auto simp: conflict_min_analysis_inv_def)

fun conflict_min_analysis_stack
  :: ('v, 'a) ann_lits  'v clauses  'v clause  'v conflict_min_analyse  bool
where
  conflict_min_analysis_stack M NU D []  True |
  conflict_min_analysis_stack M NU D ((L, E) # [])  -L  lits_of_l M |
  conflict_min_analysis_stack M NU D ((L, E) # (L', E') # analyse) 
     (C. set_mset NU ⊨p add_mset (-L') C 
       (K∈#C-add_mset L E'. set_mset NU ⊨p (filter_to_poslev M L' D) + {#-K#} 
           K ∈# filter_to_poslev M L' D) 
       (K∈#C. index_in_trail M K < index_in_trail M L') 
       E' ⊆# C) 
     -L'  lits_of_l M 
     -L  lits_of_l M 
     index_in_trail M L < index_in_trail M L' 
     conflict_min_analysis_stack M NU D ((L', E') # analyse)

lemma conflict_min_analysis_stack_change_hd:
  conflict_min_analysis_stack M NU D ((L, E) # ana) 
     conflict_min_analysis_stack M NU D ((L, E') # ana)
  by (cases ana, auto)

lemma conflict_min_analysis_stack_sorted:
  conflict_min_analysis_stack M NU D analyse 
    sorted (map (index_in_trail M o fst) analyse)
  by (induction rule: conflict_min_analysis_stack.induct)
    auto
lemma conflict_min_analysis_stack_sorted_and_distinct:
  conflict_min_analysis_stack M NU D analyse 
    sorted (map (index_in_trail M o fst) analyse) 
     distinct (map (index_in_trail M o fst) analyse)
  by (induction rule: conflict_min_analysis_stack.induct)
    auto

lemma conflict_min_analysis_stack_distinct_fst:
  assumes conflict_min_analysis_stack M NU D analyse
  shows distinct (map fst analyse) and  distinct (map (atm_of o fst) analyse)
proof -
  have dist: distinct (map (index_in_trail M  fst) analyse)
    using conflict_min_analysis_stack_sorted_and_distinct[of M NU D analyse, OF assms]
    by auto
  then show distinct (map fst analyse)
    by (auto simp: intro!: distinct_mapI[of (index_in_trail M)])
  show distinct (map (atm_of o fst) analyse)
  proof (rule ccontr)
    assume ¬?thesis
    from not_distinct_decomp[OF this]
    obtain xs L ys zs where map (atm_of o fst) analyse = xs @ L # ys @ L # zs
      by auto
   then show False
     using dist
     by (auto simp: map_eq_append_conv atm_of_eq_atm_of Int_Un_distrib image_Un)
  qed
qed

lemma conflict_min_analysis_stack_neg:
  conflict_min_analysis_stack M NU D analyse 
    M ⊨as CNot (fst `# mset analyse)
  by (induction M NU D analyse rule: conflict_min_analysis_stack.induct)
    auto


fun conflict_min_analysis_stack_hd
  :: ('v, 'a) ann_lits  'v clauses  'v clause  'v conflict_min_analyse  bool
where
  conflict_min_analysis_stack_hd M NU D []  True |
  conflict_min_analysis_stack_hd M NU D ((L, E) # _) 
     (C. set_mset NU ⊨p add_mset (-L) C 
     (K∈#C. index_in_trail M K < index_in_trail M L)  E ⊆# C  -L  lits_of_l M 
     (K∈#C-E. set_mset NU ⊨p (filter_to_poslev M L D) + {#-K#}  K ∈# filter_to_poslev M L D))
lemma conflict_min_analysis_stack_tl:
  conflict_min_analysis_stack M NU D analyse  conflict_min_analysis_stack M NU D (tl analyse)
  by (cases (M, NU, D, analyse) rule: conflict_min_analysis_stack.cases) auto

definition lit_redundant_inv
  :: ('v, 'v clause) ann_lits  'v clauses  'v clause  'v conflict_min_analyse 
        'v conflict_min_cach × 'v conflict_min_analyse × bool  bool where
  lit_redundant_inv M NU D init_analyse = (λ(cach, analyse, b).
           conflict_min_analysis_inv M cach NU D 
           (analyse  []  fst (hd init_analyse) = fst (last analyse)) 
           (analyse = []  b  cach (atm_of (fst (hd init_analyse))) = SEEN_REMOVABLE) 
           conflict_min_analysis_stack M NU D analyse 
           conflict_min_analysis_stack_hd M NU D analyse)

definition lit_redundant_rec_loop_inv :: ('v, 'v clause) ann_lits 
    'v conflict_min_cach × 'v conflict_min_analyse × bool  bool where
lit_redundant_rec_loop_inv M = (λ(cach, analyse, b).
    (uminus o fst) `# mset analyse ⊆# lit_of `# mset M 
    (L  set analyse. cach (atm_of (fst L)) = SEEN_UNKNOWN))

definition lit_redundant_rec :: ('v, 'v clause) ann_lits  'v clauses  'v clause 
     'v conflict_min_cach  'v conflict_min_analyse 
      ('v conflict_min_cach × 'v conflict_min_analyse × bool) nres
where
  lit_redundant_rec M NU D cach analysis =
      WHILETlit_redundant_rec_loop_inv M(λ(cach, analyse, b). analyse  [])
        (λ(cach, analyse, b). do {
            ASSERT(analyse  []);
            ASSERT(length analyse  length M);
            ASSERT(-fst (hd analyse)  lits_of_l M);
            if snd (hd analyse) = {#}
            then
              RETURN(cach (atm_of (fst (hd analyse)) := SEEN_REMOVABLE), tl analyse, True)
            else do {
              (L, analyse)  get_literal_and_remove_of_analyse analyse;
              ASSERT(-L  lits_of_l M);
              b  RES UNIV;
              if (get_level M L = 0  cach (atm_of L) = SEEN_REMOVABLE  L ∈# D)
              then RETURN (cach, analyse, False)
              else if b  cach (atm_of L) = SEEN_FAILED
              then do {
                 cach  mark_failed_lits NU analyse cach;
                 RETURN (cach, [], False)
              }
              else do {
                 ASSERT(cach (atm_of L) = SEEN_UNKNOWN);
                 C  get_propagation_reason M (-L);
                 case C of
                   Some C  do {
		     ASSERT (distinct_mset C  ¬tautology C);
		     RETURN (cach, (L, C - {#-L#}) # analyse, False)}
                 | None  do {
                     cach  mark_failed_lits NU analyse cach;
                     RETURN (cach, [], False)
                 }
            }
          }
        })
        (cach, analysis, False)

definition lit_redundant_rec_spec where
  lit_redundant_rec_spec M NU D L =
    SPEC(λ(cach, analysis, b). (b  NU ⊨pm add_mset (-L) (filter_to_poslev M L D)) 
     conflict_min_analysis_inv M cach NU D)

lemma WHILEIT_rule_stronger_inv_keepI':
  assumes
    wf R and
    I s and
    I' s and
    s. I s  I' s  b s  f s  SPEC (λs'. I' s') and
    s. I s  I' s  b s  f s  SPEC (λs'. I' s'   (I s'  (s', s)  R)) and
    s. I s  I' s  ¬ b s  Φ s
 shows WHILETIb f s  SPEC Φ
proof -
  have A[iff]: f s  SPEC (λv. I' v  I v  (v, s)  R)  f s   SPEC (λs'. I s'   I' s'  (s', s)  R) for s
    by (rule cong[of  λn. f s  n]) auto
  then have H: I s  I' s  b s  f s  SPEC (λs'. I s'   I' s'  (s', s)  R) for s
   using SPEC_rule_conjI [OF assms(4,5)[of s]] by auto
  have WHILETIb f s  WHILETλs. I s  I' sb f s
    by (metis (mono_tags, lifting) WHILEIT_weaken)

  also have WHILETλs. I s  I' sb f s  SPEC Φ
    by (rule WHILEIT_rule) (use assms H in auto simp: )
  finally show ?thesis .
qed

lemma lit_redundant_rec_spec:
  fixes L :: 'v literal
  assumes invs: cdclW_restart_mset.cdclW_all_struct_inv (M, N + NE, U + UE, D')
  assumes
    init_analysis: init_analysis = [(L, C)] and
    in_trail: Propagated (-L) (add_mset (-L) C)  set M and
    conflict_min_analysis_inv M cach (N + NE + U + UE) D and
    L_D: L ∈# D and
    M_D: M ⊨as CNot D and
    unknown: cach (atm_of L) = SEEN_UNKNOWN
  shows
    lit_redundant_rec M (N + U) D cach init_analysis 
      lit_redundant_rec_spec M (N + U + NE + UE) D L
proof -
  let ?N = N + NE + U + UE
  obtain M2 M1 where
    M: M = M2 @ Propagated (- L) (add_mset (- L) C) # M1
    using split_list[OF in_trail] by (auto 5 5)
  have a @ Propagated L mark # b = trail (M, N + NE, U + UE, D') 
       b ⊨as CNot (remove1_mset L mark)  L ∈# mark for L mark a b
    using invs
    unfolding cdclW_restart_mset.cdclW_all_struct_inv_def
       cdclW_restart_mset.cdclW_conflicting_def
    by fast
  then have M1 ⊨as CNot C
    by (force simp: M)
  then have M_C: M ⊨as CNot C
    unfolding M by (simp add: true_annots_append_l)
  have set (get_all_mark_of_propagated (trail (M, N + NE, U + UE, D')))
     set_mset (cdclW_restart_mset.clauses (M, N + NE, U + UE, D'))
    using invs
    unfolding cdclW_restart_mset.cdclW_all_struct_inv_def
       cdclW_restart_mset.cdclW_learned_clause_alt_def
    by fast
  then have add_mset (-L) C ∈# ?N
    using in_trail cdclW_restart_mset.in_get_all_mark_of_propagated_in_trail[of add_mset (-L) C M]
    by (auto simp: clauses_def)
  then have NU_C: ?N ⊨pm add_mset (- L) C
    by auto
  have n_d: no_dup M
    using invs
    unfolding cdclW_restart_mset.cdclW_all_struct_inv_def
       cdclW_restart_mset.cdclW_M_level_inv_def
    by auto

  let ?f = λanalysis. fold_mset (+) D (snd `# mset analysis)
  define I' where
    I' = (λ(cach :: 'v conflict_min_cach, analysis :: 'v conflict_min_analyse, b::bool).
        lit_redundant_inv M ?N D init_analysis (cach, analysis, b)  M ⊨as CNot (?f analysis) 
        distinct (map (atm_of o fst) analysis))
  define R where
    R = {((cach :: 'v conflict_min_cach, analysis :: 'v conflict_min_analyse, b::bool),
           (cach' :: 'v conflict_min_cach, analysis' :: 'v conflict_min_analyse, b' :: bool)).
           (analysis'  []  (minimize_conflict_support M) (?f analysis') (?f analysis)) 
           (analysis'  []  analysis = tl analysis'  snd (hd analysis') = {#}) 
           (analysis'  []  analysis = [])}
  have wf_R: wf R
  proof -
    have R: R =
              {((cach, analysis, b), (cach', analysis', b')).
                 analysis'  [] analysis = []} 
              ({((cach, analysis, b), (cach', analysis', b')).
                  analysis'  []  (minimize_conflict_support M) (?f analysis') (?f analysis)} 
              {((cach, analysis, b), (cach', analysis', b')).
                  analysis'  []  analysis = tl analysis'  snd (hd analysis') = {#}})
      (is _ = ?end  (?Min  ?ana))
      unfolding R_def by auto
    have 1: wf {((cach:: 'v conflict_min_cach, analysis:: 'v conflict_min_analyse, b::bool),
         (cach':: 'v conflict_min_cach, analysis':: 'v conflict_min_analyse, b'::bool)).
       length analysis < length analysis'}
      using wf_if_measure_f[of measure length, of λ(_, xs, _). xs] apply auto
      apply (rule subst[of _ _ wf])
       prefer 2 apply assumption
      apply auto
      done

    have 2: wf {(C', C).minimize_conflict_support M C C'}
      by (rule wf_minimize_conflict_support[OF invs])
    from wf_if_measure_f[OF this, of ?f]
    have 2: wf {(C', C). minimize_conflict_support M (?f C) (?f C')}
      by auto
    from wf_fst_wf_pair[OF this, where 'b = bool]
    have wf {((analysis':: 'v conflict_min_analyse, _ :: bool),
               (analysis:: 'v conflict_min_analyse,  _:: bool)).
            (minimize_conflict_support M) (?f analysis) (?f analysis')}
      by blast
    from wf_snd_wf_pair[OF this, where 'b = 'v conflict_min_cach]
    have wf {((M' :: 'v conflict_min_cach, N'), Ma, N).
      (case N' of
       (analysis' :: 'v conflict_min_analyse, _ :: bool) 
         λ(analysis, _).
            minimize_conflict_support M (fold_mset (+) D (snd `# mset analysis))
              (fold_mset (+) D (snd `# mset analysis'))) N}
      by blast
    then have wf_Min: wf ?Min
      apply (rule wf_subset)
      by auto
    have wf_ana: wf ?ana
      by (rule wf_subset[OF 1])  auto
    have wf: wf (?Min  ?ana)
      apply (rule wf_union_compatible)
      subgoal by (rule wf_Min)
      subgoal by (rule wf_ana)
      subgoal by (auto elim!: neq_NilE)
      done
    have wf_end: wf ?end
    proof (rule ccontr)
      assume ¬ ?thesis
      then obtain f where f: (f (Suc i), f i)  ?end for i
        unfolding wf_iff_no_infinite_down_chain by auto
      have fst (snd (f (Suc 0))) = []
        using f[of 0] by auto
      moreover have fst (snd (f (Suc 0)))  []
        using f[of 1] by auto
      ultimately show False by blast
    qed
    show ?thesis
      unfolding R
      apply (rule wf_Un)
      subgoal by (rule wf_end)
      subgoal by (rule wf)
      subgoal by auto
      done
  qed
  have uL_M: - L  lits_of_l M
    using in_trail by (force simp: lits_of_def)
  then have init_I: lit_redundant_inv M ?N D init_analysis (cach, init_analysis, False)
    using assms NU_C Propagated_in_trail_entailed[OF invs in_trail]
    unfolding lit_redundant_inv_def
    by (auto simp: ac_simps)

  have (minimize_conflict_support M) D (remove1_mset L (C + D))
    using minimize_conflict_support.resolve_propa[OF in_trail, of remove1_mset L D] L_D
    by (auto simp: ac_simps)

  then have init_I': I' (cach, init_analysis, False)
    using M_D L_D M_C init_I unfolding I'_def by (auto simp: init_analysis)

  have hd_M: - fst (hd analyse)  lits_of_l M
    if
      inv_I': I' s and
      s: s = (cach, s') s' = (analyse, ba) and
      nempty: analyse  []
    for analyse s s' ba cach
  proof -
    have
      cach: conflict_min_analysis_inv M cach ?N D and
      ana: conflict_min_analysis_stack M ?N D analyse and
      stack: conflict_min_analysis_stack M ?N D analyse and
      stack_hd: conflict_min_analysis_stack_hd M ?N D analyse and
      last_analysis: analyse  []  fst (last analyse) = fst (hd init_analysis) and
      b: analyse = []  ba  cach (atm_of (fst (hd init_analysis))) = SEEN_REMOVABLE
      using inv_I' unfolding lit_redundant_inv_def s I'_def by auto
    show ?thesis
      using stack_hd nempty by (cases analyse) auto
  qed

  have all_removed: lit_redundant_inv M ?N D init_analysis
       (cach(atm_of (fst (hd analysis)) := SEEN_REMOVABLE), tl analysis, True) (is ?I) and
     all_removed_I': I' (cach(atm_of (fst (hd analysis)) := SEEN_REMOVABLE), tl analysis, True)
       (is ?I') and
     all_removed_J: lit_redundant_rec_loop_inv M (cach(atm_of (fst (hd analysis)) := SEEN_REMOVABLE), tl analysis, True) (is ?J)
    if
      inv_I': I' s and inv_J: lit_redundant_rec_loop_inv M s
      case s of (cach, analyse, b)  analyse  [] and
      s: s = (cach, s')
         s' = (analysis, b) and
      nemtpy_stack: analysis  [] and
      finished: snd (hd analysis) = {#}
    for s cach s' analysis b
  proof -
    obtain L ana' where analysis: analysis = (L, {#}) # ana'
      using nemtpy_stack finished by (cases analysis)  auto
    have
      cach: conflict_min_analysis_inv M cach ?N D and
      ana: conflict_min_analysis_stack M ?N D analysis and
      stack: conflict_min_analysis_stack M ?N D analysis and
      stack_hd: conflict_min_analysis_stack_hd M ?N D analysis and
      last_analysis: analysis  []  fst (last analysis) = fst (hd init_analysis) and
      b: analysis = []  b  cach (atm_of (fst (hd init_analysis))) = SEEN_REMOVABLE and
      dist: distinct (map (atm_of o fst) analysis)
      using inv_I' unfolding lit_redundant_inv_def s I'_def by auto
    obtain C where
       NU_C: ?N ⊨pm add_mset (-L) C and
       IH: K. K ∈# C  ?N ⊨pm add_mset (-K) (filter_to_poslev M L D) 
         K ∈# filter_to_poslev M L D and
       index_K: K∈#C  index_in_trail M K < index_in_trail M L and
       L_M: -L  lits_of_l M for K
      using stack_hd unfolding analysis by auto

    have NU_D: ?N ⊨pm add_mset (- fst (hd analysis)) (filter_to_poslev M (fst (hd analysis)) D)
      using conflict_minimize_intermediate_step_filter_to_poslev[OF _ NU_C, simplified, OF index_K]
        IH
      unfolding analysis by auto
    have ana': conflict_min_analysis_stack M ?N D (tl analysis)
      using ana by (auto simp: conflict_min_analysis_stack_tl)
    have -fst (hd analysis)  lits_of_l M
      using L_M by (auto simp: analysis I'_def s ana)
    then have cach':
      conflict_min_analysis_inv M (cach(atm_of (fst (hd analysis)) := SEEN_REMOVABLE)) ?N D
      using NU_D n_d by (auto simp: conflict_min_analysis_inv_update_removable cach)
    have stack_hd': conflict_min_analysis_stack_hd M ?N D ana'
    proof (cases ana' = [])
      case True
      then show ?thesis by auto
    next
      case False
      then obtain L' C' ana'' where ana'': ana' = (L', C') # ana''
        by (cases ana'; cases hd ana') auto
      then obtain E' where
         NU_E': ?N ⊨pm add_mset (- L') E' and
         K∈#E' - add_mset L C'. ?N ⊨pm add_mset (- K) (filter_to_poslev M L' D) 
           K ∈# filter_to_poslev M L' D and
         index_C': K∈#E'. index_in_trail M K < index_in_trail M L' and
         index_L'_L: index_in_trail M L < index_in_trail M L' and
         C'_E': C' ⊆# E' and
         uL'_M: - L'  lits_of_l M
         using stack by (auto simp: analysis ana'')
       moreover have ?N ⊨pm add_mset (- L) (filter_to_poslev M L D)
         using NU_D analysis by auto
       moreover have K ∈# E' - C'  K ∈# E' - add_mset L C'  K = L for K
         by (cases L ∈# E')
           (fastforce simp: minus_notin_trivial dest!: multi_member_split[of L]
             dest: in_remove1_msetI)+
       moreover have K ∈# E' - C'  index_in_trail M K  index_in_trail M L' for K
         by (meson in_diffD index_C' less_or_eq_imp_le)
       ultimately have K ∈# E' - C'  ?N ⊨pm add_mset (- K) (filter_to_poslev M L' D) 
             K ∈# filter_to_poslev M L' D for K
         using filter_to_poslev_mono_entailement_add_mset[of M K L']
           filter_to_poslev_mono[of M L L']
         by fastforce
       then show ?thesis
         using NU_E' uL'_M index_C' C'_E' unfolding ana'' by (auto intro!: exI[of _ E'])
    qed

    have fst (hd init_analysis) = fst (last (tl analysis)) if tl analysis  []
      using last_analysis tl_last[symmetric, OF that] that unfolding ana' by auto
    then show ?I
      using ana' cach' last_analysis stack_hd' dist unfolding lit_redundant_inv_def
      by (cases ana'; auto simp: analysis atm_of_eq_atm_of split: if_splits)
    then show I': ?I'
      using inv_I' unfolding I'_def s by (auto simp: analysis)
    have distinct (map (λx. - fst x) (tl analysis))
      using dist distinct_mapI[of atm_of o uminus map (uminus o fst) (tl analysis)]
       conflict_min_analysis_stack_neg[OF ana'] by (auto simp: comp_def map_tl
       simp flip: distinct_mset_image_mset)
    then show ?J
      using inv_J unfolding lit_redundant_rec_loop_inv_def prod.case s
      apply (subst distinct_subseteq_iff[symmetric])
      using conflict_min_analysis_stack_neg[OF ana']  no_dup_distinct[OF n_d] dist
      by (auto simp: comp_def entails_CNot_negate_ann_lits negate_ann_lits_def
        analysis ana' rev_image_eqI
       simp flip: distinct_mset_image_mset)
  qed
  have all_removed_R:
      ((cach(atm_of (fst (hd analyse)) := SEEN_REMOVABLE), tl analyse, True), s)  R
    if
      s: s = (cach, s') s' = (analyse, b) and
      nempty: analyse  [] and
      finished: snd (hd analyse) = {#}
    for s cach s' analyse b
    using nempty finished unfolding R_def s by auto
  have
    seen_removable_inv: lit_redundant_inv M ?N D init_analysis (cach, ana, False) (is ?I) and
    seen_removable_I': I' (cach, ana, False) (is ?I') and
    seen_removable_R: ((cach, ana, False), s)  R (is ?R) and
    seen_removable_J: lit_redundant_rec_loop_inv M (cach, ana, False) (is ?J)
    if
      inv_I': I' s and inv_J: lit_redundant_rec_loop_inv M s and
      cond: case s of (cach, analyse, b)  analyse  [] and
      s: s = (cach, s') s' = (analyse, b) x = (L, ana) and
      nemtpy_stack: analyse  [] and
      snd (hd analyse)  {#} and
      next_lit: case x of
        (L, ana)  L ∈# snd (hd analyse)  tl ana = tl analyse  ana  [] 
          hd ana = (fst (hd analyse), remove1_mset L (snd (hd analyse))) and
      lev0_removable: get_level M L = 0  cach (atm_of L) = SEEN_REMOVABLE  L ∈# D
    for s cach s' analyse b x L ana
  proof -
    obtain K C ana' where analysis: analyse = (K, C) # ana'
      using nemtpy_stack by (cases analyse) auto
    have ana': ana = (K, remove1_mset L C) # ana' and L_C: L ∈# C
      using next_lit unfolding s by (cases ana; auto simp: analysis)+
    have
      cach: conflict_min_analysis_inv M cach (?N) D and
      ana: conflict_min_analysis_stack M ?N D analyse and
      stack: conflict_min_analysis_stack M ?N D analyse and
      stack_hd: conflict_min_analysis_stack_hd M ?N D analyse and
      last_analysis: analyse  []  fst (last analyse) = fst (hd init_analysis) and
      b: analyse = []  b  cach (atm_of (fst (hd init_analysis))) = SEEN_REMOVABLE and
      dist: distinct (map (atm_of  fst) analyse)
      using inv_I' unfolding lit_redundant_inv_def s I'_def prod.case by auto

    have last_analysis': ana  []  fst (hd init_analysis) = fst (last ana)
      using last_analysis next_lit unfolding analysis s
      by (cases ana) (auto split: if_splits)
    have uL_M: -L  lits_of_l M
      using inv_I' L_C unfolding analysis ana s I'_def
      by (auto dest!: multi_member_split)
    have uK_M: - K  lits_of_l M
      using stack_hd unfolding analysis by auto
    consider
      (lev0) get_level M L = 0 |
      (Removable) cach (atm_of L) = SEEN_REMOVABLE |
      (in_D) L ∈# D
      using lev0_removable by fast
    then have H: CK. ?N ⊨pm add_mset (- K) CK 
           (Ka∈#CK - remove1_mset L C. ?N ⊨pm  (filter_to_poslev M K D) + {#- Ka#} 
             Ka ∈# filter_to_poslev M K D) 
           (Ka∈#CK. index_in_trail M Ka < index_in_trail M K) 
           remove1_mset L C ⊆# CK
      (is C. ?P C)
    proof cases
      case Removable
      then have L: ?N ⊨pm add_mset (- L) (filter_to_poslev M L D)
        using cach uL_M unfolding conflict_min_analysis_inv_def by auto
      obtain CK where
        ?N ⊨pm add_mset (- K) CK and
        K'∈#CK - C. ?N ⊨pm (filter_to_poslev M K D) + {#- K'#}  K' ∈# filter_to_poslev M K D and
        index_CK: Ka∈#CK. index_in_trail M Ka < index_in_trail M K and
        C_CK: C ⊆# CK
        using stack_hd unfolding analysis by auto
      moreover have remove1_mset L C ⊆# CK
        using C_CK by (meson diff_subset_eq_self subset_mset.dual_order.trans)
      moreover have index_in_trail M L < index_in_trail M K
        using index_CK C_CK L_C unfolding analysis ana' by auto
      moreover have index_CK': Ka∈#CK. index_in_trail M Ka  index_in_trail M K
        using index_CK by auto
      ultimately have ?P CK
         using filter_to_poslev_mono_entailement_add_mset[of M _ _]
           filter_to_poslev_mono[of M K L]
         using L L_C C_CK by (auto simp: minus_remove1_mset_if)
      then show ?thesis by blast
    next
      assume lev0: get_level M L = 0
      have M ⊨as CNot (?f analyse)
        using inv_I' unfolding I'_def s by auto
      then have -L  lits_of_l M
        using next_lit unfolding analysis s by (auto dest: multi_member_split)
      then have ?N ⊨pm {#-L#}
        using lev0 cdclW_restart_mset.literals_of_level0_entailed[OF invs, of -L]
        by (auto simp: clauses_def ac_simps)
      moreover obtain CK where
        ?N ⊨pm add_mset (- K) CK and
        K'∈#CK - C. ?N ⊨pm (filter_to_poslev M K D) + {#- K'#}  K' ∈# filter_to_poslev M K D and
        Ka∈#CK. index_in_trail M Ka < index_in_trail M K and
        C_CK: C ⊆# CK
        using stack_hd unfolding analysis by auto
      moreover have remove1_mset L C ⊆# CK
        using C_CK by (meson diff_subset_eq_self subset_mset.order_trans)
      ultimately have ?P CK
        by (auto simp: minus_remove1_mset_if intro: conflict_minimize_intermediate_step)
      then show ?thesis by blast
    next
      case in_D
      obtain CK where
        ?N ⊨pm add_mset (- K) CK and
        Ka∈#CK - C. ?N ⊨pm (filter_to_poslev M K D) + {#- Ka#}  Ka ∈# filter_to_poslev M K D and
        index_CK: Ka∈#CK. index_in_trail M Ka < index_in_trail M K and
        C_CK: C ⊆# CK
        using stack_hd unfolding analysis by auto
      moreover have remove1_mset L C ⊆# CK
        using C_CK by (meson diff_subset_eq_self subset_mset.order_trans)
      moreover have L ∈# filter_to_poslev M K D
        using in_D L_C index_CK C_CK by (fastforce simp: filter_to_poslev_def)

      ultimately have ?P CK
        using in_D
         using filter_to_poslev_mono_entailement_add_mset[of M L K]
         by (auto simp: minus_remove1_mset_if dest!:
             intro: conflict_minimize_intermediate_step)
      then show ?thesis by blast
    qed note H = this
    have stack': conflict_min_analysis_stack M ?N D ana
      using stack unfolding ana' analysis by (cases ana') auto
    have stack_hd': conflict_min_analysis_stack_hd M ?N D ana
      using H uL_M uK_M unfolding ana' by auto

    show ?I
      using last_analysis' cach stack' stack_hd' unfolding lit_redundant_inv_def s
      by auto
    have M ⊨as CNot (?f ana)
      using inv_I' unfolding I'_def s ana analysis ana'
      by (cases L ∈# C) (auto dest!: multi_member_split)
    then show ?I'
      using inv_I' ?I unfolding I'_def s by (auto simp: analysis ana')

    show ?R
      using next_lit
      unfolding R_def s by (auto simp: ana' analysis dest!: multi_member_split
          intro: minimize_conflict_support.intros)
    have distinct (map (λx. - fst x) ana)
      using dist distinct_mapI[of atm_of o uminus map (uminus o fst) (tl analyse)]
       conflict_min_analysis_stack_neg[OF stack'] by (auto simp: comp_def map_tl
          analysis ana'
       simp flip: distinct_mset_image_mset)
    then show ?J
      using inv_J unfolding lit_redundant_rec_loop_inv_def prod.case s
      apply (subst distinct_subseteq_iff[symmetric])
      using conflict_min_analysis_stack_neg[OF stack'] no_dup_distinct[OF n_d]
      apply (auto simp: comp_def entails_CNot_negate_ann_lits negate_ann_lits_def
       simp flip: distinct_mset_image_mset)
     apply (force simp add: analysis ana ana')
     done

  qed
  have
    failed_I: lit_redundant_inv M ?N D init_analysis
       (cach', [], False) (is ?I) and
    failed_I': I' (cach', [], False) (is ?I') and
    failed_R: ((cach', [], False), s)  R (is ?R) and
    failed_J: lit_redundant_rec_loop_inv M (cach', [], False) (is ?J)
    if
      inv_I': I' s and inv_J: lit_redundant_rec_loop_inv M s and
      cond: case s of (cach, analyse, b)  analyse  [] and
      s: s = (cach, s') s' = (analyse, b) and
      nempty: analyse  [] and
      snd (hd analyse)  {#} and
      case x of (L, ana)  L ∈# snd (hd analyse)  tl ana = tl analyse 
        ana  []  hd ana = (fst (hd analyse), remove1_mset L (snd (hd analyse))) and
      x = (L, ana) and
      ¬ (get_level M L = 0  cach (atm_of L) = SEEN_REMOVABLE  L ∈# D) and
      cach_update: L. cach' L = SEEN_REMOVABLE  cach L = SEEN_REMOVABLE
    for s cach s' analyse b x L ana E cach'
  proof -
    have
      cach: conflict_min_analysis_inv M cach ?N D and
      ana: conflict_min_analysis_stack M ?N D analyse and
      stack: conflict_min_analysis_stack M ?N D analyse and
      last_analysis: analyse  []  fst (last analyse) = fst (hd init_analysis) and
      b: analyse = []  b  cach (atm_of (fst (hd init_analysis))) = SEEN_REMOVABLE
      using inv_I' unfolding lit_redundant_inv_def s I'_def by auto
    have conflict_min_analysis_inv M cach' ?N D
      using cach cach_update by (auto simp: conflict_min_analysis_inv_def)
    moreover have conflict_min_analysis_stack M ?N D []
      by simp
    ultimately show ?I
      unfolding lit_redundant_inv_def by simp
    then show ?I'
      using M_D unfolding I'_def by auto
    show ?R
      using nempty unfolding R_def s by auto
    show ?J
      by (auto simp: lit_redundant_rec_loop_inv_def)
  qed
  have is_propagation_inv: lit_redundant_inv M ?N D init_analysis
       (cach, (L, remove1_mset (-L) E') # ana, False) (is ?I) and
    is_propagation_I': I' (cach, (L, remove1_mset (-L) E') # ana, False) (is ?I') and
    is_propagation_R: ((cach, (L, remove1_mset (-L) E') # ana, False), s)  R (is ?R) and
    is_propagation_dist: distinct_mset E' (is ?dist) and
    is_propagation_tauto: ¬tautology E' (is ?tauto) and
    is_propagation_J': lit_redundant_rec_loop_inv M (cach, (L, remove1_mset (-L) E') # ana, False) (is ?J)
    if
      inv_I': I' s and inv_J: lit_redundant_rec_loop_inv M s and
      case s of (cach, analyse, b)  analyse  [] and
      s: s = (cach, s') s' = (analyse, b) x = (L, ana) and
      nemtpy_stack: analyse  [] and
      snd (hd analyse)  {#} and
      next_lit: case x of (L, ana) 
       L ∈# snd (hd analyse) 
       tl ana = tl analyse 
       ana  [] 
       hd ana =
       (fst (hd analyse),
        remove1_mset L (snd (hd analyse))) and
      ¬ (get_level M L = 0  cach (atm_of L) = SEEN_REMOVABLE  L ∈# D) and
      E: E  None  Propagated (- L) (the E)  set M E = Some E' and
      st: cach (atm_of L) = SEEN_UNKNOWN
    for s cach s' analyse b x L ana E E'
  proof -
    obtain K C ana' where analysis: analyse = (K, C) # ana'
      using nemtpy_stack by (cases analyse) auto
    have ana': ana = (K, remove1_mset L C) # ana'
      using next_lit unfolding s by (cases ana) (auto simp: analysis)
    have
      cach: conflict_min_analysis_inv M cach ?N D and
      ana: conflict_min_analysis_stack M ?N D analyse and
      stack: conflict_min_analysis_stack M ?N D analyse and
      stack_hd: conflict_min_analysis_stack_hd M ?N D analyse and
      last_analysis: analyse  []  fst (last analyse) = fst (hd init_analysis) and
      b: analyse = []  b  cach (atm_of (fst (hd init_analysis))) = SEEN_REMOVABLE and
      dist_ana: distinct (map (atm_of  fst) analyse)
      using inv_I' unfolding lit_redundant_inv_def s I'_def by auto
    have
      NU_E: ?N ⊨pm add_mset (- L) (remove1_mset (-L) E') and
      uL_E: -L ∈# E' and
      M_E': M ⊨as CNot (remove1_mset (- L) E') and
      tauto: ¬ tautology E' and
      dist: distinct_mset E' and
      lev_E': K ∈# remove1_mset (- L) E'  index_in_trail M K < index_in_trail M (- L) for K
      using Propagated_in_trail_entailed[OF invs, of -L E'] E by (auto simp: ac_simps)
    have uL_M: - L  lits_of_l M
      using next_lit inv_I' unfolding s analysis I'_def by (auto dest!: multi_member_split)
    obtain C' where
      ?N ⊨pm add_mset (- K) C' and
      Ka∈#C'. index_in_trail M Ka < index_in_trail M K and
      C ⊆# C' and
      Ka∈#C' - C.
        ?N ⊨pm add_mset (- Ka) (filter_to_poslev M K D) 
        Ka ∈# filter_to_poslev M K D and
      uK_M: - K  lits_of_l M
      using stack_hd
      unfolding s ana'[symmetric]
      by (auto simp: analysis ana' conflict_min_analysis_stack_change_hd)

    then have cmas: conflict_min_analysis_stack M ?N D ((L, remove1_mset (-L) E') # ana)
      using stack E next_lit NU_E uL_E uL_M
        filter_to_poslev_mono_entailement_add_mset[of M _ _ set_mset ?N _ D]
        filter_to_poslev_mono[of M ] uK_M
      unfolding s ana'[symmetric] prod.case
      by (auto simp: analysis ana' conflict_min_analysis_stack_change_hd)
    moreover have conflict_min_analysis_stack_hd M ?N D ((L, remove1_mset (- L) E') # ana)
      using NU_E lev_E' uL_M by (auto intro!:exI[of _ remove1_mset (- L) E'])
    moreover have fst (hd init_analysis) = fst (last ((L, remove1_mset (- L) E') # ana))
      using last_analysis unfolding analysis ana' by auto
    ultimately show ?I
      using cach b unfolding lit_redundant_inv_def analysis by auto
    moreover have L  K
       using cmas
       unfolding ana' conflict_min_analysis_stack.simps(3) by blast
    moreover have L  -K
       using cmas
       unfolding ana' conflict_min_analysis_stack.simps(3) by auto
    ultimately show ?I'
      using M_E' inv_I' conflict_min_analysis_stack_distinct_fst[OF cmas]
      unfolding I'_def s ana analysis ana'
      by (auto simp: true_annot_CNot_diff atm_of_eq_atm_of uminus_lit_swap)

    have L ∈# C and in_trail: Propagated (- L) (the E)  set M and EE': the E = E'
      using next_lit E by (auto simp: analysis ana' s)
    then obtain E'' C' where
      E': E' = add_mset (-L) E'' and
      C: C = add_mset L C'
      using uL_E by (blast dest: multi_member_split)

    have minimize_conflict_support M (C + fold_mset (+) D (snd `# mset ana'))
           (remove1_mset (- L) E' + (remove1_mset L C + fold_mset (+) D (snd `# mset ana')))
      using minimize_conflict_support.resolve_propa[OF in_trail,
        of C' + fold_mset (+) D (snd `# mset ana')]
      unfolding C E' EE'
      by (auto simp: ac_simps)

    then show ?R
      using nemtpy_stack unfolding s analysis ana' by (auto simp: R_def
          intro: resolve_propa)
    have distinct (map (λx. - fst x) analyse)
      using dist_ana distinct_mapI[of atm_of o uminus map (uminus o fst) analyse]
       conflict_min_analysis_stack_neg[OF cmas] unfolding analysis ana'
       by (auto simp: comp_def map_tl
          simp flip: distinct_mset_image_mset)
    then show ?J
      using inv_J st unfolding lit_redundant_rec_loop_inv_def prod.case s
      apply (intro conjI)
      apply (subst distinct_subseteq_iff[symmetric])
      using conflict_min_analysis_stack_neg[OF cmas] no_dup_distinct[OF n_d] uL_M
        L  -K L  K  conflict_min_analysis_stack_distinct_fst[OF cmas]
      apply (auto simp: comp_def entails_CNot_negate_ann_lits
        negate_ann_lits_def lits_of_def uminus_lit_swap
       simp flip: distinct_mset_image_mset)[2]
    using that by (clarsimp_all simp add: analysis ana')

    show ?tauto
      using tauto .
    show ?dist
      using dist .
  qed
  have length_aa_le: length aa  length M
    if
      I' s and
      case s of (cach, analyse, b)  analyse  [] and
      s = (a, b) and
      b = (aa, ba) and
      aa  [] for s a b aa ba
  proof -
    have M ⊨as CNot (fst `# mset aa) and distinct (map (atm_of o fst) aa) and
       distinct (map fst aa) and
      conflict_min_analysis_stack M (N + NE + U + UE) D aa
      using distinct_mapI[of atm_of map fst aa]
      using that by (auto simp: I'_def lit_redundant_inv_def
        dest: conflict_min_analysis_stack_neg)

    then have set (map fst aa)  uminus ` lits_of_l M
      by (auto simp: true_annots_true_cls_def_iff_negation_in_model lits_of_def image_image
          uminus_lit_swap
         dest!: multi_member_split)
    from card_mono[OF _ this] have length (map fst aa)  length M
      using distinct (map (fst) aa) distinct_card[of map fst aa] n_d
     by (auto simp: card_image[OF lit_of_inj_on_no_dup[OF n_d]] lits_of_def image_image
        distinct_card[OF no_dup_imp_distinct])
    then show ?thesis by auto
  qed

  show ?thesis
    unfolding lit_redundant_rec_def lit_redundant_rec_spec_def mark_failed_lits_def
      get_literal_and_remove_of_analyse_def get_propagation_reason_def
    apply (refine_vcg WHILEIT_rule_stronger_inv[where R = R and I' = I'])
      ― ‹Well-foundness
    subgoal by (rule wf_R)
    subgoal using assms by (auto simp: lit_redundant_rec_loop_inv_def lits_of_def
       dest!: multi_member_split)
    subgoal by (rule init_I')
    subgoal by auto
    subgoal by (rule length_aa_le)
      ― ‹Assertion:
    subgoal by (rule hd_M)
        ― ‹We finished one stage:
    subgoal by (rule all_removed_J)
    subgoal by (rule all_removed_I')
    subgoal by (rule all_removed_R)
      ― ‹Assertion:
    subgoal for s cach s' analyse ba
      by (cases analyse) (auto simp: I'_def dest!: multi_member_split)

        ― ‹Cached or level 0:
    subgoal by (rule seen_removable_J)
    subgoal by (rule seen_removable_I')
    subgoal by (rule seen_removable_R)
        ― ‹Failed:
    subgoal by (rule failed_J)
    subgoal by (rule failed_I')
    subgoal by (rule failed_R)
    subgoal for s a b aa ba x ab bb xa by (cases a (atm_of ab)) auto
    subgoal by (rule failed_J)
    subgoal by (rule failed_I')
    subgoal by (rule failed_R)
        ― ‹The literal was propagated:
    subgoal by (rule is_propagation_dist)
    subgoal by (rule is_propagation_tauto)
    subgoal by (rule is_propagation_J')
    subgoal by (rule is_propagation_I')
    subgoal by (rule is_propagation_R)
        ― ‹End of Loop invariant:
    subgoal
      using uL_M by (auto simp: lit_redundant_inv_def conflict_min_analysis_inv_def init_analysis
          I'_def ac_simps)
    subgoal by (auto simp: lit_redundant_inv_def conflict_min_analysis_inv_def init_analysis
          I'_def ac_simps)
    done
qed

definition literal_redundant_spec where
  literal_redundant_spec M NU D L =
    SPEC(λ(cach, analysis, b). (b  NU ⊨pm add_mset (-L) (filter_to_poslev M L D)) 
     conflict_min_analysis_inv M cach NU D)

definition literal_redundant where
  literal_redundant M NU D cach L = do {
     ASSERT(-L  lits_of_l M);
     if get_level M L = 0  cach (atm_of L) = SEEN_REMOVABLE
     then RETURN (cach, [], True)
     else if cach (atm_of L) = SEEN_FAILED
     then RETURN (cach, [], False)
     else do {
       C  get_propagation_reason M (-L);
       case C of
         Some C  do{
	   ASSERT(distinct_mset C  ¬tautology C);
	   lit_redundant_rec M NU D cach [(L, C - {#-L#})]}
       | None  do {
           RETURN (cach, [], False)
     }
    }
  }

lemma true_clss_cls_add_self: NU ⊨p D' + D'  NU ⊨p D'
  by (metis subset_mset.sup_idem true_clss_cls_sup_iff_add)

lemma true_clss_cls_add_add_mset_self: NU ⊨p add_mset L (D' + D')  NU ⊨p add_mset L D'
  using true_clss_cls_add_self true_clss_cls_mono_r by fastforce


lemma filter_to_poslev_remove1:
  filter_to_poslev M L (remove1_mset K D) =
      (if index_in_trail M K  index_in_trail M L then remove1_mset K (filter_to_poslev M L D)
   else filter_to_poslev M L D)
  unfolding filter_to_poslev_def
  by (auto simp: multiset_filter_mono2)


lemma filter_to_poslev_add_mset:
  filter_to_poslev M L (add_mset K D) =
      (if index_in_trail M K < index_in_trail M L then add_mset K (filter_to_poslev M L D)
   else filter_to_poslev M L D)
  unfolding filter_to_poslev_def
  by (auto simp: multiset_filter_mono2)

lemma filter_to_poslev_conflict_min_analysis_inv:
  assumes
    L_D: L ∈# D and
    NU_uLD: N+U ⊨pm add_mset (-L) (filter_to_poslev M L D) and
    inv: conflict_min_analysis_inv M cach (N + U) D
  shows conflict_min_analysis_inv M cach (N + U) (remove1_mset L D)
  unfolding conflict_min_analysis_inv_alt_def
proof (intro allI impI)
  fix K
  assume -K  lits_of_l M and cach (atm_of K) = SEEN_REMOVABLE
  then have K: N + U ⊨pm add_mset (- K) (filter_to_poslev M K D)
    using inv unfolding conflict_min_analysis_inv_alt_def by blast
  obtain D' where D: D = add_mset L D'
    using multi_member_split[OF L_D] by blast
  have N + U ⊨pm add_mset (- K) (filter_to_poslev M K D')
  proof (cases index_in_trail M L < index_in_trail M K)
    case True
    then have N + U ⊨pm add_mset (- K) (add_mset L (filter_to_poslev M K D'))
      using K by (auto simp: filter_to_poslev_add_mset D)
    then have 1: N + U ⊨pm add_mset L (add_mset (-K) (filter_to_poslev M K D'))
      by (simp add: add_mset_commute)
    have H: index_in_trail M L  index_in_trail M K
      using True by simp
    have 2: N+U ⊨pm add_mset (-L) (filter_to_poslev M K D')
      using filter_to_poslev_mono_entailement_add_mset[OF H] NU_uLD
      by (metis (no_types, opaque_lifting) D NU_uLD filter_to_poslev_add_mset
          order_less_irrefl)
    show ?thesis
      using true_clss_cls_or_true_clss_cls_or_not_true_clss_cls_or[OF 2 1]
      by (auto simp: true_clss_cls_add_add_mset_self)
  next
    case False
    then show ?thesis using K by (auto simp: filter_to_poslev_add_mset D split: if_splits)
  qed
  then show N + U ⊨pm add_mset (- K) (filter_to_poslev M K (remove1_mset L D))
    by (simp add: D)
qed

lemma can_filter_to_poslev_can_remove:
  assumes
    L_D: L ∈# D and
    M ⊨as CNot D and
    NU_D: NU ⊨pm D and
    NU_uLD: NU ⊨pm add_mset (-L) (filter_to_poslev M L D)
  shows NU ⊨pm remove1_mset L D
proof -
  obtain D' where
    D: D = add_mset L D'
    using multi_member_split[OF L_D] by blast
  then have filter_to_poslev M L D ⊆# D'
    by (auto simp: filter_to_poslev_def)
  then have NU ⊨pm add_mset (-L) D'
    using NU_uLD true_clss_cls_mono_r[of _  add_mset (- L) (filter_to_poslev M (-L) D) ]
    by (auto simp: mset_subset_eq_exists_conv)
  from true_clss_cls_or_true_clss_cls_or_not_true_clss_cls_or[OF this, of D']
  show NU ⊨pm remove1_mset L D
    using NU_D by (auto simp: D true_clss_cls_add_self)
qed

lemma literal_redundant_spec:
  fixes L :: 'v literal
  assumes invs: cdclW_restart_mset.cdclW_all_struct_inv (M, N + NE, U + UE, D')
  assumes
    inv: conflict_min_analysis_inv M cach (N + NE + U + UE) D and
    L_D: L ∈# D and
    M_D: M ⊨as CNot D
  shows
    literal_redundant M (N + U) D cach L  literal_redundant_spec M (N + U + NE + UE) D L
proof -
  have lit_redundant_rec: lit_redundant_rec M (N + U) D cach [(L, remove1_mset (- L) E')]
       literal_redundant_spec M (N + U + NE + UE) D L
    if
      E: E  None  Propagated (- L) (the E)  set M and
      E': E = Some E' and
      failed: ¬ (get_level M L = 0  cach (atm_of L) = SEEN_REMOVABLE)
        cach (atm_of L)  SEEN_FAILED
    for E E'
  proof -
    have
      [simp]: -L ∈# E' and
      in_trail: Propagated (- L) (add_mset (-L) (remove1_mset (-L) E'))  set M
      using Propagated_in_trail_entailed[OF invs, of -L E'] E E'
      by auto
    have H: lit_redundant_rec_spec M (N + U + NE + UE) D L 
       literal_redundant_spec M (N + U + NE + UE) D L
      by (auto simp: lit_redundant_rec_spec_def literal_redundant_spec_def ac_simps)
    show ?thesis
      apply (rule order.trans)
       apply (rule lit_redundant_rec_spec[OF invs _ in_trail])
      subgoal ..
      subgoal by (rule inv)
      subgoal using assms by fast
      subgoal by (rule M_D)
      subgoal using failed by (cases cach (atm_of L)) auto
      subgoal unfolding literal_redundant_spec_def[symmetric] by (rule H)
      done
  qed

  have
    L_dist: distinct_mset (C) and
    L_tauto: ¬tautology C
  if
    in_trail: Propagated (- L) C  set M
    for C
    using that
    Propagated_in_trail_entailed[of M N+NE U+UE D' -L C] invs
    by (auto simp: )
  have uL_M: -L  lits_of_l M
    using L_D M_D by (auto dest!: multi_member_split)
  show ?thesis
    unfolding literal_redundant_def get_propagation_reason_def literal_redundant_spec_def
    apply (refine_vcg)
    subgoal using uL_M .
    subgoal
      using inv uL_M cdclW_restart_mset.literals_of_level0_entailed[OF invs, of -L]
        true_clss_cls_mono_r'
      by (fastforce simp: mark_failed_lits_def conflict_min_analysis_inv_def
          clauses_def ac_simps)
    subgoal using inv by (auto simp: ac_simps)
    subgoal by auto
    subgoal using inv by (auto simp: ac_simps)
    subgoal using inv by (auto simp: mark_failed_lits_def conflict_min_analysis_inv_def)
    subgoal using inv by (auto simp: mark_failed_lits_def conflict_min_analysis_inv_def ac_simps)
    subgoal using L_dist by simp
    subgoal using L_tauto by simp
    subgoal for E E'
      unfolding literal_redundant_spec_def[symmetric]
      by (rule lit_redundant_rec)
    done
qed

definition set_all_to_list where
  set_all_to_list e ys = do {
     S  WHILE⇗λ(i, xs). i  length xs  (x  set (take i xs). x = e)  length xs = length ys(λ(i, xs). i < length xs)
       (λ(i, xs). do {
         ASSERT(i < length xs);
         RETURN(i+1, xs[i := e])
        })
       (0, ys);
    RETURN (snd S)
    }

lemma
  set_all_to_list e ys  SPEC(λxs. length xs = length ys  (x  set xs. x = e))
  unfolding set_all_to_list_def
  apply (refine_vcg)
  subgoal by auto
  subgoal by auto
  subgoal by auto
  subgoal by auto
  subgoal by auto
  subgoal by (auto simp: take_Suc_conv_app_nth list_update_append)
  subgoal by auto
  subgoal by auto
  subgoal by auto
  done

definition get_literal_and_remove_of_analyse_wl
   :: 'v clause_l  (nat × nat × nat × nat) list  'v literal × (nat × nat × nat × nat) list where
  get_literal_and_remove_of_analyse_wl C analyse =
    (let (i, k, j, ln) = last analyse in
     (C ! j, analyse[length analyse - 1 := (i, k, j + 1, ln)]))


definition mark_failed_lits_wl
where
  mark_failed_lits_wl NU analyse cach = SPEC(λcach'.
     (L. cach' L = SEEN_REMOVABLE  cach L = SEEN_REMOVABLE))


definition lit_redundant_rec_wl_ref where
  lit_redundant_rec_wl_ref NU analyse 
    ((i, k, j, ln)  set analyse. j  ln  i ∈# dom_m NU  i > 0 
      ln  length (NU  i)  k < length (NU  i) 
    distinct (NU  i) 
    ¬tautology (mset (NU  i))) 
    ((i, k, j, ln)  set (butlast analyse). j > 0)

definition lit_redundant_rec_wl_inv where
  lit_redundant_rec_wl_inv M NU D = (λ(cach, analyse, b). lit_redundant_rec_wl_ref NU analyse)

definition lit_redundant_reason_stack
  :: 'v literal  'v clauses_l  nat  (nat × nat × nat × nat) where
lit_redundant_reason_stack L NU C' =
  (if length (NU  C') > 2 then (C', 0, 1, length (NU  C'))
  else if NU  C' ! 0 = L then (C', 0, 1, length (NU  C'))
  else (C', 1, 0, 1))

definition lit_redundant_rec_wl :: ('v, nat) ann_lits  'v clauses_l  'v clause 
     _  _  _ 
      (_ × _ × bool) nres
where
  lit_redundant_rec_wl M NU D cach analysis _ =
      WHILETlit_redundant_rec_wl_inv M NU D(λ(cach, analyse, b). analyse  [])
        (λ(cach, analyse, b). do {
            ASSERT(analyse  []);
            ASSERT(length analyse  length M);
	    let (C, k, i, ln) = last analyse;
            ASSERT(C ∈# dom_m NU);
            ASSERT(length (NU  C) > k);
            ASSERT(NU  C!k  lits_of_l M);
            let C = NU  C;
            if i  ln
            then
              RETURN(cach (atm_of (C ! k) := SEEN_REMOVABLE), butlast analyse, True)
            else do {
	      let (L, analyse) = get_literal_and_remove_of_analyse_wl C analyse;
              ASSERT(fst(snd(snd (last analyse)))  0);
	      ASSERT(-L  lits_of_l M);
	      b  RES (UNIV);
	      if (get_level M L = 0  cach (atm_of L) = SEEN_REMOVABLE  L ∈# D)
	      	     then RETURN (cach, analyse, False)
	      else if b  cach (atm_of L) = SEEN_FAILED
	      then do {
		cach  mark_failed_lits_wl NU analyse cach;
		RETURN (cach, [], False)
	      }
	      else do {
                ASSERT(cach (atm_of L) = SEEN_UNKNOWN);
		C'  get_propagation_reason M (-L);
		case C' of
		  Some C'  do {
		    ASSERT(C' ∈# dom_m NU);
		    ASSERT(length (NU  C')  2);
		    ASSERT (distinct (NU  C')  ¬tautology (mset (NU  C')));
		    ASSERT(C' > 0);
		    RETURN (cach, analyse @ [lit_redundant_reason_stack (-L) NU C'], False)
		  }
		| None  do {
		    cach  mark_failed_lits_wl NU analyse cach;
		    RETURN (cach, [], False)
		}
	     }
           }
        })
       (cach, analysis, False)

fun convert_analysis_l where
  convert_analysis_l NU (i, k, j, le) = (-NU  i ! k, mset (Misc.slice j le (NU  i)))

definition convert_analysis_list where
  convert_analysis_list NU analyse = map (convert_analysis_l NU) (rev analyse)

lemma convert_analysis_list_empty[simp]:
  convert_analysis_list NU [] = []
  convert_analysis_list NU a = []  a = []
  by (auto simp: convert_analysis_list_def)


lemma trail_length_ge2:
  assumes
    ST: (S, T)  twl_st_l None and
    list_invs: twl_list_invs S and
    struct_invs: twl_struct_invs T and
    LaC: Propagated L C  set (get_trail_l S) and
    C0: C > 0
  shows
    length (get_clauses_l S  C)  2
proof -
  have conv:
   (get_trail_l S, get_trail T)  convert_lits_l (get_clauses_l S) (get_kept_unit_clauses_l S)
    using ST unfolding twl_st_l_def by auto

  have cdclW_restart_mset.cdclW_conflicting (stateW_of T) and
    lev_inv: cdclW_restart_mset.cdclW_M_level_inv (stateW_of T)
    using struct_invs unfolding twl_struct_invs_def cdclW_restart_mset.cdclW_all_struct_inv_def
      pcdcl_all_struct_invs_def stateW_of_def
    by fast+

  have n_d: no_dup (get_trail_l S)
    using ST lev_inv unfolding cdclW_restart_mset.cdclW_M_level_inv_def
    by (auto simp: twl_st_l twl_st)
  have
    C: C ∈# dom_m (get_clauses_l S)
    using list_invs C0 LaC by (auto simp: twl_list_invs_def all_conj_distrib)
  have twl_st_inv T
    using struct_invs unfolding twl_struct_invs_def by fast
  then show le2: length (get_clauses_l S  C)  2
    using C ST multi_member_split[OF C] unfolding twl_struct_invs_def
    by (cases S; cases T)
      (auto simp: twl_st_inv.simps twl_st_l_def
        image_Un[symmetric])
qed

lemma clauses_length_ge2:
  assumes
    ST: (S, T)  twl_st_l None and
    list_invs: twl_list_invs S and
    struct_invs: twl_struct_invs T and
    C: C ∈# dom_m (get_clauses_l S)
  shows
    length (get_clauses_l S  C)  2
proof -
  have twl_st_inv T
    using struct_invs unfolding twl_struct_invs_def by fast
  then show le2: length (get_clauses_l S  C)  2
    using C ST multi_member_split[OF C] unfolding twl_struct_invs_def
    by (cases S; cases T)
      (auto simp: twl_st_inv.simps twl_st_l_def
        image_Un[symmetric])
qed

lemma lit_redundant_rec_wl:
  fixes S :: nat twl_st_wl and S' :: nat twl_st_l and S'' :: nat twl_st and NU M analyse
  defines
    [simp]: S'''  stateW_of S''
  defines
    M  get_trail_wl S and
    M': M'  trail S''' and
    NU: NU  get_clauses_wl S and
    NU': NU'  mset `# ran_mf NU and
    analyse'  convert_analysis_list NU analyse
  assumes
    S_S': (S, S')  state_wl_l None and
    S'_S'': (S', S'')  twl_st_l None and
    bounds_init: lit_redundant_rec_wl_ref NU analyse and
    struct_invs: twl_struct_invs S'' and
    add_inv: twl_list_invs S'
  shows
    lit_redundant_rec_wl M NU D cach analyse lbd  
       (Id ×r {(analyse, analyse'). analyse' = convert_analysis_list NU analyse 
          lit_redundant_rec_wl_ref NU analyse} ×r bool_rel)
       (lit_redundant_rec M' NU' D cach analyse')
   (is _   (_ ×r ?A ×r _) _ is _   ?R _)
proof -
  obtain D' NE UE Q W NEk UEk NS US N0 U0 where
    S: S = (M, NU, D', NE, UE, NEk, UEk, NS, US, N0, U0, Q, W)
    using M_def NU by (cases S) auto
  have M'_def: (M, M')  convert_lits_l NU (NEk + UEk)
    using NU S_S' S'_S'' unfolding M' by (auto simp: S state_wl_l_def twl_st_l_def)
  then have [simp]: lits_of_l M' = lits_of_l M
    by auto
  have [simp]: fst (convert_analysis_l NU x) = -NU  (fst x) ! (fst (snd x)) for x
    by (cases x) auto
  have [simp]: snd (convert_analysis_l NU x) =
    mset (Misc.slice (fst (snd (snd x))) (snd (snd (snd x))) (NU  fst x)) for x
    by (cases x) auto

  have
    no_smaller_propa: cdclW_restart_mset.no_smaller_propa S''' and
    struct_invs: cdclW_restart_mset.cdclW_all_struct_inv S'''
    using struct_invs unfolding twl_struct_invs_def S'''_def[symmetric]
      pcdcl_all_struct_invs_def stateW_of_def[symmetric]
    by fast+
  have annots: set (get_all_mark_of_propagated (trail S''')) 
     set_mset (cdclW_restart_mset.clauses S''')
    using struct_invs
    unfolding cdclW_restart_mset.cdclW_all_struct_inv_def
       cdclW_restart_mset.cdclW_learned_clause_alt_def
    by fast
  have no_dup (get_trail_wl S)
    using struct_invs S_S' S'_S'' unfolding cdclW_restart_mset.cdclW_all_struct_inv_def
      cdclW_restart_mset.cdclW_M_level_inv_def
    by (auto simp: twl_st_wl twl_st_l twl_st)
  then have n_d: no_dup M
    by (auto simp: S)
  then have n_d': no_dup M'
    using M'_def by (auto simp: S)
  let ?B = {(analyse, analyse'). analyse' = convert_analysis_list NU analyse 
          lit_redundant_rec_wl_ref NU analyse  fst (snd (snd (last analyse))) > 0}
  have get_literal_and_remove_of_analyse_wl:
    RETURN (get_literal_and_remove_of_analyse_wl (NU  x1d) x1c)
	  (Id ×r ?B)
	   (get_literal_and_remove_of_analyse x1a)
    if
      xx': (x, x')   ?R and
      case x of (cach, analyse, b)  analyse  [] and
      case x' of (cach, analyse, b)  analyse  [] and
      lit_redundant_rec_wl_inv M NU D x and
      s: x2 = (x1a, x2a)
        x' = (x1, x2)
        x2d = (x1f, x2e)
        x2c = (x1e, x2d)
        (fst (last x1c), fst (snd (last x1c)), fst (snd (snd (last x1c))),
	snd (snd (snd (last x1c)))) =
         (x1d, x2c)
        x2b = (x1c, x2f)
        x = (x1b, x2b) and
      x1a  [] and
      - fst (hd x1a)  lits_of_l M' and
      x1c: x1c  [] and
      x1d ∈# dom_m NU and
      x1e < length (NU  x1d) and
      NU  x1d ! x1e  lits_of_l M and
      length: ¬ x2e  x1f and
      snd (hd x1a)  {#}
    for x x' x1 x2 x1a x2a x1b x2b x1c x1d x2c x1e x2d x1f x2e x2f
  proof -
    have x1d: x1d = fst (last x1c)
      using s by auto
    have last x1c = (a, b, c, d)  d  length (NU  a)
      last x1c = (a, b, c, d)  c  d for aa ba list a b c d
      using xx' x1c length unfolding s convert_analysis_list_def
        lit_redundant_rec_wl_ref_def
      by (cases x1c rule: rev_cases; auto; fail)+
    then show ?thesis
      supply convert_analysis_list_def[simp] hd_rev[simp] last_map[simp] rev_map[symmetric, simp]
      using x1c xx' length s
      using Cons_nth_drop_Suc[of snd (snd (snd (last x1c))) NU  fst (last x1c), symmetric]
      unfolding lit_redundant_rec_wl_ref_def x1d
      by (cases x1c; cases last x1c)
        (auto simp: get_literal_and_remove_of_analyse_wl_def nth_in_sliceI mset_tl
          get_literal_and_remove_of_analyse_def convert_analysis_list_def slice_Suc
	  slice_head
          intro!: RETURN_SPEC_refine elim!: neq_Nil_revE split: if_splits)
  qed

  have get_propagation_reason: get_propagation_reason M (- x1h)
	  ({(C', C). C = mset (NU  C')  C'  0 
	      Propagated (- x1g) (mset (NUC'))  set M'
		   Propagated (- x1g) C'  set M  C' ∈# dom_m NU 
		  length (NU  C')  2}
		option_rel)
	   (get_propagation_reason M' (- x1g))
      (is _   (?get_propagation_reasonoption_rel) _)
    if
      (x, x')   ?R and
      case x of (cach, analyse, b)  analyse  [] and
      case x' of (cach, analyse, b)  analyse  [] and
      lit_redundant_rec_wl_inv M NU D x and
      st:
	x2 = (x1a, x2a)
	x' = (x1, x2)
	x2d = (x1f, x2e)
	x2c = (x1e, x2d)
	(fst (last x1c), fst (snd (last x1c)), fst (snd (snd (last x1c))),
	  snd (snd (snd (last x1c)))) =
	 (x1d, x2c)
	x2b = (x1c, x2f)
	x = (x1b, x2b)
        x'a = (x1g, x2g) and
      x1a  [] and
      - fst (hd x1a)  lits_of_l M' and
      x1c  [] and
      x1d: x1d ∈# dom_m NU and
      x1e < length (NU  x1d) and
      NU  x1d ! x1e  lits_of_l M and
      ¬ x2e  x1f and
      snd (hd x1a)  {#} and
      H: (get_literal_and_remove_of_analyse_wl (NU  x1d) x1c, x'a)
             Id ×f ?B
        get_literal_and_remove_of_analyse_wl (NU  x1d) x1c = (x1h, x2h) and
      - x1g  lits_of_l M' and
      - x1h  lits_of_l M and
      (b, ba)  bool_rel and
      b  UNIV and
      ba  UNIV and
      ¬ (get_level M x1h = 0  x1b (atm_of x1h) = SEEN_REMOVABLE  x1h ∈# D) and
      cond: ¬ (get_level M' x1g = 0  x1 (atm_of x1g) = SEEN_REMOVABLE  x1g ∈# D) and
      ¬ (b  x1b (atm_of x1h) = SEEN_FAILED) and
      ¬ (ba  x1 (atm_of x1g) = SEEN_FAILED)
   for x x' x1 x2 x1a x2a x1b x2b x1c x1d x2c x1e x2d x1f x2e x2f x'a x1g x2g x1h
	 x2h b ba
  proof -
    have [simp]: x1h = x1g
      using st H by auto
    have le2: length (NU  x1d)  2
      using clauses_length_ge2[OF S'_S'' add_inv assms(10), of x1d] x1d st S_S'
      by (auto simp: S)
    have
      Propagated (- x1g) (mset (NU  a))  set M' (is ?propa) and
      a  0 (is ?a) and
      a ∈# dom_m NU (is ?L) and
      length (NU  a)  2 (is ?len)
      if x1e_M: Propagated (- x1g) a  set M
      for a
    proof -
      have [simp]: a  0
      proof
        assume [simp]: a = 0
        obtain E' where
           x1d_M': Propagated (- x1g) E'  set M' and
           E' ∈# NEk + UEk
          using x1e_M M'_def by (auto dest: split_list simp: convert_lits_l_def p2rel_def
              convert_lit.simps
              elim!: list_rel_in_find_correspondanceE split: if_splits)
        moreover have unit_clss S'' = NE + NEk + UE + UEk
          using S_S' S'_S'' x1d_M' by (auto simp: S)
        moreover have Propagated (- x1g) E'  set (get_trail S'')
          using S_S' S'_S'' x1d_M' by (auto simp: S state_wl_l_def twl_st_l_def M')
        moreover have 0 < count_decided (get_trail S'')
          using cond S_S' S'_S'' count_decided_ge_get_level[of M x1g]
          by (auto simp: S M' twl_st)
        ultimately show False
          using clauses_in_unit_clss_have_level0(1)[of S'' E' - x1g] cond twl_struct_invs S''
          S_S' S'_S''  M'_def
          by (auto simp: S)
      qed
      show ?propa and ?a
        using that M'_def by (auto simp: convert_lits_l_def p2rel_def convert_lit.simps
              elim!: list_rel_in_find_correspondanceE split: if_splits)
      then show ?L
        using that add_inv S_S' S'_S'' S unfolding twl_list_invs_def
        by (auto 5 5 simp: state_wl_l_def twl_st_l_def)
      show ?len
        using trail_length_ge2[OF S'_S'' add_inv assms(10), of - x1g a] that S_S'
	by (force simp: S)
    qed
    then show ?thesis
      apply (auto simp: get_propagation_reason_def refine_rel_defs intro!: RES_refine)
      apply (case_tac s)
      by auto
  qed
  have resolve: ((x1b, x2h @ [lit_redundant_reason_stack (- x1h) NU xb], False), x1,
	 (x1g, remove1_mset (- x1g) x'c) # x2g, False)
	 Id ×f
	  ({(analyse, analyse').
	    analyse' = convert_analysis_list NU analyse 
	    lit_redundant_rec_wl_ref NU analyse} ×f
	   bool_rel)
    if
      xx': (x, x')  Id ×r ?A ×r bool_rel and
      case x of (cach, analyse, b)  analyse  [] and
      case x' of (cach, analyse, b)  analyse  [] and
      lit_redundant_rec_wl_inv M NU D x and
      s:
        x2 = (x1a, x2a)
        x' = (x1, x2)
        x2d = (x1f, x2e)
        x2c = (x1e, x2d)
        (fst (last x1c), fst (snd (last x1c)), fst (snd (snd (last x1c))),
         	snd (snd (snd (last x1c)))) =
         (x1d, x2c)
        x2b = (x1c, x2f)
        x = (x1b, x2b)
	x'a = (x1g, x2g)  and
      [simp]: x1a  [] and
      - fst (hd x1a)  lits_of_l M' and
      [simp]: x1c  [] and
      x1d ∈# dom_m NU and
      x1e < length (NU  x1d) and
      NU  x1d ! x1e  lits_of_l M and
      ¬ x2e  x1f and
      snd (hd x1a)  {#} and
      get_literal_and_remove_of_analyse_wl:
        (get_literal_and_remove_of_analyse_wl (NU  x1d) x1c, x'a)
        Id ×f
	 {(analyse, analyse').
	  analyse' = convert_analysis_list NU analyse 
	  lit_redundant_rec_wl_ref NU analyse 
	  0 < fst (snd (snd (last analyse)))} and
      get_lit: get_literal_and_remove_of_analyse_wl (NU  x1d) x1c = (x1h, x2h) and
      - x1g  lits_of_l M' and
      fst (snd (snd (last x2h)))  0 and
      - x1h  lits_of_l M and
      bba: (b, ba)  bool_rel and
      ¬ (get_level M x1h = 0  x1b (atm_of x1h) = SEEN_REMOVABLE  x1h ∈# D) and
      ¬ (get_level M' x1g = 0  x1 (atm_of x1g) = SEEN_REMOVABLE  x1g ∈# D) and
      ¬ (b  x1b (atm_of x1h) = SEEN_FAILED) and
      ¬ (ba  x1 (atm_of x1g) = SEEN_FAILED) and
      xb_x'c: (xa, x'b)
        ?get_propagation_reason x1goption_rel and
      xa: xa = Some xb x'b = Some x'c and
      (xb, x'c)
        (?get_propagation_reason x1g) and
      dist_tauto: distinct_mset x'c  ¬ tautology x'c and
      xb ∈# dom_m NU and
      2  length (NU  xb)
     for x x' x1 x2 x1a x2a x1b x2b x1c x1d x2c x1e x2d x1f x2e x2f x'a x1g x2g x1h
       x2h b ba xa x'b xb x'c
  proof -
    have [simp]: mset (tl C) = remove1_mset (C!0) (mset C) for C
      by (cases C) auto
    have [simp]:
      x2 = (x1a, x2a)
      x' = (x1, x1a, x2a)
      x2d = (x1f, x2e)
      x2c = (x1e, x1f, x2e)
      last x1c = (x1d, x1e, x1f, x2e)
      x2b = (x1c, x2f)
      x = (x1b, x1c, x2f)
      xa = Some xb
      x'b = Some x'c
      x'c = mset (NU  xb)
      using s get_literal_and_remove_of_analyse_wl xa xb_x'c
      unfolding get_lit convert_analysis_list_def
      by auto
    then have x1d0: length (NU  xb) > 2  x1g = -NU  xb ! 0 NU  xb  [] and
      x1d: -x1g  set (watched_l (NU  xb))
      using add_inv xb_x'c S_S' S'_S'' S unfolding twl_list_invs_def
      by (auto 5 5 simp: state_wl_l_def twl_st_l_def)

    have le2: length (NU  xb)  2
      using clauses_length_ge2[OF S'_S'' add_inv assms(10)] xb_x'c S_S'
      by (auto simp: S)
    have 0: case lit_redundant_reason_stack (-x1g) NU xb of (i, k, j, ln) 
            j  ln  i ∈# dom_m NU  0  j  0 < i  ln  length (NU  i) 
	      k < length (NU  i)  distinct (NU  i)  ¬ tautology (mset (NU  i))
      for i j ln k
      using s xx' get_literal_and_remove_of_analyse_wl xb_x'c x1d le2 dist_tauto
      unfolding get_lit convert_analysis_list_def lit_redundant_rec_wl_ref_def
      lit_redundant_reason_stack_def
      by (auto split: if_splits)

    have (x1g, remove1_mset (- x1g) (mset (NU  xb))) =
      convert_analysis_l NU (lit_redundant_reason_stack (- x1g) NU xb)
      using s xx' get_literal_and_remove_of_analyse_wl xb_x'c x1d le2
      unfolding get_lit convert_analysis_list_def lit_redundant_rec_wl_ref_def
        lit_redundant_reason_stack_def
      by (auto split: simp: Misc.slice_def drop_Suc simp: x1d0(1)
        dest!: list_decomp_2)
    then show ?thesis
      using s xx' get_literal_and_remove_of_analyse_wl xb_x'c x1d 0
      unfolding get_lit convert_analysis_list_def lit_redundant_rec_wl_ref_def
      by (cases x2h rule: rev_cases)
        (auto simp: drop_Suc uminus_lit_swap butlast_append
        dest: list_decomp_2)
  qed
  have mark_failed_lits_wl: mark_failed_lits_wl NU x2e x1b   Id (mark_failed_lits NU' x2d x1)
    if
      (x, x')  ?R and
      x' = (x1, x2) and
      x = (x1b, x2b)
    for x x' x2e x1b x1 x2 x2b x2d
    using that unfolding mark_failed_lits_wl_def mark_failed_lits_def by auto

  have ana: last analyse = (fst (last analyse), fst (snd (last analyse)),
    fst (snd (snd (last analyse))), snd (snd (snd (last analyse)))) for analyse
      by (cases last analyse) auto

  show ?thesis
    supply convert_analysis_list_def[simp] hd_rev[simp] last_map[simp] rev_map[symmetric, simp]
    unfolding lit_redundant_rec_wl_def lit_redundant_rec_def
    apply (rewrite at let _ = _  _ in _ Let_def)
    apply (rewrite in let _ = _ in _ ana)
    apply (rewrite at let _ = (_, _, _) in _ Let_def)
    apply refine_rcg
    subgoal using bounds_init unfolding analyse'_def by auto
    subgoal for x x'
      by (cases x, cases x')
         (auto simp: lit_redundant_rec_wl_inv_def lit_redundant_rec_wl_ref_def)
    subgoal by auto
    subgoal by auto
    subgoal using M'_def by (auto dest: convert_lits_l_imp_same_length)
    subgoal by (auto simp: lit_redundant_rec_wl_inv_def lit_redundant_rec_wl_ref_def
       elim!: neq_Nil_revE)
    subgoal by (auto simp: lit_redundant_rec_wl_inv_def lit_redundant_rec_wl_ref_def
       elim!: neq_Nil_revE)
    subgoal by (auto simp: map_butlast rev_butlast_is_tl_rev lit_redundant_rec_wl_ref_def
          dest: in_set_butlastD)
    subgoal by (auto simp: map_butlast rev_butlast_is_tl_rev lit_redundant_rec_wl_ref_def
            Misc.slice_def
          dest: in_set_butlastD
          elim!: neq_Nil_revE)
    subgoal by (auto simp: map_butlast rev_butlast_is_tl_rev lit_redundant_rec_wl_ref_def
            Misc.slice_def
          dest: in_set_butlastD
          elim!: neq_Nil_revE)
    apply (rule get_literal_and_remove_of_analyse_wl; assumption)
    subgoal by auto
    subgoal by auto
    subgoal using M'_def by auto
    subgoal by auto
    subgoal by auto
    apply (rule mark_failed_lits_wl; assumption)
    subgoal by (auto simp: lit_redundant_rec_wl_ref_def)
    subgoal by auto
    apply (rule get_propagation_reason; assumption)
    apply assumption
    apply (rule mark_failed_lits_wl; assumption)
    subgoal by (auto simp: lit_redundant_rec_wl_ref_def)
    subgoal by auto
    subgoal by auto
    subgoal by auto
    subgoal by auto
    subgoal by (auto simp: lit_redundant_rec_wl_ref_def)
    subgoal for x x' x1 x2 x1a x2a x1b x2b x1c x1d x2c x1e x2d x1f x2e x2f x'a x1g x2g x1h
       x2h b ba xa x'b xb x'c
      by (rule resolve)
    done
qed


definition literal_redundant_wl where
  literal_redundant_wl M NU D cach L lbd = do {
     ASSERT(-L  lits_of_l M);
     if get_level M L = 0  cach (atm_of L) = SEEN_REMOVABLE
     then RETURN (cach, [], True)
     else if cach (atm_of L) = SEEN_FAILED
     then RETURN (cach, [], False)
     else do {
       C  get_propagation_reason M (-L);
       case C of
         Some C  do{
	   ASSERT(C ∈# dom_m NU);
	   ASSERT(length (NU  C)  2);
	   ASSERT(distinct (NU  C)   ¬tautology (mset (NU  C)));
	   lit_redundant_rec_wl M NU D cach [lit_redundant_reason_stack (-L) NU C] lbd
	 }
       | None  do {
           RETURN (cach, [], False)
       }
     }
  }

lemma literal_redundant_wl_literal_redundant:
  fixes S :: nat twl_st_wl and S' :: nat twl_st_l and S'' :: nat twl_st and NU M
  defines
    [simp]: S'''  stateW_of S''
  defines
    M  get_trail_wl S and
    M': M'  trail S''' and
    NU: NU  get_clauses_wl S and
    NU': NU'  mset `# ran_mf NU
  assumes
    S_S': (S, S')  state_wl_l None and
    S'_S'': (S', S'')  twl_st_l None and
    M  get_trail_wl S and
    M': M'  trail S''' and
    NU: NU  get_clauses_wl S and
    NU': NU'  mset `# ran_mf NU
  assumes
    struct_invs: twl_struct_invs S'' and
    add_inv: twl_list_invs S' and
    L_D: L ∈# D and
    M_D: M ⊨as CNot D
  shows
    literal_redundant_wl M NU D cach L lbd  
       (Id ×r {(analyse, analyse'). analyse' = convert_analysis_list NU analyse 
          lit_redundant_rec_wl_ref NU analyse} ×r bool_rel)
       (literal_redundant M' NU' D cach L)
   (is _   (_ ×r ?A ×r _) _ is _   ?R _)
proof -
  obtain D' NE UE NEk UEk Q W NS US N0 U0 where
    S: S = (M, NU, D', NE, UE, NEk, UEk, NS, US, N0, U0, Q, W)
    using M_def NU by (cases S) auto
  have M'_def: (M, M')  convert_lits_l NU (NEk+UEk)
    using NU S_S' S'_S'' S M' by (auto simp: twl_st_l_def state_wl_l_def)
  have [simp]: lits_of_l M' = lits_of_l M
    using M'_def by auto
  have
    no_smaller_propa: cdclW_restart_mset.no_smaller_propa S''' and
    struct_invs': cdclW_restart_mset.cdclW_all_struct_inv S'''
    using struct_invs unfolding twl_struct_invs_def S'''_def[symmetric]
      pcdcl_all_struct_invs_def stateW_of_def[symmetric]
    by fast+
  have annots: set (get_all_mark_of_propagated (trail S''')) 
     set_mset (cdclW_restart_mset.clauses S''')
    using struct_invs'
    unfolding cdclW_restart_mset.cdclW_all_struct_inv_def
       cdclW_restart_mset.cdclW_learned_clause_alt_def
    by fast
  have n_d: no_dup (get_trail_wl S)
    using struct_invs' S_S' S'_S'' unfolding cdclW_restart_mset.cdclW_all_struct_inv_def
      cdclW_restart_mset.cdclW_M_level_inv_def
    by (auto simp: twl_st_wl twl_st_l twl_st)
  then have n_d: no_dup M
    by (auto simp: S)
  then have n_d': no_dup M'
    using M'_def by (auto simp: S)
  have uL_M: -L  lits_of_l M
    using L_D M_D by (auto dest!: multi_member_split)
  have H: lit_redundant_rec_wl M NU D cach analyse lbd
        ?R (lit_redundant_rec M' NU' D cach analyse')
    if analyse' = convert_analysis_list NU analyse and
       lit_redundant_rec_wl_ref NU analyse
     for analyse analyse'
    using lit_redundant_rec_wl[of S S' S'' analyse D cach lbd, unfolded S'''_def[symmetric],
      unfolded
      M_def[symmetric] M'[symmetric] NU[symmetric] NU'[symmetric],
      OF S_S' S'_S'' _ struct_invs add_inv]
    that by (auto simp: )
  have get_propagation_reason: get_propagation_reason M (-L)
        ({(C', C).  C = mset (NU  C')  C'  0  Propagated (-L) (mset (NUC'))  set M'
                 Propagated (-L) C'  set M  length (NUC')  2}
              option_rel)
          (get_propagation_reason M' (-L))
      (is _   (?get_propagation_reasonoption_rel) _ is ?G1) and
    propagated_L:
       Propagated (-L) a  set M  a  0  Propagated (- L) (mset (NU  a))  set M'
       (is ?H2  ?G2)
    if
      lev0_rem: ¬ (get_level M' L = 0  cach (atm_of L) = SEEN_REMOVABLE) and
      ux1e_M: - L  lits_of_l M
    for a
    proof -
      have Propagated (- L) (mset (NU  a))  set M' (is ?propa) and
        a  0 (is ?a) and
	length (NU  a)  2 (is ?len)
        if L_M: Propagated (-L) a  set M
        for a
      proof -
        have [simp]: a  0
        proof
          assume [simp]: a = 0
          obtain E' where
            x1d_M': Propagated (- L) E'  set M' and
            E' ∈# NEk + UEk
            using L_M M'_def by (auto dest: split_list simp: convert_lits_l_def p2rel_def
                convert_lit.simps
                elim!: list_rel_in_find_correspondanceE split: if_splits)
          moreover have unit_clss S'' = NE + NEk + UE + UEk
            using S_S' S'_S'' x1d_M' by (auto simp: S)
          moreover have Propagated (- L) E'  set (get_trail S'')
            using S_S' S'_S'' x1d_M' by (auto simp: S state_wl_l_def twl_st_l_def M')
          moreover have 0 < count_decided (get_trail S'')
            using lev0_rem S_S' S'_S'' count_decided_ge_get_level[of M L]
            by (auto simp: S M' twl_st)
          ultimately show False
            using clauses_in_unit_clss_have_level0(1)[of S'' E' - L] lev0_rem twl_struct_invs S''
              S_S' S'_S''  M'_def
            by (auto simp: S)
        qed

        show ?propa and ?a
          using that M'_def by (auto simp: convert_lits_l_def p2rel_def convert_lit.simps
              elim!: list_rel_in_find_correspondanceE split: if_splits)
	show ?len
	  using trail_length_ge2[OF S'_S'' add_inv struct_invs, of - L a] that S_S'
	  by (force simp: S)
      qed note H = this
     show ?H2  ?G2
       using H by auto
     show ?G1
       using H
       apply (auto simp: get_propagation_reason_def refine_rel_defs
           get_propagation_reason_def intro!: RES_refine)
       apply (case_tac s)
       by auto
    qed
  have S''': S''' = (get_trail S'', get_all_init_clss S'', get_all_learned_clss S'',
      get_conflict S'')
    by (cases S'') (auto simp: S'''_def)
  have [simp]: mset (tl C) = remove1_mset (C!0) (mset C) for C
    by (cases C) auto
  have S''_M': (get_trail S'') = M'
    using M' S''' by auto

  have [simp]: length (NU  C) > 2  NU  C ! 0 = -L and
    L_watched: -L  set (watched_l (NU  C)) and
    L_dist: distinct (NU  C) and
    L_tauto: ¬tautology (mset (NU  C))
  if
    in_trail: Propagated (- L) C  set M and
    lev: ¬ (get_level M' L = 0  cach (atm_of L) = SEEN_REMOVABLE)
    for C
    using add_inv that propagated_L[OF lev _ in_trail] uL_M S_S' S'_S''
    Propagated_in_trail_entailed[of get_trail S'' get_all_init_clss S'' get_all_learned_clss S''
      get_conflict S'' -L mset (NU  C)] struct_invs' unfolding S'''[symmetric]
    by (auto simp: S twl_list_invs_def S''_M'; fail)+

  have [dest]: C  {#} if Propagated (- L) C  set M' for C
  proof -
    have a @ Propagated L mark # b = trail S'''  b ⊨as CNot (remove1_mset L mark)  L ∈# mark
      for L mark a b
      using struct_invs' unfolding cdclW_restart_mset.cdclW_all_struct_inv_def
        cdclW_restart_mset.cdclW_conflicting_def
      by fast
    then show ?thesis
      using that S_S' S'_S'' M'_def M'
      by (fastforce simp: S state_wl_l_def
          twl_st_l_def convert_lits_l_def convert_lit.simps
          list_rel_append2 list_rel_append1
          elim!: list_relE3 list_relE4
          elim: list_rel_in_find_correspondanceE split: if_splits
          dest!: split_list p2relD)
  qed
  have le2: Propagated (- L) C  set M  C > 0  length (NU  C)  2 for C
    using trail_length_ge2[OF S'_S'' add_inv struct_invs, of _ C] S_S'
    by (auto simp: S)
  have [simp]: Propagated (- L) C  set M  C > 0  C ∈# dom_m NU for C
    using add_inv S_S' S'_S'' propagated_L[of C]
    by (auto simp: S twl_list_invs_def state_wl_l_def
        twl_st_l_def)
  show ?thesis
    unfolding literal_redundant_wl_def literal_redundant_def
    apply (refine_rcg H get_propagation_reason)
    subgoal by simp
    subgoal using M'_def by simp
    subgoal using M'_def by (auto simp: lit_redundant_rec_wl_ref_def)
    subgoal by simp
    subgoal by (auto simp: lit_redundant_rec_wl_ref_def)
    apply (assumption)
    subgoal by (auto simp: lit_redundant_rec_wl_ref_def)
    subgoal by simp
    subgoal by simp
    subgoal for x x' C x'a
      using le2[of C] L_watched[of C] L_dist[of C] L_tauto[of C]
      by (auto simp: convert_analysis_list_def drop_Suc slice_0
          lit_redundant_reason_stack_def slice_Suc slice_head slice_end
        dest!: list_decomp_2)
    subgoal for x x' C x'a
      using le2[of C] L_watched[of C] L_dist[of C] L_tauto[of C]
      by (auto simp: convert_analysis_list_def drop_Suc slice_0
          lit_redundant_reason_stack_def slice_Suc slice_head slice_end
        dest!: list_decomp_2)
    subgoal for x x' C x'a
      using le2[of C] L_watched[of C] L_dist[of C] L_tauto[of C]
      by (auto simp: convert_analysis_list_def drop_Suc slice_0
          lit_redundant_reason_stack_def slice_Suc slice_head slice_end
        dest!: list_decomp_2)
    subgoal for x x' C x'a
      using le2[of C] L_watched[of C] L_dist[of C] L_tauto[of C]
      by (auto simp: lit_redundant_reason_stack_def lit_redundant_rec_wl_ref_def)
    done
qed

definition mark_failed_lits_stack_inv where
  mark_failed_lits_stack_inv NU analyse = (λcach.
       ((i, k, j, len)  set analyse. j  len  len  length (NU  i)  i ∈# dom_m NU 
          k < length (NU  i)  j > 0))

text We mark all the literals from the current literal stack as failed, since every minimisation
call will find the same minimisation problem.
definition mark_failed_lits_stack where
  mark_failed_lits_stack 𝒜in NU analyse cach = do {
    ( _, cach)  WHILETλ(_, cach). mark_failed_lits_stack_inv NU analyse cach(λ(i, cach). i < length analyse)
      (λ(i, cach). do {
        ASSERT(i < length analyse);
        let (cls_idx, _, idx, _) = analyse ! i;
        ASSERT(atm_of (NU  cls_idx ! (idx - 1)) ∈# 𝒜in);
        RETURN (i+1, cach (atm_of (NU  cls_idx ! (idx - 1)) := SEEN_FAILED))
      })
      (0, cach);
    RETURN cach
   }

lemma mark_failed_lits_stack_mark_failed_lits_wl:
  shows
    (uncurry2 (mark_failed_lits_stack 𝒜), uncurry2 mark_failed_lits_wl) 
       [λ((NU, analyse), cach). literals_are_in_ℒin_mm 𝒜 (mset `# ran_mf NU) 
          mark_failed_lits_stack_inv NU analyse cach]f
       Id ×f Id ×f Id  Idnres_rel
proof -
  have mark_failed_lits_stack 𝒜 NU analyse cach  (mark_failed_lits_wl NU analyse cach)
    if
      NU_ℒin: literals_are_in_ℒin_mm 𝒜 (mset `# ran_mf NU) and
      init: mark_failed_lits_stack_inv NU analyse cach
    for NU analyse cach
  proof -
    define I where
      I = (λ(i :: nat, cach'). (L. cach' L = SEEN_REMOVABLE  cach L = SEEN_REMOVABLE))
    have valid_atm: atm_of (NU  cls_idx ! (idx - 1)) ∈# 𝒜
      if
        I s and
        case s of (i, cach)  i < length analyse and
        case s of (i, cach)  mark_failed_lits_stack_inv NU analyse cach and
        s = (i, cach) and
        i: i < length analyse and
        analyse ! i = (cls_idx, k) k = (k0, k') k' = (idx, len)
      for s i cach cls_idx idx k len k' k'' k0
    proof -
      have [iff]: (a b. (a, b)  set analyse)  False
        using i by (cases analyse) auto
      show ?thesis
        unfolding in_ℒall_atm_of_in_atms_of_iff[symmetric]
        apply (subst atms_of_ℒall_𝒜in[symmetric])
        unfolding atms_of_def
        apply (rule imageI)
        apply (rule literals_are_in_ℒin_mm_in_ℒall)
        using NU_ℒin that nth_mem[of i analyse]
        by (auto simp: mark_failed_lits_stack_inv_def I_def)
    qed
    show ?thesis
      unfolding mark_failed_lits_stack_def mark_failed_lits_wl_def
      apply (refine_vcg WHILEIT_rule_stronger_inv[where R = measure (λ(i, _). length analyse -i)
         and I' = I])
      subgoal by auto
      subgoal using init by simp
      subgoal unfolding I_def by auto
      subgoal by auto
      subgoal for s i cach cls_idx idx
        by (rule valid_atm)
      subgoal unfolding mark_failed_lits_stack_inv_def by auto
      subgoal unfolding I_def by auto
      subgoal by auto
      subgoal unfolding I_def by auto
      done
  qed
  then show ?thesis
    by (intro frefI nres_relI) auto
qed

end