Skip to content
Snippets Groups Projects
Select Git revision
  • e34fa81642dd3a6171dd9b07e228acbed6e6050e
  • master default protected
2 results

imperativeBL.ml

Blame
  • imperativeBL.ml 20.19 KiB
    open! DataStruct 
    
    module type S = 
    sig 
      type elt 
      val pqtree : elt PqTree.t
      val index : elt -> int 
    end
    
    
    module Make = 
      functor (T : S) -> 
      struct
    
        open PqTree
        type elt = T.elt 
    
        let max_nb_nodes = 3 * length T.pqtree
        let pool = ref (Pool.make_int_pool max_nb_nodes)
                 
        let get_new_index () = 
          let (index, p) = Pool.get !pool in 
          pool := p;
          index 
    
        let free_index index = 
          pool := Pool.free index !pool
    
        type kind = L of T.elt | PNode | QNode 
        type status = Full | Partial | Empty 
    
        type node = int 
    
        let root = ref 0
        let parent = Array.make max_nb_nodes None 
        let next = Array.make max_nb_nodes None 
        let previous = Array.make max_nb_nodes None 
        let leftmost_child = Array.make max_nb_nodes None 
        let rightmost_child = Array.make max_nb_nodes None 
        let kind = Array.make max_nb_nodes PNode 
        let nb_children = Array.make max_nb_nodes 0
    
        let is_visited = Array.make max_nb_nodes false 
        let active_children = Array.make max_nb_nodes []
        let status = Array.make max_nb_nodes Empty 
        let visited = ref [] 
    
        let leaf = Array.make (length T.pqtree) (-1) 
    
    
        let clean_node node =
          if is_visited.(node) then 
            begin
              is_visited.(node) <- false;
              active_children.(node) <- [];
              status.(node) <- Empty
            end
    
        let clean () = 
          List.iter clean_node !visited;
          visited := [] 
    
    
        let free = free_index
    
                
        let add_child ?(left_of=None) p child =
          next.(child) <- left_of;
          parent.(child) <- Some p;
          nb_children.(p) <- nb_children.(p) + 1;
          match left_of with 
          | None ->
             begin match rightmost_child.(p) with
             | Some right -> next.(right) <- Some child
             | None -> leftmost_child.(p) <- Some child
             end;
             previous.(child) <- rightmost_child.(p);
             rightmost_child.(p) <- Some child
          | Some c ->
             begin match previous.(c) with 
             | Some left -> next.(left) <- Some child 
             | None -> leftmost_child.(p) <- Some child
             end;
             previous.(child) <- previous.(c);
             previous.(c) <- Some child
          
        let add_last_child ?(rev=false) p child = 
          if rev then add_child ~left_of:leftmost_child.(p) p child 
          else add_child ~left_of:None p child 
    
        let add_first_child ?(rev=false) p child = 
          add_last_child ~rev:(not rev) p child
    
    
        let detach child =
          match parent.(child) with 
          | None -> ()
          | Some p -> 
             parent.(child) <- None;
             nb_children.(p) <- nb_children.(p) - 1;
             begin match previous.(child) with 
             | None -> leftmost_child.(p) <- next.(child)
             | Some left -> next.(left) <- next.(child)
             end;
             begin match next.(child) with 
             | None -> rightmost_child.(p) <- previous.(child) 
             | Some right -> previous.(right) <- previous.(child)
             end ;
             previous.(child) <- None;
             next.(child) <- None
    
    
        let replace p q =
          match parent.(p) with 
          | None -> 
             root := q;
             parent.(q) <- None 
          | Some r -> 
             let left_of = next.(p) in 
             detach p;
             add_child ~left_of r q 
            
        let contract_if_trivial p =
          match nb_children.(p) with
          | 1 -> 
             leftmost_child.(p)
             |> Option.iter (fun child ->
                    detach child;
                    replace p child;
                    free p
                  )
          | 0 -> 
              detach p;
              free p
          | _ -> () 
            
    
        let previous_seq start =
          Seq.unfold (Option.map (fun child -> (child, previous.(child)))) start |> List.of_seq
    
        let next_seq start =
          Seq.unfold (Option.map (fun child -> (child, next.(child)))) start |> List.of_seq
    
        let children_seq p = next_seq leftmost_child.(p)
    
        let rev_children_seq p = previous_seq rightmost_child.(p)
              
    
        let absorb ?(rev=false) p q =
          let left_of = next.(q) in 
          detach q;
          List.iter (add_child ~left_of p) (if rev then rev_children_seq q else children_seq q);
          free q;
          p
    
    
        let expand ?(rev=false) p q =
          let (left_seq,right_seq) = 
            if rev then (next_seq next.(q), previous_seq previous.(q))
            else (previous_seq previous.(q), next_seq next.(q))
          in
          List.iter (fun c -> detach c; add_first_child q c) left_seq;
          List.iter (fun c -> detach c; add_last_child q c) right_seq;
          detach q;
          replace p q;
          free p;
          q 
    
    
        let vertical_q_merge (p,p_is_leftist) (q,q_is_leftist) =
          let rev = p_is_leftist <> q_is_leftist in 
          if nb_children.(p) >= nb_children.(q) then 
            (absorb ~rev p q, p_is_leftist)
          else 
            (expand ~rev p q, q_is_leftist)
    
    
        let horizontal_q_merge partial1 full_nodes partial2 =
          let ((small, small_is_leftist),(big,big_is_leftist)) = 
            if nb_children.(fst partial1) >= nb_children.(fst partial2) then (partial2, partial1)
            else (partial1, partial2)
          in 
          let rev = big_is_leftist in 
          detach small;
          Option.iter (fun node -> add_last_child ~rev big node) full_nodes;
          List.iter 
            (fun c -> detach c; add_last_child ~rev big c)
            (if small_is_leftist then 
               children_seq small
             else 
               rev_children_seq small
            );
          free small;
          big 
    
          
        let create_node ?(p=None) ~k ?(left=None) ?(right=None) children =
          let index = get_new_index () in 
          parent.(index) <- p;
          kind.(index) <- k; 
          next.(index) <- right;
          previous.(index) <- left;
          leftmost_child.(index) <- None;
          rightmost_child.(index) <- None;
          nb_children.(index) <- 0;
          List.iter (add_last_child index) children;
          begin match k with 
          |  L elt -> leaf.(T.index elt) <- index
          |  _ -> ()
          end;
          is_visited.(index) <- false;
          active_children.(index) <- [];
          status.(index) <- Empty;
          index
    
    
        let join_p = function 
          | [] -> assert false
          | [single] -> 
             detach single; 
             single
          | full_trees -> 
             List.iter detach full_trees;
             create_node ~k:PNode full_trees 
    
    
        let initialize () = 
          let rec go node = 
            match node with 
            | Leaf elt -> 
               create_node ~k:(L elt) ~p:None []
            | P children_nodes -> 
               let children = List.map go children_nodes in 
               create_node ~k:PNode ~p:None children
            | Q children_nodes -> 
               let children = List.map go children_nodes in
               create_node ~k:QNode ~p:None children 
          in 
          root := go T.pqtree
                      
               
        let mark_active_nodes leaves =
          let visit node queue = 
            is_visited.(node) <- true;
            visited := node :: !visited;
            match parent.(node) with 
            | Some p -> 
               let is_new_node = active_children.(p) = [] in 
               active_children.(p) <- node :: active_children.(p);
               if is_new_node then 
                 begin
                   FQueue.enqueue queue p 
                 end
               else queue 
            | None -> FQueue.enqueue queue node 
          in
          let rec go queue =
            match FQueue.view queue with 
            | None -> assert false 
            | Some (index, others) when FQueue.is_empty others -> 
               if (not is_visited.(index)) then 
                 begin
                   is_visited.(index) <- true;
                   visited := index :: !visited
                 end;
               index 
            | Some (index, others) -> go (visit index others)
          in 
          leaves 
          |> FQueue.of_list
          |> go
    
    
        let find_pertinent_node leaves =
          let common_ancestor = mark_active_nodes leaves in 
          let rec go ancestor = 
            match active_children.(ancestor) with 
            | [single_child] -> go single_child 
            |  _ -> ancestor
          in 
          go common_ancestor 
    
    
        let rec set_status node = 
          let are_all_fulls = 
            active_children.(node)
            |> List.map set_status  
            |> List.for_all (fun b -> b) 
          in 
          let is_full = are_all_fulls && List.length active_children.(node) = nb_children.(node) in
          status.(node) <- if is_full then Full else Partial;
          is_full 
    
                          
        type active_interval =
          | LeftInterval of node 
          | RightInterval of node 
          | MiddleInterval of node * node 
          | NotAnInterval
    
        let is_left_end_of_interval node = 
          match previous.(node) with 
          | None -> true 
          | Some sibling -> status.(sibling) = Empty 
    
        let is_youngest node =
          next.(node) = None 
    
        let is_right_end_of_interval node =
          match next.(node) with 
          | None -> true
          | Some sibling -> status.(sibling) = Empty
    
        let is_oldest node =
          previous.(node) = None
    
        let is_middle node =
          not (is_left_end_of_interval node) && not (is_right_end_of_interval node)
    
        let is_middle_partial node =
          is_middle node && status.(node) = Partial 
    
    
        let get_active_interval node =
          let achildren = active_children.(node) in
          let leftmosts = List.filter is_left_end_of_interval achildren in 
          let rightmosts = List.filter is_right_end_of_interval achildren in 
          match leftmosts, rightmosts with 
          | _ when List.exists is_middle_partial achildren -> NotAnInterval 
          | [lchild], [rchild] when lchild = rchild ->
             if is_oldest lchild then LeftInterval rchild 
             else if is_youngest rchild then RightInterval lchild 
             else MiddleInterval (lchild, rchild)
          | [lchild], [rchild] -> 
             if is_oldest lchild && not (status.(lchild) == Partial) then 
               LeftInterval rchild
             else if is_youngest rchild && not (status.(rchild) == Partial) then 
               RightInterval lchild
             else 
               MiddleInterval (lchild, rchild)                                   
          | _, _ ->  NotAnInterval
    
             
     
    
        type 'subtree reducible =
          | Reduced of (node * bool)
          | Reduction of 'subtree
    
        type 'subtree reducibility = 
          | Irreducible 
          | Reducible of 'subtree reducible
    
          
        type partial_subtree =
          | PartialP0 of node * node list
          | PartialP1 of node * partial_subtree reducible * node list
          | PartialQ1 of node * bool * partial_subtree reducible
    
        type pertinent_subtree =
          | PartialSubtree of partial_subtree 
          | PertinentP2 of node * partial_subtree reducible * partial_subtree reducible * node list 
          | PertinentQMiddlePBoth of node * partial_subtree reducible * partial_subtree reducible
          | PertinentQMiddlePLeft of node * partial_subtree reducible
          | PertinentQMiddlePRight of node * partial_subtree reducible
          
    
    
        let is_full node = status.(node) = Full 
    
        let partition_p_children node = 
          List.partition is_full active_children.(node)
    
    
    
        let (>>=) e f = match e with 
          | Irreducible -> Irreducible 
          | Reducible reduction -> f reduction
    
    
        let rec make_q_subtree node = function 
          | NotAnInterval -> Irreducible
          | LeftInterval rchild when status.(rchild) = Partial -> 
             make_subtree rchild >>= fun sub -> 
             Reducible (Reduction (PartialQ1 (node, true, sub)))
          | LeftInterval _ -> 
             Reducible (Reduced (node, true))
          | RightInterval lchild when status.(lchild) = Partial -> 
             make_subtree lchild >>= fun sub -> 
             Reducible (Reduction (PartialQ1 (node, false, sub)))
          | RightInterval _ -> 
             Reducible (Reduced (node, false))
          | MiddleInterval _ -> Irreducible
          
    
        and make_p_subtree node = function 
          | fulls, [] ->  
             Reducible (Reduction (PartialP0 (node, fulls)))
          | fulls, [partial] -> 
             make_subtree partial >>= fun sub -> 
             Reducible (Reduction (PartialP1 (node, sub, fulls)))
          | _ -> Irreducible
    
        and make_subtree node =
          if status.(node) = Full then Reducible (Reduced (node, false))
          else match kind.(node) with 
               | PNode -> make_p_subtree node (partition_p_children node) 
               | QNode -> make_q_subtree node (get_active_interval node) 
               | L _ -> Reducible (Reduced (node, false))
    
        let lift_to_pertinent = function
          | Reduced _ as red -> Reducible red
          | Reduction subtree -> Reducible (Reduction (PartialSubtree subtree))
    
        let make_pertinent_p_subtree pertinent_node = function 
          | fulls, [partial1; partial2] -> 
             make_subtree partial1 >>= fun sub1 -> 
             make_subtree partial2 >>= fun sub2 ->
             Reducible (Reduction (PertinentP2 (pertinent_node, sub1, sub2, fulls)))
          | partition -> 
             make_p_subtree pertinent_node partition >>= lift_to_pertinent
     
    
        let make_pertinent_q_subtree pertinent_node = function
          | MiddleInterval (lchild, rchild) 
               when status.(lchild) = Partial && status.(rchild) = Partial ->
             make_subtree lchild >>= fun sub1 ->
             make_subtree rchild >>= fun sub2 ->
             Reducible (Reduction (PertinentQMiddlePBoth (pertinent_node, sub1, sub2)))
          | MiddleInterval (lchild, _) when status.(lchild) = Partial ->
             make_subtree lchild >>= fun sub1 ->
             Reducible (Reduction (PertinentQMiddlePLeft (pertinent_node, sub1)))
          | MiddleInterval (_, rchild) when status.(rchild) = Partial ->
             make_subtree rchild >>= fun sub2 ->
             Reducible (Reduction (PertinentQMiddlePRight (pertinent_node, sub2)))
          | MiddleInterval (_,_) -> 
             Reducible (Reduced (pertinent_node, false))
          | interval -> 
             make_q_subtree pertinent_node interval >>= lift_to_pertinent
    
        let make_pertinent_subtree pertinent_node =
          match kind.(pertinent_node) with 
          | _ when status.(pertinent_node) = Full -> 
             Reducible (Reduced (pertinent_node, false))
          | PNode -> make_pertinent_p_subtree pertinent_node (partition_p_children pertinent_node)
          | QNode -> make_pertinent_q_subtree pertinent_node (get_active_interval pertinent_node)
          | L _ -> Irreducible
    
    
        (* always returns a partial Q-node *)
        let rec reduce = function
          | Reduced red -> red
          | Reduction (PartialP0 (p, fulls)) -> 
             let full = join_p fulls in 
             if nb_children.(p) = 1 then 
               begin
                 kind.(p) <- QNode;
                 add_last_child p full;
                 (p, false)             
               end
             else 
               begin
                 let q = create_node ~k:QNode [full] in 
                 replace p q;
                 add_first_child q p;
                 (q,false)
               end
          | Reduction (PartialP1 (p, partial, fulls)) -> 
             let (q,q_is_leftist) = reduce partial in
             detach q;
             replace p q;
             add_first_child ~rev:q_is_leftist q p;
             if fulls <> [] then 
               begin
                 let full = join_p fulls in 
                 add_last_child ~rev:q_is_leftist q full 
               end;
             contract_if_trivial p;
             (q, q_is_leftist)
          | Reduction (PartialQ1 (p, p_is_leftist, reducible)) ->
             vertical_q_merge (p,p_is_leftist) (reduce reducible)
    
    
        let reduce_pertinent = function
          | Reduced _ -> ()
          | Reduction (PartialSubtree (PartialP0 (p,fulls))) ->
             let q = join_p fulls in 
             add_last_child p q 
          | Reduction (PartialSubtree (PartialP1 (p, partial, fulls))) -> 
             let (q, rev) = reduce partial in 
             List.iter detach fulls;
             detach q;
             if fulls <> [] then 
               begin
                 let s = join_p fulls in 
                 add_last_child ~rev q s
               end;
             if nb_children.(p) > 0 then 
                 add_last_child p q
             else
               begin
                 replace p q;
                 free p 
               end
          | Reduction (PartialSubtree sub) -> 
             let (q,_ ) = reduce (Reduction sub) in 
             if kind.(q) = QNode && nb_children.(q) = 2 then 
               kind.(q) <- PNode
          | Reduction (PertinentP2 (p, partial1, partial2, full_nodes)) ->
             let full =
               if full_nodes = [] then None 
               else Some (join_p full_nodes)
             in
             let qnode = horizontal_q_merge (reduce partial1) full (reduce partial2) in 
             if nb_children.(p) = 1 then 
               begin 
                 replace p qnode;
                 free p
               end
          | Reduction (PertinentQMiddlePLeft (p, partial_left)) -> 
             vertical_q_merge (p, false) (reduce partial_left) |> ignore
          | Reduction (PertinentQMiddlePRight (p, partial_right)) -> 
             vertical_q_merge (p,true) (reduce partial_right) |> ignore
          | Reduction (PertinentQMiddlePBoth (p, partial_left, partial_right)) -> 
             let (r, r_is_left) =  vertical_q_merge (p, false) (reduce partial_left) in
             vertical_q_merge (r, not r_is_left) (reduce partial_right) |> ignore
    
    
    
        let subtree_is_block = function 
          | Reducible (Reduced _) -> true
          | _ -> false 
    
        let subtree_is_interval = function 
          | Reducible _ -> true
          | _ -> false
    
        let pertinent_subtree interval =
          let leaves = List.map (fun elt -> leaf.(T.index elt)) interval in 
          let pertinent_node = find_pertinent_node leaves in 
          let _ = set_status pertinent_node in 
          let subtree = make_pertinent_subtree pertinent_node in 
          clean ();
          subtree
    
        let test_subtree condition = function      
          | [] -> true 
          | [_] -> true 
          | interval ->
             condition (pertinent_subtree interval)
    
        let is_interval = 
          test_subtree subtree_is_interval
    
        let is_block = 
          test_subtree subtree_is_block
    
    
        let insert = function 
          | [] | [_] -> true
          | interval ->
             match pertinent_subtree interval with 
             | Irreducible -> false 
             | Reducible red -> 
                reduce_pertinent red;
                true
          
    
        let get () =
          let rec go node = 
            match kind.(node) with 
            | L elt -> Leaf elt  
            | PNode -> 
               children_seq node 
               |> List.map go 
               |> fun children -> P children 
            | QNode -> 
               children_seq node 
               |> List.map go 
               |> fun children -> Q children 
          in 
          go !root
    
    
          module Test = struct 
            
            let write_option fmt = function
                | None -> Format.fprintf fmt "-" 
                | Some node -> Format.fprintf fmt "%d" node 
    
                             
            let indent k = String.make (2 * k) ' '
    
            let write_node i fmt node =
              let write_inner kind = 
                 Format.fprintf fmt "%s%s %d, parent %a, leftmost child %a, rightmost child %a, previous %a, next %a\n"
                   (indent i) 
                   kind 
                   node 
                   write_option parent.(node) 
                   write_option leftmost_child.(node) 
                   write_option rightmost_child.(node)
                   write_option previous.(node)
                   write_option next.(node)
              in
              match kind.(node) with
              | L _  -> 
                 Format.fprintf fmt "%sLeaf %d, parent %a, previous %a, next %a\n"
                   (indent i)
                   node
                   write_option parent.(node)
                   write_option previous.(node)
                   write_option next.(node)
              | PNode -> 
                 write_inner "P"
              | QNode ->  
                 write_inner "Q"
    
            let write_tree fmt = 
              let rec go i node = 
                write_node i fmt node;
                Option.iter (go (i+1)) leftmost_child.(node);
                Option.iter (go i) next.(node)
              in 
              go 0 !root
              
          end
    
      end
    
    module type BLSig = 
    sig
       
      type elt 
         
      val is_interval : elt list -> bool 
    
      val is_block : elt list -> bool
    
      val insert : elt list -> bool 
    
      val get : unit -> elt PqTree.t 
    
      module Test : sig 
    
        val write_tree : Format.formatter -> unit
    
      end
    
    end
    
    
    
    let get_bl_module (type e) (module T : S with type elt = e) =
      let module BLAlgo = Make(T) in 
      BLAlgo.initialize ();
      (module BLAlgo : BLSig with type elt = e)
      
    
    type 'elt algorithms = 
      { is_interval : 'elt list -> bool;
        is_block : 'elt list -> bool;
        insert : 'elt list -> bool;
        get : unit -> 'elt PqTree.t
      }
    
    let get_algorithms (type elt) pqtree index =
      let module R = 
        struct
          type nonrec elt = elt
          let pqtree = pqtree 
          let index = index 
        end
      in
      let (module BL : BLSig with type elt = elt) = 
        get_bl_module (module R : S with type elt = elt) 
      in 
      BL.({ is_interval; is_block; insert; get})