(**************************************************************************)
(*                                                                        *)
(*  Ocamlgraph: a generic graph library for OCaml                         *)
(*  Copyright (C) 2004-2010                                               *)
(*  Sylvain Conchon, Jean-Christophe Filliatre and Julien Signoles        *)
(*                                                                        *)
(*  This software is free software; you can redistribute it and/or        *)
(*  modify it under the terms of the GNU Library General Public           *)
(*  License version 2.1, with the special exception on linking            *)
(*  described in file LICENSE.                                            *)
(*                                                                        *)
(*  This software is distributed in the hope that it will be useful,      *)
(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                  *)
(*                                                                        *)
(**************************************************************************)

(* $Id: mcs_m.mli,v 1.2 2004-10-19 15:21:44 signoles Exp $ *)

module MaximalCardinalitySearch = struct

  module WeightedV(V : Sig.COMPARABLE) = struct
    include Util.DataV(struct type t = int end)(V)
    let weight = data
    let set_weight = set_data
  end

  module P(Gr : Sig.P) = struct
    type edgelist = (Gr.V.t * Gr.V.t) list

    module NewV = WeightedV(Gr.V)
    module G = Persistent.Graph.Concrete(NewV)
    module EdgeSet = Set.Make(G.E)
    module VerticesSet = Set.Make(NewV)
    module Choose = Oper.Choose(G)
    module H = Hashtbl.Make(NewV)

    let check_path g u v =
      let h = H.create 97 in
      let maxw = NewV.weight u in
      let rec aux x : bool =
        if H.mem h x then
          false
        else
        if G.V.equal x v then true
        else
        if NewV.weight x < maxw || G.V.equal x u then
          begin
            H.add h x ();
            G.fold_succ
              (fun x found ->
                 if not found then aux x
                 else found)
              g x false
          end
        else (H.add h x (); false)
      in aux u

    module Copy = Gmap.Vertex(Gr)(struct include G include Builder.P(G) end)

    let fold f d =
      let rec aux = function
          (true, a) -> aux (f a)
        | (false, a) -> a
      in aux d

    let mcsm g =
      let g' = Copy.map (NewV.create 0) g in
      let (_, _, ord, triang) =
        fold
          (fun ((i, g', a, f) as x)->
             if i = 0 then (false, x)
             else
               let v =
                 G.fold_vertex
                   (fun x max ->
                      if NewV.weight x > NewV.weight max then x else max)
                   g' (ref 0, snd (Choose.choose_vertex g'))
               in
               let s =
                 G.fold_vertex
                   (fun x s ->
                      if G.V.equal x v then s
                      else
                      if check_path g' x v then
                        VerticesSet.add x s
                      else s)
                   g' VerticesSet.empty
               in
               let f' =
                 VerticesSet.fold
                   (fun x f ->
                      NewV.set_weight x (succ (NewV.weight x));
                      if not (G.mem_edge g' x v) then
                        EdgeSet.add (x,v) f
                      else f)
                   s f
               in
               let g' = G.remove_vertex g' v in
               let a' = (i, NewV.label v) :: a in
               (true, (i - 1, g', a', f')))
          (true, (Gr.nb_vertex g, g', [], EdgeSet.empty))
      in
      (List.rev ord,
       EdgeSet.fold
         (fun (x, y) e -> (NewV.label x, NewV.label y) :: e)
         triang [])

    let triangulate g =
      let (_, triang) = mcsm g in
      List.fold_left (fun g (x, y) -> Gr.add_edge g x y) g triang
  end

  module I(Gr : Sig.I) = struct
    type edgelist = (Gr.V.t * Gr.V.t) list

    module NewV = WeightedV(Gr.V)
    module G = Imperative.Graph.Concrete(NewV)
    module EdgeSet = Set.Make(G.E)
    module VerticesSet = Set.Make(NewV)
    module Choose = Oper.Choose(G)
    module H = Hashtbl.Make(NewV)

    let check_path g u v =
      let h = H.create 97 in
      let maxw = NewV.weight u in
      let rec aux x : bool =
        if H.mem h x then
          false
        else
        if G.V.equal x v then true
        else
        if NewV.weight x < maxw || G.V.equal x u then begin
          H.add h x ();
          G.fold_succ
            (fun x found ->
               if not found then aux x
               else found)
            g x false
        end else (H.add h x (); false)
      in aux u

    module Copy = Gmap.Vertex(Gr)(struct include G include Builder.I(G) end)

    let mcsm g =
      let f = ref EdgeSet.empty
      and a = ref []
      and g' = Copy.map (NewV.create 0) g in
      for i = Gr.nb_vertex g downto 1 do
        let v =
          G.fold_vertex
            (fun x max ->
               if NewV.weight x > NewV.weight max then x else max)
            g' (ref 0, snd (Choose.choose_vertex g'))
        in
        let s =
          G.fold_vertex
            (fun x s ->
               if G.V.equal x v then s
               else
               if check_path g' x v then
                 VerticesSet.add x s
               else s)
            g' VerticesSet.empty
        in
        let f' =
          VerticesSet.fold
            (fun x f ->
               NewV.set_weight x (succ (NewV.weight x));
               if not (G.mem_edge g' x v) then
                 EdgeSet.add (x,v) f
               else f)
            s !f
        in
        f := f';
        G.remove_vertex g' v;
        a := (i, NewV.label v) :: !a;
      done;
      (List.rev !a,
       EdgeSet.fold
         (fun (x, y) e -> (NewV.label x, NewV.label y) :: e)
         !f [])

    let triangulate g =
      let (_, triang) = mcsm g in
      List.iter (fun (x, y) -> Gr.add_edge g x y) triang
  end
end
