Put the move type of automata in a Move module and add auxiliary function to create...
[tatoo.git] / src / ata.ml
index 79d47a9..3263e46 100644 (file)
@@ -22,6 +22,57 @@ type move = [ `First_child
             | `Previous_sibling
             | `Stay ]
 
+module Move =
+  struct
+    type t = move
+    type 'a table = 'a array
+    let idx = function
+      | `First_child -> 0
+      | `Next_sibling -> 1
+      | `Parent -> 2
+      | `Previous_sibling -> 3
+      | `Stay -> 4
+    let ridx = function
+      | 0 -> `First_child
+      | 1 -> `Next_sibling
+      | 2 -> `Parent
+      | 3 -> `Previous_sibling
+      | 4 -> `Stay
+      | _ -> assert false
+
+    let create_table a = Array.make 5 a
+    let get m k = m.(idx k)
+    let set m k v = m.(idx k) <- v
+    let iter f m = Array.iteri (fun i v -> f (ridx i) v) m
+    let fold f m acc =
+      let acc = ref acc in
+      iter (fun i v -> acc := f i v !acc) m;
+      !acc
+    let for_all p m =
+      try
+        iter (fun i v -> if not (p i v) then raise Exit) m;
+        true
+      with
+        Exit -> false
+    let exists p m =
+      try
+        iter (fun i v -> if p i v then raise Exit) m;
+        false
+      with
+        Exit -> true
+    let print ppf m =
+      match m with
+        `First_child -> fprintf ppf "%s" Pretty.down_arrow
+      | `Next_sibling -> fprintf ppf "%s" Pretty.right_arrow
+      | `Parent -> fprintf ppf "%s" Pretty.up_arrow
+      | `Previous_sibling -> fprintf ppf "%s" Pretty.left_arrow
+      | `Stay -> fprintf ppf "%s" Pretty.bullet
+
+    let print_table pr_e ppf m =
+      iter (fun i v -> fprintf ppf "%a: %a" print i pr_e v;
+        if (idx i) < 4 then fprintf ppf ", ") m
+  end
+
 type predicate = Move of move * State.t
                  | Is_first_child
                  | Is_next_sibling
@@ -43,15 +94,8 @@ struct
 
   let print ppf a =
     match a.node with
-    | Move (m, q) -> begin
-      match m with
-        `First_child -> fprintf ppf "%s" Pretty.down_arrow
-      | `Next_sibling -> fprintf ppf "%s" Pretty.right_arrow
-      | `Parent -> fprintf ppf "%s" Pretty.up_arrow
-      | `Previous_sibling -> fprintf ppf "%s" Pretty.left_arrow
-      | `Stay -> fprintf ppf "%s" Pretty.bullet
-    end;
-        fprintf ppf "%a" State.print q
+    | Move (m, q) ->
+      fprintf ppf "%a%a" Move.print m State.print q
     | Is_first_child -> fprintf ppf "%s?" Pretty.up_arrow
     | Is_next_sibling -> fprintf ppf "%s?" Pretty.left_arrow
     | Is k -> fprintf ppf "is-%a?" Tree.NodeKind.print k
@@ -107,22 +151,39 @@ struct
 
   let stay q = mk_move `Stay q
 
-  let get_states phi =
-    fold (fun phi acc ->
+  let get_states_by_move phi =
+    let table = Move.create_table StateSet.empty in
+    iter (fun phi ->
       match expr phi with
-      | Boolean.Atom ({ Atom.node = Move(_,q) ; _ }, _) ->  StateSet.add q acc
-      | _ -> acc
-    ) phi StateSet.empty
+      | Boolean.Atom ({ Atom.node = Move(v,q) ; _ }, _) ->
+        let s = Move.get table v in
+        Move.set table v (StateSet.add q s)
+      | _ -> ()
+    ) phi;
+    table
+  let get_states phi =
+    let table = get_states_by_move phi in
+    Move.fold (fun _ s acc -> StateSet.union s acc) table StateSet.empty
 
 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
@@ -147,24 +208,12 @@ type 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
@@ -173,14 +222,17 @@ 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 =
@@ -215,6 +267,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.
@@ -225,7 +299,7 @@ let complete_transitions a =
   StateSet.iter (fun q ->
     if StateSet.mem q a.starting_states then ()
     else
-      let qtrans = try Hashtbl.find a.transitions q with Not_found -> eprintf "Not found here 226\n%!"; raise Not_found in
+      let qtrans = Hashtbl.find a.transitions q in
       let rem =
         List.fold_left (fun rem (labels, _) ->
           QNameSet.diff rem labels) QNameSet.any qtrans
@@ -317,13 +391,16 @@ let normalize_negations auto =
           in
           Hashtbl.add memo_state key nq; nq
     in
-    let trans = try Hashtbl.find auto.transitions q with Not_found -> eprintf "Not_found here 318\n%!"; [] 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
@@ -394,3 +471,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)
+