Library MetaCoq.Checker.wGraph


Notation "p .1" := (fst p)
  (at level 2, left associativity, format "p .1") : pair_scope.
Notation "p .2" := (snd p)
  (at level 2, left associativity, format "p .2") : pair_scope.
Notation "p ..1" := (proj1 p)
  (at level 2, left associativity, format "p ..1") : pair_scope.
Notation "p ..2" := (proj2 p)
  (at level 2, left associativity, format "p ..2") : pair_scope.
Coercion is_left A B (u : {A} + {B}) := match u with left _true | _false end.

Definition fst_eq {A B} {x x' : A} {y y' : B}
  : (x, y) = (x', y') x = x'.

Definition snd_eq {A B} {x x' : A} {y y' : B}
  : (x, y) = (x', y') y = y'.

Lemma InAeq_In {A} (l : list A) x :
  InA eq x l In x l.

Lemma fold_max_In n m l (H : fold_left max l n = m)
  : n = m In m l.

Lemma fold_max_le n m l (H : n m Exists (Peano.le n) l)
  : n fold_left Nat.max l m.

Lemma fold_max_le' n m l (H : In n (m :: l))
  : n fold_left Nat.max l m.


Definition eq_max n m k : max n m = k n = k m = k.
Qed.

Module Nbar.
  Definition t := option nat.
  Definition max (n m : t) : t :=
    match n, m with
    | Some n, Some mSome (max n m)
    | Some n, NoneSome n
    | None, Some mSome m
    | _, _None
    end.
  Definition add (n m : t) : t :=
    match n, m with
    | Some n, Some mSome (n + m)
    | _, _None
    end.
  Definition le (n m : t) : Prop :=
    match n, m with
    | Some n, Some mn m
    | Some _, NoneFalse
    | None, _True
    end.


  Infix "+" := add : nbar_scope.
  Infix "≤" := le : nbar_scope.


  Instance le_refl : Reflexive le.

  Instance le_trans : Transitive le.




  Definition add_assoc n m p : n + (m + p) = n + m + p.

  Definition add_0_r n : (n + Some 0 = n)%nbar.

  Definition max_lub n m p : n p m p max n m p.

  Definition add_max_distr_r n m p : max (n + p) (m + p) = max n m + p.

  Definition max_le' n m p : p n p m p max n m.

  Definition plus_le_compat_l n m p : n m p + n p + m.

  Definition plus_le_compat n m p q : n m p q n + p m + q.

  Definition max_idempotent n : max n n = n.

  Lemma eq_max n m k (H : max n m = k) : n = k m = k.

  Lemma fold_max_In n m l (H : fold_left max l n = m)
    : n = m In m l.

  Lemma fold_max_le n m l (H : n m Exists (le n) l)
    : n fold_left max l m.

  Lemma fold_max_le' n m l (H : In n (m :: l))
    : n fold_left max l m.

  Lemma le_dec n m : {n m} + {¬ n m}.

  Lemma le_plus_r n m : m Some n + m.

End Nbar.


