(***********************************************************************) (* *) (* TAToo *) (* *) (* Lucca Hirschi, LRI UMR8623 *) (* Université Paris-Sud & CNRS *) (* *) (* Copyright 2010-2012 Université Paris-Sud and Centre National de la *) (* Recherche Scientifique. All rights reserved. This file is *) (* distributed under the terms of the GNU Lesser General Public *) (* License, with the special exception on linking described in file *) (* ../LICENSE. *) (* *) (***********************************************************************) INCLUDE "utils.ml" module Node = struct type t = int let hash n = n let compare = (-) let equal = (=) end module NodeHash = Hashtbl.Make (Node) type t = (StateSet.t*StateSet.t) NodeHash.t (** Map from nodes to query and recognizing states *) (* Note that we do not consider nil nodes *) exception Oracle_fail exception Over_max_fail exception Max_fail (* Hash Consign modules *) module type Oracle_fixpoint = sig type t = StateSet.t*StateSet.t*StateSet.t*((StateSet.elt*Formula.t) list)*QName.t val equal : t -> t -> bool val hash : t -> int end type dStateS = StateSet.t*StateSet.t module type Run_fixpoint = sig type t = dStateS*dStateS*dStateS*(State.t*Formula.t) list*QName.t val equal : t -> t -> bool val hash : t -> int end module Oracle_fixpoint : Oracle_fixpoint = struct type t = StateSet.t*StateSet.t*StateSet.t*((StateSet.elt*Formula.t) list)*QName.t let equal (s,l,r,list,t) (s',l',r',list',t') = StateSet.equal s s' && StateSet.equal l l' && StateSet.equal r r' && QName.equal t t' let hash (s,l,r,list,t) = HASHINT4(StateSet.hash s, StateSet.hash l, StateSet.hash r, QName.hash t) end let dequal (x,y) (x',y') = StateSet.equal x x' && StateSet.equal y y' let dhash (x,y) = HASHINT2(StateSet.hash x, StateSet.hash y) module Run_fixpoint : Run_fixpoint = struct type t = dStateS*dStateS*dStateS*(State.t*Formula.t) list*QName.t let equal (s,l,r,list,t) (s',l',r',list',t') = dequal s s' && dequal l l' && dequal r r' && QName.equal t t' let hash (s,l,r,list,t) = HASHINT4(dhash s, dhash l, dhash r, QName.hash t) end module HashOracle = Hashtbl.Make(Oracle_fixpoint) module HashRun = Hashtbl.Make(Run_fixpoint) (* Mapped sets for leaves *) let map_leaf asta = (Asta.bot_states_s asta, StateSet.empty) (* Build the Oracle *) let rec bu_oracle asta run tree tnode hashOracle= let node = Tree.preorder tree tnode in if Tree.is_leaf tree tnode then if tnode == Tree.nil then () else NodeHash.add run node (map_leaf asta) else let tfnode = Tree.first_child_x tree tnode and tnnode = Tree.next_sibling tree tnode in let fnode,nnode = (* their preorders *) (Tree.preorder tree tfnode, Tree.preorder tree tnnode) in begin bu_oracle asta run tree tfnode hashOracle; bu_oracle asta run tree tnnode hashOracle; (* add states which satisfy a transition *) let rec result set qfr qnr flag = function | [] -> set,flag | (q,form) :: tl -> if Formula.eval_form (set,qfr,qnr) form (* evaluates the formula*) then if StateSet.mem q set then result set qfr qnr 0 tl else result (StateSet.add q set) qfr qnr 1 tl else result set qfr qnr 0 tl in (* compute the fixed point of states of node *) let rec fix_point set_i qfr qnr list_tr t = try HashOracle.find hashOracle (set_i, qfr, qnr, list_tr, t) with _ -> let set,flag = result set_i qfr qnr 0 list_tr in HashOracle.add hashOracle (set_i,qfr,qnr,list_tr,t) (set); if flag = 0 then set else fix_point set qfr qnr list_tr t in let q_rec n = (* compute the set for child/sibling *) try NodeHash.find run n with Not_found -> map_leaf asta in let (_,qfr),(_,qnr) = q_rec fnode,q_rec nnode (* computed in rec call *) and lab = Tree.tag tree tnode in let _,list_tr = Asta.transitions_lab asta lab in (*only reco. tran.*) NodeHash.add run node (StateSet.empty, fix_point StateSet.empty qfr qnr list_tr lab) end (* Build the over-approx. of the maximal run *) let rec bu_over_max asta run tree tnode hashRun = if (Tree.is_leaf tree tnode) (* BU_oracle has already created the map *) then () else let tfnode = Tree.first_child_x tree tnode and tnnode = Tree.next_sibling tree tnode in begin bu_over_max asta run tree tfnode hashRun; bu_over_max asta run tree tnnode hashRun; let (fnode,nnode) = (Tree.preorder tree tfnode, Tree.preorder tree tnnode) and node = Tree.preorder tree tnode in let q_rec n = try NodeHash.find run n with Not_found -> map_leaf asta in let qf,qn = q_rec fnode,q_rec nnode in let lab = Tree.tag tree tnode in let list_tr,_ = Asta.transitions_lab asta lab (* only take query st. *) and _,resultr = try NodeHash.find run node with _ -> raise Over_max_fail in let rec result set flag = function | [] -> if flag = 0 then set else result set 0 list_tr | (q,form) :: tl -> if StateSet.mem q set then result set 0 tl else if Formula.infer_form (set,resultr) qf qn form then result (StateSet.add q set) 1 tl else result set 0 tl in let result_set = result StateSet.empty 0 list_tr in (* we keep the old recognizing states set *) NodeHash.replace run node (result_set, resultr) end (* Build the maximal run *) let rec tp_max asta run tree tnode hashRun = if (Tree.is_leaf tree tnode) (* BU_oracle has already created the map *) then () else let node = Tree.preorder tree tnode and tfnode = Tree.first_child_x tree tnode and tnnode = Tree.next_sibling tree tnode in let (fnode,nnode) = (Tree.preorder tree tfnode, Tree.preorder tree tnnode) in begin if tnode == Tree.root tree (* we must intersect with top states *) then let setq,_ = try NodeHash.find run node with _ -> raise Max_fail in NodeHash.replace run node ((StateSet.inter (Asta.top_states_s asta) setq),StateSet.empty) else (); let q_rec n = try NodeHash.find run n with Not_found -> map_leaf asta in let qf,qn = q_rec fnode,q_rec nnode in let lab = Tree.tag tree tnode in let list_tr,_ = Asta.transitions_lab asta lab in (* only take query. *) let (self_q,self_r) = try NodeHash.find run node with Not_found -> raise Max_fail in (* We must compute again accepting states from self transitions since previous calls of tp_max may remove them *) let rec result_q self_q queue = function (* for initializing the queue *) | [] -> self_q,queue | (q,form) :: tl -> if (StateSet.mem q self_q) then begin let q_cand,_,_ = Formula.st form in StateSet.iter (fun x -> Queue.push x queue) q_cand; result_q (StateSet.add q self_q) queue tl; end else result_q self_q queue tl and result_st_q self_q queue flag = function (*for computing the fixed p*) | [] -> flag,queue | form :: tl -> if Formula.infer_form (self_q,self_r) qf qn form then begin let q_cand,_,_ = Formula.st form in StateSet.iter (fun x -> Queue.push x queue) q_cand; result_st_q self_q queue 1 tl; end else result_st_q self_q queue flag tl in let rec comp_acc_self self_q_i queue = (* compute the fixed point *) if Queue.is_empty queue then self_q_i else let q = Queue.pop queue in let list_q,_ = Asta.transitions_st_lab asta q lab in let flag,queue = result_st_q self_q_i queue 0 list_q in let self_q = if flag = 1 then StateSet.add q self_q_i else self_q_i in comp_acc_self self_q queue in let self,queue_init = result_q self_q (Queue.create()) list_tr in let self_q = comp_acc_self self_q queue_init in NodeHash.replace run node (self_q,self_r); (* From now, the correct set of states is mapped to node! *) let rec result = function | [] -> [] | (q,form) :: tl -> if (StateSet.mem q self) && (* infers & trans. can start here *) (Formula.infer_form (self_q,self_r) qf qn form) then form :: (result tl) else result tl in let list_form = result list_tr in (* tran. candidates *) (* compute states occuring in transition candidates *) let rec add_st (ql,qr) = function | [] -> ql,qr | f :: tl -> let sqs,sql,sqr = Formula.st f in let ql' = StateSet.union sql ql and qr' = StateSet.union sqr qr in add_st (ql',qr') tl in let ql,qr = add_st (StateSet.empty, StateSet.empty) list_form in let qfq,qfr = try NodeHash.find run fnode with | _ -> map_leaf asta and qnq,qnr = try NodeHash.find run nnode with | _ -> map_leaf asta in begin if tfnode == Tree.nil || Tree.is_attribute tree tnode then () else NodeHash.replace run fnode (StateSet.inter qfq ql,qfr); if tnnode == Tree.nil || Tree.is_attribute tree tnode then () else NodeHash.replace run nnode (StateSet.inter qnq qr,qnr); (* indeed we delete all states from self transitions! *) tp_max asta run tree tfnode hashRun; tp_max asta run tree tnnode hashRun; end; end let compute tree asta = let flag = 2 in (* debug *) let size_tree = 10000 in (* todo (Tree.size ?) *) let size_hcons_O = 1000 in (* todo size Hashtbl *) let size_hcons_M = 1000 in (* todo size Hashtbl *) let map = NodeHash.create size_tree in let hashOracle = HashOracle.create(size_hcons_O) in bu_oracle asta map tree (Tree.root tree) hashOracle; HashOracle.clear hashOracle; if flag > 0 then begin let hashRun = HashRun.create(size_hcons_M) in bu_over_max asta map tree (Tree.root tree) hashRun; if flag = 2 then tp_max asta map tree (Tree.root tree) hashRun else (); HashRun.clear hashRun; end else (); map let selected_nodes tree asta = let run = compute tree asta in NodeHash.fold (fun key set acc -> if not(StateSet.is_empty (StateSet.inter (fst set) (Asta.selec_states asta))) then key :: acc else acc) run [] let print fmt run = let print_d_set fmt (s_1,s_2) = Format.fprintf fmt "(%a,%a)" StateSet.print s_1 StateSet.print s_2 in let print_map fmt run = let pp = Format.fprintf fmt in if NodeHash.length run = 0 then Format.fprintf fmt "ø" else NodeHash.iter (fun cle set -> pp "| %i->%a @ " cle print_d_set set) run in let print_box fmt run = let pp = Format.fprintf fmt in pp "@[@. # Mapping:@. @[%a@]@]" print_map run in Format.fprintf fmt "@[##### RUN #####@, %a@]@." print_box run