Module SSAfastliveprecomputeRTproof

Require Import SetoidList.

Require Import Coqlib.
Require Import Maps.
Require Import Iteration.

Require Import SSAfastliveutils.
Require Import SSAfastliveprecomputeRT.

Local Open Scope positive_scope.

Module Import Definitions.

Inductive reachable (g: graph) (root: positive) : positive -> Prop :=
  | reachable_root:
      reachable g root root
  | reachable_succ: forall x succs y,
      reachable g root x -> g!x = Some succs -> In y succs ->
      reachable g root y.

Lemma reachable_trans : forall g u1 u2 u3,
  reachable g u1 u2 ->
  reachable g u2 u3 ->
  reachable g u1 u3.
Proof.
  intros. revert dependent u1. induction H0; intros.
  - assumption.
  - econstructor. apply IHreachable; eassumption. eassumption. eassumption.
Qed.

Inductive reduced_reachable (g: graph) (back : list (positive * positive))
  (root: positive) : positive -> Prop :=
  | reduced_reachable_root:
      reduced_reachable g back root root
  | reduced_reachable_succ: forall x succs y,
      reduced_reachable g back root x -> g!x = Some succs -> In y succs ->
      ~ In (x, y) back ->
      reduced_reachable g back root y.

Lemma reduced_reachable_trans : forall g back u1 u2 u3,
  reduced_reachable g back u1 u2 ->
  reduced_reachable g back u2 u3 ->
  reduced_reachable g back u1 u3.
Proof.
  intros. revert dependent u1. induction H0; intros.
  - assumption.
  - econstructor. apply IHreduced_reachable; eassumption.
    eassumption. eassumption. eassumption.
Qed.

Lemma reduced_reachable_pred : forall g back u v,
  reduced_reachable g back u v <->
  u = v \/ exists succs w,
  g ! u = Some succs /\ In w succs /\ ~ In (u, w) back /\ reduced_reachable g back w v.
Proof.
  split; intros.
  { induction H.
  - left; reflexivity.
  - right. destruct IHreduced_reachable.
    + subst x. exists succs, y. split. assumption. split. assumption.
      split. assumption. constructor.
    + destruct H3 as (succs1 & w1 & H31 & H32 & H33 & H34).
      exists succs1, w1. split; [|split; [|split]]; try assumption.
      eapply reduced_reachable_succ; eassumption.
  }
  {
  destruct H. subst v. constructor.
  destruct H as (succs & w & H1 & H2 & H3 & H4).
  revert dependent u. induction H4; intros.
  - eapply reduced_reachable_succ; [constructor| eassumption..].
  - eapply reduced_reachable_succ. apply IHreduced_reachable. assumption.
    assumption. eassumption. eassumption. eassumption.
  }
Qed.

Lemma reduced_reachable_is_reachable : forall g back u v,
  reduced_reachable g back u v ->
  reachable g u v.
Proof.
  induction 1; econstructor; eauto.
Qed.

Inductive reduced_reachable_bis (g: graph) (back : list (node * node))
  : positive -> positive -> Prop :=
  | reduced_reachable_bis_root:
      forall root, reduced_reachable_bis g back root root
  | reduced_reachable_bis_pred: forall root succs x y,
      g ! root = Some succs -> In x succs -> ~ In (root, x) back ->
      reduced_reachable_bis g back x y ->
      reduced_reachable_bis g back root y.

Lemma reduced_reachable_bis_succ :
  forall g back u v,
  reduced_reachable_bis g back u v <->
  u = v \/ exists w succs,
  reduced_reachable_bis g back u w /\ g!w = Some succs /\ In v succs /\
  ~ In (w, v) back.
Proof.
  split; intros.
  - induction H.
    + left; reflexivity.
    + right. destruct IHreduced_reachable_bis.
      * subst y. exists root, succs. repeat split; try eassumption.
        constructor.
      * destruct H3 as (w & succs' & H31 & H32 & H33 & H34).
        exists w, succs'. repeat split; try eassumption.
        econstructor; eauto.
  - destruct H.
    + subst v. constructor.
    + destruct H as (w & succs & H1 & H2 & H3 & H4).
      induction H1.
      * econstructor; [eassumption..|constructor].
      * econstructor; eauto.
Qed.

Lemma reduced_reachable_equiv : forall g back u v,
  reduced_reachable g back u v <-> reduced_reachable_bis g back u v.
Proof.
  split; intros.
  - induction H.
    + constructor.
    + eapply reduced_reachable_bis_succ. right.
      exists x, succs. eauto.
  - induction H.
    + constructor.
    + eapply reduced_reachable_pred. right.
      exists succs, x. eauto.
Qed.

Definition is_in_t_up g back u v :=
  ~ reduced_reachable g back u v
  /\ exists s,
     In (s, v) back
     /\ reduced_reachable g back u s.

Definition directly_included r u v :=
  exists n1 n2 m1 m2,
  r ! u = Some (n1, n2) /\ r ! v = Some (m1, m2) /\
  is_included (m1, m2) (n1, n2).

Lemma directly_included_refl : forall r u,
  r ! u <> None ->
  directly_included r u u.
Proof.
  intros. destruct (r !u) as [(n1, n2)|] eqn:Heq; [|contradiction].
  exists n1, n2, n1, n2.
  split; [|split]; try assumption.
  split; reflexivity.
Qed.

Lemma directly_included_trans : forall r u v w,
  directly_included r u v ->
  directly_included r v w ->
  directly_included r u w.
Proof.
  intros. destruct H as (n1 & n2 & m1 & m2 & H1 & H2 & H3).
  destruct H0 as (m1' & m2' & o1 & o2 & H01 & H02 & H03).
  exists n1, n2, o1, o2.
  rewrite H2 in H01. inv H01.
  split; [|split]; eauto using is_included_trans.
Qed.

Lemma directly_included_dec : forall r u v,
  directly_included r u v \/ ~ directly_included r u v.
Proof.
  intros r u v.
  unfold directly_included.
  destruct (r ! u) as [(n1, n2)|].
  - destruct (r ! v) as [(m1, m2)|].
    + destruct (is_included_dec m1 m2 n1 n2).
      * left. exists n1, n2, m1, m2. eauto.
      * right. intros (n1' & n2' & m1' & m2' & contra1 & contra2 & contra3).
        congruence.
    + right. intros (_ & _ & m1' & m2' & _ & contra2 & _).
      discriminate.
  - right. intros (n1' & n2' & _ & _ & contra1 & _).
    discriminate.
Qed.

Definition cross_included r c u v :=
  exists set w, c ! u = Some set /\ set ! w = Some tt /\ directly_included r w v.

Lemma can_reach_spec : forall r c u v
  (r_defined : r ! u <> None),
  can_reach r c u v = true <-> cross_included r c u v.
Proof.
  intros.
  unfold can_reach, cross_included.
  destruct (r ! v) as [(m1, m2)|] eqn:Hv.
  - split; intros.
    + destruct (c ! u) as [set|] eqn:Hset; [|discriminate].
      exists set. revert H. apply PTree_Properties.fold_rec.
      * intros. apply H0 in H1. destruct H1 as (w & H1).
        exists w. rewrite <- !H. split; [reflexivity | apply H1].
      * intros; discriminate.
      * intros. apply orb_true_iff in H2. destruct H2.
        apply H1 in H2. destruct H2 as (w & H2).
        exists w. repeat split; try apply H2.
        rewrite PTree.gso. apply H2. destruct H2 as (_ & H2 & _). congruence.
        exists k. split; [reflexivity|].
        destruct (r ! k) as [(o1, o2)|] eqn:Heq; [|discriminate].
        split. rewrite PTree.gss. destruct v0; reflexivity.
        exists o1, o2, m1, m2. apply test_is_included_spec in H2.
        split; [|split]; assumption.
    + destruct H as (set & w & H1 & H2 & H3).
      rewrite H1. revert H2. apply PTree_Properties.fold_rec.
      * intros. apply H0. rewrite H; assumption.
      * intros H. rewrite PTree.gempty in H. discriminate.
      * intros. rewrite PTree.gsspec in H4. apply orb_true_iff.
        destruct (peq w k).
        -- subst k. right.
           destruct H3 as (o1 & o2 & m1' & m2' & H31 & H32 & H33).
           rewrite H31. apply test_is_included_spec. congruence.
        -- left; auto.
  - split; intros; [discriminate|].
    destruct H as (_ & _ & _ & _ & _ & _ & m1 & m2 & _ & H & _). congruence.
Qed.

End Definitions.


Section POSTORDER.

Variable ginit: graph.
Variable root: node.

Inductive invariant (s: state) : Prop :=
  Invariant
    (SUB: forall x y, s.(gr)!x = Some y -> ginit!x = Some y)
    (ROOT: s.(gr)!root = None)
    (BELOW1 : forall x n l post, In (x, n, l, post) s.(wrk) ->
              Plt n s.(next))
    (BELOW2: forall u n1 n2, s.(r)!u = Some (n1, n2) -> Ple n1 n2 /\
             Plt n2 s.(next))
    (BELOW3 : forall u n l post, In (u, n, l, post) s.(wrk) ->
              forall v m, s.(r) ! v <> Some (n, m))
    (BELOW4 : NoDupA (fun '(_, n1, _, _) '(_, n2, _, _) => n1 = n2) s.(wrk))
    (INJ: forall u v m n1 n2, s.(r)!u = Some (m, n1) ->
          s.(r)!v = Some (m, n2) -> u = v)
    (REM: forall u l, s.(gr)!u = Some l -> s.(r)!u = None)
    (COLOR: forall u succs n1 n2 v,
            ginit!u = Some succs -> s.(r)!u = Some (n1, n2) ->
            In v succs -> s.(gr)!v = None)
    (WKLIST: forall u n l post, In (u, n, l, post) s.(wrk) ->
             s.(gr)!u = None /\
             exists l', ginit!u = Some l'
                    /\ (forall v, In v l' -> ~In v l -> s.(gr)!v = None)
                    /\ incl l l')
    (GREY: forall u, ginit!u <> None -> s.(gr)!u = None -> s.(r)!u = None ->
           exists n l post, In (u,n,l, post) s.(wrk)).

Inductive postcondition
  (acc : PTree.t (positive * positive)
       * PTree.t (PTree.t unit)
       * PTree.t (list (node * Z))
       * list (node * node))
  : Prop :=
  Postcondition
    (INJ: let '(r, c, t, back) := acc in forall u v m n1 n2, r!u = Some (m, n1) ->
          r!v = Some (m, n2) -> u = v)
    (ROOT: let '(r, c, t, back) := acc in ginit!root <> None -> r!root <> None)
    (SUCCS: let '(r, c, t, back) := acc in
       forall x succs y, ginit!x = Some succs -> r!x <> None ->
       In y succs -> ginit!y <> None -> r!y <> None)
    (BELOW : let '(r, c, t, back) := acc in
      forall u n1 n2, r ! u = Some (n1, n2) -> Ple n1 n2).

Lemma transition_spec:
  forall dom_pre s, invariant s ->
  match transition dom_pre s with inr s' => invariant s' | inl m => postcondition m end.
Proof.
  intros. inv H. unfold transition. destruct (wrk s) as [ | [[[u n] succs] [s_c s_t]] l].
  - (* finished *)
    constructor; intros.
    + eauto.
    + caseEq ((r s)!root); intros. congruence. exploit GREY; eauto.
      intros (?, (?,(?,?))); contradiction.
    + destruct (s.(r)!x) as [(n1, n2)|] eqn:?; try congruence.
      destruct (s.(r)!y) eqn:?; try congruence.
      exploit COLOR; eauto. intros. exploit GREY; eauto.
      intros (?, (?,(?,?))); contradiction.
    + eapply BELOW2. eassumption.
  - (* not finished *)
    destruct succs as [ | v succs ].
    + (* all children of x were traversed *)
      constructor; simpl; intros.
      * eauto.
      * assumption.
      * eapply BELOW1. right. eassumption.
      * rewrite PTree.gsspec in H. destruct (peq u0 u).
        -- inv H. exploit BELOW1. left; reflexivity. xomega.
        -- eauto.
      * (* below3 *)
        rewrite PTree.gsspec. destruct (peq v u).
        -- inv BELOW4. intros contra. apply H2. apply InA_alt.
           eexists. split; [|apply H]. congruence.
        -- eapply BELOW3. right; eassumption.
      * inv BELOW4. assumption.
      * (* inj *)
        rewrite PTree.gsspec in H. rewrite PTree.gsspec in H0.
        destruct (peq u0 u), (peq v u); subst.
        -- reflexivity.
        -- eapply BELOW3 in H0. contradiction. inv H.
           left.
           reflexivity.
        -- eapply BELOW3 in H. contradiction. inv H0.
           left.
           reflexivity.
        -- eauto.
      * (* rem *)
        rewrite PTree.gso; eauto. red; intros; subst u0.
        exploit (WKLIST u); eauto with coqlib.
        intros [A _]. congruence.
      * (* color *)
        rewrite PTree.gsspec in H0. destruct (peq u0 u).
        inv H0. exploit (WKLIST u); eauto with coqlib.
        intros [A [l' [B C]]].
        assert (l' = succs) by congruence. subst l'.
        apply C; auto.
        eauto.
      * (* wklist *)
        eapply WKLIST. eauto with coqlib.
      * (* grey *)
        rewrite PTree.gsspec in H1. destruct (peq u0 u). inv H1.
        exploit GREY; eauto. intros [n' [l' [post A]]]. simpl in A; destruct A.
        congruence.
        exists n', l', post; auto.
    + (* children y needs traversing *)
      destruct ((gr s)!v) as [ succs_v | ] eqn:?.
      * (* y has children *)
        constructor; simpl; intros.
        -- (* sub *)
           rewrite PTree.grspec in H. destruct (PTree.elt_eq x v); eauto. inv H.
        -- (* root *)
           rewrite PTree.gro. auto. congruence.
        -- (* below1 *)
           destruct H as [H | [H | H]].
           ++ inv H. xomega.
           ++ inv H. apply Plt_trans_succ. eapply BELOW1. left; reflexivity.
           ++ apply Plt_trans_succ. eapply BELOW1. right; eassumption.
        -- (* below2 *)
           apply BELOW2 in H. destruct H.
           split; eauto using Plt_trans_succ.
        -- destruct H as [H | [H | H]].
           ++ inv H. intros contra. apply BELOW2 in contra. xomega.
           ++ inv H. eapply BELOW3. left; reflexivity.
           ++ eapply BELOW3. right; eassumption.
        -- constructor; eauto. intros contra. apply InA_alt in contra.
           destruct contra as [(((u0, n0), l0), post0) [contra1 contra2]].
           exploit BELOW1. eassumption. subst n0. xomega.
        -- (* inj *) eauto.
        -- (* rem *)
           rewrite PTree.grspec in H. destruct (PTree.elt_eq u0 v); eauto. inv H.
        -- (* color *)
           rewrite PTree.grspec. destruct (PTree.elt_eq v0 v); eauto.
        -- (* wklist *)
           destruct H as [H | [H | H]].
           ++ inv H. split. apply PTree.grs. exists l0; split. eauto.
              split.
              intros. contradiction. apply incl_refl.
           ++ inv H. exploit WKLIST; eauto with coqlib. intros [A [l' [B [C D]]]].
              split.
              ** rewrite PTree.grspec. destruct (PTree.elt_eq u0 v); auto.
              ** exists l'; split. auto. split. intros. rewrite PTree.grspec.
                 destruct (PTree.elt_eq v0 v); auto. assumption.
           ++ exploit (WKLIST u0); eauto with coqlib. intros [A [l' [B [C D]]]].
              split.
              ** rewrite PTree.grspec. destruct (PTree.elt_eq u0 v); auto.
              ** exists l'; split; auto. split. intros.
                 rewrite PTree.grspec. destruct (PTree.elt_eq v0 v); auto.
                 assumption.
        -- (* grey *)
           rewrite PTree.grspec in H0. destruct (PTree.elt_eq u0 v) in H0.
           subst. eauto with coqlib.
           exploit GREY; eauto. simpl. intros [n1 [l1 [post1 A]]]. destruct A.
           ++ inv H2. eexists; eauto.
           ++ do 3 eexists; eauto.
     * (* y has no children *)
       destruct (s.(r) ! v) as [ (n1, n2) |] eqn:?.
       -- destruct (Pos.ltb_spec0 n1 n).
          ++ (* cross-edge *)
             constructor; simpl; intros; eauto.
             ** (* below 1 *)
                destruct H.
                --- inv H. eapply BELOW1. left. reflexivity.
                --- eapply BELOW1. right. eassumption.
             ** (* BELOW3 *)
                destruct H.
                --- inv H. eapply BELOW3. left; reflexivity.
                --- eapply BELOW3. right. eassumption.
             ** inv BELOW4. constructor.
                --- intros contra. apply H1. apply InA_alt in contra.
                    apply InA_alt. destruct contra as [(((u0, n0), l01), post1) [contra1 contra2]].
                    exists (u0, n0, l01, post1). split; assumption.
                --- assumption.
             ** (* WKLIST *)
                destruct H.
                --- inv H. exploit WKLIST; eauto with coqlib.
                    intros [A [l' [B [C D]]]].
                    split. auto. exists l'; split. auto. split.
                    intros. destruct (peq v v0). congruence.
                    apply C; auto. simpl. intuition congruence.
                    eapply incl_cons_inv; eassumption.
                --- eapply WKLIST; eauto with coqlib.
             ** (* grey *)
                exploit GREY; eauto. intros [n3 [l3 [post3 A]]].
                simpl in A. destruct A.
                inv H2. do 3 eexists; eauto.
                do 3 eexists; eauto.
          ++ constructor; simpl; intros; eauto.
             ** (* below 1 *)
                destruct H.
                --- inv H. eapply BELOW1. left. reflexivity.
                --- eapply BELOW1. right. eassumption.
             ** (* BELOW3 *)
                destruct H.
                --- inv H. eapply BELOW3. left; reflexivity.
                --- eapply BELOW3. right. eassumption.
             ** inv BELOW4. constructor.
                --- intros contra. apply H1. apply InA_alt in contra.
                    apply InA_alt. destruct contra as [(((?, ?), ?), ?) [contra1 contra2]].
                    eexists (_, _, _, _). split; eassumption.
                --- assumption.
             ** (* WKLIST *)
                destruct H.
                --- inv H. exploit WKLIST; eauto with coqlib.
                    intros [A [l' [B [C D]]]].
                    split. auto. exists l'; split. auto. split.
                    intros. destruct (peq v v0). congruence.
                    apply C; auto. simpl. intuition congruence.
                    eapply incl_cons_inv; eassumption.
                --- eapply WKLIST; eauto with coqlib.
             ** (* grey *)
                exploit GREY; eauto. intros [n3 [l3 [post3 A]]].
                simpl in A. destruct A.
                inv H2. do 3 eexists; eauto.
                do 3 eexists; eauto.
       -- { constructor; simpl; intros; eauto.
             ** (* below 1 *)
                destruct H.
                --- inv H. eapply BELOW1. left. reflexivity.
                --- eapply BELOW1. right. eassumption.
             ** (* BELOW3 *)
                destruct H.
                --- inv H. eapply BELOW3. left; reflexivity.
                --- eapply BELOW3. right. eassumption.
             ** inv BELOW4. constructor.
                --- intros contra. apply H1. apply InA_alt in contra.
                    apply InA_alt. destruct contra as [(((?, ?), ?), ?) [contra1 contra2]].
                    eexists (_, _, _, _). split; eassumption.
                --- assumption.
             ** (* WKLIST *)
                destruct H.
                --- inv H. exploit WKLIST; eauto with coqlib.
                    intros [A [l' [B [C D]]]].
                    split. auto. exists l'; split. auto. split.
                    intros. destruct (peq v v0). congruence.
                    apply C; auto. simpl. intuition congruence.
                    eapply incl_cons_inv; eassumption.
                --- eapply WKLIST; eauto with coqlib.
             ** (* grey *)
                exploit GREY; eauto. intros [n3 [l3 [post3 A]]].
                simpl in A. destruct A.
                inv H2. do 3 eexists; eauto.
                do 3 eexists; eauto. }
Qed.

Lemma initial_state_spec:
  invariant (init_state ginit root).
Proof.
  unfold init_state. destruct (ginit!root) as [succs|] eqn:?.
 root has succs *)  constructor; simpl; intros.
 sub *)  rewrite PTree.grspec in H. destruct (PTree.elt_eq x root). inv H. auto.
 root *)  apply PTree.grs.
 below *)  destruct H; [|contradiction]. inv H. xomega.
 below 2 *)  rewrite PTree.gempty in H; inv H.
 below 3 *)  rewrite PTree.gempty. discriminate.
 below 4 *)  constructor. intros contra; inversion contra. constructor.
 inj *)  rewrite PTree.gempty in H; inv H.
 rem *)  apply PTree.gempty.
 color *)  rewrite PTree.gempty in H0; inv H0.
 wklist *)  destruct H; inv H.
  split. apply PTree.grs. eexists; split; eauto.
  split; intuition.
 grey *)  rewrite PTree.grspec in H0. destruct (PTree.elt_eq u root).
  subst. eexists; eauto.
  contradiction.

 root has no succs *)  constructor; simpl; intros.
 sub *)  auto.
 root *)  auto.
 below1 *)  contradiction.
 below 2 *)  rewrite PTree.gempty in H; inv H.
 below 3 *)  rewrite PTree.gempty; discriminate.
 below 4 *)  constructor.
 inj *)  rewrite PTree.gempty in H; inv H.
 rem *)  apply PTree.gempty.
 color *)  rewrite PTree.gempty in H0; inv H0.
 wklist *)  contradiction.
 grey *)  contradiction.
Qed.