Module WeightedGraph (V : UsualOrderedType).
  Module VSet := MSetList.Make V.
  Module VSetFact := WFactsOn V VSet.
  Module VSetProp := WPropertiesOn V VSet.
  Module Edge.
    Definition t := (V.t × nat × V.t)%type.
    Definition eq : t t Prop := eq.
    Definition eq_equiv : RelationClasses.Equivalence eq := _.

    Definition lt : t t Prop :=
      fun '(x, n, y) '(x', n', y')(V.lt x x') (V.eq x x' n < n')
                                    (V.eq x x' n = n' V.lt y y').
    Definition lt_strorder : StrictOrder lt.
    Qed.
    Definition lt_compat : Proper (Logic.eq ==> Logic.eq ==> iff) lt.
    Qed.
    Definition compare : t t comparison
      := fun '(x, n, y) '(x', n', y')match V.compare x x' with
                                      | LtLt
                                      | GtGt
                                      | Eqmatch PeanoNat.Nat.compare n n' with
                                             | LtLt
                                             | GtGt
                                             | EqV.compare y y'
                                             end
                                      end.
    Definition compare_spec :
       x y : t, CompareSpec (x = y) (lt x y) (lt y x) (compare x y).
    Defined.

    Definition eq_dec : x y : t, {x = y} + {x y}.
    Defined.
    Definition eqb : t t bool := fun x ymatch compare x y with
                                          | Eqtrue
                                          | _false
                                          end.
  End Edge.
  Module EdgeSet:= MSets.MSetList.Make Edge.
  Module EdgeSetFact := WFactsOn Edge EdgeSet.
  Module EdgeSetProp := WPropertiesOn Edge EdgeSet.

  Definition t := (VSet.t × EdgeSet.t × V.t)%type.


  Definition e_source : Edge.t V.t := fst fst.
  Definition e_target : Edge.t V.t := snd.
  Definition e_weight : Edge.t nat := snd fst.
  Notation "x ..s" := (e_source x) (at level 3, format "x '..s'").
  Notation "x ..t" := (e_target x) (at level 3, format "x '..t'").
  Notation "x ..w" := (e_weight x) (at level 3, format "x '..w'").

  Definition labelling := V.t nat.

  Section graph.
    Context (G : t).

    Definition add_node x : t :=
      (VSet.add x (V G), (E G), (s G)).

    Definition add_edge e : t :=
      (VSet.add e..s (VSet.add e..t (V G)), EdgeSet.add e (E G), (s G)).

    Definition Edges x y := n, EdgeSet.In (x, n, y) (E G).

    Inductive Paths : V.t V.t Type :=
    | paths_refl x : Paths x x
    | paths_step x y z : Edges x y Paths y z Paths x z.


    Fixpoint weight {x y} (p : Paths x y) :=
      match p with
      | paths_refl x ⇒ 0
      | paths_step x y z e pe.π1 + weight p
      end.

    Fixpoint nodes {x y} (p : Paths x y) : VSet.t :=
      match p with
      | paths_refl xVSet.empty
      | paths_step x y z e pVSet.add x (nodes p)
      end.

    Fixpoint concat {x y z} (p : Paths x y) : Paths y z Paths x z :=
      match p with
      | paths_refl _fun qq
      | paths_step _ _ _ e pfun qpaths_step e (concat p q)
      end.

    Fixpoint length {x y} (p : Paths x y) :=
      match p with
      | paths_refl x ⇒ 0
      | paths_step x y z e pS (length p)
      end.


    Class invariants := mk_invariant :
      
      ( e, EdgeSet.In e (E G) VSet.In e..s (V G) VSet.In e..t (V G))
      
       VSet.In (s G) (V G)
      
       ( x, VSet.In x (V G) Paths (s G) x ).

    Context {HI : invariants}.

    Definition PosPaths x y := p : Paths x y, weight p > 0.

    Definition acyclic_no_loop := x (p : Paths x x), weight p = 0.

    Definition acyclic_no_loop' := x, ¬ (PosPaths x x).

    Fact acyclic_no_loop_loop' : acyclic_no_loop acyclic_no_loop'.

    Definition correct_labelling (l : labelling) :=
      l (s G) = 0
       e, EdgeSet.In e (E G) l e..s + e..w l e..t.

    Definition leq_vertices n x y := l, correct_labelling l l x + n l y.

    Definition DisjointAdd x s s' := VSetProp.Add x s s' ¬ VSet.In x s.

    Inductive SimplePaths : VSet.t V.t V.t Type :=
    | spaths_refl s x : SimplePaths s x x
    | spaths_step s s' x y z : DisjointAdd x s s' Edges x y
                                SimplePaths s y z SimplePaths s' x z.



    Fixpoint to_paths {s x y} (p : SimplePaths s x y) : Paths x y :=
      match p with
      | spaths_refl _ xpaths_refl x
      | spaths_step _ _ x y z _ e ppaths_step e (to_paths p)
      end.

    Fixpoint sweight {s x y} (p : SimplePaths s x y) :=
      match p with
      | spaths_refl _ _ ⇒ 0
      | spaths_step _ _ x y z _ e pe.π1 + sweight p
      end.

    Lemma sweight_weight {s x y} (p : SimplePaths s x y)
      : sweight p = weight (to_paths p).

    Fixpoint is_simple {x y} (p : Paths x y) :=
      match p with
      | paths_refl xtrue
      | paths_step x y z e pnegb (VSet.mem x (nodes p)) && is_simple p
      end.

    Program Fixpoint to_simple {x y} (p : Paths x y) (Hp : is_simple p = true)
            {struct p} : SimplePaths (nodes p) x y :=
      match p with
      | paths_refl xspaths_refl _ _
      | paths_step x y z e pspaths_step _ e (to_simple p _)
      end.

    Lemma weight_concat {x y z} (p : Paths x y) (q : Paths y z)
      : weight (concat p q) = weight p + weight q.


    Lemma DisjointAdd_add1 {s0 s1 s2 x y}
          (H1 : DisjointAdd x s0 s1) (H2 : DisjointAdd y s1 s2)
      : DisjointAdd x (VSet.add y s0) s2.

    Lemma DisjointAdd_add2 {s x} (H : ¬ VSet.In x s)
      : DisjointAdd x s (VSet.add x s).

    Lemma DisjointAdd_add3 {s0 s1 s2 x y}
          (H1 : DisjointAdd x s0 s1) (H2 : DisjointAdd y s1 s2)
      : DisjointAdd y s0 (VSet.add y s0).

    Fixpoint add_end {s x y} (p : SimplePaths s x y)
      : {z} (e : Edges y z) {s'} (Hs : DisjointAdd y s s'), SimplePaths s' x z
      := match p with
         | spaths_refl s xfun z e s' Hsspaths_step Hs e (spaths_refl _ _)
         | spaths_step s0 s x x0 y H e p
           ⇒ fun z e' s' Hsspaths_step (DisjointAdd_add1 H Hs) e
                                        (add_end p e' (DisjointAdd_add3 H Hs))
         end.

    Lemma weight_add_end {s x y} (p : SimplePaths s x y) {z e s'} Hs
      : sweight (@add_end s x y p z e s' Hs) = sweight p + e.π1.

    Lemma DisjointAdd_remove {s s' x y} (H : DisjointAdd x s s') (H' : x y)
      : DisjointAdd x (VSet.remove y s) (VSet.remove y s').


    Definition SimplePaths_sub {s s' x y}
      : VSet.Subset s s' SimplePaths s x y SimplePaths s' x y.

    Definition weight_SimplePaths_sub {s s' x y} Hs p
      : sweight (@SimplePaths_sub s s' x y Hs p) = sweight p.

    Lemma DisjointAdd_Subset {x s s'}
      : DisjointAdd x s s' VSet.Subset s s'.

    Fixpoint snodes {s x y} (p : SimplePaths s x y) : VSet.t :=
      match p with
      | spaths_refl s xVSet.empty
      | spaths_step s s' x y z H e pVSet.add x (snodes p)
      end.

    Definition split {s x y} (p : SimplePaths s x y)
      : u, {VSet.In u (snodes p)} + {u = y}
              SimplePaths (VSet.remove u s) x u × SimplePaths s u y.

    Lemma weight_split {s x y} (p : SimplePaths s x y) {u Hu}
      : sweight (split p u Hu).1 + sweight (split p u Hu).2 = sweight p.



    Definition split' {s x y} (p : SimplePaths s x y)
      : SimplePaths (VSet.remove y s) x y × SimplePaths s y y
      := split p y (right eq_refl).

    Lemma weight_split' {s x y} (p : SimplePaths s x y)
      : sweight (split' p).1 + sweight (split' p).2 = sweight p.

    Lemma DisjointAdd_remove1 {s x} (H : VSet.In x s)
      : DisjointAdd x (VSet.remove x s) s.

    Lemma Add_In {s x} (H : VSet.In x s)
      : VSetProp.Add x s s.

    Lemma Add_Add {s s' x} (H : VSetProp.Add x s s')
      : VSetProp.Add x s' s'.

    Lemma simplify_aux1 {s0 s1 s2} (H : VSet.Equal (VSet.union s0 s1) s2)
      : VSet.Subset s0 s2.

    Lemma simplify_aux2 {s0 x} (Hx : VSet.mem x s0 = true)
          {s1 s2}
          (Hs : VSet.Equal (VSet.union s0 (VSet.add x s1)) s2)
      : VSet.Equal (VSet.union s0 s1) s2.

    Lemma simplify_aux3 {s0 s1 s2 x}
          (Hs : VSet.Equal (VSet.union s0 (VSet.add x s1)) s2)
      : VSet.Equal (VSet.union (VSet.add x s0) s1) s2.

    Fixpoint simplify {s x y} (q : Paths y x)
      : (p : SimplePaths s x y) {s'},
        VSet.Equal (VSet.union s (nodes q)) s' x', SimplePaths s' x' x' :=
      match q with
      | paths_refl xfun p s' Hs(x; SimplePaths_sub (simplify_aux1 Hs) p)
      | paths_step y y' _ e q
        fun p s' Hsmatch VSet.mem y s as X return VSet.mem y s = X _ with
              | truefun XXlet '(p1, p2) := split' p in
                       if 0 <? sweight p2
                       then (y; SimplePaths_sub (simplify_aux1 Hs) p2)
                       else (simplify q (add_end p1 e
                                          (DisjointAdd_remove1 (VSetFact.mem_2 XX)))
                                      (simplify_aux2 XX Hs))
              | falsefun XX ⇒ (simplify q (add_end p e
                            (DisjointAdd_add2 ((VSetFact.not_mem_iff _ _)..2 XX)))
                                         (simplify_aux3 Hs))
              end eq_refl
      end.



    Lemma weight_simplify {s x y} q (p : SimplePaths s x y)
      : {s'} Hs, 0 < weight q 0 < sweight p
         0 < sweight (simplify q p (s':=s') Hs).π2.

    Definition succs (x : V.t) : list (nat × V.t)
      := let l := List.filter (fun eV.eq_dec e..s x) (EdgeSet.elements (E G)) in
         List.map (fun e(e..w, e..t)) l.


    Fixpoint lsp00 fuel (s : VSet.t) (x z : V.t) : Nbar.t :=
      let base := if V.eq_dec x z then Some 0 else None in
      match fuel with
      | 0 ⇒ base
      | S fuel
        match VSet.mem x s with
        | true
          let ds := List.map
                      (fun '(n, y)Some n + lsp00 fuel (VSet.remove x s) y z)%nbar
                      (succs x) in
          List.fold_left Nbar.max ds base
        | falsebase end
      end.

    Definition lsp0 s := lsp00 (VSet.cardinal s) s.

    Lemma lsp0_eq s x z : lsp0 s x z =
      let base := if V.eq_dec x z then Some 0 else None in
      match VSet.mem x s with
      | true
        let ds := List.map (fun '(n, y)Some n + lsp0 (VSet.remove x s) y z)%nbar
                           (succs x) in
        List.fold_left Nbar.max ds base
      | falsebase end.



    Definition lsp := lsp0 (V G).

    Lemma lsp0_VSet_Equal {s s' x y} :
      VSet.Equal s s' lsp0 s x y = lsp0 s' x y.

    Lemma lsp0_spec_le {s x y} (p : SimplePaths s x y)
      : (Some (sweight p) lsp0 s x y)%nbar.

    Lemma lsp0_spec_eq {s x y} n
      : lsp0 s x y = Some n p : SimplePaths s x y, sweight p = n.

    Lemma correct_labelling_Paths l (Hl : correct_labelling l)
      : x y (p : Paths x y), l x + weight p l y.

    Lemma acyclic_labelling l : correct_labelling l acyclic_no_loop.

    Lemma lsp0_triangle_inequality {HG : acyclic_no_loop} s x y1 y2 n
          (He : EdgeSet.In (y1, n, y2) (E G))
          (Hy : VSet.In y1 s)
      : (lsp0 s x y1 + Some n lsp0 s x y2)%nbar.

    Lemma acyclic_lsp0_xx {HG : acyclic_no_loop} s x
      : lsp0 s x x = Some 0.

    Definition lsp0_sub {s s' x y}
      : VSet.Subset s s' (lsp0 s x y lsp0 s' x y)%nbar.

    Definition snodes_Subset {s x y} (p : SimplePaths s x y)
      : VSet.Subset (snodes p) s.

    Definition reduce {s x y} (p : SimplePaths s x y)
      : SimplePaths (snodes p) x y.

    Definition simplify2 {x z} (p : Paths x z) : SimplePaths (nodes p) x z.

    Lemma weight_reduce {s x y} (p : SimplePaths s x y)
      : sweight (reduce p) = sweight p.

    Lemma weight_simplify2 {HG : acyclic_no_loop} {x z} (p : Paths x z)
      : sweight (simplify2 p) = weight p.

    Lemma nodes_subset {x y} (p : Paths x y)
      : VSet.Subset (nodes p) (V G).

    Definition simplify2' {x z} (p : Paths x z) : SimplePaths (V G) x z.

    Lemma weight_simplify2' {HG : acyclic_no_loop} {x z} (p : Paths x z)
      : sweight (simplify2' p) = weight p.

    Lemma lsp_s x (Hx : VSet.In x (V G))
      : n, lsp (s G) x = Some n.

    Lemma lsp_correctness {HG : acyclic_no_loop} :
        correct_labelling (fun xoption_get 0 (lsp (s G) x)).

    Lemma SimplePaths_In {s x y} (p : SimplePaths s x y)
      : sweight p > 0 VSet.In x s.

    Lemma lsp_codistance {HG : acyclic_no_loop} x y z
      : (lsp x y + lsp y z lsp x z)%nbar.

    Lemma lsp_xx_acyclic
      : VSet.For_all (fun xlsp x x = Some 0) (V G) acyclic_no_loop'.

    Lemma VSet_Forall_reflect P f (Hf : x, reflect (P x) (f x)) s
      : reflect (VSet.For_all P s) (VSet.for_all f s).

    Lemma reflect_logically_equiv {A B} (H : A B) f
      : reflect B f reflect A f.

    Definition is_acyclic := VSet.for_all (fun xmatch lsp x x with
                                                 | Some 0 ⇒ true
                                                 | _false
                                                 end) (V G).

Main results about acyclicity


    Lemma acyclic_caract1
      : acyclic_no_loop l, correct_labelling l.

    Lemma acyclic_caract2 :
      acyclic_no_loop (VSet.For_all (fun xlsp x x = Some 0) (V G)).


    Lemma is_acyclic_spec : is_acyclic acyclic_no_loop.

  End graph.

  Section graph2.
    Context (G : t) {HI : invariants G} {HG : acyclic_no_loop G}.

    Section subgraph.
      Context (n : nat) {x_0 y_0 : V.t} (Vx : VSet.In x_0 (V G))
              (Vy : VSet.In y_0 (V G)) (kx ky : nat)
              (Hkx : lsp G (s G) x_0 = Some kx)
              (Hky : lsp G (s G) y_0 = Some ky)
              (Hle : leq_vertices G n x_0 y_0)
              (p_0 : SimplePaths G (V G) (s G) y_0)
              (Hp_0 : lsp G (s G) y_0 = Some (sweight G p_0))
              (Hxs : lsp G x_0 (s G) = None)
              (Hx_0_0 : VSet.mem x_0 (snodes G p_0) = false).

      Definition K := Peano.max kx (S ky).

      Let G' : t
        := (V G, EdgeSet.add (s G, K, x_0) (E G), s G).

      Definition to_G' {u v} (q : Paths G u v) : Paths G' u v.


      Definition from_G' {S u v} (q : SimplePaths G' S u v)
        : SimplePaths G S u v + (SimplePaths G S u (s G) × SimplePaths G S x_0 v).


      Lemma from_G'_weight {S u v} (q : SimplePaths G' S u v)
        : sweight q = match from_G' q with
                      | inl q'sweight q'
                      | inr (q1, q2)sweight q1 + K + sweight q2
                      end.


      Lemma lsp_xy_le_n : (Some n lsp G x_0 y_0)%nbar.

    End subgraph.

    Lemma leq_vertices_caract0 {n x y} (Vy : VSet.In y (V G)) :
      leq_vertices G n x y (Some n lsp G x y)%nbar.

    Lemma leq_vertices_caract {n x y} :
      leq_vertices G n x y (if VSet.mem y (V G) then Some n lsp G x y
                             else n = 0 (x = y Some 0 lsp G x (s G)))%nbar.

    Definition leqb_vertices n x y : bool :=
      if VSet.mem y (V G) then le_dec (Some n) (lsp G x y)
      else Nat.eqb n 0 && (V.eq_dec x y || le_dec (Some 0) (lsp G x (s G))).

    Lemma leqb_vertices_correct n x y
      : leq_vertices G n x y leqb_vertices n x y.

  End graph2.
End WeightedGraph.