Also print the number of states when printing automata.
[tatoo.git] / src / ata.ml
index 7310839..a4ea306 100644 (file)
@@ -116,13 +116,23 @@ struct
 
 end
 
-module Transition = Hcons.Make (struct
+module Transition =
+  struct
+    include Hcons.Make (struct
   type t = State.t * QNameSet.t * Formula.t
   let equal (a, b, c) (d, e, f) =
     a == d && b == e && c == f
   let hash (a, b, c) =
     HASHINT4 (PRIME1, a, ((QNameSet.uid b) :> int), ((Formula.uid c) :> int))
 end)
+    let print ppf t =
+      let q, l, f = t.node in
+      fprintf ppf "%a, %a %s %a"
+        State.print q
+        QNameSet.print l
+        Pretty.double_right_arrow
+        Formula.print f
+  end
 
 
 module TransList : sig
@@ -142,27 +152,17 @@ end =
 type t = {
   id : Uid.t;
   mutable states : StateSet.t;
+  mutable starting_states : StateSet.t;
   mutable selecting_states: StateSet.t;
   transitions: (State.t, (QNameSet.t*Formula.t) list) Hashtbl.t;
 }
 
-
+let uid t = t.id
 
 let get_states a = a.states
+let get_starting_states a = a.starting_states
 let get_selecting_states a = a.selecting_states
 
-let get_trans a tag states =
-  StateSet.fold (fun q acc0 ->
-    try
-      let trs = Hashtbl.find a.transitions q in
-      List.fold_left (fun acc1 (labs, phi) ->
-           if QNameSet.mem tag labs then
-             TransList.cons (Transition.make (q, labs, phi)) acc1
-           else acc1) acc0 trs
-    with Not_found -> acc0
-  ) states TransList.nil
-
-
 
 let _pr_buff = Buffer.create 50
 let _str_fmt = formatter_of_buffer _pr_buff
@@ -171,13 +171,18 @@ let _flush_str_fmt () = pp_print_flush _str_fmt ();
   Buffer.clear _pr_buff; s
 
 let print fmt a =
+  let _ = _flush_str_fmt() in
   fprintf fmt
     "Internal UID: %i@\n\
      States: %a@\n\
+     Number of states: %i@\n\
+     Starting states: %a@\n\
      Selection states: %a@\n\
      Alternating transitions:@\n"
     (a.id :> int)
     StateSet.print a.states
+    (StateSet.cardinal a.states)
+    StateSet.print a.starting_states
     StateSet.print a.selecting_states;
   let trs =
     Hashtbl.fold
@@ -211,6 +216,28 @@ let print fmt a =
   ) strs_strings;
   fprintf fmt "%s@\n" line
 
+
+let get_trans a tag states =
+  StateSet.fold (fun q acc0 ->
+    try
+      let trs = Hashtbl.find a.transitions q in
+      List.fold_left (fun acc1 (labs, phi) ->
+        if QNameSet.mem tag labs then
+          TransList.cons (Transition.make (q, labs, phi)) acc1
+        else acc1) acc0 trs
+    with Not_found -> acc0
+  ) states TransList.nil
+
+
+let get_form a tag q =
+  try
+    let trs = Hashtbl.find a.transitions q in
+    List.fold_left (fun aphi (labs, phi) ->
+      if QNameSet.mem tag labs then Formula.or_ aphi phi else aphi
+    ) Formula.false_ trs
+  with
+    Not_found -> Formula.false_
+
 (*
   [complete transitions a] ensures that for each state q
   and each symbols s in the alphabet, a transition q, s exists.
@@ -219,19 +246,24 @@ let print fmt a =
 
 let complete_transitions a =
   StateSet.iter (fun q ->
-    let qtrans = Hashtbl.find a.transitions q in
-    let rem =
-      List.fold_left (fun rem (labels, _) ->
-        QNameSet.diff rem labels) QNameSet.any qtrans
-    in
-    let nqtrans =
-      if QNameSet.is_empty rem then qtrans
-      else
-        (rem, Formula.false_) :: qtrans
-    in
-    Hashtbl.replace a.transitions q nqtrans
+    if StateSet.mem q a.starting_states then ()
+    else
+      let qtrans = Hashtbl.find a.transitions q in
+      let rem =
+        List.fold_left (fun rem (labels, _) ->
+          QNameSet.diff rem labels) QNameSet.any qtrans
+      in
+      let nqtrans =
+        if QNameSet.is_empty rem then qtrans
+        else
+          (rem, Formula.false_) :: qtrans
+      in
+      Hashtbl.replace a.transitions q nqtrans
   ) a.states
 
+(* [cleanup_states] remove states that do not lead to a
+   selecting states *)
+
 let cleanup_states a =
   let memo = ref StateSet.empty in
   let rec loop q =
@@ -308,13 +340,16 @@ let normalize_negations auto =
           in
           Hashtbl.add memo_state key nq; nq
     in
-    let trans = Hashtbl.find auto.transitions q in
+    let trans = try Hashtbl.find auto.transitions q with Not_found -> [] in
     let trans' = List.map (fun (lab, f) -> lab, flip b f) trans in
     Hashtbl.replace auto.transitions q' trans';
   done;
   cleanup_states auto
 
 
+
+
+
 module Builder =
   struct
     type auto = t
@@ -326,6 +361,7 @@ module Builder =
         {
           id = next ();
           states = StateSet.empty;
+          starting_states = StateSet.empty;
           selecting_states = StateSet.empty;
           transitions = Hashtbl.create MED_H_SIZE;
         }
@@ -350,8 +386,9 @@ module Builder =
       ); *)
       auto
 
