# Formalising 2-Sat in Coq (source code TwoSat.v, a note TwoSat.pdf)

Require Import ssreflect ssrfun ssrbool eqtype ssrnat seq fintype finfun bigop.
Require Import path fingraph.

Section TwoSat.

(* The set of variables *)
Variable var : finType.

Implicit Type v : var.

(* A litteral is a signed variable *)
Notation lit := (bool * var)%type.

Implicit Type l : lit.

Definition flip l := (~~ l.1, l.2).

Lemma flipK : cancel flip flip.
Proof. by do 2 case. Qed.

Lemma flip_neq l : flip l != l.
Proof. by case: l => [] []. Qed.

(* A clause is a pair of litterals *)
Local Notation clause := (lit * lit)%type.

Implicit Type cl : clause.

(* Clauses are sequences of clauses *)
Local Notation clauses := (seq clause).

Implicit Type cls : clauses.

(* An assignment is a function for var to bool *)

Local Notation val := (option (bool * bool)).
Local Notation mem:= {ffun var -> val}.

Implicit Types m : mem.

(* The list of clauses *)
Variable cls : clauses.

(* Implication between literals *)
Definition imply := [rel a b | ((flip a, b) \in cls) || ((b, flip a) \in cls)].

Notation " a [->] b " := (imply a b) (at level 40).

Lemma imply_flip l1 l2 : flip l1 [->] flip l2 = l2 [->] l1.
Proof. by rewrite /imply /= flipK orbC. Qed.

Lemma path_flip l1 l2 p :
path imply (flip l1) (rcons p (flip l2)) =
path imply l2 (rcons (map flip (rev p)) l1).
Proof.
elim: p l1 l2 => [l1 l2 /=|l p IH l1 l2].
by (rewrite !andbT; exact: imply_flip).
rewrite /= -{3}(flipK l) IH rev_cons map_rcons [X in _ = X]rcons_path andbC.
by rewrite last_rcons -{3}(flipK l1) imply_flip.
Qed.

Lemma connect_flip l1 l2 :
connect imply (flip l1) (flip l2) = connect imply l2 l1.
Proof.
apply/connectP/connectP => [] [p e_p e_f].
elim/last_ind: p e_p e_f => //= [_ /(can_inj flipK)->|p l3 _ e_p e_f].
by exists [::].
exists (rcons (map flip (rev p)) l1); rewrite last_rcons in e_f.
by rewrite -path_flip e_f.
by rewrite last_rcons.
elim/last_ind: p e_p e_f => //= [_ ->|p l3 _ e_p e_f]; first by exists [::].
exists (rcons (map flip (rev p)) (flip l2)); rewrite last_rcons in e_f.
by rewrite -path_flip !flipK e_f.
by rewrite last_rcons.
Qed.

(* Flip value *)
Definition vflip (x : val) : val :=
omap (fun x => (x.1, negb x.2)) x.

Lemma vflipK : cancel vflip vflip.
Proof. by move=> [] // [] [] []. Qed.

(* Getting the value of a literal *)
Definition lit_get m l : val := if l.1 then m l.2 else vflip (m l.2).

Lemma lit_get_flip m l : lit_get m (flip l) = vflip (lit_get m l).
Proof. by case: l => [] // [] b; rewrite /lit_get /= ?vflipK. Qed.

(* Setting the value of a literal *)
Definition lit_set m l r : mem :=
[ffun x => if x == l.2 then if l.1 then r else vflip r
else m x].

Lemma lit_set_eq m l x : lit_get (lit_set m l x) l = x.
Proof.
by rewrite /lit_get /lit_set !ffunE eqxx; case: l.1; rewrite ?vflipK.
Qed.

