+(* Copyright (C) 2000, HELM Team.
+ *
+ * This file is part of HELM, an Hypertextual, Electronic
+ * Library of Mathematics, developed at the Computer Science
+ * Department, University of Bologna, Italy.
+ *
+ * HELM is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * HELM 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HELM; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * For details, see the HELM World-Wide-Web page,
+ * http://cs.unibo.it/helm/.
+ *)
+
+(******************************************************************************)
+(* *)
+(* PROJECT HELM *)
+(* *)
+(* Enrico Tassi <tassi@cs.unibo.it> *)
+(* 23/04/2004 *)
+(* *)
+(* This module implements the aciclic graph of universes. *)
+(* *)
+(******************************************************************************)
+
+(*
+
+ todo:
+ - in add_eq there is probably no need of add_gt, simple @ the gt lists
+ - the problem of duplicates in the 1steg gt/ge list if two of them are
+ add_eq may be "fixed" in some ways:
+ - lazy, do nothing on add_eq and eventually update the ge list
+ on closure
+ - add a check_duplicates_after_eq function called by add_eq
+ - do something like rmap, add a list of canonical that point to us
+ so when we collapse we have the list of the canonical we may update
+ - don't use failure but another exception
+
+*)
+
+(* ************************************************************************** *)
+(* TYPES *)
+(* ************************************************************************** *)
+type universe = int
+
+type canonical_repr = {
+ mutable ge:universe list;
+ mutable gt:universe list;
+ (* since we collapse we may need the reverse map *)
+ mutable eq:universe list;
+ (* the canonical representer *)
+ u:universe
+}
+
+module UniverseType = struct
+ type t = universe
+ let compare = Pervasives.compare
+end
+
+module MapUC = Map.Make(UniverseType)
+
+(* ************************************************************************** *)
+(* Globals *)
+(* ************************************************************************** *)
+
+let map = ref MapUC.empty
+let used = ref (-1)
+
+(* ************************************************************************** *)
+(* Helpers *)
+(* ************************************************************************** *)
+
+(* create a canonical for [u] *)
+let mk_canonical u =
+ {u = u ; gt = [] ; ge = [] ; eq = [u] }
+
+(* give a new universe *)
+let fresh () =
+ used := !used + 1;
+ map := MapUC.add !used (mk_canonical !used) !map;
+ !used
+
+let reset () =
+ map := MapUC.empty;
+ used := -1
+
+(* ************************************************************************** *)
+(* Pretty printing *)
+(* ************************************************************************** *)
+(* pp *)
+let string_of_universe = string_of_int
+
+(* pp *)
+let canonical_to_string c =
+ let s_gt =
+ List.fold_left (fun s u -> s ^ (string_of_universe u) ^ " ") "" c.gt in
+ let s_ge =
+ List.fold_left (fun s u -> s ^ (string_of_universe u) ^ " ") "" c.ge in
+ let s_eq =
+ List.fold_left (fun s u -> s ^ (string_of_universe u) ^ " ") "" c.eq in
+ let s_u = (string_of_universe c.u) in
+ "{ u:" ^ s_u ^ " ; gt:" ^ s_gt ^ " ; ge:" ^ s_ge ^ " ; eq: " ^ s_eq ^ "}"
+
+(* print the content of map *)
+let print_map () =
+ MapUC.iter (fun u c ->
+ prerr_endline
+ (" " ^ (string_of_universe u) ^ " -> " ^ (canonical_to_string c)))
+ !map;
+ prerr_endline ""
+
+(* ************************************************************************** *)
+(* The way we fail *)
+(* ************************************************************************** *)
+(* we are doing bad *)
+let error s =
+ (*prerr_endline " ======= Universe Inconsistency =========";
+ print_map ();*)
+ prerr_endline (" " ^ s ^ "\n");
+ failwith s
+
+(* ************************************************************************** *)
+(* The real code *)
+(* ************************************************************************** *)
+(* <--------> *)
+
+(* the canonical representer of the [u] equaliti class *)
+let repr u =
+ try
+ MapUC.find u !map
+ with
+ Not_found -> error ("map inconsistency, unbound " ^ (string_of_universe u))
+
+(* all the nodes we can ifer in the ge list of u *)
+let close_ge u =
+ let repr_u = repr u in
+ let rec close_ge_aux tofollow bag =
+ match tofollow with
+ [] -> bag
+ | v::tl -> let repr_v = repr v in
+ if List.mem repr_v bag then (* avoid loops *)
+ (close_ge_aux tl bag )
+ else
+ (close_ge_aux (tl @ repr_v.ge) (repr_v::bag))
+ (* we assume that v==u -> v \notin (repr u).ge *)
+ in
+ close_ge_aux repr_u.ge []
+
+(* all the nodes we can ifer in the gt list of u,
+ we must follow bot gt and ge arcs, but we must put in bag only
+ the nodes that have a gt in theys path
+*)
+let close_gt u =
+ let repr_u = repr u in
+ let rec close_gt_aux bag todo inspected =
+ (*print_all bag todo;Unix.sleep 1;*)
+ match todo with
+ [],[] -> bag
+ | [],p::may -> let repr_p = repr p in
+ if List.mem repr_p.u inspected then (* avoid loops *)
+ close_gt_aux bag ([],may) inspected
+ else
+ close_gt_aux bag (repr_p.gt,repr_p.ge @ may)
+ (repr_p.u::inspected)
+ | s::secure,may -> let repr_s = repr s in
+ if List.mem repr_s.u inspected then (* avoid loops *)
+ if List.mem repr_s bag then
+ close_gt_aux bag (secure,may) inspected
+ else
+ (* even if we ave already inspected the node, now
+ it is in the secure list so we want it in the bag
+ *)
+ close_gt_aux (repr_s::bag) (secure,may) inspected
+ else
+ close_gt_aux ((repr_s)::bag)
+ (repr_s.gt @ repr_s.ge,may) (repr_s.u::inspected)
+ in
+ close_gt_aux [] (repr_u.gt,repr_u.ge) []
+
+(* when we add an eq we have to change the mapping of u to c*)
+let remap u c =
+ let repr_u = repr u in
+ List.iter (fun u' -> if u <> u' then map := MapUC.remove u' !map) repr_u.eq;
+ List.iter (fun u' -> map := MapUC.add u' c !map) repr_u.eq
+
+(* we suspect that u and v are connected by a == implyed by two >= *)
+let rec collapse u v =
+ let repr_u = repr u in
+ let repr_v = repr v in
+ let ge_v = close_ge v in
+ let ge_u = close_ge u in
+ if List.mem repr_u ge_v && List.mem repr_v ge_u then
+ add_eq u v
+
+(* we have to add u == v *)
+and add_eq u v =
+ let repr_u = repr u in
+ let repr_v = repr v in
+ (* if we already have u == v then do nothing *)
+ if repr_u = repr_v then
+ ()
+ else
+ (* if we already have v > u then fail *)
+ let gt_v = close_gt v in
+ if List.mem repr_u gt_v then
+ error ("Asking for " ^ (string_of_universe u) ^ " == " ^
+ (string_of_universe v) ^ " but " ^
+ (string_of_universe v) ^ " > " ^ (string_of_universe u))
+ else
+ (* if we already have u > v then fail *)
+ let gt_u = close_gt u in
+ if List.mem repr_v gt_u then
+ error ("Asking for " ^ (string_of_universe u) ^ " == " ^
+ (string_of_universe v) ^ " but " ^
+ (string_of_universe u) ^ " > " ^ (string_of_universe v))
+ else
+ (* add the inherited > constraints *)
+ List.iter (fun v -> add_gt u v ) (*gt_v*) repr_v.gt;
+ (* add the inherited >= constraints *)
+ (* close_ge assumes that v==u -> v \notin (repr u).ge *)
+ repr_u.ge <- List.filter (fun x -> x <> u && x <> v)
+ (repr_v.ge @ repr_u.ge);
+ (* mege the eq list, we assume they are disjuncted *)
+ repr_u.eq <- repr_u.eq @ repr_v.eq;
+ (* we have to remap all node represented by repr_v to repr_u *)
+ remap v repr_u;
+ (* FIXME: not sure this is what we have to do
+ think more to the list of suspected cicles
+ *)
+ List.iter (fun x -> collapse u x) repr_u.ge
+
+(* we have to add u >= v *)
+and add_ge u v =
+ let repr_u = repr u in
+ let repr_v = repr v in
+ (* if we already have u == v then do nothing *)
+ if repr_u = repr_v then
+ ()
+ else
+ (* if we already have v > u then fail *)
+ let gt = close_gt v in
+ if List.memq repr_u gt then
+ error ("Asking for " ^ (string_of_universe u) ^ " >= " ^
+ (string_of_universe v) ^ " but " ^
+ (string_of_universe v) ^ " > " ^ (string_of_universe u))
+ else
+ (* it is now safe to add u >= v *)
+ repr_u.ge <- v::repr_u.ge;
+ (* but we may have introduced a cicle *)
+ collapse u v (* FIXME: not sure it is from u to v, think more. *)
+
+(* we have to add u > v *)
+and add_gt u v =
+ let repr_u = repr u in
+ let repr_v = repr v in
+ (* if we already have u == v then fail *)
+ if repr_u = repr_v then
+ error ("Asking for " ^ (string_of_universe u) ^ " > " ^
+ (string_of_universe v) ^ " but " ^
+ (string_of_universe u) ^ " == " ^ (string_of_universe v))
+ else
+ (* if we already have u > v do nothing *)
+ let gt_u = close_gt u in
+ if List.memq repr_v gt_u then
+ ()
+ else
+ (* if we already have v > u then fail *)
+ let gt = close_gt v in
+ if List.memq repr_u gt then
+ error ("Asking for " ^ (string_of_universe u) ^ " > " ^
+ (string_of_universe v) ^ " but " ^
+ (string_of_universe v) ^ " > " ^ (string_of_universe u))
+ else
+ (* if we already have v >= u then fail *)
+ let ge = close_ge v in
+ if List.memq repr_u ge then
+ error ("Asking for " ^ (string_of_universe u) ^ " > " ^
+ (string_of_universe v) ^ " but " ^
+ (string_of_universe v) ^ " >= " ^ (string_of_universe u))
+ else
+ (* it is safe to add u > v *)
+ repr_u.gt <- v::repr_u.gt
+
+let add_gt u v =
+ try
+ add_gt u v; true
+ with
+ exn -> false
+
+let add_ge u v =
+ try
+ add_ge u v; true
+ with
+ exn -> false
+
+let add_eq u v =
+ try
+ add_eq u v; true
+ with
+ exn -> false
+
+(* <--------> *)
+
+(* ************************************************************************** *)
+(* To make tests *)
+(* ************************************************************************** *)
+
+(*
+let check_status_eq l =
+ let s = List.fold_left (fun s u -> s^(string_of_universe u) ^ ";") "" l in
+ let repr_u = repr (List.hd l) in
+ let rec check_status_eq_aux c =
+ match c with
+ [] -> print_endline (" Result check_status_eq["^s^"] = OK");true
+ | u::tl -> if repr u != repr_u then
+ (print_endline (" Result check_status_eq["^s^"] = FAILED");
+ print_endline ((string_of_universe u) ^ " != " ^
+ (string_of_universe repr_u.u));
+ print_map ();false)
+ else
+ check_status_eq_aux tl
+ in
+ check_status_eq_aux (List.tl l)
+*)
+
+(* ************************************************************************** *)
+(* Fake implementation *)
+(* ************************************************************************** *)
+
+(* <--------> *
+let add_ge u v = true
+let add_gt u v = true
+let add_eq u v = true
+* <--------> *)