ocaml-containers/src/data/CCFun_vec.ml
2019-04-13 03:20:56 -05:00

380 lines
9.2 KiB
OCaml

(* This file is free software, part of containers. See file "license" for more details. *)
(*$inject
let _listuniq =
let g = Q.(small_list (pair small_int small_int)) in
Q.map_same_type
(fun l ->
CCList.sort_uniq ~cmp:(fun a b -> Pervasives.compare (fst a)(fst b)) l
) g
;;
*)
(** {1 Hash Tries} *)
type 'a sequence = ('a -> unit) -> unit
type 'a gen = unit -> 'a option
type 'a printer = Format.formatter -> 'a -> unit
type 'a ktree = unit -> [`Nil | `Node of 'a * 'a ktree list]
(* TODO
(** {2 Transient IDs} *)
module Transient = struct
type state = { mutable frozen: bool }
type t = Nil | St of state
let empty = Nil
let equal a b = Pervasives.(==) a b
let create () = St {frozen=false}
let active = function Nil -> false | St st -> not st.frozen
let frozen = function Nil -> true | St st -> st.frozen
let freeze = function Nil -> () | St st -> st.frozen <- true
let with_ f =
let r = create() in
try
let x = f r in
freeze r;
x
with e ->
freeze r;
raise e
exception Frozen
end
*)
(* function array *)
module A = struct
type 'a t = 'a array
let length_log = 5
let max_length = 32
let mask = max_length-1
let () = assert (max_length = 1 lsl length_log)
let length = Array.length
let iteri = Array.iteri
let iter = Array.iter
let fold = Array.fold_left
let map = Array.map
let iteri_rev f a =
for i = length a-1 downto 0 do f i a.(i) done
let create () = [| |]
let empty = [| |]
let is_empty a = length a = 0
let return x = [| x |]
let get a i =
if i<0 || i >= length a then invalid_arg "A.get";
Array.unsafe_get a i
(* push at the back *)
let push x a =
let n = length a in
if n = max_length then invalid_arg "A.push";
let arr = Array.make (n+1) x in
Array.blit a 0 arr 0 n;
arr
let pop a =
let n = length a in
if n=0 then invalid_arg "A.pop";
Array.sub a 0 (n-1)
let append a b =
let n_a = length a in
let n_b = length b in
if n_a + n_b > max_length then invalid_arg "A.append";
if n_a = 0 then b
else if n_b = 0 then a
else (
let arr = Array.make (n_a+n_b) (a.(0)) in
Array.blit a 0 arr 0 n_a;
Array.blit b 0 arr n_a n_b;
arr
)
let set ~mut a i x =
if i<0 || i > length a || i >= max_length then invalid_arg "A.set";
if i=length a then (
(* insert in a longer copy *)
let arr = Array.make (i+1) x in
Array.blit a 0 arr 0 i;
arr
) else if mut then (
(* replace element at [i] in place *)
a.(i) <- x;
a
) else (
(* replace element at [i] in copy *)
let arr = Array.copy a in
arr.(i) <- x;
arr
)
end
(** {2 Functors} *)
type 'a t = {
size: int;
leaves: 'a A.t;
subs: 'a t A.t;
}
(* invariant:
- [A.length leaves < A.max_length ==> A.is_empty subs]
- either:
* [exists n. forall i. subs[i].size = n] (all subtrees of same size)
* [exists n i.
(forall j<i. sub[j].size=32^{n+1}-1) &
(forall j>=i, sub[j].size<32^{n+1}-1)]
(prefix of subs has size of complete binary tree; suffix has
smaller size (actually decreasing))
*)
let empty = {size=0; leaves=A.empty; subs=A.empty}
let is_empty {size;_} = size=0
(*$T
is_empty empty
*)
let length {size;_} = size
(*$T
not (is_empty (return 2))
length (return 2) = 1
*)
let return x = {leaves=A.return x; subs=A.empty; size=1}
type idx_l =
| I_one of int
| I_cons of int * idx_l
(* split an index into a low and high parts *)
let low_idx_ i = i land A.mask
let high_idx_ i = i lsr A.length_log
let combine_idx i j = (i lsl A.length_log) lor j
(* split an index into a high part, < 32, and a low part *)
let split_idx i : idx_l =
let rec aux high low =
if high = 0 then low
else if high < A.max_length then I_cons (high-1, low)
else aux (high_idx_ high) (I_cons (low_idx_ high, low))
in
aux (high_idx_ i) (I_one(low_idx_ i))
let get_ (i:int) (m:'a t) : 'a =
let rec aux l m = match l with
| I_one x ->
assert (x < A.length m.leaves);
A.get m.leaves x
| I_cons (x, tl) -> aux tl (A.get m.subs x)
in
aux (split_idx i) m
(*$Q
_listuniq (fun l -> \
let m = of_list l in \
List.for_all (fun (i,y) -> get_exn i m = y) @@ List.mapi CCPair.make l)
*)
let get_exn i v =
if i >= 0 && i < length v then get_ i v else raise Not_found
let get i v =
if i >= 0 && i < length v then Some (get_ i v) else None
let push_ (i:int) (x:'a) (m:'a t) : 'a t =
let rec aux l m = match l with
| I_one i ->
assert (i=A.length m.leaves);
assert (A.length m.leaves < A.max_length);
assert (A.is_empty m.subs);
{m with size=m.size+1; leaves=A.push x m.leaves}
| I_cons (i,tl) -> aux_replace_sub tl m i
and aux_replace_sub l m x =
assert (x <= A.length m.subs);
(* insert in subtree, possibly a new one *)
let sub_m =
if x < A.length m.subs then A.get m.subs x else empty
in
let sub_m = aux l sub_m in
{m with size=m.size+1; subs=A.set ~mut:false m.subs x sub_m}
in
aux (split_idx i) m
let push x (v:_ t) : _ t = push_ v.size x v
let pop_ i (m:'a t) : 'a * 'a t =
let rec aux l m = match l with
| I_one x ->
assert (x+1 = A.length m.leaves); (* last one *)
let x = A.get m.leaves x in
x, {m with size=m.size-1; leaves=A.pop m.leaves}
| I_cons (x,tl) -> aux_remove_sub tl m x
and aux_remove_sub l m x =
let sub = A.get m.subs x in
let y, sub' = aux l sub in
if is_empty sub' then (
assert (i+1 = A.length m.subs); (* last one *)
y, {m with size=m.size-1; subs=A.pop m.subs}
) else (
y, {m with size=m.size-1; subs=A.set ~mut:false m.subs x sub}
)
in
aux (split_idx i) m
let pop_exn (v:'a t) : 'a * 'a t =
if v.size=0 then failwith "Fun_vec.pop_exn";
pop_ (v.size-1) v
let pop (v:'a t) : ('a * 'a t) option =
if v.size=0 then None else Some (pop_ (v.size-1) v)
let iteri ~f (m : 'a t) : unit =
(* basically, a 32-way BFS traversal.
The queue contains subtrees to explore, along with their high_idx_ offsets *)
let q : (int * 'a t) Queue.t = Queue.create() in
Queue.push (0,m) q;
while not (Queue.is_empty q) do
let high, m = Queue.pop q in
A.iteri (fun i x -> f (combine_idx high i) x) m.leaves;
A.iteri (fun i sub -> Queue.push (combine_idx i high, sub) q) m.subs;
done
let iteri_rev ~f (m : 'a t) : unit =
(* like {!iteri} but last element comes first *)
let rec aux high m =
A.iteri_rev (fun i sub -> aux (combine_idx i high) sub) m.subs;
(* only now, explore current leaves *)
A.iteri_rev (fun i x -> f (combine_idx high i) x) m.leaves;
in
aux 0 m
let foldi ~f ~x m =
let acc = ref x in
iteri m
~f:(fun i x -> acc := f !acc i x);
!acc
let foldi_rev ~f ~x m =
let acc = ref x in
iteri_rev m
~f:(fun i x -> acc := f !acc i x);
!acc
let iter ~f m = iteri ~f:(fun _ x -> f x) m
let fold ~f ~x m = foldi ~f:(fun acc _ x -> f acc x) ~x m
let fold_rev ~f ~x m = foldi_rev ~f:(fun acc _ x -> f acc x) ~x m
let rec map f m : _ t =
{ subs=A.map (map f) m.subs;
leaves=A.map f m.leaves;
size=m.size;
}
(*$QR
Q.(pair (fun1 Observable.int bool)(small_list int)) (fun (f,l) ->
let f = Q.Fn.apply f in
(List.map f l) = (of_list l |> map f |> to_list)
)
*)
let append a b =
if is_empty b then a
else fold ~f:(fun v x -> push x v) ~x:a b
(*$QR
Q.(pair (small_list int)(small_list int)) (fun (l1,l2) ->
(l1 @ l2) = (append (of_list l1)(of_list l2) |> to_list)
)
*)
let add_list v l = List.fold_left (fun v x -> push x v) v l
let of_list l = add_list empty l
let to_list m = fold_rev m ~f:(fun acc x -> x::acc) ~x:[]
(*$QR
Q.(small_list int) (fun l ->
l = to_list (of_list l))
*)
let add_seq v seq =
let v = ref v in
seq (fun x -> v := push x !v);
!v
let of_seq s = add_seq empty s
let to_seq m yield = iteri ~f:(fun _ v -> yield v) m
(*$Q
_listuniq (fun l -> \
(List.sort Pervasives.compare l) = \
(l |> Iter.of_list |> of_seq |> to_seq |> Iter.to_list \
|> List.sort Pervasives.compare) )
*)
let rec add_gen m g = match g() with
| None -> m
| Some x -> add_gen (push x m) g
let of_gen g = add_gen empty g
(* traverse the tree by increasing hash order, where the order compares
hashes lexicographically by A.length_log-wide chunks of bits,
least-significant chunks first *)
let to_gen m =
let q_cur : 'a Queue.t = Queue.create() in
let q_sub : 'a t Queue.t = Queue.create() in
Queue.push m q_sub;
let rec next() =
if not (Queue.is_empty q_cur) then (
Some (Queue.pop q_cur)
) else if not (Queue.is_empty q_sub) then (
let m = Queue.pop q_sub in
A.iter (fun x -> Queue.push x q_cur) m.leaves;
A.iter (fun sub -> Queue.push sub q_sub) m.subs;
next()
) else None
in next
(*$Q
_listuniq (fun l -> \
(List.sort Pervasives.compare l) = \
(l |> Gen.of_list |> of_gen |> to_gen |> Gen.to_list \
|> List.sort Pervasives.compare) )
*)
let choose m = to_gen m ()
(*$T
choose empty = None
choose (of_list [1,1; 2,2]) <> None
*)
let choose_exn m = match choose m with
| None -> raise Not_found
| Some (k,v) -> k, v
let pp ppv out m =
let first = ref true in
iter m
~f:(fun v ->
if !first then first := false else Format.fprintf out ";@ ";
ppv out v
)