Lemma lit_set_neq m l1 l2 x : l2 != l1 -> l2 != flip l1 ->
lit_get (lit_set m l1 x) l2 = lit_get m l2.
Proof.
by ((case: l1 => [[] v1]; case: l2 => [[] v2];
rewrite /lit_set /lit_get !ffunE //=; case: (v2 =P v1))
=> //->) => [|_|_|] /negP[].
Qed.

(* The list of literals implied by a literal *)
Definition inext : lit -> seq lit := rgraph imply.

Lemma inext_flip l1 l2 :
flip l1 \in inext (flip l2) = (l2 \in inext l1).
Proof. by rewrite ![_ \in _](rgraphK) imply_flip. Qed.

Lemma inext_imply l l1 : l1 \in inext l = l [->] l1.
Proof. by rewrite [_ \in _]rgraphK. Qed.

(* The propagation algorithm *)
Fixpoint propagate n m s :=
if n is n1.+1 then
if s is (l, ls) :: s1 then
if lit_get m l is Some (b1, true) then
(* We check that l is still set to true not to propagate
wrong values. Example:
A  -> {~B, E, B}, ~A -> {}, B -> {A}, ~B -> {~E, A}, E  ->  {B}, ~E -> {~A}
*)

if ls is l1 :: ls1 then
if lit_get m l1 is Some (b, v) then
if v then propagate n1 m ((l, ls1) :: s1)
else
if b then Some (false, n1.+1, m)
else
let m1 := lit_set m l1 (Some (true, true)) in
propagate n1 m1 [:: (l1, inext l1), (l, ls1) & s1]
else
let m1 := lit_set m l1 (Some (b1, true)) in
propagate n1 m1 [:: (l1, inext l1), (l, ls1) & s1]
else propagate n1 m s1
else propagate n1 m s1
else Some (true, n1.+1, m)
else None.

(* Check if a literal is true *)
Definition lit_is_true m l := oapp (fun x => x.2) false (lit_get m l).

Lemma lit_is_true_set_eq m x l :
lit_is_true (lit_set m l x) l = oapp (fun x => x.2) false x.
Proof. by rewrite /lit_is_true lit_set_eq. Qed.

Lemma lit_is_true_flip m l : lit_is_true m (flip l) -> ~~ lit_is_true m l.
Proof. by rewrite /lit_is_true lit_get_flip; case: lit_get => // [[[] []]]. Qed.

Lemma lit_is_true_set_eq_flip m x l :
lit_is_true (lit_set m l x) (flip l) = oapp (fun x => x.2) false (vflip x).
Proof. by rewrite /lit_is_true lit_get_flip lit_set_eq. Qed.

Lemma lit_is_true_set_neq m x l1 l2 :
l2 != l1 -> l2 != flip l1 ->
lit_is_true (lit_set m l1 x) l2 = lit_is_true m l2.
Proof. by move=> H1 H2; rewrite /lit_is_true lit_set_neq. Qed.

Lemma lit_is_true_set_neq_flip m x l1 l2 :
l2 != l1 -> l2 != flip l1 ->
lit_is_true (lit_set m l1 x) (flip l2) = lit_is_true m (flip l2).
Proof.
by move=> H1 H2; rewrite /lit_is_true lit_get_flip lit_set_neq // lit_get_flip.
Qed.

Lemma lit_is_true_set_flip m b l l1 :
lit_is_true (lit_set m l (Some (b, true))) l1 -> l1 != flip l.
Proof.
move=> H; apply/eqP => El1fl.
by move: H; rewrite El1fl lit_is_true_set_eq_flip.
Qed.

(* Check if a literal is strongly true, i.e. its value can't be changed *)
Definition lit_is_strue m l := oapp (fun x => x.2 && x.1) false (lit_get m l).

Lemma lit_is_strueW m l : lit_is_strue m l -> lit_is_true m l.
Proof.
by rewrite /lit_is_true /lit_is_strue; case: lit_get => // [] [[] []].
Qed.

Lemma lit_is_strue_set_eq m x l :
lit_is_strue (lit_set m l x) l = oapp (fun i => i.1 && i.2) false x.
Proof. by rewrite /lit_is_strue lit_set_eq; case: x => // [[[] []]]. Qed.

Lemma lit_is_strue_set_eq_flip m x l :
lit_is_strue (lit_set m l x) (flip l) = oapp (fun i => i.1 && ~~ i.2) false x.
Proof.
by rewrite /lit_is_strue lit_get_flip lit_set_eq; case: x => // [[[] []]].
Qed.

Lemma lit_is_strue_set_neq m x l1 l2 :
l2 != l1 -> l2 != flip l1 ->
lit_is_strue (lit_set m l1 x) l2 = lit_is_strue m l2.
Proof. by move=> H1 H2; rewrite /lit_is_strue lit_set_neq. Qed.

Lemma lit_is_strue_set_neq_flip m x l1 l2 :
l2 != l1 -> l2 != flip l1 ->
lit_is_strue (lit_set m l1 x) (flip l2) = lit_is_strue m (flip l2).
Proof.
by move=> H1 H2; rewrite /lit_is_strue lit_get_flip lit_set_neq // lit_get_flip.
Qed.

Lemma lit_is_strue_set_flip m b l l1 :
lit_is_strue (lit_set m l (Some (b, true))) l1 -> l1 != flip l.
Proof.
move=> H; apply/eqP => El1fl.
by move: H; rewrite El1fl lit_is_strue_set_eq_flip /= andbF.
Qed.

(* l has an implication cycle, so it must be true *)
Definition lit_cycle l := connect imply (flip l) l.

Lemma lit_cycle_inext l l1 :
l1 \in inext l -> lit_cycle l -> lit_cycle l1.
Proof.
move=> Il1 Cl.
have Coll1 : connect imply l l1 by apply: connect1; rewrite -inext_imply.
have Cofl1fl : connect imply (flip l1) (flip l).
by apply: connect1; rewrite -inext_imply inext_flip.
apply: connect_trans Coll1.
by apply: connect_trans Cofl1fl _.
Qed.

(* Stack is composed of an implication *)
Definition istack s := if rev s is l :: ls then path imply l ls else true.

Lemma istack_cons l l1 ls :
istack (l :: ls) -> l1 \in inext l -> istack [:: l1, l & ls].
Proof.
rewrite inext_imply /istack !rev_cons; case: rev => /= [|l2 ls1 H1 H2].
by rewrite andbT.
by rewrite rcons_path H1 last_rcons.
Qed.

Lemma istack_consE l ls : istack (l :: ls) -> istack ls.
Proof.
rewrite /istack rev_cons; case: rev => //= l2 ls1.
by rewrite rcons_path => /andP[].
Qed.

Lemma path_head_connect (A : finType) (r : rel A) (a b c : A) p :
path r a (rcons p b) -> c \in (a :: p) -> connect r c b.
Proof.
elim: p a c => /= [a c | d p IH a c].
by rewrite inE andbT => H /eqP->; apply: connect1.
rewrite inE => /andP[H1 H2] /orP[/eqP->|]; last by exact: IH.
apply: connect_trans (connect1 H1) _.
by apply/connectP; exists (rcons p b) => //; rewrite last_rcons.
Qed.

Lemma mem_istack l l1 ls :
istack (l :: ls) -> l1 \in l :: ls -> connect imply l1 l.
Proof.
rewrite inE => H1 /orP[/eqP->//|H2].
have : l1 \in rev ls by rewrite mem_rev.
move : H1; rewrite /istack rev_cons; case: rev => //= l2 ls1 H3 H4.
Qed.

(* Ordered stack, i.e. strong values are at the top of the stack *)
Definition ostack m (s : seq lit) (s1 := [seq x <- s | lit_is_true m x]) :=
s1 = [seq x <- s1 | lit_is_strue m x] ++ [seq x <- s1 | ~~ lit_is_strue m x].

Lemma ostackE m l s : ostack m (l :: s) -> ostack m s.
Proof.
rewrite /ostack /=.
case Ll : lit_is_true => //=.
case Sl : lit_is_strue => /=; first by case.
case Es : [seq _ <- [seq _ <- _ | _]| _] => /= [|l1 s1]; first by case.
case => El.
have: l1 \in l1 :: s1 by rewrite inE eqxx.
by rewrite -Es -El mem_filter Sl.
Qed.

Lemma ostack_neg_lit_is_strue m l1 l2 s :
lit_is_true m l1 -> ~~ lit_is_strue m l1 ->
ostack m (l1 :: s) -> l2 \in (l1 :: s) -> ~~ lit_is_strue m l2.
Proof.
move=> Ll1 Sl1.
rewrite /ostack inE /= Ll1 /= Sl1 (negPf Sl1) => El1s /orP[/eqP->//|l2Is].
apply/negP => Sl2; move: El1s.
set d := [seq x <- [seq x <- s | _] | _].
have: l2 \in d by rewrite !mem_filter Sl2 lit_is_strueW.
case Ed : d => [|l3 ls3] //= l2Il2 [] El1l3.
have: l1 \notin d by rewrite mem_filter negb_and Sl1.
by rewrite Ed -El1l3 inE eqxx.
Qed.

Lemma ostack_strue m l s :
lit_is_strue m l -> ostack m s -> ostack m (l :: s).
Proof.
by move=> Sl; rewrite /ostack /= (lit_is_strueW _ _ Sl) /= Sl /= => <-.
Qed.

Lemma filter_cons (T : Type) (p : pred T) (s : seq T) x:
filter p (x :: s) = if p x then x :: filter p s else filter p s.
Proof. by []. Qed.

Lemma ostack_same m l1 l2 s :
lit_get m l1 = lit_get m l2 ->
ostack m (l1 :: s) -> ostack m (l2 :: l1 :: s).
Proof.
move=> Egl1gl2; rewrite /ostack !filter_cons.
have ETl1Tl2 : lit_is_true m l1 = lit_is_true m l2.
by rewrite /lit_is_true Egl1gl2.
have ESl1Sl2 : lit_is_strue m l1 = lit_is_strue m l2.
by rewrite /lit_is_strue Egl1gl2.
rewrite -ETl1Tl2 /=.
case: lit_is_true => //=.
rewrite -ESl1Sl2.
case E : lit_is_strue => //=; first by move->.
set d := [seq x <- s | lit_is_true m x].
case E1 : [seq x <- d | lit_is_strue m x] => [|l3 d1] //=.
by case=> <-.
case=> El1l3.
have: l1 \in l1 :: d1 by rewrite inE eqxx.
by rewrite El1l3 -E1 mem_filter -El1l3 E.
Qed.

Lemma ostack_weak m1 m2 s :
(forall l, l \in s -> lit_is_true m1 l -> lit_is_true m2 l) ->
(forall l, l \in s -> lit_is_strue m1 l = lit_is_strue m2 l) ->
ostack m2 s -> ostack m1 s.
Proof.
move=> FL FSL.
rewrite /ostack => Om2.
suff {1}-> : [seq x <- s | lit_is_true m1 x] =
[seq x <- [seq x <- s | lit_is_true m1 x] | lit_is_strue m2 x] ++
[seq x <- [seq x <- s | lit_is_true m1 x] | ~~ lit_is_strue m2 x].
by congr (_ ++ _); apply: eq_in_filter=> l;
rewrite mem_filter => /andP[_ HH]; rewrite FSL.
have Hf P :
[seq x <- [seq x <- s | lit_is_true m1 x] | P x] =
[seq x <-
[seq x <- [seq x <- s | lit_is_true m2 x] | P x] | lit_is_true m1 x].
rewrite -!filter_predI.
apply: eq_in_filter=> l lIs /=.
by case: (P _); case: lit_is_true (FL _ lIs); case: lit_is_true => //=
/(_ is_true_true).
rewrite !Hf -filter_cat -Om2 -filter_predI.
apply: eq_in_filter=> l lIs /=.
by case: lit_is_true (FL _ lIs); case: lit_is_true => //= /(_ is_true_true).
Qed.

(* Main invariant *)
Definition valid m (s : seq (lit * seq lit)) :=
[/\
let s1 := [seq i.1 | i <- s] in
[/\ uniq s1, istack s1, ostack m s1 &
forall l, l \in s1 -> lit_is_true m l || lit_is_strue m (flip l)],
forall l, forall ls, (l, ls) \in s -> ls \subset inext l,
forall l, lit_is_strue m l -> lit_cycle l &
forall l1 l2,
(forall ls, (l1, ls) \in s -> l2 \notin ls) ->
(forall ls, lit_is_strue m (flip l2) ->
(flip l2, ls) \in s -> flip l1 \notin ls) ->
l2 \in inext l1 -> lit_is_true m l1 -> lit_is_true m l2].

Definition wf_mem m := forall l1 l2,
l2 \in inext l1 -> lit_is_true m l1 -> lit_is_true m l2.

Lemma wf_nil m : valid m [::] -> wf_mem m.
Proof. by move=> [_ _ _ F] l l1 Tl l1INl; apply: F l1INl. Qed.

Lemma lit_dec l1 l2 : [\/ l1 = l2, l1 = flip l2 | l1 != l2 /\ l1 != flip l2].
Proof.
case: eqP => [->|_]; first by apply: Or31.
by case: eqP => [->|_]; [apply: Or32 | apply: Or33].
Qed.

Lemma valid_init m l (m1 := lit_set m l (Some (false, true))) :
valid m [::] -> lit_get m l = None -> valid m1 [:: (l, inext l)].
Proof.
move=> [_ _ Cl F] Gl.
split=> //= [|l1 ls|l1|l1 l2 D Dr l2INl1 Tm1l1]; first (split => [|||l1] //=).
- by rewrite /ostack /= lit_is_true_set_eq /= lit_is_strue_set_eq.
- by rewrite inE => /eqP->; rewrite lit_is_true_set_eq lit_is_strue_set_eq_flip.
- by rewrite inE xpair_eqE => /andP[/eqP->/eqP->].
- have [->|->| [Dl1l Dl1fl]] := lit_dec l1 l.
- by rewrite lit_is_strue_set_eq.
- by rewrite lit_is_strue_set_eq_flip.
by rewrite lit_is_strue_set_neq //; exact: Cl.
have [El1l|El1fl| [Dl1l Dl1fl]] := lit_dec l1 l.
- have/negP[] : l2 \notin inext l.
by apply: D; rewrite El1l inE eqxx.
by rewrite -El1l.
- by move: Tm1l1; rewrite El1fl lit_is_true_set_eq_flip.
have : lit_is_true m l2.
apply: F l2INl1 _ => //.
by move: Tm1l1; rewrite lit_is_true_set_neq.
have [->|->| [Dl2l Dl2fl]] := lit_dec l2 l.
- by rewrite lit_is_true_set_eq.
- by rewrite /lit_is_true lit_get_flip Gl.
by rewrite lit_is_true_set_neq.
Qed.

Lemma valid_cons m l ls s :
valid m ((l, ls) :: s) -> ~~ lit_is_true m l -> valid m s.
Proof.
move=> [[V1 V2 V3 V4] V5 V6 V7] NTml;
split => [|l1 ls1 Il1||l1 l2 D Dr Tml1 l2INl1] //;
first (split=> [|||l1 l1Is] //).
- by apply: subseq_uniq V1; apply: map_subseq; apply: subseq_cons.
- by apply: istack_consE V2.
- by move: V3; rewrite /ostack /= (negPf NTml).
- by apply: V4; rewrite inE l1Is orbT.
- by apply: V5; rewrite inE Il1 orbT.
have [/eqP El1l | Dl1l] := boolP (l1 == l).
by case/negP:NTml ; rewrite -El1l.
have [/eqP El2l | Dfl2l] := boolP (flip l2 == l).
rewrite -(flipK l2) El2l.
have/V4 : l \in [seq i.1 | i <- (l, ls) :: s] by rewrite /= inE eqxx.
by move: NTml; rewrite /lit_is_true /lit_is_strue lit_get_flip; case: lit_get.
apply: V7 l2INl1 => // [ls1|V ls1];
rewrite inE xpair_eqE ?(negPf Dl1l) ?(negPf Dfl2l) //=.
by apply: D.
by apply: Dr.
Qed.

Lemma valid_cons_nil m l s : valid m ((l, [::]) :: s) -> valid m s.
Proof.
move=> [[V1 V2 V3 V4] V5 V6 V7]; split => [|l1 ls1 Il1||l1 l2 D Dr Tml1 Il1] //;
first (split => [|||l1 Il1]).
- by apply: subseq_uniq V1; apply: map_subseq; apply: subseq_cons.
- by apply: istack_consE V2.
- by apply: ostackE V3.
- by apply: V4; rewrite inE Il1 orbT.
- by apply: V5; rewrite inE Il1 orbT.
apply: V7 Il1 => // [ls|V ls]; rewrite inE xpair_eqE => /orP[/andP[HH/eqP->]|] //.
by apply: D.
by apply: Dr.
Qed.

Lemma valid_cons_true m l l1 ls1 s :
valid m ((l, l1 :: ls1) :: s) ->
lit_is_true m l -> lit_is_true m l1 ->
valid m ((l, ls1) :: s).
Proof.
move=> [[V1 V2 V3 V4] V5 V6 V7] Tml Tml1.
split => [|l2 ls2||l2 l3 D Dr Il3 Tml2] //.
rewrite inE => /orP[/eqP[]->->|Il].
apply: subset_trans (_ : l1 :: ls1 \subset _) => //.
by apply/subsetP=> l3 Il3; rewrite inE Il3 orbT.
by apply: V5; rewrite inE eqxx.
by apply: V5; rewrite inE Il orbT.
have [/eqP->//| Dl3l1] := boolP (l3 == l1).
apply: V7 Il3 _ => // [ls|V ls].
rewrite inE xpair_eqE => /orP[/andP[/eqP El2l /eqP ->]|Il2].
rewrite inE negb_or Dl3l1.
by apply: D; rewrite El2l inE eqxx.
by apply: D; rewrite inE Il2 orbT.
have [/eqP Efl2l1| Dfl2l1] := boolP (flip l2 == l1).
by have := Tml1; rewrite -Efl2l1=> /lit_is_true_flip/negP[].
rewrite inE xpair_eqE => /orP[/andP[/eqP Efl3l /eqP ->]|If].
by rewrite inE negb_or Dfl2l1 //= Dr // inE Efl3l eqxx.
by rewrite Dr // inE If orbT.
Qed.

Lemma valid_add_true m b l l1 ls1 s :
valid m ((l, l1 :: ls1) :: s) ->
lit_get m l == Some (b, true) ->
lit_get m l1 == None ->
valid (lit_set m l1 (Some (b, true))) [:: (l1, inext l1), (l, ls1) & s].
Proof.
move=> [[V1 V2 V3 V4] V5 V6 V7] Gml Gml1.
have [/eqP El1l | Dl1l] := boolP (l1 == l).
by move: Gml Gml1; rewrite El1l => /eqP->.
have l1INl : l1 \in inext l.
have /subsetP->// : (l1 :: ls1) \subset inext l.
by apply: V5; rewrite inE eqxx.
by rewrite inE eqxx.
have Us : uniq [seq i.1 | i <- [:: (l1, inext l1), (l, ls1) & s]].
rewrite map_cons cons_uniq V1 andbT /=.
rewrite inE negb_or (negPf Dl1l).
apply/negP=> Il1.
case: (orP (V4 l1 _)).
- by rewrite map_cons inE Il1 orbT.
- by rewrite /lit_is_true; case: lit_get Gml1 => [[[] []]|].
by rewrite /lit_is_strue lit_get_flip; case: lit_get Gml1 => [[[] []]|].
split => [|l2 ls2|l2|l2 l3 D Dr Il3 Tm1l2] //;
first (split => [|||l2] //=).
- by apply: istack_cons.
- have Dl1fl : l1 != flip l.
by apply/eqP=> HH; move: Gml1; rewrite HH lit_get_flip (eqP Gml).
apply: ostack_same.
rewrite lit_set_eq lit_set_neq ?(eqP Gml) 1?eq_sym //.
by apply: contra Dl1fl => /eqP<-; rewrite flipK.
apply: ostack_weak V3 => [l2 Hl2|l2 Hl2].
have [/eqP El2l| Dl2l1] := boolP (l2 == l1).
move: Hl2 Us; rewrite /= El2l !inE negb_or => /orP[/eqP->//|->//].
by rewrite eqxx.
by rewrite andbF.
move=> HH; move: (HH).
by rewrite lit_is_true_set_neq // (lit_is_true_set_flip _ _ _ _ HH).
have [El2l1|->|[Dl2l1 Dl2fl1]] := lit_dec l2 l1.
- move: Hl2 Us; rewrite /= El2l1 !inE negb_or => /orP[/eqP->//|->//].
by rewrite eqxx.
by rewrite andbF.
- by rewrite /lit_is_strue !lit_get_flip lit_set_eq (eqP Gml1).
by rewrite lit_is_strue_set_neq.
- have [->|->|[Dl2l1 Dl2fl1]] := lit_dec l2 l1.
- by rewrite lit_is_true_set_eq.
- rewrite inE (negPf (flip_neq _)) => /V4.
by rewrite flipK /lit_is_true /lit_is_strue lit_get_flip (eqP Gml1).
rewrite inE (negPf Dl2l1) => /V4.
by rewrite lit_is_true_set_neq // lit_is_strue_set_neq_flip.
- rewrite !inE => /or3P[/eqP[]->->|/eqP[]->->|Il2] //.
apply: subset_trans (_ : l1 :: ls1 \subset _).
by apply/subsetP=> l4; rewrite inE orbC => ->.
by apply: V5; rewrite inE eqxx.
by apply: V5; rewrite inE Il2 orbT.
- have [->|->|[Dl2l1 Dl2fl1]] := lit_dec l2 l1.
- rewrite lit_is_strue_set_eq => /= /andP[B _].
have F : lit_cycle l.
by apply: V6; rewrite /lit_is_strue (eqP Gml).
by apply: lit_cycle_inext F.
- by rewrite lit_is_strue_set_eq_flip /= andbF.
by rewrite lit_is_strue_set_neq //; exact: V6.
have [El2l1|El2fl1|[Dl2l1 Dl2fl1]] := lit_dec l2 l1.
- have: l3 \notin inext l1.
by apply: D; rewrite El2l1 inE eqxx.
by rewrite -El2l1 Il3.
- by move : Tm1l2; rewrite El2fl1 lit_is_true_set_eq_flip.
have Tml2 : lit_is_true m l2.
by move: Tm1l2; rewrite lit_is_true_set_neq.
have [->|El3fl1|[Dl3l1 Dl3fl1]] := lit_dec l3 l1.
- by rewrite lit_is_true_set_eq.
- suff: lit_is_true m l3.
by rewrite El3fl1 /lit_is_true lit_get_flip (eqP Gml1).
apply: V7 Il3 _ => // [ls|].
rewrite El3fl1 !inE xpair_eqE => /orP[/andP[/eqP El2l /eqP->]|Il2].
by rewrite inE negb_or (negPf (flip_neq _)) -El3fl1 D //
El2l !inE eqxx orbT.
by rewrite -El3fl1; apply: D; rewrite !inE Il2 !orbT.
by rewrite El3fl1 flipK /lit_is_strue (eqP Gml1).
rewrite lit_is_true_set_neq //.
apply: V7 Il3 _ => // [ls|HH ls];
rewrite !inE ?xpair_eqE => /orP[/andP[/eqP El /eqP->]|Il].
- by rewrite inE negb_or Dl3l1 D // El !inE eqxx orbT.
- by apply: D; rewrite !inE Il !orbT.
- rewrite inE negb_or Dr ?andbT //.
- by apply: contra Dl2fl1 => /eqP<-; rewrite flipK.
- by rewrite lit_is_strue_set_neq_flip.
by rewrite El !inE eqxx orbT.
apply: Dr; first by rewrite lit_is_strue_set_neq_flip.
by rewrite !inE Il !orbT.
Qed.

Lemma valid_add_true_rev m b l l1 ls1 s :
valid m ((l, l1 :: ls1) :: s) ->
lit_get m l == Some (b, true) ->
lit_get m l1 == Some (false, false) ->
valid (lit_set m l1 (Some (true, true))) [:: (l1, inext l1), (l, ls1) & s].
Proof.
move=> [[V1 V2 V3 V4] V5 V6 V7] Gml Gml1.
have [/eqP El1l | Dl1l] := boolP (l1 == l).
by move: Gml Gml1; rewrite El1l => /eqP->; case: b.
have l1INl : l1 \in inext l.
have /subsetP->// : (l1 :: ls1) \subset inext l.
by apply: V5; rewrite inE eqxx.
by rewrite inE eqxx.
have Us : uniq [seq i.1 | i <- [:: (l1, inext l1), (l, ls1) & s]].
rewrite map_cons cons_uniq V1 andbT /=.
rewrite inE negb_or (negPf Dl1l).
apply/negP=> Il1.
case: (orP (V4 l1 _)).
- by rewrite map_cons inE Il1 orbT.
- by rewrite /lit_is_true; case: lit_get Gml1 => [[[] []]|].
by rewrite /lit_is_strue lit_get_flip; case: lit_get Gml1 => [[[] []]|].
split => [|l2 ls2|l2|l2 l3 D Dr Il3 Tm1l2] //;
first (split => [|||l2] //=).
- by apply: istack_cons.
- apply: ostack_strue; first by rewrite lit_is_strue_set_eq.
apply: ostack_weak V3 => l2 Il2.
have [/eqP El2l1| Dl2l1] := boolP (l2 == l1).
move: Il2 Us; rewrite /= El2l1 !inE negb_or => /orP[/eqP->//|->//].
by rewrite eqxx.
by rewrite andbF.
move=> HH; move: (HH).
by rewrite lit_is_true_set_neq // (lit_is_true_set_flip _ _ _ _ HH).
have [El2l1| ->| [Dl2l1 Dl2fl1]] := lit_dec l2 l1.
- move: Il2 Us; rewrite /= El2l1 !inE negb_or => /orP[/eqP->//|->//].
by rewrite eqxx.
by rewrite andbF.
- by rewrite lit_is_strue_set_eq_flip /lit_is_strue lit_get_flip (eqP Gml1).
by rewrite lit_is_strue_set_neq.
- have [->|->| [Dl2l1 Dl2fl1]] := lit_dec l2 l1.
- by rewrite lit_is_true_set_eq.
- by rewrite flipK lit_is_strue_set_eq orbT.
rewrite inE (negPf Dl2l1) lit_is_true_set_neq //
lit_is_strue_set_neq_flip //=.
by apply: V4.
- rewrite !inE => /or3P[/eqP[]->->|/eqP[]->->|Il2] //.
apply: subset_trans (_ : l1 :: ls1 \subset _).
by apply/subsetP=> l4; rewrite inE orbC => ->.
by apply: V5; rewrite inE eqxx.
by apply: V5; rewrite inE Il2 orbT.
- have [-> _|->|[Dl2l1 Dl2fl1]] := lit_dec l2 l1.
- have F : lit_is_true m (flip l1).
by rewrite /lit_is_true lit_get_flip (eqP Gml1).
have [Ifl1|NIfl1] :=
boolP (flip l1 \in [seq i.1 | i <- (l, l1 :: ls1) :: s]).
apply: connect_trans (_ : connect imply l l1).
by apply: mem_istack V2 _.
by apply: connect1; rewrite -inext_imply.
case: b Gml => Gml.
have : lit_cycle l by apply: V6; rewrite /lit_is_strue (eqP Gml).
by apply: lit_cycle_inext.
have: lit_is_true m (flip l).
apply: V7 F => // [ls Hls||].
by case/negP: NIfl1; apply: map_f Hls.
by rewrite /lit_is_strue flipK (eqP Gml).
by rewrite inext_flip.
by rewrite /lit_is_true lit_get_flip (eqP Gml).
- by rewrite lit_is_strue_set_eq_flip.
by rewrite lit_is_strue_set_neq //; exact: V6.
have [El2l1|El2fl1| [Dl21l Dl2fl1]] := lit_dec l2 l1.
- suff: l3 \notin inext l1 by rewrite -El2l1 Il3.
by apply: D; rewrite inE El2l1 eqxx.
- by move : Tm1l2; rewrite El2fl1 lit_is_true_set_eq_flip.
have Tml2 : lit_is_true m l2 by move: Tm1l2; rewrite lit_is_true_set_neq.
have [->|El3fl1| [Dl3l1 Dl3fl1]] := lit_dec l3 l1.
- by rewrite lit_is_true_set_eq.
- have: flip l2 \notin (inext l1).
apply: Dr; first by rewrite El3fl1 flipK lit_is_strue_set_eq.
by rewrite El3fl1 flipK inE eqxx.
by rewrite -(flipK l1) -El3fl1 inext_flip Il3.
suff: lit_is_true m l3 by rewrite lit_is_true_set_neq.
apply: V7 Il3 _ => // [ls|HH ls].
rewrite !inE xpair_eqE => /orP[/andP[/eqP El2l/eqP->]|Il2].
by rewrite inE negb_or Dl3l1 D // El2l !inE eqxx orbT.
by apply: D; rewrite !inE Il2 !orbT.
rewrite !inE xpair_eqE => /orP[/andP[/eqP Efl3l/eqP->]|Ifl3].
rewrite inE negb_or; apply/andP; split.
by apply: contra Dl2fl1 => /eqP<-; rewrite flipK.
apply: Dr => //.
by rewrite lit_is_strue_set_neq_flip.
by rewrite Efl3l !inE eqxx orbT.
apply: Dr => //.
by rewrite lit_is_strue_set_neq_flip.
by rewrite !inE Ifl3 !orbT.
Qed.

Lemma valid_propage_true n m m1 s k :
valid m s -> propagate n m s = Some (true, k, m1) -> valid m1 [::].
Proof.
elim: n m m1 s => //= n iH m m1 [H1 [_ <-]|[l ls] s1 Vm] //.
case E : lit_get => [[b []]|]; last 2 first.
- apply: iH; apply: valid_cons Vm _.
by rewrite /lit_is_true /= E.
- have [[_ _ _ H] _ _ _] := Vm.
have : l \in [seq i.1 | i <- (l, ls) :: s1] by rewrite inE eqxx.
by move/H; rewrite /lit_is_true /lit_is_strue lit_get_flip E.
case: ls Vm => [Vm|l1 ls1 Vm].
by apply: iH; apply: valid_cons_nil Vm.
case E1 : lit_get => [[b1 []]|] //.
- apply: iH; apply: valid_cons_true Vm _ _.
by rewrite /lit_is_true E.
by rewrite /lit_is_true E1.
- case: b1 E1 => // E1.
apply: iH.
by apply: valid_add_true_rev => //; apply/eqP.
apply: iH.
by apply: valid_add_true => //; [rewrite /lit_is_true E | rewrite E1].
Qed.

Lemma valid_propage_false n m m1 s k :
valid m s -> propagate n m s = Some (false, k, m1) ->
exists l, lit_cycle l /\ lit_cycle (flip l).
Proof.
elim: n m m1 s => //= n iH m m1 [|[l ls] s Vm] //.
case E : lit_get => [[b []]|]; last 2 first.
- apply: iH; apply: valid_cons Vm _.
by rewrite /lit_is_true /= E.
- have [[_ _ _ H] _ _ _] := Vm.
have : l \in [seq i.1 | i <- (l, ls) :: s] by rewrite inE eqxx.
by move/H; rewrite /lit_is_true /lit_is_strue lit_get_flip E.
case: ls Vm => [Vm|l1 ls1 Vm].
by apply: iH; apply: valid_cons_nil Vm.
have X1 : l1 \in inext l.
have/subsetP->// : l1 :: ls1 \subset inext l.
have [_ HH _ _] := Vm.
by apply: HH; rewrite !inE eqxx.
by rewrite inE eqxx.
case E1 : lit_get => [[b1 []]|] //.
- apply: iH; apply: valid_cons_true Vm _ _.
by rewrite /lit_is_true E.
by rewrite /lit_is_true E1.
- case: b1 E1 => // E1.
have F1 : lit_is_strue m (flip l1).
by rewrite /lit_is_strue lit_get_flip E1.
case: b E => E _.
have F : lit_cycle l.
have [_ _ HH _] := Vm.
by apply: HH; rewrite /lit_is_strue E.
exists l1; split; first by apply: lit_cycle_inext F.
have [_ _ HH _] := Vm.
by apply: HH.
have [F2 | F2] := boolP (flip l1 \in [seq i.1 | i <- (l, l1 :: ls1) :: s]).
have [[_ _ HH _] _ _ _] := Vm.
have : ~~ lit_is_strue m (flip l1).
apply: ostack_neg_lit_is_strue HH _ => //=.
- by rewrite /lit_is_true E.
- by rewrite /lit_is_strue E.
by rewrite F1.
have : lit_is_true m (flip l).
have F : flip l \in inext (flip l1) by rewrite inext_flip.
have [_ _ _ HH] := Vm.
apply: HH F _ => // [ls||].
- by move/(map_f (fun x => x.1)) => HH; case/negP: F2.
- by rewrite flipK /lit_is_strue E.
by apply: lit_is_strueW.
by rewrite /lit_is_true lit_get_flip E.
apply: iH.
by apply: valid_add_true_rev => //; apply/eqP.
apply: iH.
by apply: valid_add_true => //; [rewrite /lit_is_true E | rewrite E1].
Qed.

(* Compute the number of iterations that are needed to end the computation *)

Definition mweight m (s : seq (lit * seq lit)) :=
\sum_(i <- s) (size i.2).+1 +
\sum_(l | (l \notin [seq i.1 | i <- s]) &&
((lit_get m l == None) || (lit_get m l == Some (false, false))))
(size (inext l)).+1.

Lemma mweight_lt1 b m l ls s :
valid m ((l, ls) :: s) -> lit_get m l = Some (b, false) ->
mweight m s < mweight m ((l, ls) :: s).
Proof.
move=> Hv Hg.
rewrite /mweight /= big_cons.
set n1 := \sum_(j <- s) _; set n2 := \sum_(j | _) _; set n3 := \sum_(j | _) _.
suff->: n3 = n2.
apply: eq_bigl => l1; rewrite inE negb_or.
case: (l1 =P l) => [->|//].
rewrite Hg //=.
have : lit_is_true m l || lit_is_strue m (flip l).
have [[_ _ _ HH] _ _ _] := Hv.
by apply: HH; rewrite inE eqxx.
rewrite /lit_is_true /lit_is_strue lit_get_flip Hg.
by case: (b); rewrite //= andbF.
Qed.

Lemma mweight_lt2 m l ls s :
valid m ((l, ls) :: s) -> lit_get m l = None ->
mweight m s < mweight m ((l, ls) :: s).
Proof.
move=> Hv Hg.
rewrite /mweight /= big_cons.
set n1 := \sum_(j <- s) _; set n2 := \sum_(j | _) _; set n3 := \sum_(j | _) _.
suff->: n3 = n2.
apply: eq_bigl => l1; rewrite inE negb_or.
case: (l1 =P l) => [->|//].
rewrite Hg //=.
have : lit_is_true m l || lit_is_strue m (flip l).
have [[_ _ _ HH1] _ _ _] := Hv.
by apply: HH1; rewrite inE eqxx.
by rewrite /lit_is_true /lit_is_strue lit_get_flip Hg.
Qed.

Lemma mweight_lt3 m b l s :
valid m ((l, [::]) :: s) -> lit_get m l = Some (b, true) ->
mweight m s < mweight m ((l, [::]) :: s).
Proof.
move=> Hv Hg.
rewrite /mweight /= big_cons.
set n1 := \sum_(j <- s) _; set n2 := \sum_(j | _) _; set n3 := \sum_(j | _) _.
suff->: n3 = n2 by [].
apply: eq_bigl => l1; rewrite inE negb_or.
case: (l1 =P l) => [->|//].
by rewrite Hg //=; case: (b); rewrite andbF.
Qed.

Lemma mweight_lt4 m l l1 ls1 s (m1 := lit_set m l1 (Some (true, true))) :
valid m1 [:: (l1, inext l1), (l, ls1) & s] ->
lit_get m l1 = Some (false, false) ->
mweight m1 [:: (l1, inext l1), (l, ls1) & s] <
mweight m ((l, l1 :: ls1) :: s).
Proof.
move=> Hv Hg.
rewrite /mweight /= !big_cons /=.
set n1 := \sum_(j <- s) _; set n2 := \sum_(j | _) _; set n3 := \sum_(j | _) _.
rewrite {n3}[n3](bigD1 l1) /=; last first.
rewrite Hg andbT.
by have [[/= /andP[HH _] _ _ _] _ _ _] := Hv.
set n3 := \sum_(j | _) _.
suff->: n2 = n3.
apply: eq_bigl => l2; rewrite inE negb_or.
have [->|->|[Dl2l1 Dl2fl1]] := lit_dec l2 l1.
- by rewrite eqxx andbF.
- by rewrite !lit_get_flip Hg lit_set_eq /= !andbF.
by rewrite lit_set_neq // Dl2l1 /= andbT.
Qed.

Lemma mweight_lt5 m b l l1 ls1 s (m1 := lit_set m l1 (Some (b, true))) :
valid m1 [:: (l1, inext l1), (l, ls1) & s] ->
lit_get m l1 = None ->
mweight m1 [:: (l1, inext l1), (l, ls1) & s] < mweight m ((l, l1 :: ls1) :: s).
Proof.
move=> Hv Hg.
rewrite /mweight /= !big_cons /=.
set n1 := \sum_(j <- s) _; set n2 := \sum_(j | _) _; set n3 := \sum_(j | _) _.
rewrite {n3}[n3](bigD1 l1) /=; last first.
rewrite Hg andbT.
by have [[/= /andP[HH _] _ _ _] _ _ _] := Hv.
set n3 := \sum_(j | _) _.
suff FF : n2 <= n3.
have [H1|] := boolP ((flip l1 \in l :: [seq i.1 | i <- s]) || ~~b).
suff->: n2 = n3 by [].
apply: eq_bigl => l2; rewrite inE negb_or.
have [->|->|[Dl2l1 Dl2fl1]] := lit_dec l2 l1.
- by rewrite eqxx andbF.
- rewrite !lit_get_flip Hg lit_set_eq /= .
by case: (_ \in _) H1 => //; case: (b); rewrite ?andbF flip_neq.
by rewrite lit_set_neq // Dl2l1 andbT.
rewrite negb_or negbK !inE negb_or => /andP[/andP[H1 H2] H3].
rewrite [n3](bigD1 (flip l1)) /=; last first.
rewrite lit_get_flip Hg !inE negb_or H1 H2 //=.
by case: (l1) => [] [].
set n4 := \sum_(j | _) _.
suff->: n4 = n2 by apply: leq_addl.
apply: eq_bigl => l2; rewrite inE negb_or.
have [->|->|[Dl2l1 Dl2fl1]] := lit_dec l2 l1.
- by rewrite Hg !inE !eqxx !negb_or //= andbF.
- by rewrite !lit_get_flip lit_set_eq /= H3 eqxx !andbF.
rewrite !inE (negPf Dl2l1) !negb_or !andbT lit_set_neq //=.
by rewrite Dl2fl1 andbT.
Qed.

Lemma mweight_init m l l1 (m1 := lit_set m l1 (Some (false, true))) :
valid m1 [:: (l1, inext l1)] ->
lit_get m l1 = None ->
mweight m1 [:: (l1, inext l1)] = mweight m [::].
Proof.
move=> Hv Hg.
rewrite /mweight /= !big_cons /= big_nil.
set n1 := \sum_(j | _) _; set n2 := \sum_(j | _) _.
rewrite {n2}[n2](bigD1 l1) /=; last first.
by rewrite Hg.
set n2 := \sum_(j | _) _.
rewrite {n2}[n2](bigD1 (flip l1)) /=; last first.
rewrite lit_get_flip Hg /=.
by case: (l1) => [] [].
rewrite {n1}[n1](bigD1 (flip l1)) /=; last first.
rewrite lit_get_flip lit_set_eq //= inE eqxx andbT.
by case: (l1) => [] [].
set n1 := \sum_(j | _) _; set n2 := \sum_(j | _) _.
congr (_ + (_ + _)).+2.
apply: eq_bigl => l2.
have [->|->|[Dl2l1 Dl2fl1]] := lit_dec l2 l1.
- by rewrite inE eqxx andbF.
- by rewrite eqxx !andbF.
by rewrite !inE Dl2l1 lit_set_neq ?andbT.
Qed.

Lemma valid_propage_none n m s :
valid m s -> mweight m s < n -> propagate n m s != None.
Proof.
elim: n m s => //= n iH m [|[l ls] s HH VV] //=.
rewrite ltnS in VV.
case E : lit_get => [[b []]|]; last 2 first.
- apply: iH => //.
apply: valid_cons HH _.
by rewrite /lit_is_true /= E.
apply: leq_trans VV.
by apply: mweight_lt1 E.
- apply: iH => //.
apply: valid_cons HH _.
by rewrite /lit_is_true /= E.
apply: leq_trans VV.
by apply: mweight_lt2 E.
case: ls HH VV => [Hv|l1 ls1 Hv] VV.
apply: iH.
by apply: valid_cons_nil Hv.
apply: leq_trans VV.
by apply: mweight_lt3 E.
case E1 : lit_get => [[b1 []]|] //.
- apply: iH.
- apply: valid_cons_true Hv _ _.
by rewrite /lit_is_true E.
- by rewrite /lit_is_true E1.
by move: VV; rewrite /mweight /= !big_cons //.
- case: b1 E1 => // E1.
have Va :
valid (lit_set m l1 (Some (true, true))) [:: (l1, inext l1), (l, ls1) & s]
by apply: valid_add_true_rev => //; apply/eqP.
apply: iH => //.
apply: leq_trans VV.
by apply: mweight_lt4 E1.
have Va :
valid (lit_set m l1 (Some (b, true))) [:: (l1, inext l1), (l, ls1) & s].
by apply: valid_add_true => //; [rewrite /lit_is_true E | rewrite E1].
apply: iH => //.
apply: leq_trans VV.
by apply: mweight_lt5 E1.
Qed.

Lemma valid_propage_red n k m m1 s :
valid m s -> propagate n m s = Some (true, k, m1) ->
mweight m s < n -> mweight m1 [::] < k.
Proof.
elim: n k m m1 s => //= n iH k m m1 [|[l ls] s HH PP VV] //=.
by move=> H1 [<- <-].
rewrite ltnS in VV.
case E : lit_get PP => [[b []]|] PP; last 2 first.
- apply: iH PP _ => //.
apply: valid_cons HH _.
by rewrite /lit_is_true /= E.
apply: leq_trans VV.
by apply: mweight_lt1 E.
- apply: iH PP _ => //.
apply: valid_cons HH _.
by rewrite /lit_is_true /= E.
apply: leq_trans VV.
by apply: mweight_lt2 E.
case: ls HH VV PP => [Hv|l1 ls1 Hv] VV PP.
apply: iH PP _.
by apply: valid_cons_nil Hv.
apply: leq_trans VV.
by apply: mweight_lt3 E.
case E1 : lit_get PP => [[b1 []]|] PP //.
- apply: iH PP _.
- apply: valid_cons_true Hv _ _.
by rewrite /lit_is_true E.
- by rewrite /lit_is_true E1.
by move: VV; rewrite /mweight /= !big_cons.
- case: b1 E1 PP => // E1 PP.
have Va :
valid (lit_set m l1 (Some (true, true))) [:: (l1, inext l1), (l, ls1) & s]
by apply: valid_add_true_rev => //; apply/eqP.
apply: iH (Va) _ _ => //.
apply: leq_trans VV.
by apply: mweight_lt4 E1.
have Va :
valid (lit_set m l1 (Some (b, true))) [:: (l1, inext l1), (l, ls1) & s].
by apply: valid_add_true => //; [rewrite /lit_is_true E | rewrite E1].
apply: iH (Va) _ _ => //.
apply: leq_trans VV.
by apply: mweight_lt5 E1.
Qed.

Lemma propage_reduce_none n k m m1 s l :
propagate n m s = Some (true, k, m1) ->
lit_get m1 l = None -> lit_get m l = None.
Proof.
elim: n m m1 s => //= n iH m m1[|[l1 ls] s] //=.
by move=> [_ <-].
case E : lit_get => [[b []]|]; last 2 first.
- by apply: iH.
- by apply: iH.
case: ls => [|l2 ls1].
by apply: iH.
case E1 : lit_get => [[[] []]|] //.
- by apply: iH.
- by apply: iH.
- move=> H1 H2.
have := iH _ _ _ H1 H2.
have [->|->|[Dll2 Dlfl2]] := lit_dec l l2.
- by rewrite lit_set_eq.
- by rewrite lit_get_flip lit_set_eq.
by rewrite lit_set_neq.
move=> H1 H2.
have := iH _ _ _ H1 H2.
have [->|->|[Dll2 Dxfl2]] := lit_dec l l2.
- by rewrite lit_set_eq.
- by rewrite lit_get_flip lit_set_eq.
by rewrite lit_set_neq.
Qed.

(* Set the unset variable to true and propagate *)
Fixpoint check_satl m n ls :=
if ls is l :: ls1 then
if lit_get m l is None then
let m1 := lit_set m l (Some (false, true)) in
if propagate n m1 [:: (l, inext l)] is Some (true, n2, m2) then
check_satl m2 n2 ls1
else None
else check_satl m n ls1
else Some m.

Lemma check_satl_correct m n ls :
mweight m [::] < n -> valid m [::] ->
if check_satl m n ls is Some m1 then
[/\ valid m1 [::],
forall l, l \in ls -> lit_get m1 l != None &
forall l, lit_get m1 l = None -> lit_get m l = None]
else exists l, lit_cycle l /\ lit_cycle (flip l).
Proof.
elim: ls m n => //= l ls1 IH m n Hms Hv.
case E: lit_get => [bb|].
have := IH _ _ Hms Hv.
case: check_satl => // m1 [H1 H2 H3]; split => // l1.
rewrite inE => /orP[/eqP->|/H2] //.
by apply/eqP=> /H3; rewrite E.
set M := lit_set _ _ _.
have Mv : valid M [:: (l, inext l)] by apply: valid_init.
have Mms : mweight M [:: (l, inext l)] < n by rewrite mweight_init.
case E1 : propagate => [[[[] n1] m1]|].
- have F : valid m1 [::] by apply: valid_propage_true Mv E1.
have G : mweight m1 [::] < n1 by apply: valid_propage_red E1 _.
have := IH _ _ G F.
case: check_satl => // m2 [H1 H2 H3]; split => // l1.
rewrite inE => /orP[/eqP->|/H2] //.
apply/eqP=> /H3 /(propage_reduce_none _ _ _ _ _ _ E1).
by rewrite lit_set_eq.
move/H3/(propage_reduce_none _ _ _ _ _ _ E1).
have [->|->|[Dl1l Dl1fl]] := lit_dec l1 l.
- by rewrite lit_set_eq.
- by rewrite lit_get_flip lit_set_eq.
by rewrite lit_set_neq.
- by apply: valid_propage_false Mv E1.
have : propagate n M [:: (l, inext l)] != None.
by apply: valid_propage_none.
by rewrite E1.
Qed.

(* Top function *)
Definition check_sat :=
let m := [ffun x => None] in
let n := mweight m [::] in
check_satl m n.+1 [seq (true, v) | v <- enum var].

Definition valuation := {ffun var -> bool}.

Definition eval (val : valuation) l :=
if l.1 then val l.2 else ~~ val l.2.

Lemma eval_flip val l : eval val (flip l) = ~~ eval val l.
Proof.
by case: l => [] [] v //=; rewrite /eval //= negbK.
Qed.

Definition satisfiable :=
[exists val : valuation,
forall cl, (cl \in cls) ==> (eval val cl.1 || eval val cl.2)].

Lemma check_sat_correct : satisfiable = check_sat.
Proof.
rewrite /check_sat.
set m := [ffun x => None].
set n := mweight _ _.
set ls := [seq _ | _ <- _].
have V : valid m [::].
by split => // [[[] b1]|[[] b1] l1 _ _];
rewrite /lit_is_true /lit_is_strue /lit_get ffunE.
have := check_satl_correct m n.+1 ls (leqnn _) V.
case: check_satl => [m1 [H1m H2m H3m]|HH] /=.
apply/idP/existsP.
pose M := [ffun v => (odflt (false, false) (m1 v)).2].
have F1 l : lit_get m1 l != None.
case: l => [b v].
have F2 : lit_get m1 (true, v) != None.
by apply: H2m; apply: map_f => //; rewrite mem_enum.
case: b; rewrite ?(lit_get_flip _ (true, v)) //.
by case: lit_get F2.
have HM l : eval M l = lit_is_true m1 l.
have := F1 l.
by case: l => [[] v];
rewrite /M /eval /lit_is_true /lit_get /= ffunE; case: (m1 _).
exists M.
apply/forallP => [[l1 l2]]; apply/implyP => H1 /=.
rewrite !HM.
have F : l2 \in inext (flip l1).
by rewrite [_ \in _](rgraphK) /= flipK H1.
case E: lit_is_true => //=.
have [_ _ _ HH] := H1m.
apply: HH F _ => //.
by move: E (F1 l1); rewrite /lit_is_true lit_get_flip;
case: lit_get => //= [[[] []]].
apply/idP/negP; rewrite negb_exists; apply/forallP=> /= m1.
rewrite negb_forall; apply/negP=> /forallP /= HF.
have F l1 l2 : l1 [->] l2 -> eval m1 l1 -> eval m1 l2.
by move => /orP[] /(implyP (HF _)) /=;
rewrite eval_flip; do 2 case: eval.
have G l1 l2 : connect imply l1 l2 -> eval m1 l1 -> eval m1 l2.
move=> /connectP [p e_p ->].
elim: p l1 e_p => //= l ls1 IH l1 /andP[/F oH /IH oH1] eH.
by apply/oH1/oH.
case: HH => l [/G H1 /G H2].
move: H1 H2; rewrite flipK eval_flip.
case: eval => //= H1 H2.
by have: false by apply: H2.
by have: false by apply: H1.
Qed.

End TwoSat.

Section MWeightInit.

Variable var : finType.

Lemma card_l : #|[finType of (bool * var)]| = 2 * #|var|.
Proof. by rewrite eq_card_prod //= card_bool. Qed.

Lemma inext_nil l : inext var [::] l = [::].
Proof.
case E : (inext var [::] l) => [|l1 ls] //.
have: l1 \in inext var [::] l by rewrite E inE eqxx.
by rewrite [_ \in _]rgraphK.
Qed.

Lemma card_inext_size cls l : #|inext var cls l| = size (inext var cls l).
Proof.
by rewrite -cardE; apply: eq_card => l1 //=; rewrite [_ \in _]rgraphK.
Qed.

Lemma inext_incr_l cls l1 l2 :
#|inext var ((l1, l2) :: cls) (flip _ l1)| <= #|inext var cls (flip _ l1)|.+1.
Proof.
rewrite (cardD1 l2) /= [_ \in _]rgraphK /imply /= flipK inE eqxx /= ltnS.
apply: subset_leq_card; apply/subsetP => i.
rewrite !inE ![_ \in _]rgraphK /= flipK !inE !xpair_eqE eqxx /=.
case: eqP => //= H1.
case: (_ \in _) => //=.
case: (_ \in _) => //=; rewrite orbF.
by case: eqP => //= <-; case: eqP.
Qed.

Lemma inext_incr_r cls l1 l2 :
#|inext var ((l1, l2) :: cls) (flip _ l2)| <= #|inext var cls (flip _ l2)|.+1.
Proof.
rewrite (cardD1 l1) /= [_ \in _]rgraphK /imply /= flipK !inE eqxx /= orbT ltnS.
apply: subset_leq_card; apply/subsetP => i.
rewrite !inE ![_ \in _]rgraphK /= flipK !inE !xpair_eqE eqxx /=.
case: eqP => //= H1.
case: (_ \in _) => //=.
case: (_ \in _) => //=; rewrite orbF.
by case: eqP => //= ->; case: eqP.
Qed.

Lemma inext_same cls l l1 l2 :
l != flip _ l1 -> l != flip _ l2 ->
#|inext var ((l1, l2) :: cls) l| = #|inext var cls l|.
Proof.
move=> H1 H2.
apply: eq_card => l3.
rewrite ![_ \in _]rgraphK /imply /= !inE !xpair_eqE.
case: eqP => [HH|_ /=]; first by rewrite -HH flipK eqxx in H1.
case: (_ =P l2) => [HH|_ /=]; first by rewrite -HH flipK eqxx in H2.
by rewrite andbF.
Qed.

(* Show that the complexity is linear *)
Lemma check_sat_mweight cls :
mweight var cls [ffun _ => None] [::] <= 2 * (size cls + #|var|).
Proof.
rewrite /mweight /=.
set s := \sum_(l | _) _.
have {s}->: s = \sum_l (size (inext var cls l)).+1.
by apply: eq_bigl => [[[] v]] /=; rewrite /lit_get /= ffunE.
elim: cls => /= [|[l1 l2] cls IH].
set s := \sum_(l | _) _.
have {s}->: s = \sum_(l : (bool * var)) 1.
by apply: eq_bigr => l _; rewrite inext_nil.
by rewrite sum1_card /= card_l.
apply: leq_trans (_ : (\sum_l (size (inext var cls l)).+1).+1 < _); last first.
by rewrite !ltnS.
rewrite (bigD1 (flip _ l1)) //=.
have [/eqP<-|Dl1l2] := boolP (l1 == l2).
rewrite [X in _ <= X.+2](bigD1 (flip _ l1)) //= -!card_inext_size.
set s1 := \sum_(l | _) _; set s2 := \sum_(l | _) _.
have {s2}->: s2 = s1.
apply: eq_bigr => /= l3 Hl3.
by rewrite -!card_inext_size inext_same.
by apply: inext_incr_l.
have F : flip _ l2 != flip _ l1.
by apply: contra Dl1l2 => HH; rewrite -(flipK _ l2) (eqP HH) flipK.
rewrite (bigD1 (flip _ l2)) //=.
rewrite [X in _ <= X.+2](bigD1 (flip _ l1)) //= -addSn.
rewrite [X in _ <= (_ + X).+1](bigD1 (flip _ l2)) //= !addnA -addSn.
set s1 := \sum_(l | _) _; set s2 := \sum_(l | _) _.
have {s2}->: s2 = s1.
apply: eq_bigr => /= l3 /andP[H1l3 H2l3].
by rewrite -!card_inext_size inext_same.