Library Kosaraju (source code Kosaraju.v, a note Kosaraju.pdf)

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

Set Implicit Arguments.

Section Kosaraju.

Variable T : finType.
Implicit Types s : {set T}.
Implicit Types l : seq T.
Implicit Types A B C : pred T.
Implicit Types x y z : T.

(* Some missing theorems of the library *)
Section Lib.

Lemma disjoint_setU s1 s2 A :
   [disjoint s1 :|: s2 & A] = [disjoint s1 & A] && [disjoint s2 & A].

Lemma disjoint_setUr s1 s2 A :
   [disjoint A & s1 :|: s2] = [disjoint A & s1] && [disjoint A & s2].

Lemma disjoint_catr l1 l2 A :
       [disjoint A & l1 ++ l2 ] = [disjoint A & l1] && [disjoint A & l2].

Lemma disjoint_set1 x A : [disjoint [set x] & A] = (x \notin A).

Lemma disjoint_setU1 x s A :
   [disjoint x |: s & A] = (x \notin A) && [disjoint s & A].

Lemma disjoint_setU1r x s A :
   [disjoint A & x |: s] = (x \notin A) && [disjoint A & s].

Lemma disjoint_consr x l A :
   [disjoint A & x :: l] = (x \notin A) && [disjoint A & l].

Lemma disjoint_transr A B C :
   B \subset C -> [disjoint A & C] -> [disjoint A & B].

End Lib.

Section Stack.

Section Rconnect.

Variable r : rel T.

(* x is connected to y avoiding elements of l *)
Definition rconnect s :=
   connect [rel x y | (r x y) && (y \notin s)].

Local Notation "x -[ s ]-> y" := (rconnect s x y)
  (at level 10, format "x -[ s ]-> y").

Local Notation "x -[]-> y" := (rconnect set0 x y)
  (at level 10, format "x -[]-> y").

Lemma path_relE s x p :
  path [rel x y | (r x y) && (y \notin s)] x p = path r x p && [disjoint p & s].

Lemma rconnectP s x y :
   reflect
    (exists2 p, path r x p & ((y = last x p) /\ [disjoint p & s]))
    (x -[s]-> y).

Lemma rconnect_set0 : rconnect set0 =2 connect r.

Lemma rconnect_ref s : reflexive (rconnect s).

Lemma rconnect1 s x y : y \notin s -> r x y -> x -[s]-> y.

Lemma rconnect_trans s : transitive (rconnect s).

Lemma rconnect_subset (s1 s2 : {set T}) x y :
  {subset s1 <= s2} -> x -[s2]-> y -> x -[s1]-> y.

Lemma rconnect_setU s1 s2 x y :
  [disjoint [set z | x -[s1]-> z && z -[s1]-> y] & s2] ->
  x -[s1]-> y -> x -[s2 :|: s1]-> y.

Lemma rconnect_setU1r s x y z :
  ~~ z -[s]-> y -> x -[s]-> y -> x -[z |: s]-> y.

Lemma rconnect_setU1l s x y z :
  ~~ (x -[s]-> z) -> x -[s]-> y -> x -[z |: s]-> y.

Lemma rconnect_id_setU1 s x y : x -[x |: s]-> y = x -[s]-> y.

(* x is biconnected to y avoiding the elements of s *)
Definition requiv s x y := x -[s]-> y && y -[s]-> x.

Local Notation "x =[ s ]= y" := (requiv s x y)
  (at level 10, format "x =[ s ]= y").

Local Notation "x =[]= y" := (requiv set0 x y)
  (at level 10, format "x =[]= y").

Lemma requiv_set0 : requiv set0 =2 (fun x y => connect r x y && connect r y x).

Lemma requiv_ref s : reflexive (requiv s).

Lemma requiv_sym s : symmetric (requiv s).

Lemma requiv_trans s : transitive (requiv s).

Lemma lrequiv_subset s1 s2 x y : {subset s1 <= s2} -> x =[s2]= y -> x =[s1]= y.

(* Canonical element in a list : find the first element of l1
   that is equivalent to x avoiding l2 *)

Definition rcan x p := nth x p.2 (find (requiv p.1 x) p.2).

Notation "C[ x ]_ p" := (rcan x p)
  (at level 9, format "C[ x ]_ p").

Lemma mem_rcan x p : x \in p.2 -> C[x]_p \in p.2.

Lemma rcan_cons x y s l :
  C[x]_(s, y :: l) = if x =[s]= y then y else C[x]_(s,l).

Lemma rcan_cat x s l1 l2 : x \in l1 -> C[x]_(s, l1 ++ l2) = C[x]_(s, l1).

Lemma requiv_can x s l : x \in l -> C[x]_(s, l) =[s]= x.

(* x occurs before y in l *)
Definition before l x y := index x l <= index y l.

Lemma before_filter_inv a x y l (l1 := [seq i <- l | a i]) :
  x \in l1 -> y \in l1 -> before l1 x y -> before l x y.

Lemma before_filter x y l a (l1 := [seq i <- l | a i]) :
  x \in l1 -> before l x y -> before l1 x y.

Lemma leq_index_nth x l i : index (nth x l i) l <= i.

Lemma index_find x l a : has a l -> index (nth x l (find a l)) l = find a l.

