+(* [compute_dependencies auto] returns a hash table storing for each
+ states [q] a Move.table containing the set of states on which [q]
+ depends (loosely). [q] depends on [q'] if there is a transition
+ [q, {...} -> phi], where [q'] occurs in [phi].
+*)
+let compute_dependencies auto =
+ let edges = Hashtbl.create 17 in
+ StateSet.iter
+ (fun q -> Hashtbl.add edges q (Move.create_table StateSet.empty))
+ auto.starting_states;
+ Hashtbl.iter (fun q trans ->
+ let moves = try Hashtbl.find edges q with Not_found ->
+ let m = Move.create_table StateSet.empty in
+ Hashtbl.add edges q m;
+ m
+ in
+ List.iter (fun (_, phi) ->
+ let m_phi = Formula.get_states_by_move phi in
+ Move.iter (fun m set ->
+ Move.set moves m (StateSet.union set (Move.get moves m)))
+ m_phi) trans) auto.transitions;
+
+ edges
+
+
+let compute_rank auto =
+ let dependencies = compute_dependencies auto in
+ let upward = [ `Stay ; `Parent ; `Previous_sibling ] in
+ let downward = [ `Stay; `First_child; `Next_sibling ] in
+ let swap dir = if dir == upward then downward else upward in
+ let is_satisfied dir q t =
+ Move.for_all (fun d set ->
+ if List.mem d dir then
+ StateSet.(is_empty (remove q set))
+ else StateSet.is_empty set) t
+ in
+ let update_dependencies dir initacc =
+ let rec loop acc =
+ let new_acc =
+ Hashtbl.fold (fun q deps acc ->
+ let to_remove = StateSet.union acc initacc in
+ List.iter
+ (fun m ->
+ Move.set deps m (StateSet.diff (Move.get deps m) to_remove)
+ )
+ dir;
+ if is_satisfied dir q deps then StateSet.add q acc else acc
+ ) dependencies acc
+ in
+ if acc == new_acc then new_acc else loop new_acc
+ in
+ let satisfied = loop StateSet.empty in
+ StateSet.iter (fun q ->
+ Hashtbl.remove dependencies q) satisfied;
+ satisfied
+ in
+ let current_states = ref StateSet.empty in
+ let rank_list = ref [] in
+ let rank = ref 0 in
+ let current_dir = ref upward in
+ let detect_cycle = ref 0 in
+ while Hashtbl.length dependencies != 0 do
+ let new_sat = update_dependencies !current_dir !current_states in
+ if StateSet.is_empty new_sat then incr detect_cycle;
+ if !detect_cycle > 2 then assert false;
+ rank_list := (!rank, new_sat) :: !rank_list;
+ rank := !rank + 1;
+ current_dir := swap !current_dir;
+ current_states := StateSet.union new_sat !current_states;
+ done;
+ let by_rank = Hashtbl.create 17 in
+ List.iter (fun (r,s) ->
+ let set = try Hashtbl.find by_rank r with Not_found -> StateSet.empty in
+ Hashtbl.replace by_rank r (StateSet.union s set)) !rank_list;
+ auto.ranked_states <-
+ Array.init (Hashtbl.length by_rank) (fun i -> Hashtbl.find by_rank i)
+
+
+module Builder =
+ struct
+ type auto = t
+ type t = auto
+ let next = Uid.make_maker ()
+
+ let make () =
+ let auto =
+ {
+ id = next ();
+ states = StateSet.empty;
+ starting_states = StateSet.empty;
+ selecting_states = StateSet.empty;
+ transitions = Hashtbl.create MED_H_SIZE;
+ ranked_states = [| |]
+ }
+ in
+ auto
+
+ 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 =
+ if not (StateSet.mem q a.states) then add_state a q;
+ let trs = try Hashtbl.find a.transitions q with Not_found -> [] in
+ let cup, ntrs =
+ List.fold_left (fun (acup, atrs) (labs, phi) ->
+ let lab1 = QNameSet.inter labs s in
+ let lab2 = QNameSet.diff labs s in
+ let tr1 =
+ if QNameSet.is_empty lab1 then []
+ else [ (lab1, Formula.or_ phi f) ]
+ in
+ let tr2 =
+ if QNameSet.is_empty lab2 then []
+ else [ (lab2, Formula.or_ phi f) ]
+ in
+ (QNameSet.union acup labs, tr1@ tr2 @ atrs)
+ ) (QNameSet.empty, []) trs
+ in
+ let rem = QNameSet.diff s cup in
+ let ntrs = if QNameSet.is_empty rem then ntrs
+ else (rem, f) :: ntrs
+ in
+ Hashtbl.replace a.transitions q ntrs
+
+ let finalize a =
+ complete_transitions a;
+ normalize_negations a;
+ compute_rank 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;
+ ranked_states = Array.map (map_set rename) a.ranked_states
+ }
+
+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;
+ let a = { a1 with
+ states = StateSet.union a1.states a2.states;
+ selecting_states = a2.selecting_states;
+ transitions = a1.transitions;
+ }
+ in compute_rank a; a
+
+let merge a1 a2 =
+ let a1 = copy a1 in
+ let a2 = copy a2 in
+ let a = { 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
+ } in
+ compute_rank a ; a
+
+
+let link a1 a2 q link_phi =
+ let a = { 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
+ }
+ in
+ compute_rank a; a
+
+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; compute_rank a; a