Inductive black_reduced_reachable_bis (g: graph)
  (r:PTree.t (positive * positive)) (back : list (node * node))
  : node -> node -> Prop :=
  | black_reduced_reachable_bis_root:
      forall root, black_reduced_reachable_bis g r back root root
  | black_reduced_reachable_bis_pred: forall root succs x y,
      g ! root = Some succs -> In x succs -> r ! x <> None -> ~ In (root, x) back ->
      black_reduced_reachable_bis g r back x y ->
      black_reduced_reachable_bis g r back root y.

Lemma black_reduced_reachable_bis_succ :
  forall g r back u v,
  black_reduced_reachable_bis g r back u v <->
  u = v \/ exists w succs,
  black_reduced_reachable_bis g r back u w /\ g!w = Some succs /\ In v succs /\
  r ! v <> None /\ ~ In (w, v) back.
Proof.
  split; intros.
  - induction H.
    + left; reflexivity.
    + right. destruct IHblack_reduced_reachable_bis.
      * subst y. exists root0, succs. repeat split; try eassumption.
        constructor.
      * destruct H4 as (w & succs' & H31 & H32 & H33 & H34 & H35).
        exists w, succs'. repeat split; try eassumption.
        econstructor; eauto.
  - destruct H.
    + subst v. constructor.
    + destruct H as (w & succs & H1 & H2 & H3 & H4 & H5).
      induction H1.
      * econstructor; [eassumption..|constructor].
      * econstructor; eauto.
Qed.

Lemma black_reduced_reachable_bis_is_reduced_reachable : forall g r back u v,
  black_reduced_reachable_bis g r back u v ->
  reduced_reachable g back u v.
Proof.
  intros. apply reduced_reachable_equiv.
  induction H; econstructor; eauto.
Qed.

Lemma black_reduced_reachable_bis_monotone : forall g r back u v w n1 n2,
  black_reduced_reachable_bis g r back u v ->
  black_reduced_reachable_bis g (PTree.set w (n1, n2) r) back u v.
Proof.
  intros. induction H; econstructor; eauto.
  rewrite PTree.gsspec. destruct (peq x w); [discriminate|assumption].
Qed.

Lemma black_reduced_reachable_bis_back : forall g r back u v x y,
  black_reduced_reachable_bis g r back u v ->
  r ! y = None ->
  black_reduced_reachable_bis g r ((x, y) :: back) u v.
Proof.
  intros. induction H.
  - constructor.
  - econstructor; eauto.
    intros contra. destruct contra; [|contradiction].
    congruence.
Qed.

Inductive invariant4 (s:state) : Prop :=
  Invariant4
    (WRK_NO_DUP : NoDupA (fun '(u, _, _, _) '(v, _, _, _) => u = v) s.(wrk))
    (WRK_NOT_BLACK : forall u n l post, In (u, n, l, post) s.(wrk) ->
      s.(r) ! u = None)
    (WRK_DONE : forall u n l s_c s_t, In (u, n, l, (s_c, s_t)) s.(wrk) ->
      exists l', ginit!u = Some l' /\
      (forall v, In v l' -> ~In v l ->
      s.(r) ! v <> None \/ In (u, v) s.(back)))
    (CLASSIFY_BLACK : forall u succs v, ginit ! u = Some succs ->
      s.(r)!u <> None -> In v succs ->
      In (u, v) s.(back)
      \/ (exists set, s.(c) ! u = Some set /\ set ! v = Some tt)
      \/ directly_included s.(r) u v)
    (CLASSIFY_GRAY : forall u n l s_c s_t, In (u, n, l, (s_c, s_t)) s.(wrk) ->
      exists l', ginit!u = Some l' /\
      (forall v, In v l' -> ~In v l ->
      In (u, v) s.(back)
      \/ (s_c ! v = Some tt)
      \/ exists n1 n2, s.(r) ! v = Some (n1, n2) /\ n <= n1))

    (CROSS_TARGETS_BLACK_GRAY : forall u n l s_c s_t w, In (u, n, l, (s_c, s_t)) s.(wrk) ->
      s_c ! w = Some tt -> s.(r) ! w <> None)
    (CROSS_TARGETS_BLACK_BLACK : forall u set v, s.(c) ! u = Some set -> set ! v = Some tt ->
      s.(r) ! v <> None)
    (CROSS_UPWARDS_GRAY : forall u n l s_c s_t, In (u, n, l, (s_c, s_t)) s.(wrk) ->
      exists l', ginit!u = Some l' /\
      (forall v set w, In v l' -> ~In v l -> ~ In (u, v) s.(back) ->
       s.(c) ! v = Some set -> set ! w = Some tt -> s_c ! w = Some tt))
    (CROSS_UPWARDS_BLACK : forall u succs v set w, ginit ! u = Some succs ->
      s.(r) ! u <> None ->
      In v succs -> ~ In (u, v) s.(back) -> s.(c) ! v = Some set -> set ! w = Some tt ->
      ~ directly_included s.(r) u w -> exists set', s.(c) ! u = Some set' /\ set' ! w = Some tt)

    (CROSS_REFL : forall u, s.(r) ! u <> None -> exists set, s.(c) ! u = Some set /\ set ! u = Some tt).

Inductive postcondition4
  (acc : PTree.t (positive * positive)
       * PTree.t (PTree.t unit)
       * PTree.t (list (node * Z))
       * list (node * node))
  : Prop :=
  Postcondition4
    (CLASSIFY: let '(r, c, t, back) := acc in
      forall u succs v, ginit ! u = Some succs -> r ! u <> None ->
      In v succs ->
      In (u, v) back \/
      (exists set, c ! u = Some set /\ set ! v = Some tt) \/
      directly_included r u v)
    (CROSS_UPWARDS: let '(r, c, t, back) := acc in
      forall u succs v set w, ginit ! u = Some succs -> r ! u <> None ->
      In v succs -> ~ In (u, v) back -> c ! v = Some set -> set ! w = Some tt ->
      ~ directly_included r u w -> exists set', c ! u = Some set' /\ set' ! w = Some tt)
    (CROSS_TARGETS_BLACK : let '(r, c, t, back) := acc in
      forall u set v, c ! u = Some set -> set ! v = Some tt ->
      r ! v <> None)
    (CROSS_REFL : let '(r, c, t, back) := acc in
      forall u, r ! u <> None -> exists set, c ! u = Some set /\ set ! u = Some tt).

Lemma transition_spec4:
  forall dom_pre s (INVARIANT : invariant s),
  invariant4 s ->
  match transition dom_pre s with inr s' => invariant4 s' | inl m => postcondition4 m end.
Proof.
  intros. inv H. unfold transition.
  destruct (wrk s) as [ | [[[u n] succs] [s_c s_t]] l] eqn:HH.
  - (* finished *)
    constructor; intros.
    + eauto.
    + eauto.
    + eauto.
    + eauto.
  - (* not finished *)
    destruct succs as [ | v succs ].
    + (* all children of x were traversed *)
      constructor; simpl; intros.
      * inv WRK_NO_DUP; assumption.
      * inv WRK_NO_DUP. rewrite PTree.gsspec. destruct (peq u0 u).
        -- subst u0. contradiction H2. apply InA_alt. exists (u, n0, l0, post).
           split; [reflexivity|assumption].
        -- eauto with coqlib.
      * exploit WRK_DONE. right; eassumption. intros (l' & H1 & H2).
        exists l'. split; [assumption|]. intros.
        specialize (H2 _ H0 H3). destruct H2.
        -- left. rewrite PTree.gso. assumption. intros contra. subst v.
           exploit WRK_NOT_BLACK. left; reflexivity. congruence.
        -- right; assumption.
      * rewrite PTree.gsspec in H0. destruct (peq u0 u).
        -- subst u0. exploit CLASSIFY_GRAY. left; reflexivity.
           intros (l' & H21 & H22). rewrite H21 in H. inv H.
           pose proof H1 as Hsucc.
           apply H22 in H1; [|intros contra; contradiction].
           destruct H1 as [H1|[H1|H1]].
           ++ left; assumption.
           ++ rewrite PTree.gss.
              destruct ((PTree.filter (fun (v : positive) (_ : unit) =>
                         match (PTree.set u (n, Pos.pred (next s)) (r s)) ! v with
                         | Some i' => negb (test_is_included i' (n, Pos.pred (next s)))
                         | None => false
                         end) s_c) ! v) eqn:Hv.
              --- rewrite PTree.gfilter in Hv. rewrite H1 in Hv.
                  rewrite PTree.gsspec in Hv. destruct (peq v u).
                  +++ subst v. right; right. apply directly_included_refl.
                      rewrite PTree.gss. discriminate.
                  +++ unfold pre in *. (* <- TODO: remove HACK *)
                      destruct (s.(r) ! v) as [(n1, n2)|] eqn:Hrv.
                      *** destruct (test_is_included (n1, n2) (n, Pos.pred (next s))) eqn:Htest.
                          ---- right; right.
                               exists n, (Pos.pred (next s)), n1, n2.
                               split; [|split].
                               ++++ apply PTree.gss.
                               ++++ rewrite PTree.gso by assumption. assumption.
                               ++++ apply test_is_included_spec; assumption.
                          ---- right; left. eexists. split; [reflexivity|].
                               rewrite PTree.gso by assumption.
                               rewrite PTree.gfilter. rewrite H1.
                               unfold is_directly_included. rewrite PTree.gss.
                               rewrite PTree.gso by assumption.
                               rewrite Hrv. rewrite Htest. reflexivity.
                      *** discriminate.
              --- rewrite PTree.gfilter in Hv.
                  rewrite H1 in Hv.
                  rewrite PTree.gsspec in Hv. destruct (peq v u).
                  +++ subst v. right; right. apply directly_included_refl.
                      rewrite PTree.gss. discriminate.
                  +++ unfold pre in *. (* <- TODO: remove HACK *)
                      destruct (s.(r) ! v) as [(n1, n2)|] eqn:Hrv.
                      *** destruct (test_is_included (n1, n2) (n, Pos.pred (next s))) eqn:Htest.
                          ---- right; right.
                               exists n, (Pos.pred (next s)), n1, n2.
                               split; [|split].
                               ++++ apply PTree.gss.
                               ++++ rewrite PTree.gso by assumption. assumption.
                               ++++ apply test_is_included_spec; assumption.
                          ---- discriminate.
                      *** (* impossible, v est black *)
                          exploit WRK_DONE. left; reflexivity.
                          intros (l' & H31 & H32). destruct (H32 v).
                          congruence. intros ?; contradiction.
                          congruence. left; assumption.
           ++ destruct H1 as (n1 & n2 & H11 & H12). right; right.
              do 4 eexists. rewrite PTree.gss.
               split; [reflexivity|].
              rewrite PTree.gso. split; [eassumption|].
              split. assumption.
              destruct INVARIANT as [_ _ _ INVARIANT _ _ _ _ _ _ _].
              apply INVARIANT in H11. xomega.
              exploit WRK_NOT_BLACK. left; reflexivity. congruence.
        -- exploit CLASSIFY_BLACK; try eassumption.
           intros [H2|[H2|H2]].
           ++ left; assumption.
           ++ right; left.
              rewrite PTree.gso by assumption. assumption.
           ++ right; right. destruct H2 as (n1 & n2 & m1 & m2 & H21 & H22 & H23).
              exists n1, n2, m1, m2. rewrite PTree.gso by assumption.
              rewrite PTree.gsspec. destruct (peq v u).
              *** (* impossible *) subst v. exploit WRK_NOT_BLACK.
                  left; reflexivity. congruence.
              *** split; [|split]; assumption.
      * exploit CLASSIFY_GRAY. right; eassumption.
        intros (l' & H1 & H2).
        exists l'. split; [assumption|].
        intros. destruct (H2 _ H0 H3) as [H4|[H4|H4]].
        -- left; assumption.
        -- right; left; assumption.
        -- right; right. destruct H4 as (n1 & n2 & H41 & H42).
           exists n1, n2.
           rewrite PTree.gso. split; assumption.
           exploit WRK_NOT_BLACK. left; reflexivity. congruence.
      * rewrite PTree.gsspec. destruct (peq w u). discriminate.
        eauto with coqlib.
      * rewrite PTree.gsspec. destruct (peq v u). discriminate.
        rewrite PTree.gsspec in H. destruct (peq u0 u).
        -- subst u0.
           eapply CROSS_TARGETS_BLACK_GRAY. left; reflexivity.
           inv H. rewrite PTree.gso in H0 by assumption.
           rewrite PTree.gfilter in H0. destruct (s_c ! v) as [[]|].
           reflexivity. discriminate.
        -- eauto.
      * exploit CROSS_UPWARDS_GRAY. right; eassumption.
        intros (l' & H1 & H2).
        exists l'. split; [assumption|].
        intros.
        rewrite PTree.gso in H5. eauto.
        exploit WRK_NOT_BLACK. left; reflexivity.
        exploit WRK_DONE.
        right; eassumption.
        intros (l0' & H71 & H72). rewrite H1 in H71. inv H71.
        specialize (H72 v H0 H3). destruct H72; congruence.
      * rewrite PTree.gsspec in H0. destruct (peq u0 u).
        -- subst u0. exploit CROSS_UPWARDS_GRAY. left; reflexivity.
           intros (l' & H61 & H62).
           rewrite H61 in H. inv H.
           rewrite PTree.gss. eexists. split. reflexivity.
           rewrite PTree.gsspec in H3. destruct (peq v u). congruence.
           rewrite PTree.gsspec. destruct (peq w u). reflexivity.
           rewrite PTree.gfilter. erewrite H62 by eauto.
           unfold is_directly_included. rewrite PTree.gss.
           specialize (CROSS_TARGETS_BLACK_BLACK _ _ _ H3 H4).
           rewrite PTree.gsspec.
           destruct (peq w u).
           exploit WRK_NOT_BLACK. left; reflexivity. congruence.
           unfold pre in *. (* HACK *)
           destruct ((r s) ! w) as [(m1, m2)|] eqn:Hrw; [|congruence].
           destruct (test_is_included (m1, m2) (n, Pos.pred (next s))) eqn:Htest; [|reflexivity].
           contradiction H5.
           do 4 eexists. rewrite PTree.gss. rewrite PTree.gso by assumption.
           split; [reflexivity|]. split; [eassumption|].
           apply test_is_included_spec; assumption.
        -- rewrite PTree.gso by assumption.
           rewrite PTree.gsspec in H3. destruct (peq v u).
           ** (* impossible : u0 is black, so must be its children
                 (or form a back-edge) *)

              exploit WRK_NOT_BLACK; eauto with coqlib.
              intros.
              exploit CLASSIFY_BLACK; eauto.
              intros [H7|[H7|H7]].
              contradiction.
              destruct H7 as (set' & H71 & H72).
              specialize (CROSS_TARGETS_BLACK_BLACK _ _ _ H71 H72).
              congruence.
              destruct H7 as (_ & _ & n1 & n2 & _ & H7 & _). congruence.
           ** exploit CROSS_UPWARDS_BLACK; eauto. intros contra.
              apply H5. destruct contra as (m1 & m2 & o1 & o2 & contra).
              exists m1, m2, o1, o2. rewrite PTree.gso by assumption.
              rewrite PTree.gso. assumption.
              exploit WRK_NOT_BLACK. left; reflexivity.
              destruct contra as (_ & contra & _). congruence.
      * rewrite PTree.gsspec. destruct (peq u0 u).
        -- subst u0. eexists. split; [reflexivity|].
           rewrite PTree.gss. reflexivity.
        -- rewrite PTree.gso in H by assumption. eauto.
    + (* children y needs traversing *)
      destruct ((gr s)!v) as [ succs_v | ] eqn:?.
      * (* y has children *)
        constructor; simpl; intros.
        -- constructor; [|assumption]. intros contra. apply InA_alt in contra.
           destruct contra as ((((w, m), l'), post) & contra1 & contra2).
           destruct INVARIANT as [_ _ _ _ _ _ _ _ _ INVARIANT _].
           exploit INVARIANT. rewrite HH. eassumption. intros (H & _).
           congruence.
        -- destruct H as [H|[H|H]].
           ++ inv H.
              destruct INVARIANT as [_ _ _ _ _ _ _ INVARIANT _ _ _]. eauto.
           ++ inv H. eauto with coqlib.
           ++ eauto with coqlib.
        -- destruct H as [H|[H|H]].
           ++ inv H.
              destruct INVARIANT as [INVARIANT _ _ _ _ _ _ _ _ _ _].
              apply INVARIANT in Heqo. exists l0.
              split; [assumption|]. intros; contradiction.
           ++ inv H. eauto with coqlib.
           ++ eauto with coqlib.
        -- eauto.
        -- destruct H as [H|[H|H]].
           ++ inv H.
              destruct INVARIANT as [INVARIANT _ _ _ _ _ _ _ _ _ _].
              apply INVARIANT in Heqo. exists l0.
              split; [assumption|]. intros; contradiction.
           ++ inv H. eauto with coqlib.
           ++ eauto with coqlib.
        -- destruct H as [H|[H|H]].
           ++ inv H. rewrite PTree.gempty in H0. discriminate.
           ++ inv H. eauto with coqlib.
           ++ eauto with coqlib.
        -- eauto.
        -- destruct H as [H|[H|H]].
           ++ inv H.
              destruct INVARIANT as [INVARIANT _ _ _ _ _ _ _ _ _ _].
              apply INVARIANT in Heqo. exists l0.
              split; [assumption|]. intros; contradiction.
           ++ inv H. eauto with coqlib.
           ++ eauto with coqlib.
        -- eauto.
        -- eauto.
      * (* y has no children *)
        destruct (s.(r) ! v) as [ (n1, n2) |] eqn:?.
        -- (* forward edge *)
              constructor; simpl; intros.
              ** inv WRK_NO_DUP. constructor; [|assumption].
                 intros contra; apply H1. apply InA_alt in contra.
                 apply InA_alt. destruct contra as [(((w, m), l'), post) contra].
                 exists (w, m, l', post). assumption.
              ** destruct H.
                 --- inv H. eauto with coqlib.
                 --- eauto with coqlib.
              ** destruct H.
                 --- inv H. exploit WRK_DONE. left; reflexivity.
                     intros (l' & H1 & H2).
                     exists l'. split; [assumption|].
                     intros. destruct (peq v0 v).
                     +++ left; congruence.
                     +++ apply H2. assumption. intros contra.
                         destruct contra; congruence.
                 --- eauto with coqlib.
              ** eauto.
              ** destruct H.
                 --- inv H. exploit CLASSIFY_GRAY. left; reflexivity.
                     intros (l' & H1 & H2).
                     exists l'. split; [assumption|].
                     intros. destruct (peq v0 v).
                     +++ subst v0. right; left.
                         destruct (CROSS_REFL v) as (set & H3 & H4); try congruence.
                         rewrite H3. apply union_strict_correct. left; assumption.
                     +++ edestruct H2 as [H3|[H3|H3]].
                         eassumption. intros contra; destruct contra; congruence.
                         left; assumption. right; left.
                         destruct (s.(c) ! v).
                         rewrite union_strict_correct. right; assumption.
                         assumption. right; right; assumption.
                 --- eauto with coqlib.
              ** destruct H.
                 --- inv H. destruct (s.(c) ! v) eqn:Heqc.
                     apply union_strict_correct in H0.
                     destruct H0. eauto. eauto with coqlib.
                     eauto with coqlib.
                 --- eauto with coqlib.
              ** eauto.
              ** destruct H.
                 --- inv H. exploit CROSS_UPWARDS_GRAY. left; reflexivity.
                     intros (l' & H1 & H2).
                     exists l'. split; [assumption|].
                     intros. destruct (peq v0 v). subst v0. rewrite H4.
                     apply union_strict_correct. left; assumption.
                     destruct (s.(c) ! v) eqn:Heqc.
                     +++ apply union_strict_correct. right. eapply H2; eauto.
                         intros contra; destruct contra; congruence.
                     +++ eapply H2; eauto.
                         intros contra; destruct contra; congruence.
                 --- eauto with coqlib.
              ** eauto.
              ** eauto.
        -- (* back-edge *)
           constructor; simpl; intros.
           ++ inv WRK_NO_DUP. constructor; [|assumption].
              intros contra; apply H1. apply InA_alt in contra.
              apply InA_alt. destruct contra as [(((w, m), l'), post) contra].
              exists (w, m, l', post). assumption.
           ++ destruct H.
              inv H. eauto with coqlib.
              eauto with coqlib.
           ++ destruct H.
              ** inv H. exploit WRK_DONE. left; reflexivity.
                 intros (l' & H1 & H2).
                 exists l'. split; [assumption|].
                 intros. destruct (peq v0 v).
                 +++ right; left; congruence.
                 +++ destruct (H2 v0). assumption.
                     intros contra; destruct contra; congruence.
                     left; assumption. right; right; assumption.
              ** exploit WRK_DONE. right; eassumption. intros (l' & H1 & H2).
                 exists l'. split; [assumption|]. intros.
                 destruct (H2 _ H0 H3). left; assumption.
                 right; right; assumption.
           ++ exploit CLASSIFY_BLACK; eauto.
              intros [H2|[H2|H2]]; eauto.
           ++ destruct H.
               ** inv H. exploit CLASSIFY_GRAY. left; reflexivity.
                  intros (l' & H1 & H2).
                  exists l'. split; [assumption|].
                  intros. destruct (peq v0 v).
                  --- left; left. congruence.
                  --- edestruct H2 as [H3|[H3|H3]]; eauto.
                      intros contra; destruct contra; congruence.
               ** exploit CLASSIFY_GRAY. right; eassumption.
                  intros (l' & H1 & H2). exists l'. split; [assumption|].
                  intros. specialize (H2 _ H0 H3).
                  destruct H2 as [H2|[H2|H2]]; eauto.
           ++ destruct H.
               --- inv H. eauto with coqlib.
               --- eauto with coqlib.
           ++ eauto.
           ++ destruct H.
               --- inv H. exploit CROSS_UPWARDS_GRAY. left; reflexivity.
                   intros (l' & H1 & H2).
                   exists l'. split; [assumption|].
                   intros. destruct (peq v0 v). subst v0.
                   contradiction H3. left; reflexivity.
                   eapply H2; eauto. intros contra; destruct contra; congruence.
               --- exploit CROSS_UPWARDS_GRAY. right; eassumption.
                   intros (l' & H1 & H2). exists l'. split; [assumption|].
                   eauto.
           ++ eauto.
           ++ eauto.
Qed.

Lemma initial_state_spec4:
  invariant4 (init_state ginit root).
Proof.
  unfold init_state. destruct (ginit!root) as [succs|] eqn:?.

  - (* root has succs *)
    constructor; simpl; intros.
    + repeat constructor. intros contra; inversion contra.
    + rewrite PTree.gempty. reflexivity.
    + destruct H; inv H. exists l. split; [assumption|].
      intros. contradiction.
    + rewrite PTree.gempty in H0. contradiction.
    + destruct H; inv H. exists l. split; [assumption|].
      intros. contradiction.
    + destruct H; inv H. rewrite PTree.gempty in H0. discriminate.
    + rewrite PTree.gempty in H. discriminate.
    + destruct H; inv H. exists l. split; [assumption|].
      intros. contradiction.
    + rewrite PTree.gempty in H3. discriminate.
    + rewrite PTree.gempty in H. contradiction.
  - constructor; simpl; intros.
    + repeat constructor.
    + contradiction.
    + contradiction.
    + rewrite PTree.gempty in H0. contradiction.
    + contradiction.
    + contradiction.
    + rewrite PTree.gempty in H. discriminate.
    + contradiction.
    + rewrite PTree.gempty in H3. discriminate.
    + rewrite PTree.gempty in H. contradiction.
Qed.

Inductive invariant2 (s:state) : Prop :=
  Invariant2
    (REACHABLE_GRAY_GRAY : Sorted (fun '(u, _, _, _) '(v, _, _, _) => exists succs,
      ginit ! v = Some succs /\ In u succs /\ ~ In (v, u) s.(back)) s.(wrk))
    (REACHABLE_GRAY_BLACK: Sorted (fun '(_, n, _, _) '(u, m, _, _) =>
      forall v n1 n2, s.(r) ! v = Some (n1, n2) -> m <= n1 < n ->
      black_reduced_reachable_bis ginit s.(r) s.(back) u v) s.(wrk))
    (REACHABLE_GRAY_BLACK_LAST: forall u n l post v n1 n2 wrk',
      s.(wrk) = (u, n, l, post) :: wrk' ->
      s.(r) ! v = Some (n1, n2) -> (n <= n1)%positive ->
      black_reduced_reachable_bis ginit s.(r) s.(back) u v)
    (REACHABLE: forall u v n1 n2 m1 m2, s.(r) ! u = Some (n1, n2) ->
      s.(r) ! v = Some (m1, m2) -> is_included (m1, m2) (n1, n2) ->
      black_reduced_reachable_bis ginit s.(r) s.(back) u v)
    (PREORDER_GRAY_GRAY :
      Sorted (fun '(_, n, _, _) '(_, m, _, _) => (m < n)%positive) s.(wrk))
    (PREORDER_GRAY_BLACK : forall u n l post v n1 n2, In (u, n, l, post) s.(wrk) ->
      s.(r) ! v = Some (n1, n2) -> (n < n1 \/ n2 < n)%positive)
    (CASES:
      forall u v n1 n2 m1 m2, s.(r) ! u = Some (n1, n2) ->
      s.(r) ! v = Some (m1, m2) ->
      m2 < n1 \/ n2 < m1
      \/ is_included (m1, m2) (n1, n2) \/ is_included (n1, n2) (m1, m2))

    (BACK_NOT_WHITE : forall u v, In (u, v) s.(back) ->
      s.(gr) ! u = None /\ s.(gr) ! v = None).

Inductive postcondition2
  (acc : PTree.t (positive * positive)
       * PTree.t (PTree.t unit)
       * PTree.t (list (node * Z))
       * list (node * node))
  : Prop :=
  Postcondition2
    (REACHABLE: let '(r, c, t, back) := acc in
      forall u v n1 n2 m1 m2, r ! u = Some (n1, n2) ->
      r ! v = Some (m1, m2) -> is_included (m1, m2) (n1, n2) ->
      reduced_reachable ginit back u v)
    (CASES: let '(r, c, t, back) := acc in
      forall u v n1 n2 m1 m2,
      r ! u = Some (n1, n2) ->
      r ! v = Some (m1, m2) ->
      m2 < n1 \/ n2 < m1
      \/ is_included (m1, m2) (n1, n2)
      \/ is_included (n1, n2) (m1, m2)) .

Lemma HdRel_impl : forall {A} (R1 R2 : A -> A -> Prop) a (l : list A)
  (R_impl : forall y, In y l -> R1 a y -> R2 a y) (l_Sorted : HdRel R1 a l),
  HdRel R2 a l.
Proof.
  intros. destruct l_Sorted; constructor.
  eauto with coqlib.
Qed.

Lemma Sorted_impl : forall {A} (R1 R2 : A -> A -> Prop) (l : list A)
  (R_impl : forall x y, In x l -> In y l -> R1 x y -> R2 x y) (l_Sorted : Sorted R1 l),
  Sorted R2 l.
Proof.
  intros. induction l_Sorted.
  - constructor.
  - constructor.
    + eauto with coqlib.
    + apply (HdRel_impl R1); eauto with coqlib.
Qed.

Lemma Forall_impl_in : forall {A} (P Q:A->Prop) l,
  (forall a : A, In a l -> P a -> Q a) -> Forall P l -> Forall Q l.
Proof.
  intros. induction H0.
  - constructor.
  - constructor; eauto with coqlib.
Qed.

Lemma StronglySorted_impl : forall {A} (R1 R2 : A -> A -> Prop) (l : list A)
  (R_impl : forall x y, In x l -> In y l -> R1 x y -> R2 x y) (l_Sorted : StronglySorted R1 l),
  StronglySorted R2 l.
Proof.
  intros. induction l_Sorted.
  - constructor.
  - constructor.
    + eauto with coqlib.
    + apply (Forall_impl_in (R1 a)); eauto with coqlib.
Qed.

Lemma Sorted_proj : forall {A B} (f : A -> B) (R : A -> A -> Prop) (l1 l2 : list A),
  (forall a1 a2 a3 a4, f a1 = f a2 -> f a3 = f a4 -> R a1 a3 -> R a2 a4) ->
  eqlistA (fun x y => f x = f y) l1 l2 ->
  Sorted R l1 ->
  Sorted R l2.
Proof.
  intros. revert dependent l2.
  induction H1; intros.
  - inv H0. constructor.
  - inv H2. constructor. eauto.
    inv H0; inv H7. constructor.
    constructor; eauto.
Qed.

Lemma eqlistA_refl : forall {A} (eqA : A -> A -> Prop) l,
  Reflexive eqA ->
  eqlistA eqA l l.
Proof.
  induction l; constructor; eauto.
Qed.

Lemma transition_spec2:
  forall dom_pre s (INVARIANT : invariant s) (INVARIANT4 : invariant4 s),
  invariant2 s ->
  match transition dom_pre s with inr s' => invariant2 s' | inl m => postcondition2 m end.
Proof.
  intros. inv H. unfold transition. destruct (wrk s) as [ | [[[u n] succs] [s_c s_t]] l] eqn:HH.
  - constructor; intros.
    + eapply black_reduced_reachable_bis_is_reduced_reachable; eauto.
    + eauto.
  - (* not finished *)
    destruct succs as [ | v succs ].
    + (* all children of x were traversed *)
      constructor; simpl; intros.
      * inversion REACHABLE_GRAY_GRAY; assumption.
      * inv REACHABLE_GRAY_BLACK. eapply Sorted_impl; try eassumption.
        intros (((v, n1), l1), post1) (((w, n2), l2), post2).
        intros.
        rewrite PTree.gsspec in H4. destruct (peq v0 u).
        -- inv H4. apply Sorted_StronglySorted in PREORDER_GRAY_GRAY.
           ++ inv PREORDER_GRAY_GRAY. rewrite Forall_forall in H8.
              specialize (H8 _ H). simpl in H8. xomega.
           ++ intros (((_, m1), _), _) (((_, m2), _), _) (((_, m3), _), _).
              eauto using Pos.lt_trans.
       -- apply black_reduced_reachable_bis_monotone. eauto.
      * rewrite PTree.gsspec in H0. destruct (peq v u).
        -- subst v. inv REACHABLE_GRAY_GRAY.
           inv H5. destruct H2 as (succs & H21 & H22 & H23).
           econstructor; try eassumption.
           rewrite PTree.gss. discriminate. constructor.
        -- assert (n1 < n \/ n <= n1) by xomega. destruct H2.
           ++ inv REACHABLE_GRAY_BLACK. inv H6.
              apply black_reduced_reachable_bis_monotone. eauto.
           ++ inv REACHABLE_GRAY_GRAY. inv H6.
              destruct H3 as (succs & H31 & H32 & H33).
              econstructor; eauto.
              rewrite PTree.gss; discriminate.
              apply black_reduced_reachable_bis_monotone. eauto.
      * rewrite PTree.gsspec in H, H0. destruct (peq u0 u), (peq v u).
        -- subst. constructor.
        -- subst u0. apply black_reduced_reachable_bis_monotone.
           eapply REACHABLE_GRAY_BLACK_LAST. reflexivity. eassumption.
           inv H. xomega.
        -- (* impossible *)
           subst v. inv H0. exploit PREORDER_GRAY_BLACK.
           left; reflexivity. eassumption. intros.
           destruct H0.
           xomega.
           destruct INVARIANT as [_ _ INVARIANT _ _ _ _ _ _ _ _].
           exploit INVARIANT. rewrite HH. left; reflexivity.
           xomega.
        -- apply black_reduced_reachable_bis_monotone. eauto.
      * inversion PREORDER_GRAY_GRAY; assumption.
      * rewrite PTree.gsspec in H0. destruct (peq v u).
        -- inv H0. left. apply Sorted_StronglySorted in PREORDER_GRAY_GRAY.
           ++ inv PREORDER_GRAY_GRAY. rewrite Forall_forall in H3.
              apply (H3 _ H).
           ++ intros (((_, m1), _), _) (((_, m2), _), _) (((_, m3), _), _).
              eauto using Pos.lt_trans.
        -- eauto with coqlib.
      * rewrite PTree.gsspec in H, H0. destruct (peq u0 u), (peq v u).
        -- inv H. inv H0. right; right; left. split; reflexivity.
        -- subst u0. inv H. exploit PREORDER_GRAY_BLACK.
           left; reflexivity. eassumption.
           intros [H|H].
           ++ destruct INVARIANT as [_ _ _ INVARIANT _ _ _ _ _ _ _].
              apply INVARIANT in H0. right; right; left. xomega.
           ++ left. xomega.
        -- subst v. inv H0. exploit PREORDER_GRAY_BLACK.
           left; reflexivity. eassumption.
           intros [H1|H1].
           ++ destruct INVARIANT as [_ _ _ INVARIANT _ _ _ _ _ _ _].
              apply INVARIANT in H. right; right; right. xomega.
           ++ right; left. xomega.
        -- eauto.
      * eauto with coqlib.
    + (* children y needs traversing *)
      destruct ((gr s)!v) as [ succs_v | ] eqn:?.
      * (* y has children *)
        constructor; simpl; intros.
        -- constructor. assumption. constructor.
           destruct INVARIANT as [_ _ _ _ _ _ _ _ _ INVARIANT _].
           exploit INVARIANT. rewrite HH. left; reflexivity.
           intros (_ & l' & H & H1 & H2).
           exists l'. split; [|split]; try eassumption.
           apply H2. left; reflexivity.
           intros contra. apply BACK_NOT_WHITE in contra.
           destruct contra as (_ & contra). congruence.
        -- constructor. assumption. constructor.
           intros. eapply REACHABLE_GRAY_BLACK_LAST.
           reflexivity. eassumption. apply H0.
        -- inv H.
           destruct INVARIANT as [_ _ _ INVARIANT _ _ _ _ _ _ _].
           apply INVARIANT in H0. xomega.
        -- eauto.
        -- constructor. assumption. constructor.
           destruct INVARIANT as [_ _ INVARIANT _ _ _ _ _ _ _ _].
           eapply INVARIANT. rewrite HH. left; reflexivity.
        -- destruct H as [H | [H|H]].
           ++ (* no black node has a preorder greater than v *)
              destruct INVARIANT as [_ _ _ INVARIANT _ _ _ _ _ _ _].
              apply INVARIANT in H0. inv H; xomega.
           ++ inv H. eauto with coqlib.
           ++ eauto with coqlib.
        -- eauto.
        -- exploit BACK_NOT_WHITE. eassumption. intros (H1 & H2).
           rewrite 2!PTree.grspec.
           destruct (PTree.elt_eq u0 v), (PTree.elt_eq v0 v); split; eauto.
      * (* y has no children *)
        destruct (s.(r) ! v) as [ (n1, n2) |] eqn:?.
        -- destruct (Pos.ltb_spec0 n1 n).
           ++ (* cross-edge *)
              constructor; simpl; intros.
              ** inv REACHABLE_GRAY_GRAY. constructor. assumption.
                 inv H2; constructor; assumption.
              ** inv REACHABLE_GRAY_BLACK. constructor. assumption.
                 inv H2; constructor; assumption.
              ** inv H. eauto.
              ** eauto.
              ** inv PREORDER_GRAY_GRAY. constructor. assumption.
                 inv H2; constructor; assumption.
              ** destruct H.
                 --- inv H. eauto with coqlib.
                 --- eauto with coqlib.
              ** eauto.
              ** eauto.
           ++ (* forward edge *)
              constructor; simpl; intros.
              ** inv REACHABLE_GRAY_GRAY. constructor. assumption.
                 inv H2; constructor; assumption.
              ** inv REACHABLE_GRAY_BLACK. constructor. assumption.
                 inv H2; constructor; assumption.
              ** inv H. eauto.
              ** eauto.
              ** inv PREORDER_GRAY_GRAY. constructor. assumption.
                 inv H2; constructor; assumption.
              ** destruct H.
                 --- inv H. eauto with coqlib.
                 --- eauto with coqlib.
              ** eauto.
              ** eauto.
        -- constructor; simpl; intros.
           ++ inv REACHABLE_GRAY_GRAY. constructor.
              ** eapply Sorted_impl; try eassumption.
                 intros (((x, n1), l1), post1) (((y, n2), l2), post2).
                 intros.
                 destruct H3 as (succs' & H31 & H32 & H33).
                 exists succs'. split; [assumption|]. split; [assumption|].
                 intros contra. destruct contra; [|contradiction].
                 inv H3.
                 destruct INVARIANT4 as [INVARIANT4 _ _ _ _ _ _ _ _].
                 rewrite HH in INVARIANT4.
                 inv INVARIANT4. apply H5. apply InA_alt.
                 eexists (_, _, _, _). split. reflexivity. eassumption.
              ** destruct l as [|(((w, m), l), post)]. constructor.
                 inv H2.
                 destruct H0 as (succs' & H01 & H02 & H03).
                 constructor.
                 exists succs'. split; [assumption|]. split; [assumption|].
                 intros contra. destruct contra; [|contradiction].
                 inv H.
                 destruct INVARIANT4 as [INVARIANT4 _ _ _ _ _ _ _ _].
                 rewrite HH in INVARIANT4.
                 inv INVARIANT4. apply H2. apply InA_alt.
                 eexists (_, _, _, _). split. reflexivity. left; reflexivity.
          ++ inv REACHABLE_GRAY_BLACK. constructor.
             ** eapply Sorted_impl; try eassumption.
                intros (((x, n1), l1), post1) (((y, n2), l2), post2).
                intros. apply black_reduced_reachable_bis_back; eauto.
             ** destruct l as [|(((w, m), l), post)]. constructor.
                 inv H2.
                 constructor. eauto using black_reduced_reachable_bis_back.
          ++ inv H. eauto using black_reduced_reachable_bis_back.
          ++ eauto using black_reduced_reachable_bis_back.
          ++ inv PREORDER_GRAY_GRAY. constructor. assumption.
             inv H2; constructor; assumption.
          ++ destruct H.
             ** inv H. eauto with coqlib.
             ** eauto with coqlib.
          ++ eauto.
          ++ destruct H.
             ** inv H. split; [|congruence].
                destruct INVARIANT as [_ _ _ _ _ _ _ _ _ INVARIANT _].
                exploit INVARIANT. rewrite HH. left; reflexivity.
                intros (H & _). assumption.
             ** eauto.
Qed.

Lemma initial_state_spec2:
  invariant2 (init_state ginit root).
Proof.
  unfold init_state. destruct (ginit!root) as [succs|] eqn:?.

  - (* root has succs *)
    constructor; simpl; intros.
    + repeat constructor.
    + repeat constructor.
    + rewrite PTree.gempty in H0. discriminate.
    + rewrite PTree.gempty in H. discriminate.
    + repeat constructor.
    + rewrite PTree.gempty in H0. discriminate.
    + rewrite PTree.gempty in H0. discriminate.
    + contradiction.
  - constructor; simpl; intros.
    + constructor.
    + constructor.
    + discriminate.
    + rewrite PTree.gempty in H. discriminate.
    + constructor.
    + contradiction.
    + rewrite PTree.gempty in H0. discriminate.
    + contradiction.
Qed.

Inductive invariant3 (s:state) : Prop :=
  Invariant3
    (CROSS_GRAY : forall u n l s_c s_t v, In (u, n, l, (s_c, s_t)) s.(wrk) ->
      s_c ! v = Some tt -> black_reduced_reachable_bis ginit s.(r) s.(back) u v)
    (CROSS: forall u set v, s.(c) ! u = Some set -> set ! v = Some tt ->
      black_reduced_reachable_bis ginit s.(r) s.(back) u v)

    (BACK_REVERSE_WRK :
      StronglySorted (fun '(u, _, _, _) '(v, _, _, _) => ~ In (v, u) s.(back)) s.(wrk))
    (WRK_BACK_NOT_BLACK : forall u n l post, In (u, n, l, post) s.(wrk) ->
      exists l', ginit!u = Some l' /\
      (forall v, In v l' ->
      In (u, v) s.(back) -> s.(r) ! v = None)).

Inductive postcondition3
  (acc : PTree.t (positive * positive)
       * PTree.t (PTree.t unit)
       * PTree.t (list (node * Z))
       * list (node * node))
  : Prop :=
  Postcondition3
    (CROSS: let '(r, c, t, back) := acc in
      forall u set v, c ! u = Some set -> set ! v = Some tt ->
      reduced_reachable ginit back u v).

Lemma transition_spec3:
  forall dom_pre s (INVARIANT : invariant s)
                (INVARIANT2 : invariant2 s)
                (INVARIANT4 : invariant4 s),
  invariant3 s ->
  match transition dom_pre s with inr s' => invariant3 s' | inl m => postcondition3 m end.
Proof.
  intros. inv H. unfold transition.
  destruct (wrk s) as [ | [[[u n] succs] [s_c s_t]] l] eqn:HH.
  - (* finished *)
    constructor; intros.
    eapply black_reduced_reachable_bis_is_reduced_reachable.
    eauto.
  - (* not finished *)
    destruct succs as [ | v succs ].
    + (* all children of x were traversed *)
      constructor; simpl; intros.
      * apply black_reduced_reachable_bis_monotone. eauto.
      * apply black_reduced_reachable_bis_monotone.
        rewrite PTree.gsspec in H. destruct (peq u0 u).
        ++ subst u0. inv H.
           rewrite PTree.gsspec in H0. destruct (peq v u).
           ** subst v. constructor.
           ** eapply CROSS_GRAY. left; reflexivity.
              rewrite PTree.gfilter in H0.
              destruct (s_c ! v) as [[]|]; [reflexivity|discriminate].
        ++ eauto.
      * inv BACK_REVERSE_WRK. assumption.
      * exploit WRK_BACK_NOT_BLACK. right; eassumption.
        intros (l' & H1 & H2).
        exists l'. split; [assumption|]. intros.
        rewrite PTree.gso. apply H2; assumption.
        inv BACK_REVERSE_WRK. rewrite Forall_forall in H7.
        apply H7 in H. congruence.
    + (* children y needs traversing *)
      destruct ((gr s)!v) as [ succs_v | ] eqn:?.
      * (* y has children *)
        constructor; simpl; intros.
        -- destruct H as [H|[H|H]].
           ++ inv H. rewrite PTree.gempty in H0. discriminate.
           ++ inv H. eauto with coqlib.
           ++ eauto with coqlib.
        -- eauto.
        -- constructor. assumption. apply Forall_forall.
           intros (((w, m), l'), post') H.
           intros contra.
           destruct INVARIANT2 as [_ _ _ _ _ _ _ INVARIANT2].
           apply INVARIANT2 in contra. destruct contra as (_ & contra).
           congruence.
        -- destruct H as [H|[H|H]].
           ++ inv H. exists l0. split.
              destruct INVARIANT as [INVARIANT _ _ _ _ _ _ _ _ _ _].
              eauto. intros.
              destruct INVARIANT2 as [_ _ _ _ _ _ _ INVARIANT2].
              apply INVARIANT2 in H0. destruct H0 as (H0 & _). congruence.
           ++ inv H. eauto with coqlib.
           ++ eauto with coqlib.
      * (* y has no children *)
        destruct (s.(r) ! v) as [ (n1, n2) |] eqn:?.
        -- destruct (Pos.ltb_spec0 n1 n).
           ++ (* cross-edge *)
              constructor; simpl; intros.
              ** destruct H.
                 --- inv H.
                     exploit WRK_BACK_NOT_BLACK. left; reflexivity.
                     intros (l' & H & H1).
                     destruct INVARIANT as [_ _ _ _ _ _ _ _ _ INVARIANT _].
                     exploit INVARIANT.
                     rewrite HH. left; reflexivity.
                     intros (_ & l'' & H21 & _ & H22).
                     rewrite H21 in H. inv H.
                     destruct (s.(c) ! v) eqn:Heqc.
                     *** apply union_strict_correct in H0. destruct H0.
                         ---- eapply black_reduced_reachable_bis_pred.
                              eassumption. apply H22. left; reflexivity.
                              congruence. intros contra.
                              rewrite H1 in Heqo0. discriminate.
                              apply H22; left; reflexivity. assumption.
                              eauto.
                         ---- eauto with coqlib.
                     *** eauto with coqlib.
                 --- eauto with coqlib.
              ** eauto.
              ** inv BACK_REVERSE_WRK. constructor; assumption.
              ** destruct H.
                 --- inv H. eauto with coqlib.
                 --- eauto with coqlib.
           ++ (* forward edge *)
              constructor; simpl; intros.
              ** destruct H as [H|H].
                 --- inv H.
                     exploit WRK_BACK_NOT_BLACK. left; reflexivity.
                     intros (l' & H & H1).
                     destruct INVARIANT as [_ _ _ _ _ _ _ _ _ INVARIANT _].
                     exploit INVARIANT.
                     rewrite HH. left; reflexivity.
                     intros (_ & l'' & H21 & _ & H22).
                     rewrite H21 in H. inv H.
                     destruct (s.(c) ! v) as [set|] eqn:Heq.
                     +++ apply union_strict_correct in H0. destruct H0.
                         *** eapply black_reduced_reachable_bis_pred.
                             eassumption. apply H22. left; reflexivity.
                             congruence. intros contra.
                             rewrite H1 in Heqo0. discriminate.
                             apply H22; left; reflexivity. assumption.
                             eauto.
                         *** eauto with coqlib.
                     +++ eauto with coqlib.
                 --- eauto with coqlib.
              ** eauto.
              ** inv BACK_REVERSE_WRK. constructor; assumption.
              ** destruct H.
                 --- inv H. eauto with coqlib.
                 --- eauto with coqlib.
        -- (* back-edge *)
           constructor; simpl; intros.
           ++ apply black_reduced_reachable_bis_back; [|congruence].
              destruct H as [H|H].
              ** inv H. eauto with coqlib.
              ** eauto with coqlib.
           ++ apply black_reduced_reachable_bis_back; [|congruence].
              eauto with coqlib.
           ++ inv BACK_REVERSE_WRK. constructor.
              ** eapply StronglySorted_impl; [|eassumption].
                intros (((x, n1), l1), post1) (((y, n2), l2), post2).
                intros. intros contra. destruct contra; [|contradiction].
                inv H4.
                destruct INVARIANT4 as [INVARIANT4 _ _ _ _ _ _ _ _].
                rewrite HH in INVARIANT4.
                inv INVARIANT4. apply H6. apply InA_alt.
                eexists (_, _, _, _). split; [reflexivity|eassumption].
             ** rewrite Forall_forall in * |- *.
                intros (((w, m), l'), post') H3 contra.
                specialize (H2 _ H3).
                destruct contra; [|contradiction].
                inv H.
                destruct INVARIANT4 as [INVARIANT4 _ _ _ _ _ _ _ _].
                rewrite HH in INVARIANT4.
                inv INVARIANT4. apply H4. apply InA_alt.
                eexists (_, _, _, _). split; [reflexivity|eassumption].
           ++ destruct H.
              ** inv H. exploit WRK_BACK_NOT_BLACK. left; reflexivity.
                 intros (l' & H & H1).
                 exists l'. split; [assumption|].
                 intros. destruct H2.
                 inv H2. assumption. eauto.
              ** exploit WRK_BACK_NOT_BLACK. right; eassumption.
                 intros (l' & H1 & H2).
                 exists l'. split; [assumption|].
                 intros. destruct H3.
                 inv H3. assumption. eauto.
Qed.

Lemma initial_state_spec3:
  invariant3 (init_state ginit root).
Proof.
  unfold init_state. destruct (ginit!root) as [succs|] eqn:?.

  - (* root has succs *)
    constructor; simpl; intros.
    + destruct H; inv H. rewrite PTree.gempty in H0. discriminate.
    + rewrite PTree.gempty in H. discriminate.
    + repeat constructor.
    + destruct H; inv H. exists l. split; [assumption|].
      intros. contradiction.
  - constructor; simpl; intros.
    + contradiction.
    + rewrite PTree.gempty in H. discriminate.
    + constructor.
    + contradiction.
Qed.

Inductive invariant5 (s:state) : Prop :=
  Invariant5
    (BLACK_IN_GRAPH : forall u, s.(r) ! u <> None -> ginit ! u <> None)
    (BLACK_REACHABLE_GRAY : forall u n l post, In (u, n, l, post) s.(wrk) ->
      reachable ginit root u)
    (BLACK_REACHABLE_BLACK :
      forall u, s.(r) ! u <> None -> reachable ginit root u)
    (OUT_NODES_BACK_GRAY : forall u n l post, In (u, n, l, post) s.(wrk) ->
      exists l', ginit ! u = Some l' /\
      forall v, In v l' -> ~ In v l -> ginit ! v = None ->
      In (u, v) s.(back))
    (CROSS_BLACK : forall u, s.(c) ! u <> None -> s.(r) ! u <> None)
    (OUT_NODES_BACK_BLACK : forall u succs v, s.(r) ! u <> None ->
      ginit ! u = Some succs -> In v succs -> ginit ! v = None ->
      In (u, v) s.(back))
    (BACK_SOURCE_GRAY_OR_BLACK : forall u v, In (u, v) s.(back) ->
      (exists n l post, In (u, n, l, post) s.(wrk)) \/ s.(r) ! u <> None)
    (BACK_BLACK : forall u, s.(t) ! u <> None <-> s.(r) ! u <> None)
    (BACK_IN_GRAPH : forall u v, In (u, v) s.(back) -> exists succs,
       ginit ! u = Some succs /\ In v succs).

Inductive postcondition5
  (acc : PTree.t (positive * positive)
       * PTree.t (PTree.t unit)
       * PTree.t (list (node * Z))
       * list (node * node))
  : Prop :=
  Postcondition5
    (BLACK_IN_GRAPH : let '(r, c, t, back) := acc in
      forall u, r ! u <> None -> ginit ! u <> None)
    (BLACK_REACHABLE : let '(r, c, t, back) := acc in
      forall u, r ! u <> None -> reachable ginit root u)
    (CROSS_BLACK : let '(r, c, t, back) := acc in
      forall u, c ! u <> None -> r ! u <> None)
    (OUT_NODES_BACK : let '(r, c, t, back) := acc in
      forall u succs v, ginit ! u = Some succs ->
      In v succs -> ginit ! v = None -> r ! u <> None -> In (u, v) back)
    (BACK_SOURCE_BLACK : let '(r, c, t, back) := acc in
      forall u v, In (u, v) back -> r ! u <> None)
    (BACK_BLACK : let '(r, c, t, back) := acc in
      forall u, t ! u <> None <-> r ! u <> None)
    (BACK_IN_GRAPH : let '(r, c, t, back) := acc in
      forall u v, In (u, v) back -> exists succs,
       ginit ! u = Some succs /\ In v succs).

Lemma transition_spec5:
  forall dom_pre s (INVARIANT : invariant s),
  invariant5 s ->
  match transition dom_pre s with inr s' => invariant5 s' | inl m => postcondition5 m end.
Proof.
  intros. inv H. unfold transition.
  destruct (wrk s) as [ | [[[u n] succs] [s_c s_t]] l] eqn:HH.
  - (* finished *)
    constructor; intros.
    + eauto.
    + eauto.
    + eauto.
    + eauto.
    + apply BACK_SOURCE_GRAY_OR_BLACK in H. destruct H; [|assumption].
      destruct H as (? & ? & ? & H). contradiction.
    + eauto.
    + eauto.
  - (* not finished *)
    destruct succs as [ | v succs ].
    + (* all children of x were traversed *)
      constructor; simpl; intros.
      * rewrite PTree.gsspec in H. destruct (peq u0 u).
        -- subst u0. destruct INVARIANT as [_ _ _ _ _ _ _ _ _ INVARIANT _].
           exploit INVARIANT. rewrite HH. left; reflexivity.
           intros (_ & l' & H1 & _).
           congruence.
        -- eauto.
      * eauto with coqlib.
      * rewrite PTree.gsspec in H. destruct (peq u0 u).
        -- subst u0. eauto with coqlib.
        -- eauto.
      * eauto with coqlib.
      * rewrite PTree.gsspec. destruct (peq u0 u).
        -- discriminate.
        -- rewrite PTree.gso in H by assumption. eauto.
      * rewrite PTree.gsspec in H. destruct (peq u0 u).
        -- subst u0. exploit OUT_NODES_BACK_GRAY.
           left; reflexivity.
           intros (l' & H31 & H32).
           rewrite H31 in H0. inv H0.
           eauto with coqlib.
        -- eauto.
      * rewrite PTree.gsspec. destruct (peq u0 u).
        -- right; discriminate.
        -- exploit BACK_SOURCE_GRAY_OR_BLACK; try eassumption.
           intros. destruct H0 as [(n1 & l1 & post1 & H0) | H0].
           ++ destruct H0; [congruence|]. eauto.
           ++ right; assumption.
      * rewrite 2!PTree.gsspec. destruct (peq u0 u).
        -- split; discriminate.
        -- eauto.
      * eauto.
    + (* children y needs traversing *)
      destruct ((gr s)!v) as [ succs_v | ] eqn:?.
      * (* y has children *)
        constructor; simpl; intros.
        -- eauto.
        -- destruct H as [H|[H|H]].
           ++ inv H.
              destruct INVARIANT as [_ _ _ _ _ _ _ _ _ INVARIANT _].
              exploit INVARIANT. rewrite HH. left; reflexivity.
              intros (_ & l' & H & _ & H1).
              eapply reachable_succ. eauto with coqlib.
              eassumption.
              apply H1; left; reflexivity.
           ++ inv H. eauto with coqlib.
           ++ eauto with coqlib.
        -- eauto.
        -- destruct H as [H|[H|H]].
           ++ inv H.
              destruct INVARIANT as [INVARIANT _ _ _ _ _ _ _ _ _ _].
              apply INVARIANT in Heqo. exists l0. eauto.
           ++ inv H. eauto with coqlib.
           ++ eauto with coqlib.
        -- eauto.
        -- eauto.
        -- apply BACK_SOURCE_GRAY_OR_BLACK in H.
           destruct H as [(n1 & l1 & post1 & H) | H].
           ++ left. eexists _, _ , _. right. eassumption.
           ++ right; assumption.
        -- eauto.
        -- eauto.
     * (* y has no children *)
       destruct (s.(r) ! v) as [ (n1, n2) |] eqn:?.
       -- destruct (Pos.ltb_spec0 n1 n).
          ++ (* cross-edge *)
             constructor; simpl; intros.
             ** eauto.
             ** destruct H as [H|H].
                --- inv H. eauto with coqlib.
                --- eauto with coqlib.
             ** eauto.
             ** destruct H as [H|H].
                --- inv H. exploit OUT_NODES_BACK_GRAY. left; reflexivity.
                    intros (l' & H1 & H2).
                    exists l'. split; [assumption|]. intros.
                    eapply H2; eauto.
                    intros contra. destruct contra; [|contradiction].
                    subst v0. eapply BLACK_IN_GRAPH; try apply H3. congruence.
                --- eauto with coqlib.
             ** eauto.
             ** eauto.
             ** apply BACK_SOURCE_GRAY_OR_BLACK in H.
                destruct H as [(n3 & l1 & post1 & H) | H].
                --- left. destruct H.
                    +++ inv H. eexists _, _, _. left. reflexivity.
                    +++ eexists _, _, _. right. eassumption.
                --- right; assumption.
             ** eauto.
             ** eauto.
          ++ constructor; simpl; intros.
             ** eauto.
             ** destruct H as [H|H].
                --- inv H. eauto with coqlib.
                --- eauto with coqlib.
             ** eauto.
             ** destruct H as [H|H].
                --- inv H. exploit OUT_NODES_BACK_GRAY. left; reflexivity.
                    intros (l' & H1 & H2).
                    exists l'. split; [assumption|]. intros.
                    eapply H2; eauto.
                    intros contra. destruct contra; [|contradiction].
                    subst v0. eapply BLACK_IN_GRAPH; try apply H3. congruence.
                --- eauto with coqlib.
             ** eauto.
             ** eauto.
             ** apply BACK_SOURCE_GRAY_OR_BLACK in H.
                destruct H as [(n3 & l1 & post1 & H) | H].
                --- left. destruct H.
                    +++ inv H. eexists _, _, _. left. reflexivity.
                    +++ eexists _, _, _. right. eassumption.
                --- right; assumption.
             ** eauto.
             ** eauto.
       -- constructor; simpl; intros.
          ++ eauto.
          ++ destruct H as [H|H].
             ** inv H. eauto with coqlib.
             ** eauto with coqlib.
          ++ eauto.
          ++ destruct H as [H|H].
             ** inv H. exploit OUT_NODES_BACK_GRAY. left; reflexivity.
                intros (l' & H1 & H2).
                exists l'. split; [assumption|]. intros.
                destruct (peq v v0).
                left. congruence.
                right. eapply H2; eauto.
                intros contra. destruct contra; contradiction.
             ** exploit OUT_NODES_BACK_GRAY. right; eassumption.
                intros (l' & H1 & H2). eauto.
          ++ eauto.
          ++ eauto.
          ++ destruct H.
             ** inv H. left. eexists _, _, _. left; reflexivity.
             ** apply BACK_SOURCE_GRAY_OR_BLACK in H.
                destruct H as [(n3 & l1 & post1 & H) | H].
                --- left. destruct H.
                    +++ inv H. eexists _, _, _. left. reflexivity.
                    +++ eexists _, _, _. right. eassumption.
                --- right; assumption.
          ++ eauto.
          ++ destruct H as [H|H].
             ** inv H. destruct INVARIANT as [_ _ _ _ _ _ _ _ _ INVARIANT _].
                exploit INVARIANT. rewrite HH. left; reflexivity.
                intros (_ & l' & H1 & _ & H2).
                eexists. split; [eassumption|]. apply H2. left; reflexivity.
             ** eauto.
Qed.

Lemma initial_state_spec5:
  invariant5 (init_state ginit root).
Proof.
  unfold init_state. destruct (ginit!root) as [succs|] eqn:?.

  - (* root has succs *)
    constructor; simpl; intros.
    + rewrite PTree.gempty in H. contradiction.
    + destruct H; inv H. constructor.
    + rewrite PTree.gempty in H. contradiction.
    + destruct H; inv H. exists l. split. assumption. intros; contradiction.
    + rewrite PTree.gempty in H. contradiction.
    + rewrite PTree.gempty in H. contradiction.
    + contradiction.
    + rewrite 2!PTree.gempty. split; contradiction.
    + contradiction.
  - constructor; simpl; intros.
    + rewrite PTree.gempty in H. contradiction.
    + contradiction.
    + rewrite PTree.gempty in H. contradiction.
    + contradiction.
    + rewrite PTree.gempty in H. contradiction.
    + rewrite PTree.gempty in H. contradiction.
    + contradiction.
    + rewrite 2!PTree.gempty. split; contradiction.
    + contradiction.
Qed.


Section DOM_PRE.

Variable dom_pre : node -> Z.

Local Opaque ZSortWithIndex.merge.

Inductive invariant9 (s:state) : Prop :=
  Invariant9
    (T_DOM_PRE_GRAY : forall u n l s_c s_t, In (u, n, l, (s_c, s_t)) s.(wrk) ->
      Forall (fun '(u, n) => n = dom_pre u) s_t)
    (T_DOM_PRE_BLACK : forall u set, s.(t) ! u = Some set ->
      Forall (fun '(u, n) => n = dom_pre u) set)

    (T_SORTED_GRAY : forall u n l s_c s_t, In (u, n, l, (s_c, s_t)) s.(wrk) ->
      Sorted (fun '(_, n1) '(_, n2) => n1 < n2)%Z s_t)
    (T_SORTED_BLACK : forall u set, s.(t) ! u = Some set ->
      Sorted (fun '(_, n1) '(_, n2) => n1 < n2)%Z set).

Inductive postcondition9
  (acc : PTree.t (positive * positive)
       * PTree.t (PTree.t unit)
       * PTree.t (list (node * Z))
       * list (node * node))
  : Prop :=
  Postcondition9
    (T_DOM_PRE : let '(r, c, t, back) := acc in
      forall u set, t ! u = Some set ->
      Forall (fun '(u, n) => n = dom_pre u) set)

    (T_SORTED : let '(r, c, t, back) := acc in
      forall u set, t ! u = Some set ->
      Sorted (fun '(_, n1) '(_, n2) => n1 < n2)%Z set).

Lemma transition_spec9:
  forall s,
  invariant9 s ->
  match transition dom_pre s with inr s' => invariant9 s' | inl m => postcondition9 m end.
Proof.
  intros. inv H. unfold transition.
  destruct (wrk s) as [ | [[[u n] succs] [s_c s_t]] l] eqn:HH.
  - (* finished *)
    constructor; intros.
    + eauto.
    + eauto.
  - (* not finished *)
    destruct succs as [ | v succs ].
    + (* all children of x were traversed *)
      constructor; simpl; intros.
      * eauto.
      * rewrite PTree.gsspec in H. destruct (peq u0 u).
        ++ subst u0. inv H. apply Forall_forall. intros (v, n_v) H.
           apply filter_In in H. destruct H as (H & _).
           exploit T_DOM_PRE_GRAY. left; reflexivity.
           intros. rewrite Forall_forall in H0. apply (H0 _ H).
        ++ eauto.
      * eauto.
      * rewrite PTree.gsspec in H. destruct (peq u0 u).
        ++ subst u0. inv H.
           apply filter_sort with (eqA:=eq).
           ** eauto.
           ** split.
              --- unfold Irreflexive, complement. intros (_, n1).
                  apply Z.lt_irrefl.
              --- intros (_, n1) (_, n2) (_, n3). apply Z.lt_trans.
           ** intros (v1, n_v1) (v2, n_v2) eq_v (w1, n_w1) (w2, n_w2) eq_w.
              inv eq_v. inv eq_w. reflexivity.
           ** eauto.
        ++ eauto.
    + (* children y needs traversing *)
      destruct ((gr s)!v) as [ succs_v | ] eqn:?.
      * (* y has children *)
        constructor; simpl; intros.
        -- destruct H as [H|[H|H]].
           ++ inv H. constructor.
           ++ inv H. eauto.
           ++ eauto.
        -- eauto.
        -- destruct H as [H|[H|H]].
           ++ inv H. constructor.
           ++ inv H. eauto.
           ++ eauto.
        -- eauto.
     * (* y has no children *)
       destruct (s.(r) ! v) as [ (n1, n2) |] eqn:?.
       -- destruct (Pos.ltb_spec0 n1 n).
          ++ (* cross-edge *)
             constructor; simpl; intros.
             ** destruct H as [H|H].
                --- inv H. destruct (s.(t) ! v) eqn:Heqt.
                    +++ apply Forall_forall. intros (w, n_w) H.
                        apply ZSortWithIndex.incl_merge in H.
                        apply in_app_iff in H. destruct H as [H|H].
                        *** exploit T_DOM_PRE_BLACK. eassumption. intros.
                            rewrite Forall_forall in H0. apply (H0 _ H).
                        *** exploit T_DOM_PRE_GRAY. left; reflexivity. intros.
                            rewrite Forall_forall in H0. apply (H0 _ H).
                    +++ eauto.
                --- eauto.
             ** eauto.
             ** destruct H as [H|H].
                --- inv H. destruct (s.(t) ! v) eqn:Heqt.
                    +++ apply Sorted_LocallySorted_iff.
                        apply ZSortWithIndex.Sorted_merge.
                        *** apply Sorted_LocallySorted_iff. eauto.
                        *** apply Sorted_LocallySorted_iff. eauto.
                    +++ eauto.
                --- eauto.
             ** eauto.
          ++ constructor; simpl; intros.
             ** destruct H as [H|H].
                --- inv H. destruct (s.(t) ! v) eqn:Heqt.
                    +++ apply Forall_forall. intros (w, n_w) H.
                        apply ZSortWithIndex.incl_merge in H.
                        apply in_app_iff in H. destruct H as [H|H].
                        *** exploit T_DOM_PRE_BLACK. eassumption. intros.
                            rewrite Forall_forall in H0. apply (H0 _ H).
                        *** exploit T_DOM_PRE_GRAY. left; reflexivity. intros.
                            rewrite Forall_forall in H0. apply (H0 _ H).
                    +++ eauto.
                --- eauto.
             ** eauto.
             ** destruct H as [H|H].
                --- inv H. destruct (s.(t) ! v) eqn:Heqt.
                    +++ apply Sorted_LocallySorted_iff.
                        apply ZSortWithIndex.Sorted_merge.
                        *** apply Sorted_LocallySorted_iff. eauto.
                        *** apply Sorted_LocallySorted_iff. eauto.
                    +++ eauto.
                --- eauto.
             ** eauto.
       -- constructor; simpl; intros.
          ++ destruct H as [H|H].
             ** inv H. apply Forall_forall. intros (w, n_w) H.
                apply ZSortWithIndex.incl_merge in H. simpl in H.
                destruct H as [H|H].
                --- inv H. reflexivity.
                --- exploit T_DOM_PRE_GRAY. left; reflexivity. intros.
                    rewrite Forall_forall in H0. apply (H0 _ H).
             ** eauto.
          ++ eauto.
          ++ destruct H as [H|H].
             ** inv H.
                apply Sorted_LocallySorted_iff.
                apply ZSortWithIndex.Sorted_merge.
                --- constructor.
                --- apply Sorted_LocallySorted_iff. eauto.
             ** eauto.
           ++ eauto.
Qed.

Lemma initial_state_spec9:
  invariant9 (init_state ginit root).
Proof.
  unfold init_state. destruct (ginit!root) as [succs|] eqn:?.

  - (* root has succs *)
    constructor; simpl; intros.
    + destruct H; inv H. constructor.
    + rewrite PTree.gempty in H. discriminate.
    + destruct H; inv H. constructor.
    + rewrite PTree.gempty in H. discriminate.
  - constructor; simpl; intros.
    + contradiction.
    + rewrite PTree.gempty in H. discriminate.
    + contradiction.
    + rewrite PTree.gempty in H. discriminate.
Qed.

Section DOM_PRE_HYPS.

Hypothesis dom_pre_inj : forall u v,
  reachable ginit root u ->
  reachable ginit root v ->
  dom_pre u = dom_pre v ->
  u = v.

Inductive invariant6 (s:state) : Prop :=
  Invariant6
    (BACK_SOURCE_GRAY : forall u n l s_c s_t v, In (u, n, l, (s_c, s_t)) s.(wrk) ->
      In v (List.map fst s_t) ->
      exists w, In (w, v) s.(back)
      /\ black_reduced_reachable_bis ginit s.(r) s.(back) u w)
    (BACK_SOURCE : forall u set v, s.(t) ! u = Some set -> In v (List.map fst set) ->
      exists w, In (w, v) s.(back)
      /\ black_reduced_reachable_bis ginit s.(r) s.(back) u w)

    (BACK_TARGETS : forall u set v, s.(t) ! u = Some set -> In v (map fst set) ->
      ~ cross_included s.(r) s.(c) u v)

    (BACK_UPWARDS_GRAY : forall u n l s_c s_t, In (u, n, l, (s_c, s_t)) s.(wrk) ->
      exists l', ginit!u = Some l' /\
      (forall v set w, In v l' -> ~In v l -> ~ In (u, v) s.(back) ->
       s.(t) ! v = Some set -> In w (map fst set) -> In w (map fst s_t)))
    (BACK_UPWARDS_BLACK : forall u succs v set w, ginit ! u = Some succs ->
      s.(r) ! u <> None ->
      In v succs -> ~ In (u, v) s.(back) -> s.(t) ! v = Some set -> In w (map fst set) ->
      ~ cross_included s.(r) s.(c) u w -> exists set', s.(t) ! u = Some set' /\ In w (map fst set'))

    (CLASSIFY_BACK_GRAY : forall u n l s_c s_t, In (u, n, l, (s_c, s_t)) s.(wrk) ->
      exists l', ginit!u = Some l' /\
      (forall v, In v l' -> ~In v l ->
      In (u, v) s.(back) -> In v (map fst s_t)))
    (CLASSIFY_BACK_BLACK : forall u v, In (u, v) s.(back) -> s.(r)!u <> None ->
      u = v \/ exists set, s.(t) ! u = Some set /\ In v (map fst set)).

Inductive postcondition6
  (acc : PTree.t (positive * positive)
       * PTree.t (PTree.t unit)
       * PTree.t (list (node * Z))
       * list (node * node))
  : Prop :=
  Postcondition6
    (BACK_SOURCE : let '(r, c, t, back) := acc in
      forall u set v,
      t ! u = Some set -> In v (map fst set) ->
      exists w, In (w, v) back
      /\ reduced_reachable ginit back u w)
    (BACK_TARGETS : let '(r, c, t, back) := acc in
      forall u set v, t ! u = Some set -> In v (map fst set) ->
      ~ cross_included r c u v)
    (BACK_UPWARDS: let '(r, c, t, back) := acc in
      forall u succs v set w, ginit ! u = Some succs -> r ! u <> None ->
      In v succs -> ~ In (u, v) back -> t ! v = Some set -> In w (map fst set) ->
      ~ cross_included r c u w -> exists set', t ! u = Some set' /\ In w (map fst set'))
    (CLASSIFY_BACK : let '(r, c, t, back) := acc in
      forall u v, In (u, v) back -> r ! u <> None ->
      u = v \/ exists set, t ! u = Some set /\ In v (List.map fst set)).

Lemma transition_spec6:
  forall s (INVARIANT : invariant s) (INVARIANT2 : invariant2 s)
  (INVARIANT3 : invariant3 s) (INVARIANT4 : invariant4 s)
  (INVARIANT5 : invariant5 s) (INVARIANT9 : invariant9 s),
  invariant6 s ->
  match transition dom_pre s with inr s' => invariant6 s' | inl m => postcondition6 m end.
Proof.
  intros. inv H. unfold transition.
  destruct (wrk s) as [ | [[[u n] succs] [s_c s_t]] l] eqn:HH.
  - (* finished *)
    constructor; intros.
    + exploit BACK_SOURCE; try eassumption.
      intros (w & H1 & H2).
      exists w. split; [assumption|].
      eapply black_reduced_reachable_bis_is_reduced_reachable; eassumption.
    + eauto.
    + eauto.
    + eauto.
  - (* not finished *)
    destruct succs as [ | v succs ].
    + (* all children of x were traversed *)
      constructor; simpl; intros.
      * exploit BACK_SOURCE_GRAY. right; eassumption. eassumption.
        intros (w & H1 & H2).
        exists w. split; [assumption|].
        apply black_reduced_reachable_bis_monotone. assumption.
      * rewrite PTree.gsspec in H. destruct (peq u0 u).
        ++ subst u0.
           assert (exists w : node, In (w, v) (back s) /\ black_reduced_reachable_bis ginit s.(r) (back s) u w).
           { eapply BACK_SOURCE_GRAY. left; reflexivity. inv H.
             apply in_map_iff in H0. destruct H0 as ((v', n_v) & H0' & H0).
             simpl in H0'. subst.
             apply filter_In in H0. destruct H0 as (H0 & _).
             apply in_map_iff. eexists (_, _); split; [reflexivity|eassumption].
           }
          destruct H1 as (? & ? & ?).
           eauto using black_reduced_reachable_bis_monotone.
        ++ exploit BACK_SOURCE; try eassumption.
           intros (w & H1 & H2).
           eauto using black_reduced_reachable_bis_monotone.
      * rewrite PTree.gsspec in H. destruct (peq u0 u).
        ** inv H.
           apply in_map_iff in H0. destruct H0 as ((v', n_v) & H0' & H0).
           simpl in H0'. subst.
           apply filter_In in H0.
           destruct H0 as (H01 & H02).
           apply negb_true_iff, not_true_iff_false in H02.
           intros contra. apply H02. apply can_reach_spec.
           rewrite PTree.gss; discriminate. assumption.
        ** intros contra. eapply BACK_TARGETS; try eassumption.
           destruct contra as (set' & w' & contra1 & contra2 & n1 & n2 & m1 & m2 & contra3).
           exists set', w'.
           rewrite PTree.gso in contra1.
           split; [assumption|]. split; [assumption|].
           exists n1, n2, m1, m2.
           rewrite PTree.gso in contra3.
           rewrite PTree.gsspec in contra3.
           destruct (peq v u); [|assumption].
 impossible: is_included (n, Pos.pred (next s)) (n1, n1) *)           destruct contra3 as (contra31 & contra32 & contra33 & contra34).
           destruct INVARIANT2 as [_ _ _ _ _ INVARIANT2 _ _].
           exploit INVARIANT2. rewrite HH. left; reflexivity.
           eassumption.
           destruct INVARIANT as [_ _ BELOW1 BELOW2 _ _ _ _ _ _ _].
           exploit BELOW1. rewrite HH. left; reflexivity.
           apply BELOW2 in contra31. inv contra32. xomega.
           destruct INVARIANT4
             as [_ WRK_NOT_BLACK _ _ _ _ CROSS_TARGETS_BLACK_BLACK _ _].
           eapply CROSS_TARGETS_BLACK_BLACK in contra1; try eassumption.
           exploit WRK_NOT_BLACK. rewrite HH. left; reflexivity.
           congruence.
           destruct INVARIANT4 as [_ INVARIANT4 _ _ _ _ _ _ _].
           exploit INVARIANT4. rewrite HH. left; reflexivity.
           intros. intros contra4.
           destruct INVARIANT5 as [_ _ _ _ _ _ _ INVARIANT5 _].
           eapply (proj1 (INVARIANT5 _)); try eassumption. congruence.
      * exploit BACK_UPWARDS_GRAY. right; eassumption.
        intros (l' & H1 & H2).
        exists l'. split; [assumption|].
        intros.
        rewrite PTree.gso in H5. eauto.
        destruct INVARIANT4 as [_ WRK_NOT_BLACK WRK_DONE _ _ _ _ _ _].
        exploit WRK_NOT_BLACK. rewrite HH. left; reflexivity.
        exploit WRK_DONE. rewrite HH. right; eassumption.
        intros (l0' & H71 & H72). rewrite H1 in H71. inv H71.
        specialize (H72 v H0 H3). destruct H72; congruence.
      * rewrite PTree.gsspec in H0. destruct (peq u0 u).
        -- subst u0. exploit BACK_UPWARDS_GRAY. left; reflexivity.
           intros (l' & H61 & H62).
           rewrite H61 in H. inv H.
           rewrite PTree.gss. eexists. split. reflexivity.
           rewrite PTree.gsspec in H3. destruct (peq v u). congruence.
           exploit H62; eauto. intros H7.
           apply in_map_iff in H7. destruct H7 as ((w', n_w) & H7' & H7).
           simpl in H7'. subst.
           apply in_map_iff. eexists (_, _); split; [reflexivity|].
           apply filter_In.
           split.
           eassumption.
           apply negb_true_iff, not_true_iff_false.
           rewrite can_reach_spec. assumption.
           rewrite PTree.gss. discriminate.
        -- rewrite PTree.gso by assumption.
           rewrite PTree.gsspec in H3. destruct (peq v u).
           ** (* impossible : u0 is black, so must be its children
                 (or form a back-edge) *)

              destruct INVARIANT4 as [_ WRK_NOT_BLACK _ CLASSIFY_BLACK _ _ CROSS_TARGETS_BLACK_BLACK _ _].
              exploit WRK_NOT_BLACK; eauto. rewrite HH. left; reflexivity.
              intros.
              exploit CLASSIFY_BLACK; eauto.
              intros [H8|[H8|H8]].
              contradiction.
              destruct H8 as (set' & H81 & H82).
              specialize (CROSS_TARGETS_BLACK_BLACK _ _ _ H81 H82).
              congruence.
              destruct H8 as (_ & _ & n1 & n2 & _ & H8 & _). congruence.
           ** exploit BACK_UPWARDS_BLACK; eauto. intros contra.
              apply H5.
              destruct contra as (set' & w' & contra1 & contra2 & n1' & n2 & m1 & m2 & contra3).
              exists set', w'.
              rewrite PTree.gso by assumption. split; [assumption|].
              split; [assumption|].
              exists n1', n2, m1, m2. rewrite PTree.gso.
              rewrite PTree.gso. assumption.
              destruct INVARIANT4 as [_ INVARIANT4 _ _ _ _ _ _ _].
              exploit INVARIANT4. rewrite HH. left; reflexivity.
              destruct contra3 as (_ & contra3 & _). congruence.
              destruct INVARIANT4 as [_ INVARIANT4 _ _ _ _ _ _ _].
              exploit INVARIANT4. rewrite HH. left; reflexivity.
              destruct contra3 as (contra3 & _ & _). congruence.
      * eauto.
      * rewrite PTree.gsspec. destruct (peq u0 u).
        ++ subst. destruct (peq u v).
           ** left; assumption.
           ** right. eexists. split; [reflexivity|].
              exploit CLASSIFY_BACK_GRAY. left; reflexivity.
              intros (l' & H2 & H3).
              destruct INVARIANT5 as [_ _ _ _ _ _ _ _ INVARIANT5].
              destruct (INVARIANT5 _ _ H) as (succs & H41 & H42).
              rewrite H41 in H2. inv H2.
              exploit H3; eauto.
              intros. apply in_map_iff in H1.
              destruct H1 as ((v', n_v) & H1' & H1).
              simpl in H1'. subst.
              apply in_map_iff. eexists (_, _). split; [reflexivity|].
              apply filter_In. split; [eassumption|].
              apply negb_true_iff.
              unfold can_reach.
              rewrite PTree.gso by congruence.
              destruct INVARIANT3 as [_ _ _ INVARIANT3].
              exploit INVARIANT3. rewrite HH. left; reflexivity.
              intros (l'' & H51 & H52).
              rewrite H51 in H41. inv H41. rewrite H52 by assumption.
              reflexivity.
        ++ rewrite PTree.gso in H0 by assumption. eauto.
    + (* children y needs traversing *)
      destruct ((gr s)!v) as [ succs_v | ] eqn:?.
      * (* y has children *)
        constructor; simpl; intros.
        -- destruct H as [H|[H|H]].
           ++ inv H. contradiction H0.
           ++ inv H. eauto with coqlib.
           ++ eauto with coqlib.
        -- eauto.
        -- eauto.
        -- destruct H as [H|[H|H]].
           ++ inv H.
              destruct INVARIANT as [INVARIANT _ _ _ _ _ _ _ _ _ _].
              apply INVARIANT in Heqo. exists l0.
              split; [assumption|]. intros; contradiction.
           ++ inv H. eauto with coqlib.
           ++ eauto with coqlib.
        -- eauto.
        -- destruct H as [H|[H|H]].
           ++ inv H.
              destruct INVARIANT as [INVARIANT _ _ _ _ _ _ _ _ _ _].
              apply INVARIANT in Heqo. exists l0.
              split; [assumption|]. intros; contradiction.
           ++ inv H. eauto with coqlib.
           ++ eauto with coqlib.
        -- eauto.
     * (* y has no children *)
       destruct (s.(r) ! v) as [ (n1, n2) |] eqn:?.
       -- destruct (Pos.ltb_spec0 n1 n).
          ++ (* cross-edge *)
             constructor; simpl; intros.
             ** destruct H.
                --- inv H.
                    destruct INVARIANT3 as [_ _ _ INVARIANT3].
                    exploit INVARIANT3. rewrite HH. left; reflexivity.
                    intros (l' & H & H1).
                    destruct INVARIANT as [_ _ _ _ _ _ _ _ _ INVARIANT _].
                    exploit INVARIANT.
                    rewrite HH. left; reflexivity.
                    intros (_ & l'' & H21 & _ & H22).
                    rewrite H21 in H. inv H.
                    destruct (s.(t) ! v) eqn:Heqt.
                    +++ apply in_map_iff in H0.
                        destruct H0 as ((v0', n_v0) & H0' & H0).
                        simpl in H0'. subst.
                        apply ZSortWithIndex.incl_merge in H0.
                        rewrite in_app_iff in H0. destruct H0 as [H0|H0].
                        *** exploit BACK_SOURCE; try eassumption.
                            apply in_map_iff.
                            eexists (_, _); split; [reflexivity|eassumption].
                            intros (w & H2 & H3).
                            exists w. split; [assumption|].
                            eapply black_reduced_reachable_bis_pred.
                            eassumption. apply H22. left; reflexivity.
                            congruence. intros contra.
                            rewrite H1 in Heqo0. discriminate.
                            apply H22; left; reflexivity. assumption.
                            assumption.
                        *** exploit BACK_SOURCE_GRAY; eauto.
                            apply in_map_iff.
                            eexists (_, _); split; [reflexivity|eassumption].
                    +++ eauto with coqlib.
                --- eauto with coqlib.
             ** eauto.
             ** eauto.
             ** destruct H.
                 --- inv H. exploit BACK_UPWARDS_GRAY. left; reflexivity.
                     intros (l' & H1 & H2).
                     exists l'. split; [assumption|].
                     intros.
                     apply in_map_iff in H5.
                     destruct H5 as ((w', n_w) & H5' & H5).
                     simpl in H5'. subst.
                     destruct (peq v0 v). subst v0. rewrite H4.
                     +++ apply in_map_iff.
                         eexists (_, _); split; [reflexivity|].
                         apply ZSortWithIndex.incl_merge_2_inj.
                         { intros x1 x2 N Hx1 Hx2.
                           assert (N = dom_pre x1) as Ha.
                           { destruct INVARIANT9 as [_ INVARIANT9 _ _].
                             exploit INVARIANT9; eauto.
                             intros HForall. rewrite Forall_forall in HForall.
                             apply (HForall _ Hx1).
                           }
                           assert (N = dom_pre x2) as Hb.
                           { destruct INVARIANT9 as [INVARIANT9 _ _ _].
                             exploit INVARIANT9; eauto. rewrite HH. left; reflexivity.
                             intros HForall. rewrite Forall_forall in HForall.
                             apply (HForall _ Hx2).
                           }
                           assert (reachable ginit root x1) as Hc.
                           { assert (exists x', In (x', x1) s.(back)) as Hc'.
                             { exploit BACK_SOURCE; eauto.
                               apply in_map_iff.
                               eexists (_, _); split; [reflexivity|eassumption].
                               intros (x' & H7 & _). eexists; eassumption.
                             }
                             destruct Hc' as (x' & Hc').
                             pose proof INVARIANT5 as [_ _ _ _ _ _ _ _ BACK_IN_GRAPH].
                             destruct (BACK_IN_GRAPH _ _ Hc')
                               as (succs & H81 & H82).
                             eapply reachable_succ; try eassumption.
                             pose proof INVARIANT5
                               as [_ _ _ _ _ _ BACK_SOURCE_GRAY_OR_BLACK].
                             apply BACK_SOURCE_GRAY_OR_BLACK in Hc'.
                             destruct Hc' as [(? & ? & ? & Hc')|Hc'].
                             destruct INVARIANT5 as [_ INVARIANT5 _ _ _ _ _].
                             eapply INVARIANT5; eassumption.
                             destruct INVARIANT5 as [_ _ INVARIANT5 _ _ _ _].
                             eapply INVARIANT5; eassumption.
                           }
                           assert (reachable ginit root x2) as Hd.
                           { assert (exists x', In (x', x2) s.(back)) as Hd'.
                             { exploit BACK_SOURCE_GRAY; eauto.
                               apply in_map_iff.
                               eexists (_, _); split; [reflexivity|eassumption].
                               intros (x' & H7 & _). eexists; eassumption.
                             }
                             destruct Hd' as (x' & Hd').
                             pose proof INVARIANT5 as [_ _ _ _ _ _ _ _ BACK_IN_GRAPH].
                             destruct (BACK_IN_GRAPH _ _ Hd')
                               as (succs & H81 & H82).
                             eapply reachable_succ with (x:=x'); try eassumption.
                             pose proof INVARIANT5
                               as [_ _ _ _ _ _ BACK_SOURCE_GRAY_OR_BLACK].
                             apply BACK_SOURCE_GRAY_OR_BLACK in Hd'.
                             destruct Hd' as [(? & ? & ? & Hd')|Hd'].
                             destruct INVARIANT5 as [_ INVARIANT5 _ _ _ _ _].
                             eapply INVARIANT5; eassumption.
                             destruct INVARIANT5 as [_ _ INVARIANT5 _ _ _ _].
                             eapply INVARIANT5; eassumption.
                           }
                           eapply dom_pre_inj; eauto. congruence.
                         }
                         rewrite in_app_iff. left; eassumption.
                     +++ destruct (s.(t) ! v) eqn:Heqt.
                         *** exploit H2; eauto.
                             intros [contra|contra]; congruence.
                             apply in_map_iff.
                             eexists (_, _); split; [reflexivity|eassumption].
                             intros H6.
                             apply in_map_iff in H6.
                             destruct H6 as ((w', n_w') & H6' & H6).
                             simpl in H6'. subst.
                             apply in_map_iff.
                             eexists (_, _); split; [reflexivity|].
                             apply ZSortWithIndex.incl_merge_2_inj.
                             { intros x1 x2 N Hx1 Hx2.
                               assert (N = dom_pre x1) as Ha.
                               { destruct INVARIANT9 as [_ INVARIANT9 _ _].
                                 exploit INVARIANT9; eauto.
                                 intros HForall. rewrite Forall_forall in HForall.
                                 apply (HForall _ Hx1).
                               }
                               assert (N = dom_pre x2) as Hb.
                               { destruct INVARIANT9 as [INVARIANT9 _ _ _].
                                 exploit INVARIANT9; eauto. rewrite HH. left; reflexivity.
                                 intros HForall. rewrite Forall_forall in HForall.
                                 apply (HForall _ Hx2).
                               }
                               assert (reachable ginit root x1) as Hc.
                               { assert (exists x', In (x', x1) s.(back)) as Hc'.
                                 { exploit BACK_SOURCE; eauto.
                                   apply in_map_iff.
                                   eexists (_, _); split; [reflexivity|eassumption].
                                   intros (x' & H7 & _). eexists; eassumption.
                                 }
                                 destruct Hc' as (x' & Hc').
                                 pose proof INVARIANT5 as [_ _ _ _ _ _ _ _ BACK_IN_GRAPH].
                                 destruct (BACK_IN_GRAPH _ _ Hc')
                                   as (succs & H81 & H82).
                                 eapply reachable_succ; try eassumption.
                                 pose proof INVARIANT5
                                   as [_ _ _ _ _ _ BACK_SOURCE_GRAY_OR_BLACK].
                                 apply BACK_SOURCE_GRAY_OR_BLACK in Hc'.
                                 destruct Hc' as [(? & ? & ? & Hc')|Hc'].
                                 destruct INVARIANT5 as [_ INVARIANT5 _ _ _ _ _].
                                 eapply INVARIANT5; eassumption.
                                 destruct INVARIANT5 as [_ _ INVARIANT5 _ _ _ _].
                                 eapply INVARIANT5; eassumption.
                               }
                               assert (reachable ginit root x2) as Hd.
                               { assert (exists x', In (x', x2) s.(back)) as Hd'.
                                 { exploit BACK_SOURCE_GRAY; eauto.
                                   apply in_map_iff.
                                   eexists (_, _); split; [reflexivity|eassumption].
                                   intros (x' & H7 & _). eexists; eassumption.
                                 }
                                 destruct Hd' as (x' & Hd').
                                 pose proof INVARIANT5 as [_ _ _ _ _ _ _ _ BACK_IN_GRAPH].
                                 destruct (BACK_IN_GRAPH _ _ Hd')
                                   as (succs & H81 & H82).
                                 eapply reachable_succ with (x:=x'); try eassumption.
                                 pose proof INVARIANT5
                                   as [_ _ _ _ _ _ BACK_SOURCE_GRAY_OR_BLACK].
                                 apply BACK_SOURCE_GRAY_OR_BLACK in Hd'.
                                 destruct Hd' as [(? & ? & ? & Hd')|Hd'].
                                 destruct INVARIANT5 as [_ INVARIANT5 _ _ _ _ _].
                                 eapply INVARIANT5; eassumption.
                                 destruct INVARIANT5 as [_ _ INVARIANT5 _ _ _ _].
                                 eapply INVARIANT5; eassumption.
                               }
                               eapply dom_pre_inj; eauto. congruence.
                             }
                             rewrite in_app_iff. right; eassumption.
                         *** eapply H2; eauto.
                             intros contra; destruct contra; congruence.
                             apply in_map_iff.
                             eexists (_, _); split; [reflexivity|eassumption].
                 --- eauto with coqlib.
              ** eauto.
              ** destruct H.
                 --- inv H. exploit CLASSIFY_BACK_GRAY.
                      left; reflexivity.
                      intros (l' & H & H1).
                      exists l'. split; [assumption|].
                      intros. destruct (s.(t) ! v) eqn:Heqt.
                      +++ exploit H1; try eassumption.
                          intros [contra|contra]; [|contradiction].
                          subst.
                          destruct INVARIANT3 as [_ _ _ INVARIANT3].
                          exploit INVARIANT3. rewrite HH. left; reflexivity.
                          intros (l'' & H51 & H52).
                          rewrite H51 in H. inv H. rewrite H52 in Heqo0 by assumption.
                          discriminate.
                          intros. apply in_map_iff in H4.
                          destruct H4 as ((v0', n_v0) & H4' & H4).
                          simpl in H4'. subst.
                          apply in_map_iff. eexists (_, _). split; [reflexivity|].
                          apply ZSortWithIndex.incl_merge_2_inj.
                          { intros x1 x2 N Hx1 Hx2.
                            assert (N = dom_pre x1) as Ha.
                            { destruct INVARIANT9 as [_ INVARIANT9 _ _].
                              exploit INVARIANT9; eauto.
                              intros HForall. rewrite Forall_forall in HForall.
                              apply (HForall _ Hx1).
                            }
                            assert (N = dom_pre x2) as Hb.
                            { destruct INVARIANT9 as [INVARIANT9 _ _ _].
                              exploit INVARIANT9; eauto. rewrite HH. left; reflexivity.
                              intros HForall. rewrite Forall_forall in HForall.
                              apply (HForall _ Hx2).
                            }
                            assert (reachable ginit root x1) as Hc.
                            { assert (exists x', In (x', x1) s.(back)) as Hc'.
                              { exploit BACK_SOURCE; eauto.
                                apply in_map_iff.
                                eexists (_, _); split; [reflexivity|eassumption].
                                intros (x' & H7 & _). eexists; eassumption.
                              }
                              destruct Hc' as (x' & Hc').
                              pose proof INVARIANT5 as [_ _ _ _ _ _ _ _ BACK_IN_GRAPH].
                              destruct (BACK_IN_GRAPH _ _ Hc')
                                as (succs & H81 & H82).
                              eapply reachable_succ; try eassumption.
                              pose proof INVARIANT5
                                as [_ _ _ _ _ _ BACK_SOURCE_GRAY_OR_BLACK].
                              apply BACK_SOURCE_GRAY_OR_BLACK in Hc'.
                              destruct Hc' as [(? & ? & ? & Hc')|Hc'].
                              destruct INVARIANT5 as [_ INVARIANT5 _ _ _ _ _].
                              eapply INVARIANT5; eassumption.
                              destruct INVARIANT5 as [_ _ INVARIANT5 _ _ _ _].
                              eapply INVARIANT5; eassumption.
                            }
                            assert (reachable ginit root x2) as Hd.
                            { assert (exists x', In (x', x2) s.(back)) as Hd'.
                              { exploit BACK_SOURCE_GRAY; eauto.
                                apply in_map_iff.
                                eexists (_, _); split; [reflexivity|eassumption].
                                intros (x' & H7 & _). eexists; eassumption.
                              }
                              destruct Hd' as (x' & Hd').
                              pose proof INVARIANT5 as [_ _ _ _ _ _ _ _ BACK_IN_GRAPH].
                              destruct (BACK_IN_GRAPH _ _ Hd')
                                as (succs & H81 & H82).
                              eapply reachable_succ with (x:=x'); try eassumption.
                              pose proof INVARIANT5
                                as [_ _ _ _ _ _ BACK_SOURCE_GRAY_OR_BLACK].
                              apply BACK_SOURCE_GRAY_OR_BLACK in Hd'.
                              destruct Hd' as [(? & ? & ? & Hd')|Hd'].
                              destruct INVARIANT5 as [_ INVARIANT5 _ _ _ _ _].
                              eapply INVARIANT5; eassumption.
                              destruct INVARIANT5 as [_ _ INVARIANT5 _ _ _ _].
                              eapply INVARIANT5; eassumption.
                            }
                            eapply dom_pre_inj; eauto. congruence.
                          }
                          rewrite in_app_iff. right; eassumption.
                      +++ apply H1; try assumption. intros contra.
                          destruct contra; [|contradiction].
                          subst.
                          destruct INVARIANT3 as [_ _ _ INVARIANT3].
                          exploit INVARIANT3. rewrite HH. left; reflexivity.
                          intros (l'' & H41 & H42).
                          rewrite H41 in H. inv H. rewrite H42 in Heqo0 by assumption.
                          discriminate.
                 --- eauto with coqlib.
              ** eauto.
          ++ constructor; simpl; intros.
             ** (* forward-edge *)
                 destruct H.
                --- inv H.
                    destruct INVARIANT3 as [_ _ _ INVARIANT3].
                    exploit INVARIANT3. rewrite HH. left; reflexivity.
                    intros (l' & H & H1).
                    destruct INVARIANT as [_ _ _ _ _ _ _ _ _ INVARIANT _].
                    exploit INVARIANT.
                    rewrite HH. left; reflexivity.
                    intros (_ & l'' & H21 & _ & H22).
                    rewrite H21 in H. inv H.
                    destruct (s.(t) ! v) eqn:Heqt.
                    +++ apply in_map_iff in H0.
                        destruct H0 as ((v0', n_v0) & H0' & H0).
                        simpl in H0'. subst.
                        apply ZSortWithIndex.incl_merge in H0.
                        rewrite in_app_iff in H0. destruct H0.
                        *** exploit BACK_SOURCE; try eassumption.
                            apply in_map_iff.
                            eexists (_, _); split; [reflexivity|eassumption].
                            intros (w & H2 & H3).
                            exists w. split; [assumption|].
                            eapply black_reduced_reachable_bis_pred.
                            eassumption. apply H22. left; reflexivity.
                            congruence. intros contra.
                            rewrite H1 in Heqo0. discriminate.
                            apply H22; left; reflexivity. assumption.
                            assumption.
                        *** exploit BACK_SOURCE_GRAY; eauto.
                            apply in_map_iff.
                            eexists (_, _); split; [reflexivity|eassumption].
                    +++ eauto with coqlib.
                --- eauto with coqlib.
             ** eauto.
             ** eauto.
             ** destruct H.
                 --- inv H. exploit BACK_UPWARDS_GRAY. left; reflexivity.
                     intros (l' & H1 & H2).
                     exists l'. split; [assumption|].
                     intros.
                     apply in_map_iff in H5. destruct H5 as ((w', n_w) & H5' & H5).
                     simpl in H5'. subst.
                     destruct (peq v0 v). subst v0. rewrite H4.
                     apply in_map_iff.
                     eexists (_, _); split; [reflexivity|].
                     apply ZSortWithIndex.incl_merge_2_inj.
                     { intros x1 x2 N Hx1 Hx2.
                       assert (N = dom_pre x1) as Ha.
                       { destruct INVARIANT9 as [_ INVARIANT9 _ _].
                         exploit INVARIANT9; eauto.
                         intros HForall. rewrite Forall_forall in HForall.
                         apply (HForall _ Hx1).
                       }
                       assert (N = dom_pre x2) as Hb.
                       { destruct INVARIANT9 as [INVARIANT9 _ _ _].
                         exploit INVARIANT9; eauto. rewrite HH. left; reflexivity.
                         intros HForall. rewrite Forall_forall in HForall.
                         apply (HForall _ Hx2).
                       }
                       assert (reachable ginit root x1) as Hc.
                       { assert (exists x', In (x', x1) s.(back)) as Hc'.
                         { exploit BACK_SOURCE; eauto.
                           apply in_map_iff.
                           eexists (_, _); split; [reflexivity|eassumption].
                           intros (x' & H7 & _). eexists; eassumption.
                         }
                         destruct Hc' as (x' & Hc').
                         pose proof INVARIANT5 as [_ _ _ _ _ _ _ _ BACK_IN_GRAPH].
                         destruct (BACK_IN_GRAPH _ _ Hc')
                           as (succs & H81 & H82).
                         eapply reachable_succ; try eassumption.
                         pose proof INVARIANT5
                           as [_ _ _ _ _ _ BACK_SOURCE_GRAY_OR_BLACK].
                         apply BACK_SOURCE_GRAY_OR_BLACK in Hc'.
                         destruct Hc' as [(? & ? & ? & Hc')|Hc'].
                         destruct INVARIANT5 as [_ INVARIANT5 _ _ _ _ _].
                         eapply INVARIANT5; eassumption.
                         destruct INVARIANT5 as [_ _ INVARIANT5 _ _ _ _].
                         eapply INVARIANT5; eassumption.
                       }
                       assert (reachable ginit root x2) as Hd.
                       { assert (exists x', In (x', x2) s.(back)) as Hd'.
                         { exploit BACK_SOURCE_GRAY; eauto.
                           apply in_map_iff.
                           eexists (_, _); split; [reflexivity|eassumption].
                           intros (x' & H7 & _). eexists; eassumption.
                         }
                         destruct Hd' as (x' & Hd').
                         pose proof INVARIANT5 as [_ _ _ _ _ _ _ _ BACK_IN_GRAPH].
                         destruct (BACK_IN_GRAPH _ _ Hd')
                           as (succs & H81 & H82).
                         eapply reachable_succ with (x:=x'); try eassumption.
                         pose proof INVARIANT5
                           as [_ _ _ _ _ _ BACK_SOURCE_GRAY_OR_BLACK].
                         apply BACK_SOURCE_GRAY_OR_BLACK in Hd'.
                         destruct Hd' as [(? & ? & ? & Hd')|Hd'].
                         destruct INVARIANT5 as [_ INVARIANT5 _ _ _ _ _].
                         eapply INVARIANT5; eassumption.
                         destruct INVARIANT5 as [_ _ INVARIANT5 _ _ _ _].
                         eapply INVARIANT5; eassumption.
                       }
                       eapply dom_pre_inj; eauto. congruence.
                     }
                     rewrite in_app_iff. left; eassumption.
                     destruct (s.(t) ! v) eqn:Heqt.
                     +++ exploit H2; eauto.
                         intros contra; destruct contra; congruence.
                         apply in_map_iff.
                         eexists (_, _); split; [reflexivity|eassumption].
                         intros H6.
                         apply in_map_iff in H6.
                         destruct H6 as ((w', n_w') & H6' & H6).
                         simpl in H6'. subst.
                         apply in_map_iff.
                         eexists (_, _); split; [reflexivity|].
                         apply ZSortWithIndex.incl_merge_2_inj.
                         { intros x1 x2 N Hx1 Hx2.
                           assert (N = dom_pre x1) as Ha.
                           { destruct INVARIANT9 as [_ INVARIANT9 _ _].
                             exploit INVARIANT9; eauto.
                             intros HForall. rewrite Forall_forall in HForall.
                             apply (HForall _ Hx1).
                           }
                           assert (N = dom_pre x2) as Hb.
                           { destruct INVARIANT9 as [INVARIANT9 _ _ _].
                             exploit INVARIANT9; eauto. rewrite HH. left; reflexivity.
                             intros HForall. rewrite Forall_forall in HForall.
                             apply (HForall _ Hx2).
                           }
                           assert (reachable ginit root x1) as Hc.
                           { assert (exists x', In (x', x1) s.(back)) as Hc'.
                             { exploit BACK_SOURCE; eauto.
                               apply in_map_iff.
                               eexists (_, _); split; [reflexivity|eassumption].
                               intros (x' & H7 & _). eexists; eassumption.
                             }
                             destruct Hc' as (x' & Hc').
                             pose proof INVARIANT5 as [_ _ _ _ _ _ _ _ BACK_IN_GRAPH].
                             destruct (BACK_IN_GRAPH _ _ Hc')
                               as (succs & H81 & H82).
                             eapply reachable_succ; try eassumption.
                             pose proof INVARIANT5
                               as [_ _ _ _ _ _ BACK_SOURCE_GRAY_OR_BLACK].
                             apply BACK_SOURCE_GRAY_OR_BLACK in Hc'.
                             destruct Hc' as [(? & ? & ? & Hc')|Hc'].
                             destruct INVARIANT5 as [_ INVARIANT5 _ _ _ _ _].
                             eapply INVARIANT5; eassumption.
                             destruct INVARIANT5 as [_ _ INVARIANT5 _ _ _ _].
                             eapply INVARIANT5; eassumption.
                           }
                           assert (reachable ginit root x2) as Hd.
                           { assert (exists x', In (x', x2) s.(back)) as Hd'.
                             { exploit BACK_SOURCE_GRAY; eauto.
                               apply in_map_iff.
                               eexists (_, _); split; [reflexivity|eassumption].
                               intros (x' & H7 & _). eexists; eassumption.
                             }
                             destruct Hd' as (x' & Hd').
                             pose proof INVARIANT5 as [_ _ _ _ _ _ _ _ BACK_IN_GRAPH].
                             destruct (BACK_IN_GRAPH _ _ Hd')
                               as (succs & H81 & H82).
                             eapply reachable_succ with (x:=x'); try eassumption.
                             pose proof INVARIANT5
                               as [_ _ _ _ _ _ BACK_SOURCE_GRAY_OR_BLACK].
                             apply BACK_SOURCE_GRAY_OR_BLACK in Hd'.
                             destruct Hd' as [(? & ? & ? & Hd')|Hd'].
                             destruct INVARIANT5 as [_ INVARIANT5 _ _ _ _ _].
                             eapply INVARIANT5; eassumption.
                             destruct INVARIANT5 as [_ _ INVARIANT5 _ _ _ _].
                             eapply INVARIANT5; eassumption.
                           }
                           eapply dom_pre_inj; eauto. congruence.
                         }
                         rewrite in_app_iff. right; eassumption.
                     +++ eapply H2; eauto.
                         intros contra; destruct contra; congruence.
                         apply in_map_iff.
                         eexists (_, _); split; [reflexivity|eassumption].
                 --- eauto with coqlib.
              ** eauto.
              ** destruct H.
                 --- inv H. exploit CLASSIFY_BACK_GRAY.
                      left; reflexivity.
                      intros (l' & H & H1).
                      exists l'. split; [assumption|].
                      intros. destruct (s.(t) ! v) eqn:Heqt.
                      +++ exploit H1; try eassumption.
                          intros [contra|contra]; [|contradiction].
                          subst.
                          destruct INVARIANT3 as [_ _ _ INVARIANT3].
                          exploit INVARIANT3. rewrite HH. left; reflexivity.
                          intros (l'' & H51 & H52).
                          rewrite H51 in H. inv H. rewrite H52 in Heqo0 by assumption.
                          discriminate.
                          intros. apply in_map_iff in H4.
                          destruct H4 as ((v0', n_v0) & H4' & H4).
                          simpl in H4'. subst.
                          apply in_map_iff. eexists (_, _). split; [reflexivity|].
                          apply ZSortWithIndex.incl_merge_2_inj.
                          { intros x1 x2 N Hx1 Hx2.
                            assert (N = dom_pre x1) as Ha.
                            { destruct INVARIANT9 as [_ INVARIANT9 _ _].
                              exploit INVARIANT9; eauto.
                              intros HForall. rewrite Forall_forall in HForall.
                              apply (HForall _ Hx1).
                            }
                            assert (N = dom_pre x2) as Hb.
                            { destruct INVARIANT9 as [INVARIANT9 _ _ _].
                              exploit INVARIANT9; eauto. rewrite HH. left; reflexivity.
                              intros HForall. rewrite Forall_forall in HForall.
                              apply (HForall _ Hx2).
                            }
                            assert (reachable ginit root x1) as Hc.
                            { assert (exists x', In (x', x1) s.(back)) as Hc'.
                              { exploit BACK_SOURCE; eauto.
                                apply in_map_iff.
                                eexists (_, _); split; [reflexivity|eassumption].
                                intros (x' & H7 & _). eexists; eassumption.
                              }
                              destruct Hc' as (x' & Hc').
                              pose proof INVARIANT5 as [_ _ _ _ _ _ _ _ BACK_IN_GRAPH].
                              destruct (BACK_IN_GRAPH _ _ Hc')
                                as (succs & H81 & H82).
                              eapply reachable_succ; try eassumption.
                              pose proof INVARIANT5
                                as [_ _ _ _ _ _ BACK_SOURCE_GRAY_OR_BLACK].
                              apply BACK_SOURCE_GRAY_OR_BLACK in Hc'.
                              destruct Hc' as [(? & ? & ? & Hc')|Hc'].
                              destruct INVARIANT5 as [_ INVARIANT5 _ _ _ _ _].
                              eapply INVARIANT5; eassumption.
                              destruct INVARIANT5 as [_ _ INVARIANT5 _ _ _ _].
                              eapply INVARIANT5; eassumption.
                            }
                            assert (reachable ginit root x2) as Hd.
                            { assert (exists x', In (x', x2) s.(back)) as Hd'.
                              { exploit BACK_SOURCE_GRAY; eauto.
                                apply in_map_iff.
                                eexists (_, _); split; [reflexivity|eassumption].
                                intros (x' & H7 & _). eexists; eassumption.
                              }
                              destruct Hd' as (x' & Hd').
                              pose proof INVARIANT5 as [_ _ _ _ _ _ _ _ BACK_IN_GRAPH].
                              destruct (BACK_IN_GRAPH _ _ Hd')
                                as (succs & H81 & H82).
                              eapply reachable_succ with (x:=x'); try eassumption.
                              pose proof INVARIANT5
                                as [_ _ _ _ _ _ BACK_SOURCE_GRAY_OR_BLACK].
                              apply BACK_SOURCE_GRAY_OR_BLACK in Hd'.
                              destruct Hd' as [(? & ? & ? & Hd')|Hd'].
                              destruct INVARIANT5 as [_ INVARIANT5 _ _ _ _ _].
                              eapply INVARIANT5; eassumption.
                              destruct INVARIANT5 as [_ _ INVARIANT5 _ _ _ _].
                              eapply INVARIANT5; eassumption.
                            }
                            eapply dom_pre_inj; eauto. congruence.
                          }
                          rewrite in_app_iff. right; eassumption.
                      +++ apply H1; try assumption. intros contra.
                          destruct contra; [|contradiction].
                          subst.
                          destruct INVARIANT3 as [_ _ _ INVARIANT3].
                          exploit INVARIANT3. rewrite HH. left; reflexivity.
                          intros (l'' & H41 & H42).
                          rewrite H41 in H. inv H. rewrite H42 in Heqo0 by assumption.
                          discriminate.
                 --- eauto with coqlib.
              ** eauto.
       -- (* back-edge *)
          constructor; simpl; intros.
          ++ destruct H.
             ** inv H.
                apply in_map_iff in H0.
                destruct H0 as ((v0', n_v0) & H0' & H0).
                simpl in H0'. subst.
                apply ZSortWithIndex.incl_merge in H0.
                simpl in H0. destruct H0 as [H0|H0].
                --- inv H0. exists u0. split. left; reflexivity.
                    constructor.
                --- exploit BACK_SOURCE_GRAY; eauto.
                    apply in_map_iff.
                    eexists (_, _); split; [reflexivity|eassumption].
                    intros (w & H3 & H4).
                    exists w. split; [right; assumption|].
                    apply black_reduced_reachable_bis_back; assumption.
             ** exploit BACK_SOURCE_GRAY. right; eassumption. eassumption.
                intros (w & H1 & H2).
                exists w. split; [right; assumption|].
                apply black_reduced_reachable_bis_back; assumption.
          ++ exploit BACK_SOURCE; eauto.
             intros (w & H1 & H2).
             exists w. split; [right; assumption|].
             apply black_reduced_reachable_bis_back; assumption.
          ++ eauto.
          ++ destruct H.
               --- inv H. exploit BACK_UPWARDS_GRAY; eauto.
                   intros (l' & H1 & H2).
                   exists l'. split; [assumption|].
                   intros.
                   apply in_map_iff in H5.
                   destruct H5 as ((w', n_w) & H5' & H5).
                   simpl in H5'. subst.
                   exploit H2; eauto.
                   intros [contra|contra]; [|contradiction]. subst.
                   apply H3. left; reflexivity.
                   apply in_map_iff.
                   eexists (_, _); split; [reflexivity|eassumption].
                   intros H6.
                   apply in_map_iff in H6.
                   destruct H6 as ((w', n_w') & H6' & H6).
                   simpl in H6'. subst.
                   apply in_map_iff.
                   eexists (_, _); split; [reflexivity|].
                   apply ZSortWithIndex.incl_merge_2_inj.
                   { intros x1 x2 N Hx1 Hx2.
                     assert (N = dom_pre x1) as Ha.
                     { simpl in Hx1. destruct Hx1 as [Hx1|[]].
                       inv Hx1. reflexivity.
                     }
                     assert (N = dom_pre x2) as Hb.
                     { destruct INVARIANT9 as [INVARIANT9 _ _ _].
                       exploit INVARIANT9; eauto. rewrite HH. left; reflexivity.
                       intros HForall. rewrite Forall_forall in HForall.
                       apply (HForall _ Hx2).
                     }
                     assert (reachable ginit root x1) as Hc.
                     { simpl in Hx1. destruct Hx1 as [Hx1|[]].
                       inv Hx1.
                       destruct INVARIANT as [_ _ _ _ _ _ _ _ _ INVARIANT _].
                       exploit INVARIANT. rewrite HH. left; reflexivity.
                       intros (_ & l'' & H61 & _ & H62).
                       rewrite H61 in H1. inv H1.
                       eapply reachable_succ.
                       destruct INVARIANT5 as [_ INVARIANT5 _ _ _ _ _].
                       eapply INVARIANT5. rewrite HH. left; reflexivity.
                       eassumption.
                       apply H62. left; reflexivity.
                     }
                     assert (reachable ginit root x2) as Hd.
                     { exploit BACK_SOURCE_GRAY; eauto.
                       apply in_map_iff.
                       eexists (_, _); split; [reflexivity|eassumption].
                       intros (x' & Hd' & _).
                       pose proof INVARIANT5 as [_ _ _ _ _ _ _ _ BACK_IN_GRAPH].
                       destruct (BACK_IN_GRAPH _ _ Hd')
                         as (succs & H81 & H82).
                       eapply reachable_succ with (x:=x'); try eassumption.
                       pose proof INVARIANT5
                         as [_ _ _ _ _ _ BACK_SOURCE_GRAY_OR_BLACK].
                       apply BACK_SOURCE_GRAY_OR_BLACK in Hd'.
                       destruct Hd' as [(? & ? & ? & Hd')|Hd'].
                       destruct INVARIANT5 as [_ INVARIANT5 _ _ _ _ _].
                       eapply INVARIANT5; eassumption.
                       destruct INVARIANT5 as [_ _ INVARIANT5 _ _ _ _].
                       eapply INVARIANT5; eassumption.
                     }
                     eapply dom_pre_inj; eauto. congruence.
                   }
                   simpl. right; eassumption.
               --- exploit BACK_UPWARDS_GRAY. right; eassumption.
                   intros (l' & H1 & H2). exists l'. split; [assumption|].
                   eauto.
           ++ eauto.
           ++ destruct H.
              ** inv H. exploit CLASSIFY_BACK_GRAY. left; reflexivity.
                 intros (l' & H1 & H2).
                 exists l'. split. assumption.
                 intros.
                 destruct (peq v0 v).
                 --- subst. apply in_map_iff. eexists (_, _); split; [reflexivity|].
                     apply ZSortWithIndex.incl_merge_2_inj.
                     { intros x1 x2 N Hx1 Hx2.
                       assert (N = dom_pre x1) as Ha.
                       { simpl in Hx1. destruct Hx1 as [Hx1|[]].
                         inv Hx1. reflexivity.
                       }
                       assert (N = dom_pre x2) as Hb.
                       { destruct INVARIANT9 as [INVARIANT9 _ _ _].
                         exploit INVARIANT9; eauto. rewrite HH. left; reflexivity.
                         intros HForall. rewrite Forall_forall in HForall.
                         apply (HForall _ Hx2).
                       }
                       assert (reachable ginit root x1) as Hc.
                       { simpl in Hx1. destruct Hx1 as [Hx1|[]].
                         inv Hx1.
                         eapply reachable_succ; try eassumption.
                         destruct INVARIANT5 as [_ INVARIANT5 _ _ _ _ _].
                         eapply INVARIANT5. rewrite HH. left; reflexivity.
                       }
                       assert (reachable ginit root x2) as Hd.
                       { exploit BACK_SOURCE_GRAY; eauto.
                         apply in_map_iff.
                         eexists (_, _); split; [reflexivity|eassumption].
                         intros (x' & Hd' & _).
                         pose proof INVARIANT5 as [_ _ _ _ _ _ _ _ BACK_IN_GRAPH].
                         destruct (BACK_IN_GRAPH _ _ Hd')
                           as (succs & H81 & H82).
                         eapply reachable_succ with (x:=x'); try eassumption.
                         pose proof INVARIANT5
                           as [_ _ _ _ _ _ BACK_SOURCE_GRAY_OR_BLACK].
                         apply BACK_SOURCE_GRAY_OR_BLACK in Hd'.
                         destruct Hd' as [(? & ? & ? & Hd')|Hd'].
                         destruct INVARIANT5 as [_ INVARIANT5 _ _ _ _ _].
                         eapply INVARIANT5; eassumption.
                         destruct INVARIANT5 as [_ _ INVARIANT5 _ _ _ _].
                         eapply INVARIANT5; eassumption.
                       }
                       eapply dom_pre_inj; eauto. congruence.
                     }
                     simpl. left; reflexivity.
                 --- destruct H3; [congruence|].
                     exploit H2; eauto.
                     intros [contra|contra]; congruence.
                     intros. apply in_map_iff in H4.
                     destruct H4 as ((v0', n_v0) & H4' & H4).
                     simpl in H4'. subst.
                     apply in_map_iff. eexists (_, _). split; [reflexivity|].
                     apply ZSortWithIndex.incl_merge_2_inj.
                     { intros x1 x2 N Hx1 Hx2.
                       assert (N = dom_pre x1) as Ha.
                       { simpl in Hx1. destruct Hx1 as [Hx1|[]].
                         inv Hx1. reflexivity.
                       }
                       assert (N = dom_pre x2) as Hb.
                       { destruct INVARIANT9 as [INVARIANT9 _ _ _].
                         exploit INVARIANT9; eauto. rewrite HH. left; reflexivity.
                         intros HForall. rewrite Forall_forall in HForall.
                         apply (HForall _ Hx2).
                       }
                       assert (reachable ginit root x1) as Hc.
                       { simpl in Hx1. destruct Hx1 as [Hx1|[]].
                         inv Hx1.
                         destruct INVARIANT as [_ _ _ _ _ _ _ _ _ INVARIANT _].
                         exploit INVARIANT. rewrite HH. left; reflexivity.
                         intros (_ & l'' & H61 & _ & H62).
                         rewrite H61 in H1. inv H1.
                         eapply reachable_succ.
                         destruct INVARIANT5 as [_ INVARIANT5 _ _ _ _ _].
                         eapply INVARIANT5. rewrite HH. left; reflexivity.
                         eassumption.
                         apply H62. left; reflexivity.
                       }
                       assert (reachable ginit root x2) as Hd.
                       { exploit BACK_SOURCE_GRAY; eauto.
                         apply in_map_iff.
                         eexists (_, _); split; [reflexivity|eassumption].
                         intros (x' & Hd' & _).
                         pose proof INVARIANT5 as [_ _ _ _ _ _ _ _ BACK_IN_GRAPH].
                         destruct (BACK_IN_GRAPH _ _ Hd')
                           as (succs & H81 & H82).
                         eapply reachable_succ with (x:=x'); try eassumption.
                         pose proof INVARIANT5
                           as [_ _ _ _ _ _ BACK_SOURCE_GRAY_OR_BLACK].
                         apply BACK_SOURCE_GRAY_OR_BLACK in Hd'.
                         destruct Hd' as [(? & ? & ? & Hd')|Hd'].
                         destruct INVARIANT5 as [_ INVARIANT5 _ _ _ _ _].
                         eapply INVARIANT5; eassumption.
                         destruct INVARIANT5 as [_ _ INVARIANT5 _ _ _ _].
                         eapply INVARIANT5; eassumption.
                       }
                       eapply dom_pre_inj; eauto. congruence.
                     }
                     simpl. right; eassumption.
              ** exploit CLASSIFY_BACK_GRAY. right; eassumption.
                 intros (l' & H1 & H2).
                 exists l'. split. assumption.
                 intros. destruct H4.
                 --- inv H4.
                     destruct INVARIANT4 as [INVARIANT4 _ _ _ _ _ _ _ _].
                     rewrite HH in INVARIANT4.
                     inv INVARIANT4. contradiction H6. apply InA_alt.
                     eexists (_, _, _, _). split. reflexivity. eassumption.
                 --- eauto.
           ++ destruct H.
              ** inv H. destruct INVARIANT4 as [_ INVARIANT4 _ _ _ _ _ _ _].
                 exploit INVARIANT4. rewrite HH. left; reflexivity.
                 congruence.
              ** eauto.
Qed.

End DOM_PRE_HYPS.

End DOM_PRE.

Lemma initial_state_spec6:
  invariant6 (init_state ginit root).
Proof.
  unfold init_state. destruct (ginit!root) as [succs|] eqn:?.

  - (* root has succs *)
    constructor; simpl; intros.
    + destruct H; inv H. contradiction H0.
    + rewrite PTree.gempty in H. discriminate.
    + rewrite PTree.gempty in H. discriminate.
    + destruct H; inv H.
      exists l. split; [assumption|]. intros. contradiction.
    + rewrite PTree.gempty in H3. discriminate.
    + destruct H; inv H. exists l. split; [assumption|]. intros; contradiction.
    + contradiction.
  - constructor; simpl; intros.
    + contradiction.
    + rewrite PTree.gempty in H. discriminate.
    + rewrite PTree.gempty in H. discriminate.
    + contradiction.
    + rewrite PTree.gempty in H3. discriminate.
    + contradiction.
    + contradiction.
Qed.


Inductive invariant7 (s:state) : Prop :=
  Invariant7
    (BACK_NUMBERS_DECREASE_GRAY : forall u n l post v m1 m2,
      In (u, n, l, post) s.(wrk) ->
      s.(r) ! v = Some (m1, m2) ->
      In (v, u) s.(back) ->
      n < m1)
    (BACK_NUMBERS_DECREASE : forall u v n1 n2 m1 m2, In (u, v) s.(back) ->
      s.(r) ! u = Some (n1, n2) -> s.(r) ! v = Some (m1, m2) ->
      is_included (n1, n2) (m1, m2)).

Inductive postcondition7
  (acc : PTree.t (positive * positive)
       * PTree.t (PTree.t unit)
       * PTree.t (list (node * Z))
       * list (node * node))
  : Prop :=
  Postcondition7
    (BACK_NUMBERS_DECREASE : let '(r, c, t, back) := acc in
      forall u v n1 n2 m1 m2, In (u, v) back ->
      r ! u = Some (n1, n2) -> r ! v = Some (m1, m2) ->
      is_included (n1, n2) (m1, m2)).

Lemma transition_spec7:
  forall dom_pre s (INVARIANT : invariant s) (INVARIANT2 : invariant2 s)
  (INVARIANT3 : invariant3 s) (INVARIANT4 : invariant4 s) (INVARIANT5 : invariant5 s),
  invariant7 s ->
  match transition dom_pre s with inr s' => invariant7 s' | inl m => postcondition7 m end.
Proof.
  intros. inv H. unfold transition.
  destruct (wrk s) as [ | [[[u n] succs] [s_c s_t]] l] eqn:HH.
  - (* finished *)
    constructor; intros.
    eauto.
  - (* not finished *)
    destruct succs as [ | v succs ].
    + (* all children of x were traversed *)
      constructor; simpl; intros.
      * rewrite PTree.gsspec in H0. destruct (peq v u).
        -- subst v. inv H0.
           destruct INVARIANT2 as [_ _ _ _ INVARIANT2 _ _ _].
           apply Sorted_StronglySorted in INVARIANT2.
           rewrite HH in INVARIANT2. inv INVARIANT2.
           rewrite Forall_forall in H4. specialize (H4 _ H). apply H4.
           intros (((_, n1), _), _) (((_, n2), _), _) (((_, n3), _), _).
           eauto using Pos.lt_trans.
        -- eauto with coqlib.
      * rewrite PTree.gsspec in H0. destruct (peq u0 u).
        -- subst u0. inv H0.
           rewrite PTree.gsspec in H1. destruct (peq v u).
           ++ inv H1. split; reflexivity.
           ++ destruct INVARIANT3 as [_ _ _ INVARIANT3].
              exploit INVARIANT3. rewrite HH. left; reflexivity.
              intros (l' & H2 & H3).
              destruct INVARIANT5 as [_ _ _ _ _ _ _ _ INVARIANT5].
              destruct (INVARIANT5 _ _ H) as (succs & H41 & H42).
              rewrite H2 in H41. inv H41.
              exploit H3; try eassumption. unfold pre in *. (* HACK *)
              congruence.
        -- rewrite PTree.gsspec in H1. destruct (peq v u).
           ++ subst v. inv H1.
              destruct INVARIANT as [_ _ _ INVARIANT _ _ _ _ _ _ _].
              apply INVARIANT in H0 as H1. destruct H1 as (_ & H1).
              exploit BACK_NUMBERS_DECREASE_GRAY. left; reflexivity.
              eassumption. eassumption. xomega.
           ++ eapply BACK_NUMBERS_DECREASE; eassumption.
    + (* children y needs traversing *)
      destruct ((gr s)!v) as [ succs_v | ] eqn:?.
      * (* y has children *)
        constructor; simpl; intros.
        -- destruct H as [H|[H|H]].
           ++ inv H.
              destruct INVARIANT2 as [_ _ _ _ _ _ _ INVARIANT2].
              apply INVARIANT2 in H1 as (_ & H1). congruence.
           ++ inv H. eauto with coqlib.
           ++ eauto with coqlib.
        -- eapply BACK_NUMBERS_DECREASE; eassumption.
     * (* y has no children *)
       destruct (s.(r) ! v) as [ (n1, n2) |] eqn:?.
       -- destruct (Pos.ltb_spec0 n1 n).
          ++ (* cross-edge *)
             constructor; simpl; intros.
             ** destruct H as [H|H].
                --- inv H. eauto with coqlib.
                --- eauto with coqlib.
             ** eapply BACK_NUMBERS_DECREASE; eassumption.
          ++ constructor; simpl; intros.
             ** destruct H as [H|H].
                --- inv H. eauto with coqlib.
                --- eauto with coqlib.
             ** eapply BACK_NUMBERS_DECREASE; eassumption.
       -- constructor; simpl; intros.
          ++ destruct H as [H|H].
             ** inv H. destruct H1.
                --- inv H. congruence.
                --- eauto with coqlib.
             ** destruct H1.
               --- inv H1.
                   destruct INVARIANT4 as [_ INVARIANT4 _ _ _ _ _ _ _].
                   exploit INVARIANT4. rewrite HH. left; reflexivity.
                   congruence.
               --- eauto with coqlib.
          ++ destruct H.
             ** inv H. congruence.
             ** eapply BACK_NUMBERS_DECREASE; eassumption.
Qed.

Lemma initial_state_spec7:
  invariant7 (init_state ginit root).
Proof.
  unfold init_state. destruct (ginit!root) as [succs|] eqn:?.

  - (* root has succs *)
    constructor; simpl; intros.
    + contradiction.
    + contradiction.
  - constructor; simpl; intros.
    + contradiction.
    + contradiction.
Qed.


Inductive invariant8 (s:state) : Prop :=
  Invariant8
    (CROSS_NUMBERS_DECREASE_BLACK : forall u set v,
      s.(c) ! u = Some set -> set ! v = Some tt ->
      v <> u ->
      exists n1 n2 m1 m2,
        s.(r) ! u = Some (n1, n2) /\ s.(r) ! v = Some (m1, m2) /\
        m2 < n1).

Inductive postcondition8
  (acc : PTree.t (positive * positive)
       * PTree.t (PTree.t unit)
       * PTree.t (list (node * Z))
       * list (node * node))
  : Prop :=
  Postcondition8
    (CROSS_NUMBERS_DECREASE : let '(r, c, t, back) := acc in
      forall u set v, c ! u = Some set -> set ! v = Some tt ->
      v <> u ->
      exists n1 n2 m1 m2,
        r ! u = Some (n1, n2) /\ r ! v = Some (m1, m2) /\ m2 < n1).

Lemma transition_spec8:
  forall dom_pre s (INVARIANT : invariant s) (INVARIANT2 : invariant2 s)
  (INVARIANT4 : invariant4 s) (INVARIANT5 : invariant5 s),
  invariant8 s ->
  match transition dom_pre s with inr s' => invariant8 s' | inl m => postcondition8 m end.
Proof.
  intros. inv H. unfold transition.
  destruct (wrk s) as [ | [[[u n] succs] [s_c s_t]] l] eqn:HH.
  - (* finished *)
    constructor; intros.
    eauto.
  - (* not finished *)
    destruct succs as [ | v succs ].
    + (* all children of x were traversed *)
      constructor; simpl; intros.
      rewrite PTree.gsspec in H. destruct (peq u0 u).
      -- inv H. rewrite PTree.gso in H0 by assumption.
         rewrite PTree.gfilter in H0.
         destruct (s_c ! v) as [[]|] eqn:Heqv; [|discriminate].
         unfold is_directly_included in H0. rewrite PTree.gss in H0.
         rewrite PTree.gsspec in H0. destruct (peq v u).
         ++ exfalso. subst v.
            destruct INVARIANT4
              as [_ WRK_NOT_BLACK _ _ _ CROSS_TARGETS_BLACK_GRAY _ _ _].
            exploit WRK_NOT_BLACK. rewrite HH. left; reflexivity.
            intros.
            eapply CROSS_TARGETS_BLACK_GRAY; eauto.
            rewrite HH. left; reflexivity.
         ++ unfold pre in *. (* HACK *)
            destruct (s.(r) ! v) as [(n1, n2)|] eqn:Heqrv.
            ** destruct (test_is_included _ _) eqn:Htest; [discriminate|].
               rewrite PTree.gss. rewrite PTree.gso by assumption.
               eexists _, _, _, _. split; [reflexivity|]. split; [eassumption|].
               unfold test_is_included in Htest.
               apply not_true_iff_false in Htest. rewrite andb_true_iff in Htest.
               rewrite 2!Pos.leb_le in Htest.
               destruct INVARIANT2 as [_ _ _ _ _ INVARIANT2 _ _].
               exploit INVARIANT2. rewrite HH. left; reflexivity.
               eassumption.
               destruct INVARIANT as [_ _ _ INVARIANT _ _ _ _ _ _ _].
               apply INVARIANT in Heqrv. xomega.
            ** destruct INVARIANT4 as [_ _ _ _ _ INVARIANT4 _ _ _].
               exploit INVARIANT4; eauto. rewrite HH. left; reflexivity.
               contradiction.
      -- rewrite PTree.gso by assumption. rewrite PTree.gso; eauto.
         intros contra; subst v.
         destruct INVARIANT4 as [_ WRK_NOT_BLACK _ _ _ _ CROSS_TARGETS_BLACK_BLACK _ _].
         exploit WRK_NOT_BLACK. rewrite HH. left; reflexivity.
         intros.
         eapply CROSS_TARGETS_BLACK_BLACK; eauto.
    + (* children y needs traversing *)
      destruct ((gr s)!v) as [ succs_v | ] eqn:?.
      * (* y has children *)
        constructor; simpl; intros.
        eauto.
     * (* y has no children *)
       destruct (s.(r) ! v) as [ (n1, n2) |] eqn:?.
       -- destruct (Pos.ltb_spec0 n1 n).
          ++ (* cross-edge *)
             constructor; simpl; intros.
             eauto.
          ++ constructor; simpl; intros.
             eauto.
       -- constructor; simpl; intros.
          eauto.
Qed.

Lemma initial_state_spec8:
  invariant8 (init_state ginit root).
Proof.
  unfold init_state. destruct (ginit!root) as [succs|] eqn:?.

  - (* root has succs *)
    constructor; simpl; intros.
    rewrite PTree.gempty in H. discriminate.
  - constructor; simpl; intros.
    rewrite PTree.gempty in H. discriminate.
Qed.


Inductive invariant10 (s:state) : Prop :=
  Invariant10
    (ROOT_LAST_WRK : exists l post, last s.(wrk) (root, 1, l, post) = (root, 1, l, post))
    (ROOT_REACHABLE : s.(wrk) = nil ->
      forall u, s.(r) ! u <> None -> reduced_reachable ginit s.(back) root u).

Inductive postcondition10
  (acc : PTree.t (positive * positive)
       * PTree.t (PTree.t unit)
       * PTree.t (list (node * Z))
       * list (node * node))
  : Prop :=
  Postcondition10
    (ROOT_REACHABLE : let '(r, c, t, back) := acc in
      forall u, r ! u <> None ->
      reduced_reachable ginit back root u).

Lemma transition_spec10:
  forall dom_pre s (INVARIANT2 : invariant2 s),
  invariant10 s ->
  match transition dom_pre s with inr s' => invariant10 s' | inl m => postcondition10 m end.
Proof.
  intros. inv H. unfold transition.
  destruct (wrk s) as [ | [[[u n] succs] [s_c s_t]] l] eqn:HH.
  - (* finished *)
    constructor; intros.
    auto.
  - (* not finished *)
    destruct succs as [ | v succs ].
    + (* all children of x were traversed *)
      constructor; simpl; intros.
      * destruct ROOT_LAST_WRK as (l' & post' & H).
        exists l', post'.
        destruct l.
        -- reflexivity.
        -- assumption.
      * subst l.
        destruct ROOT_LAST_WRK as (l' & post' & ROOT_LAST_WRK).
        simpl in ROOT_LAST_WRK. inv ROOT_LAST_WRK.
        rewrite PTree.gsspec in H0. destruct (peq u0 root).
        -- subst u0. constructor.
        -- unfold pre in *.
           destruct (s.(r) ! u0) as [(n1, n2)|] eqn:Hr; [|contradiction].
           destruct INVARIANT2 as [_ _ INVARIANT2 _ _ _ _ _].
           exploit INVARIANT2; try eassumption.
           xomega.
           intros.
           apply black_reduced_reachable_bis_is_reduced_reachable in H.
           assumption.
    + (* children y needs traversing *)
      destruct ((gr s)!v) as [ succs_v | ] eqn:?.
      * (* y has children *)
        constructor; simpl; intros.
        -- assumption.
        -- discriminate.
      * (* y has no children *)
        destruct (s.(r) ! v) as [ (n1, n2) |] eqn:?.
        -- destruct (Pos.ltb_spec0 n1 n).
           ++ (* cross-edge *)
              constructor; simpl; intros.
              ** destruct ROOT_LAST_WRK as (l' & post' & H).
                 destruct l.
                 --- simpl in H. inv H.
                     eexists _, _. reflexivity.
                 --- exists l', post'. assumption.
              ** discriminate.
           ++ (* forward edge *)
              constructor; simpl; intros.
              ** destruct ROOT_LAST_WRK as (l' & post' & H).
                 destruct l.
                 --- simpl in H. inv H.
                     eexists _, _. reflexivity.
                 --- exists l', post'. assumption.
              ** discriminate.
        -- (* back-edge *)
           constructor; simpl; intros.
           ++ destruct ROOT_LAST_WRK as (l' & post' & H).
              destruct l.
              ** simpl in H. inv H.
                 eexists _, _. reflexivity.
              ** exists l', post'. assumption.
           ++ discriminate.
Qed.

Lemma initial_state_spec10:
  invariant10 (init_state ginit root).
Proof.
  unfold init_state. destruct (ginit!root) as [succs|] eqn:?.

  - (* root has succs *)
    constructor; simpl; intros.
    + eexists _, _. reflexivity.
    + rewrite PTree.gempty in H0. contradiction.
  - constructor; simpl; intros.
    + exists nil, (PTree.empty _, nil). reflexivity.
    + rewrite PTree.gempty in H0. contradiction.
Qed.

End POSTORDER.


Theorem precompute_r_t_up_correct_1:
  forall g root dom_pre,
  let '(r, c, t, back) := precompute_r_t_up g root dom_pre in
  (forall x1 x2 m n1 n2, r!x1 = Some (m, n1) -> r!x2 = Some (m, n2) -> x1 = x2)
  /\ (forall x, reachable g root x -> g!x <> None -> r!x <> None)
  /\ (forall x n1 n2, r ! x = Some (n1, n2) -> Ple n1 n2).
Proof.
  intros.
  assert (postcondition g root (precompute_r_t_up g root dom_pre)).
    unfold precompute_r_t_up.
    apply WfIter.iterate_prop with (P := invariant g root).
    apply transition_spec.
    apply initial_state_spec.
  destruct (precompute_r_t_up g root) as (((r, c), t), back).
  inv H.
  split; auto.
  split; auto.
  induction 1; intros.
 root case *)  apply ROOT; auto.
 succ case *)  eapply SUCCS; eauto. apply IHreachable. congruence.
Qed.

Lemma precompute_r_t_up_correct_2 :
  forall g root dom_pre,
  let '(r, c, t, back) := precompute_r_t_up g root dom_pre in
  (forall x1 x2 n1 n2 m1 m2, r ! x1 = Some (n1, n2) -> r!x2 = Some (m1, m2) ->
  is_included (m1, m2) (n1, n2) -> reduced_reachable g back x1 x2)
  /\ (forall u v n1 n2 m1 m2,
      r ! u = Some (n1, n2) ->
      r ! v = Some (m1, m2) ->
      m2 < n1 \/ n2 < m1
      \/ is_included (m1, m2) (n1, n2)
      \/ is_included (n1, n2) (m1, m2)) .
Proof.
  intros.
  assert (postcondition2 g (precompute_r_t_up g root dom_pre)).
    unfold precompute_r_t_up.
    apply WfIter.iterate_prop
      with (P := fun st => invariant g root st /\ invariant2 g st /\ invariant4 g st).
    intros. destruct H as (H & H0 & H1).
    pose proof (transition_spec _ _ dom_pre _ H).
    pose proof (transition_spec2 _ _ dom_pre _ H H1 H0).
    pose proof (transition_spec4 _ _ dom_pre _ H H1).
    destruct (transition dom_pre a); auto.
    split; [|split].
    apply initial_state_spec.
    apply initial_state_spec2.
    apply initial_state_spec4.
  destruct (precompute_r_t_up g root) as (((r, c), t), back) eqn:Hprecompute_r_t_up.
  inv H.
  split; assumption.
Qed.

Lemma precompute_r_t_up_correct_3 :
  forall g root dom_pre,
  let '(r, c, t, back) := precompute_r_t_up g root dom_pre in
  forall u set v, c ! u = Some set -> set ! v = Some tt ->
    reduced_reachable g back u v.
Proof.
  intros.
  assert (postcondition3 g (precompute_r_t_up g root dom_pre)).
    unfold precompute_r_t_up.
    apply WfIter.iterate_prop
      with (P := fun st => invariant g root st /\ invariant2 g st
                        /\ invariant3 g st /\ invariant4 g st).
    intros. destruct H as (H & H0 & H1 & H2).
    pose proof (transition_spec _ _ dom_pre _ H).
    pose proof (transition_spec2 _ _ dom_pre _ H H2 H0).
    pose proof (transition_spec3 _ _ dom_pre _ H H0 H2 H1).
    pose proof (transition_spec4 _ _ dom_pre _ H H2).
    destruct (transition dom_pre a); auto.
    split; [|split; [|split]].
    apply initial_state_spec.
    apply initial_state_spec2.
    apply initial_state_spec3.
    apply initial_state_spec4.
  destruct (precompute_r_t_up g root) as (((r, c), t), back) eqn:Hprecompute_r_t_up.
  inv H.
  assumption.
Qed.

Lemma precompute_r_t_up_correct_4 :
  forall g root dom_pre,
  let '(r, c, t, back) := precompute_r_t_up g root dom_pre in
  (forall u v, reachable g root u -> reduced_reachable g back u v ->
    g ! v <> None -> cross_included r c u v)
  /\ (forall u set v, c ! u = Some set -> set ! v = Some tt ->
      r ! v <> None)
  /\ (forall u, r ! u <> None ->
      exists set, c ! u = Some set /\ set ! u = Some tt).
Proof.
  intros.
  assert (postcondition4 g (precompute_r_t_up g root dom_pre)).
    unfold precompute_r_t_up.
    apply WfIter.iterate_prop with (P := fun s => invariant g root s /\ invariant4 g s).
    intros. destruct H.
    pose proof (transition_spec _ _ dom_pre _ H).
    pose proof (transition_spec4 _ _ dom_pre _ H H0).
    destruct (transition dom_pre a); auto.
    split.
    apply initial_state_spec.
    apply initial_state_spec4.
  destruct (precompute_r_t_up g root) as (((r, c), t), back) eqn:Hprecompute_r_t_up.
  inv H. split; [|split; assumption].
  intros. apply reduced_reachable_equiv in H0. induction H0.
  - (* trivial path *)
    destruct (CROSS_REFL root0) as (set & H2 & H3).
    + exploit precompute_r_t_up_correct_1. rewrite Hprecompute_r_t_up.
      intros (_ & H2 & _). eauto.
    + exists set, root0. split; [assumption|]. split; [assumption|].
      apply directly_included_refl. exploit precompute_r_t_up_correct_1.
      rewrite Hprecompute_r_t_up. intros (_ & H4 & _). eauto.
  - (* u -> x ->* y *)
    exploit CLASSIFY. eassumption.
      exploit precompute_r_t_up_correct_1. rewrite Hprecompute_r_t_up.
      intros (_ & H5 & _). apply H5. assumption. congruence. eassumption.
    intros [Hstep|Hstep]; [contradiction|].
    exploit IHreduced_reachable_bis.
    econstructor; eassumption. congruence.
    intros (set & w & H51 & H52 & H53).
    destruct (directly_included_dec r root0 w).
    * destruct (CROSS_REFL root0) as (set' & H6 & H7).
      -- destruct H5 as (m1 & m2 & _ & _ & H5 & _). congruence.
      -- exists set', root0. split; [assumption|]. split; [assumption|].
         eapply directly_included_trans; eauto.
    * exploit CROSS_UPWARDS; eauto.
      exploit precompute_r_t_up_correct_1. rewrite Hprecompute_r_t_up.
      intros (_ & H6). eapply H6; try assumption. congruence.
      intros (set' & H61 & H62).
      unfold cross_included; eauto.
Qed.

Lemma precompute_r_t_up_correct_5 :
  forall g root dom_pre,
  let '(r, c, t, back) := precompute_r_t_up g root dom_pre in
  (forall u, r ! u <> None -> reachable g root u /\ g ! u <> None)
  /\ (forall u, c ! u <> None -> r ! u <> None)
  /\ (forall u succs v, g ! u = Some succs -> In v succs ->
     g ! v = None -> r ! u <> None -> In (u, v) back)
  /\ (forall u v, In (u, v) back -> r ! u <> None)
  /\ (forall u, t ! u <> None <-> r ! u <> None)
  /\ (forall u v, In (u, v) back -> exists succs,
       g ! u = Some succs /\ In v succs).
Proof.
  intros.
  assert (postcondition5 g root (precompute_r_t_up g root dom_pre)).
    unfold precompute_r_t_up.
    apply WfIter.iterate_prop with (P := fun s => invariant g root s /\ invariant5 g root s).
    intros. destruct H.
    pose proof (transition_spec _ _ dom_pre _ H).
    pose proof (transition_spec5 _ _ dom_pre _ H H0).
    destruct (transition dom_pre a); auto.
    split.
    apply initial_state_spec.
    apply initial_state_spec5.
  destruct (precompute_r_t_up g root) as (((r, c), t), back) eqn:Hprecompute_r_t_up.
  inv H.
  split; [|repeat (split; try assumption)].
  intros. split; auto.
Qed.

Lemma precompute_r_t_up_correct_6 : forall g root dom_pre
  (dom_pre_inj : forall u v,
    reachable g root u -> reachable g root v ->
    dom_pre u = dom_pre v -> u = v),
  let '(r, c, t, back) := precompute_r_t_up g root dom_pre in
  (forall u v,
    (reachable g root u /\ g ! u <> None /\ is_in_t_up g back u v)
    <-> exists set, t ! u = Some set /\ In v (map fst set)).
Proof.
  intros.
  assert (postcondition6 g (precompute_r_t_up g root dom_pre)).
    unfold precompute_r_t_up.
    apply WfIter.iterate_prop
      with (P := fun s => invariant g root s /\ invariant2 g s /\ invariant3 g s
                       /\ invariant4 g s /\ invariant5 g root s
                       /\ invariant6 g s /\ invariant9 dom_pre s).
    intros. destruct H as (H & H2 & H3 & H4 & H5 & H6 & H9).
    pose proof (transition_spec _ _ dom_pre _ H).
    pose proof (transition_spec2 _ _ dom_pre _ H H4 H2).
    pose proof (transition_spec3 _ _ dom_pre _ H H2 H4 H3).
    pose proof (transition_spec4 _ _ dom_pre _ H H4).
    pose proof (transition_spec5 _ _ dom_pre _ H H5).
    pose proof (transition_spec6 _ _ dom_pre dom_pre_inj _ H H2 H3 H4 H5 H9 H6).
    pose proof (transition_spec9 dom_pre _ H9).
    destruct (transition dom_pre a); auto 7.
    split. apply initial_state_spec.
    split. apply initial_state_spec2.
    split. apply initial_state_spec3.
    split. apply initial_state_spec4.
    split. apply initial_state_spec5.
    split. apply initial_state_spec6.
    apply initial_state_spec9.
  destruct (precompute_r_t_up g root) as (((r, c), t), back) eqn:Hprecompute_r_t_up.
  inv H.
  split; intros.
  - destruct H as (H & H0 & H1).
    destruct H1 as (H11 & s & H12 & H13).
    apply reduced_reachable_equiv in H13. induction H13.
    + apply CLASSIFY_BACK in H12. destruct H12; [|assumption].
      subst. contradiction H11. constructor.
      exploit precompute_r_t_up_correct_1. rewrite Hprecompute_r_t_up. intros (_ & H2 & _). eauto.
    + destruct IHreduced_reachable_bis as (set & H21 & H22).
      econstructor; eassumption.
      inversion H13; [|congruence].
      subst.
      exploit precompute_r_t_up_correct_5. rewrite Hprecompute_r_t_up.
      intros (_ & _ & _ & _ & _ & BACK_IN_GRAPH).
      apply BACK_IN_GRAPH in H12. destruct H12 as (succs' & H12 & _). congruence.
      intros contra. apply H11. apply reduced_reachable_equiv. econstructor.
      eassumption. eassumption. eassumption. apply reduced_reachable_equiv. assumption.
      assumption.
      eapply BACK_UPWARDS; try eassumption.
      exploit precompute_r_t_up_correct_1. rewrite Hprecompute_r_t_up. intros (_ & H4 & _). eauto.
      intros contra. apply H11.
      destruct contra as (set' & w & H41 & H42 & H43).
      eapply reduced_reachable_trans.
      exploit precompute_r_t_up_correct_3. rewrite Hprecompute_r_t_up.
      intros H4. eapply H4; eassumption.
      exploit precompute_r_t_up_correct_2. rewrite Hprecompute_r_t_up.
      intros H6. destruct H43 as (n1 & n2 & m1 & m2 & H431 & H432 & H433).
      eapply H6; eassumption.
  - destruct H as (set & HH1 & HH2). apply and_assoc. split.
    + exploit precompute_r_t_up_correct_5. rewrite Hprecompute_r_t_up.
      intros (H1 & _ & _ & _ & H2 & _). apply H1. apply H2. congruence.
    + split; [|eauto]. intros contra.
      eapply BACK_TARGETS; try eassumption.
      exploit precompute_r_t_up_correct_4. rewrite Hprecompute_r_t_up. intros H.
      apply H; try assumption.
      exploit precompute_r_t_up_correct_5. rewrite Hprecompute_r_t_up.
      intros (H1 & _ & _ & _ & H2 & _). apply H1. apply H2. congruence.
      exploit precompute_r_t_up_correct_5. rewrite Hprecompute_r_t_up.
      intros (H1 & _ & H2 & _ & H3 & _).
      inversion contra. subst v. apply H1, H3. congruence.
      intros contra2. contradiction H6. eapply H2; try eassumption.
      exploit precompute_r_t_up_correct_1. rewrite Hprecompute_r_t_up. intros (_ & H8 & _).
      apply H8. apply reachable_trans with (u2:=u).
      apply H1, H3. congruence.
      eapply reduced_reachable_is_reachable. eassumption. congruence.
Qed.

Lemma precompute_r_t_up_correct_7 : forall g root dom_pre,
  let '(r, c, t, back) := precompute_r_t_up g root dom_pre in
  forall u v,
    In (u, v) back -> g ! v <> None ->
    directly_included r v u.
Proof.
  intros.
  assert (postcondition7 (precompute_r_t_up g root dom_pre)).
    unfold precompute_r_t_up.
    apply WfIter.iterate_prop
      with (P := fun s => invariant g root s /\ invariant2 g s /\ invariant3 g s
                       /\ invariant4 g s /\ invariant5 g root s
                       /\ invariant7 s).
    intros. destruct H as (H & H2 & H3 & H4 & H5 & H7).
    pose proof (transition_spec _ _ dom_pre _ H).
    pose proof (transition_spec2 _ _ dom_pre _ H H4 H2).
    pose proof (transition_spec3 _ _ dom_pre _ H H2 H4 H3).
    pose proof (transition_spec4 _ _ dom_pre _ H H4).
    pose proof (transition_spec5 _ _ dom_pre _ H).
    pose proof (transition_spec7 _ _ dom_pre _ H H2 H3 H4 H5 H7).
    destruct (transition dom_pre a); auto 7.
    split. apply initial_state_spec.
    split. apply initial_state_spec2.
    split. apply initial_state_spec3.
    split. apply initial_state_spec4.
    split. apply initial_state_spec5.
    apply initial_state_spec7.
  destruct (precompute_r_t_up g root) as (((r, c), t), back) eqn:Hprecompute_r_t_up.
  inv H.
  intros.
  exploit precompute_r_t_up_correct_1. rewrite Hprecompute_r_t_up. intros (_ & H2 & _).
  destruct (r ! u) as [(n1, n2)|] eqn:Hru.
  - destruct (r ! v) as [(m1, m2)|] eqn:Hrv.
    + exists m1, m2, n1, n2. eauto.
    + contradiction (H2 v).
      exploit precompute_r_t_up_correct_5. rewrite Hprecompute_r_t_up.
      intros (_ & _ & _ & _ & _ & H3).
      destruct (H3 _ _ H) as (succs & H31 & H32).
      eapply reachable_succ; try eassumption.
      exploit precompute_r_t_up_correct_5. rewrite Hprecompute_r_t_up. intros (H4 & _).
      apply H4; congruence.
  - exploit precompute_r_t_up_correct_5. rewrite Hprecompute_r_t_up. intros (_ & _ & _ & H3 & _ & _).
    apply H3 in H. contradiction.
Qed.

Lemma precompute_r_t_up_correct_8 : forall g root dom_pre,
  let '(r, c, t, back) := precompute_r_t_up g root dom_pre in
  forall u set v, c ! u = Some set -> set ! v = Some tt ->
  v <> u ->
  exists n1 n2 m1 m2,
    r ! u = Some (n1, n2) /\ r ! v = Some (m1, m2) /\ m2 < n1.
Proof.
  intros.
  assert (postcondition8 (precompute_r_t_up g root dom_pre)).
    unfold precompute_r_t_up.
    apply WfIter.iterate_prop
      with (P := fun s => invariant g root s /\ invariant2 g s /\ invariant4 g s /\ invariant5 g root s /\ invariant8 s).
    intros. destruct H as (H & H2 & H4 & H5 & H8).
    pose proof (transition_spec _ _ dom_pre _ H).
    pose proof (transition_spec2 _ _ dom_pre _ H H4 H2).
    pose proof (transition_spec4 _ _ dom_pre _ H H4).
    pose proof (transition_spec5 _ _ dom_pre _ H H5).
    pose proof (transition_spec8 _ _ dom_pre _ H H2 H4 H5 H8).
    destruct (transition dom_pre a); auto 6.
    split. apply initial_state_spec.
    split. apply initial_state_spec2.
    split. apply initial_state_spec4.
    split. apply initial_state_spec5.
    apply initial_state_spec8.
  destruct (precompute_r_t_up g root) as (((r, c), t), back) eqn:Hprecompute_r_t_up.
  inv H.
  assumption.
Qed.

Lemma precompute_r_t_up_correct_9 : forall g root dom_pre,
  let '(r, c, t, back) := precompute_r_t_up g root dom_pre in
  (forall u set, t ! u = Some set ->
    Forall (fun '(u, n) => n = dom_pre u) set)
  /\ (forall u set, t ! u = Some set ->
       Sorted (fun '(_, n1) '(_, n2) => n1 < n2)%Z set).
Proof.
  intros.
  assert (postcondition9 dom_pre (precompute_r_t_up g root dom_pre)).
    unfold precompute_r_t_up.
    apply WfIter.iterate_prop
      with (P := fun s => invariant9 dom_pre s).
    intros.
    pose proof (transition_spec9 _ _ H).
    destruct (transition dom_pre a); auto.
    apply initial_state_spec9.
  destruct (precompute_r_t_up g root dom_pre) as (((r, c), t), back).
  inv H.
  split; assumption.
Qed.

Lemma precompute_r_t_up_correct_10 : forall g root dom_pre,
  let '(r, c, t, back) := precompute_r_t_up g root dom_pre in
  forall u, r ! u <> None -> reduced_reachable g back root u.
Proof.
  intros.
  assert (postcondition10 g root (precompute_r_t_up g root dom_pre)).
    unfold precompute_r_t_up.
    apply WfIter.iterate_prop
      with (P := fun s => invariant g root s /\ invariant2 g s
                       /\ invariant4 g s /\ invariant10 g root s).
    intros. destruct H as (H & H2 & H4 & H10).
    pose proof (transition_spec _ _ dom_pre _ H).
    pose proof (transition_spec2 _ _ dom_pre _ H H4 H2).
    pose proof (transition_spec4 _ _ dom_pre _ H H4).
    pose proof (transition_spec10 _ _ dom_pre _ H2 H10).
    destruct (transition dom_pre a); auto.
    split. apply initial_state_spec.
    split. apply initial_state_spec2.
    split. apply initial_state_spec4.
    apply initial_state_spec10.
  destruct (precompute_r_t_up g root dom_pre) as (((r, c), t), back).
  inv H.
  assumption.
Qed.