-    let add_state a ?(selecting=false) q =
+    let add_state a ?(starting=false) ?(selecting=false) q =
       a.states <- StateSet.add q a.states;
+      if starting then a.starting_states <- StateSet.add q a.starting_states;
       if selecting then a.selecting_states <- StateSet.add q a.selecting_states
 
     let add_trans a q s f =
@@ -383,3 +420,135 @@ module Builder =
       normalize_negations a;
       a
   end
+
+
+let map_set f s =
+  StateSet.fold (fun q a -> StateSet.add (f q) a) s StateSet.empty
+
+let map_hash fk fv h =
+  let h' = Hashtbl.create (Hashtbl.length h) in
+  let () = Hashtbl.iter (fun k v -> Hashtbl.add h' (fk k) (fv v)) h in
+  h'
+
+let rec map_form f phi =
+  match Formula.expr phi with
+  | Boolean.Or(phi1, phi2) -> Formula.or_ (map_form f phi1) (map_form f phi2)
+  | Boolean.And(phi1, phi2) -> Formula.and_ (map_form f phi1) (map_form f phi2)
+  | Boolean.Atom({ Atom.node = Move(m,q); _}, b) ->
+      let a = Formula.mk_atom (Move (m,f q)) in
+      if b then a else Formula.not_ a
+  | _ -> phi
+
+let rename_states mapper a =
+  let rename q = try Hashtbl.find mapper q with Not_found -> q in
+  { Builder.make () with
+    states = map_set rename a.states;
+    starting_states = map_set rename a.starting_states;
+    selecting_states = map_set rename a.selecting_states;
+    transitions =
+      map_hash
+        rename
+        (fun l ->
+          (List.map (fun (labels, form) -> (labels, map_form rename form)) l))
+        a.transitions;
+  }
+
+let copy a =
+  let mapper = Hashtbl.create MED_H_SIZE in
+  let () =
+    StateSet.iter (fun q -> Hashtbl.add mapper q (State.make())) a.states
+  in
+  rename_states mapper a
+
+
+let concat a1 a2 =
+  let a1 = copy a1 in
+  let a2 = copy a2 in
+  let link_phi =
+    StateSet.fold
+      (fun q phi -> Formula.(or_ (stay q) phi))
+      a1.selecting_states Formula.false_
+  in
+  Hashtbl.iter (fun q trs -> Hashtbl.add a1.transitions q trs)
+    a2.transitions;
+  StateSet.iter
+    (fun q ->
+      Hashtbl.replace a1.transitions q [(QNameSet.any, link_phi)])
+    a2.starting_states;
+  { a1 with
+    states = StateSet.union a1.states a2.states;
+    selecting_states = a2.selecting_states;
+    transitions = a1.transitions;
+  }
+
+let merge a1 a2 =
+  let a1 = copy a1 in
+  let a2 = copy a2 in
+  { a1 with
+    states = StateSet.union a1.states a2.states;
+    selecting_states = StateSet.union a1.selecting_states a2.selecting_states;
+    starting_states = StateSet.union a1.starting_states a2.starting_states;
+    transitions =
+      let () =
+        Hashtbl.iter (fun k v -> Hashtbl.add a1.transitions k v) a2.transitions
+      in
+      a1.transitions
+  }
+
+
+let link a1 a2 q link_phi =
+  { a1 with
+    states = StateSet.union a1.states a2.states;
+    selecting_states = StateSet.singleton q;
+    starting_states = StateSet.union a1.starting_states a2.starting_states;
+    transitions =
+      let () =
+        Hashtbl.iter (fun k v -> Hashtbl.add a1.transitions k v) a2.transitions
+      in
+      Hashtbl.add a1.transitions q [(QNameSet.any, link_phi)];
+      a1.transitions
+  }
+
+let union a1 a2 =
+  let a1 = copy a1 in
+  let a2 = copy a2 in
+  let q = State.make () in
+  let link_phi =
+    StateSet.fold
+      (fun q phi -> Formula.(or_ (stay q) phi))
+      (StateSet.union a1.selecting_states a2.selecting_states)
+      Formula.false_
+  in
+  link a1 a2 q link_phi
+
+let inter a1 a2 =
+  let a1 = copy a1 in
+  let a2 = copy a2 in
+  let q = State.make () in
+  let link_phi =
+    StateSet.fold
+      (fun q phi -> Formula.(and_ (stay q) phi))
+      (StateSet.union a1.selecting_states a2.selecting_states)
+      Formula.true_
+  in
+  link a1 a2 q link_phi
+
+let neg a =
+  let a = copy a in
+  let q = State.make () in
+  let link_phi = 
+    StateSet.fold
+      (fun q phi -> Formula.(and_ (not_(stay q)) phi))
+      a.selecting_states
+      Formula.true_
+  in
+  let () = Hashtbl.add a.transitions q [(QNameSet.any, link_phi)] in
+  let a =
+    { a with
+      selecting_states = StateSet.singleton q;
+    }
+  in
+  normalize_negations a; a
+
+let diff a1 a2 = inter a1 (neg a2)
+