Lemma before_can x y s l :
  x \in l -> y \in l -> x =[s]= y -> before l C[x]_(s, l) y.

Lemma before_canW x s1 s2 l :
 x \in l -> {subset s1 <= s2} -> before l C[x]_(s1, l) C[x]_(s2, l).

(* well formed list : rconnected elements are inside and
                      canonical elements are on top *)

Definition rwf (p : {set T} * seq T) :=
  [disjoint p.1 & p.2] /\
 forall x y ,
   x \in p.2 -> x -[p.1]-> y -> y \in p.2 /\ before p.2 C[x]_p y.

Local Notation "W_[ s ] l" := (rwf (s, l)) (at level 10).
Local Notation "W_[] l " := (rwf (set0,l)) (at level 10).

Lemma rwf_nil s : W_[s] [::].

(* Removing the equivalent elements of the top preserve well-formedness *)
Lemma rwf_inv x s l :
  W_[s] (x :: l) -> W_[s] [seq y <- x :: l | ~~ x =[s]= y].

(* Computing the connected elements for the reversed graph gives
   the equivalent class of the top element of an well_formed list *)

Lemma rwf_equiv x y s l :
  W_[s] (x :: l) -> x =[s]= y = (y \in (x :: l)) && y -[s]-> x.

(* Computing well_formed list by accumulation *)
Lemma rwf_cat s1 l1 l2 : W_[s1] l1 -> W_[s1 :|: [set x in l1]] l2 -> W_[s1] (l2 ++ l1).

Lemma rwf_setU1_l x s l : x \notin l -> W_[s] l -> W_[x |: s] l.

(* Computing well_formed list by accumulation *)
Lemma rwf_cons_r x s l :
 (forall y, y \in l -> x -[s]-> y) ->
 (forall y, r x y -> y \notin x |: s -> y \in l) ->
  x \notin s -> W_[x |: s] l -> W_[s] (x :: l).

End Rconnect.

Variable r : rel T.

Local Notation "x -[ l ]-> y" := (rconnect r l x y)
  (at level 10, format "x -[ l ]-> y").
Local Notation "x -[]-> y" := (rconnect r [::] x y)
  (at level 10, format "x -[]-> y").
Local Notation "x =[ l ]= y" := (requiv r l x y)
  (at level 10, format "x =[ l ]= y").
Local Notation "x =[]= y" := (requiv r [::] x y)
  (at level 10, format "x =[]= y").
Local Notation "W_[ l1 ] l2 " := (rwf r (l1, l2)) (at level 10).
Local Notation "W_[] l" := (rwf r (set0, l)) (at level 10).

Section Pdfs.

Variable g : T -> seq T.

Fixpoint rpdfs m (p : {set T} * seq T) x :=
  if x \in p.1 then p else
  if m is m1.+1 then
     let p1 := foldl (rpdfs m1) (x |: p.1, p.2) (g x) in (p1.1, x :: p1.2)
  else p.

Definition pdfs := rpdfs #|T|.

End Pdfs.

Lemma pdfs_correct (p : {set T} * seq T) x :
  let (s, l) := p in
  uniq l /\ {subset l <= s} ->
  let p1 := pdfs (rgraph r) p x in
  let (s1, l1) := p1 in
  if x \in s then p1 = p else
       [/\ #|s| <= #|s1| & uniq l1]
    /\
       exists l2 : seq T,
       [/\ x \in l2, s1 = s :|: [set y in l2], l1 = l2 ++ l, W_[s] l2 &
           forall y, y \in l2 -> x -[s]-> y].

Lemma pdfs_connect s x :
  x \notin s ->
  let (s1, l1) := pdfs (rgraph r) (s, [::]) x in
  [/\ uniq l1, s1 = s :|: [set x in l1], [disjoint s & l1] &
      forall y, y \in l1 = x -[s]-> y].

(* Building the stack of all nodes *)
Definition stack :=
  (foldl (pdfs (rgraph r)) (set0, [::]) (enum T)).2.

(* The stack is well-formed and contains all the nodes *)
Lemma stack_correct : W_[] stack /\ forall x, x \in stack.

Lemma rconnect_rev l s1 s2 x y :
     {subset s1 <= s2} -> [disjoint s2 & x :: l] ->
     (forall z, z \in s2 :|: [set t in x :: l]) ->
     W_[s1] (x :: l) ->
     ((y \in x :: l) && y -[s1]-> x) = (rconnect [rel x y | r y x] s2 x y).

End Stack.

Variable r : rel T.

Definition kosaraju :=
  let f := pdfs (rgraph [rel x y | r y x]) in
  (foldl (fun (p : {set T} * seq (seq T)) x => if x \in p.1 then p else
                      let p1 := f (p.1, [::]) x in (p1.1, p1.2 :: p.2))
          (set0, [::]) (stack r)).2.

Lemma kosaraju_correct :
    let l := flatten kosaraju in
 [/\ uniq l, forall i, i \in l &
     forall c : seq T, c \in kosaraju ->
        exists x, forall y, (y \in c) = (connect r x y && connect r y x)].

End Kosaraju.

Print rpdfs.
Print pdfs.
Print stack.
Print kosaraju.
Print dfs.