mirror of
https://github.com/c-cube/ocaml-containers.git
synced 2025-12-06 03:05:28 -05:00
302 lines
7.1 KiB
OCaml
302 lines
7.1 KiB
OCaml
|
|
(*
|
|
copyright (c) 2013, simon cruanes
|
|
all rights reserved.
|
|
|
|
redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
|
|
redistributions of source code must retain the above copyright notice, this
|
|
list of conditions and the following disclaimer. redistributions in binary
|
|
form must reproduce the above copyright notice, this list of conditions and the
|
|
following disclaimer in the documentation and/or other materials provided with
|
|
the distribution.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*)
|
|
|
|
(** {2 Imperative Bitvectors} *)
|
|
|
|
let __width = Sys.word_size - 2
|
|
|
|
(* int with [n] ones *)
|
|
let rec __shift bv n =
|
|
if n = 0
|
|
then bv
|
|
else __shift ((bv lsl 1) lor 1) (n-1)
|
|
|
|
(* only ones *)
|
|
let __all_ones = __shift 0 __width
|
|
|
|
type t = {
|
|
mutable a : int array;
|
|
}
|
|
|
|
let empty () = { a = [| |] }
|
|
|
|
let create ~size default =
|
|
if size = 0 then { a = [| |] }
|
|
else begin
|
|
let n = if size mod __width = 0 then size / __width else (size / __width) + 1 in
|
|
let arr = if default
|
|
then Array.make n __all_ones
|
|
else Array.make n 0
|
|
in
|
|
(* adjust last bits *)
|
|
if default && (size mod __width) <> 0
|
|
then arr.(n-1) <- __shift 0 (size - (n-1) * __width);
|
|
{ a = arr }
|
|
end
|
|
|
|
(*$T
|
|
create ~size:17 true |> cardinal = 17
|
|
create ~size:32 true |> cardinal= 32
|
|
create ~size:132 true |> cardinal = 132
|
|
create ~size:200 false |> cardinal = 0
|
|
create ~size:29 true |> to_sorted_list = CCList.range 0 28
|
|
*)
|
|
|
|
let copy bv = { a=Array.copy bv.a; }
|
|
|
|
(*$Q
|
|
(Q.list Q.small_int) (fun l -> \
|
|
let bv = of_list l in to_list bv = to_list (copy bv))
|
|
*)
|
|
|
|
let length bv = Array.length bv.a
|
|
|
|
let resize bv len =
|
|
if len > Array.length bv.a
|
|
then begin
|
|
let a' = Array.make len 0 in
|
|
Array.blit bv.a 0 a' 0 (Array.length bv.a);
|
|
bv.a <- a'
|
|
end
|
|
|
|
(* count the 1 bits in [n]. See https://en.wikipedia.org/wiki/Hamming_weight *)
|
|
let __count_bits n =
|
|
let rec recurse count n =
|
|
if n = 0 then count else recurse (count+1) (n land (n-1))
|
|
in
|
|
if n < 0
|
|
then recurse 1 (n lsr 1) (* only on unsigned *)
|
|
else recurse 0 n
|
|
|
|
let cardinal bv =
|
|
let n = ref 0 in
|
|
for i = 0 to length bv - 1 do
|
|
n := !n + __count_bits bv.a.(i)
|
|
done;
|
|
!n
|
|
|
|
let is_empty bv =
|
|
try
|
|
for i = 0 to Array.length bv.a - 1 do
|
|
if bv.a.(i) <> 0 then raise Exit
|
|
done;
|
|
true
|
|
with Exit ->
|
|
false
|
|
|
|
let get bv i =
|
|
let n = i / __width in
|
|
if n < Array.length bv.a
|
|
then
|
|
let i = i - n * __width in
|
|
bv.a.(n) land (1 lsl i) <> 0
|
|
else false
|
|
|
|
let set bv i =
|
|
let n = i / __width in
|
|
if n >= Array.length bv.a
|
|
then resize bv (n+1);
|
|
let i = i - n * __width in
|
|
bv.a.(n) <- bv.a.(n) lor (1 lsl i)
|
|
|
|
(*$T
|
|
let bv = create ~size:3 false in set bv 0; get bv 0
|
|
let bv = create ~size:3 false in set bv 1; not (get bv 0)
|
|
*)
|
|
|
|
let reset bv i =
|
|
let n = i / __width in
|
|
if n >= Array.length bv.a
|
|
then resize bv (n+1);
|
|
let i = i - n * __width in
|
|
bv.a.(n) <- bv.a.(n) land (lnot (1 lsl i))
|
|
|
|
(*$T
|
|
let bv = create ~size:3 false in set bv 0; reset bv 0; not (get bv 0)
|
|
*)
|
|
|
|
let flip bv i =
|
|
let n = i / __width in
|
|
if n >= Array.length bv.a
|
|
then resize bv (n+1);
|
|
let i = i - n * __width in
|
|
bv.a.(n) <- bv.a.(n) lxor (1 lsl i)
|
|
|
|
let clear bv =
|
|
Array.iteri (fun i _ -> bv.a.(i) <- 0) bv.a
|
|
|
|
(*$T
|
|
let bv = create ~size:37 true in cardinal bv = 37 && (clear bv; cardinal bv= 0)
|
|
*)
|
|
|
|
let iter bv f =
|
|
let len = Array.length bv.a in
|
|
for n = 0 to len - 1 do
|
|
let j = __width * n in
|
|
for i = 0 to __width - 1 do
|
|
f (j+i) (bv.a.(n) land (1 lsl i) <> 0)
|
|
done
|
|
done
|
|
|
|
let iter_true bv f =
|
|
let len = Array.length bv.a in
|
|
for n = 0 to len - 1 do
|
|
let j = __width * n in
|
|
for i = 0 to __width - 1 do
|
|
if bv.a.(n) land (1 lsl i) <> 0
|
|
then f (j+i)
|
|
done
|
|
done
|
|
|
|
(*$T
|
|
of_list [1;5;7] |> iter_true |> CCSequence.to_list |> List.sort CCOrd.compare = [1;5;7]
|
|
*)
|
|
|
|
let to_list bv =
|
|
let l = ref [] in
|
|
iter_true bv (fun i -> l := i :: !l);
|
|
!l
|
|
|
|
let to_sorted_list bv =
|
|
List.rev (to_list bv)
|
|
|
|
let of_list l =
|
|
let size = List.fold_left max 0 l in
|
|
let bv = create ~size false in
|
|
List.iter (fun i -> set bv i) l;
|
|
bv
|
|
|
|
(*$T
|
|
of_list [1;32;64] |> CCFun.flip get 64
|
|
of_list [1;32;64] |> CCFun.flip get 32
|
|
of_list [1;31;63] |> CCFun.flip get 63
|
|
*)
|
|
|
|
exception FoundFirst of int
|
|
|
|
let first bv =
|
|
try
|
|
iter_true bv (fun i -> raise (FoundFirst i));
|
|
raise Not_found
|
|
with FoundFirst i ->
|
|
i
|
|
|
|
(*$T
|
|
of_list [50; 10; 17; 22; 3; 12] |> first = 3
|
|
*)
|
|
|
|
let filter bv p =
|
|
iter_true bv
|
|
(fun i -> if not (p i) then reset bv i)
|
|
|
|
(*$T
|
|
let bv = of_list [1;2;3;4;5;6;7] in filter bv (fun x->x mod 2=0); \
|
|
to_sorted_list bv = [2;4;6]
|
|
*)
|
|
|
|
let union_into ~into bv =
|
|
if length into < length bv
|
|
then resize into (length bv);
|
|
let len = Array.length bv.a in
|
|
for i = 0 to len - 1 do
|
|
into.a.(i) <- into.a.(i) lor bv.a.(i)
|
|
done
|
|
|
|
let union bv1 bv2 =
|
|
let bv = copy bv1 in
|
|
union_into ~into:bv bv2;
|
|
bv
|
|
|
|
(*$T
|
|
union (of_list [1;2;3;4;5]) (of_list [7;3;5;6]) |> to_sorted_list = CCList.range 1 7
|
|
*)
|
|
|
|
let inter_into ~into bv =
|
|
let n = min (length into) (length bv) in
|
|
for i = 0 to n - 1 do
|
|
into.a.(i) <- into.a.(i) land bv.a.(i)
|
|
done
|
|
|
|
let inter bv1 bv2 =
|
|
if length bv1 < length bv2
|
|
then
|
|
let bv = copy bv1 in
|
|
let () = inter_into ~into:bv bv2 in
|
|
bv
|
|
else
|
|
let bv = copy bv2 in
|
|
let () = inter_into ~into:bv bv1 in
|
|
bv
|
|
|
|
(*$T
|
|
inter (of_list [1;2;3;4]) (of_list [2;4;6;1]) |> to_sorted_list = [1;2;4]
|
|
*)
|
|
|
|
let select bv arr =
|
|
let l = ref [] in
|
|
begin try
|
|
iter_true bv
|
|
(fun i ->
|
|
if i >= Array.length arr
|
|
then raise Exit
|
|
else l := arr.(i) :: !l)
|
|
with Exit -> ()
|
|
end;
|
|
!l
|
|
|
|
let selecti bv arr =
|
|
let l = ref [] in
|
|
begin try
|
|
iter_true bv
|
|
(fun i ->
|
|
if i >= Array.length arr
|
|
then raise Exit
|
|
else l := (arr.(i), i) :: !l)
|
|
with Exit -> ()
|
|
end;
|
|
!l
|
|
|
|
(*$T
|
|
selecti (of_list [1;4;3]) [| 0;1;2;3;4;5;6;7;8 |] \
|
|
|> List.sort CCOrd.compare = [1, 1; 3,3; 4,4]
|
|
*)
|
|
|
|
type 'a sequence = ('a -> unit) -> unit
|
|
|
|
let to_seq bv k = iter_true bv k
|
|
|
|
let of_seq seq =
|
|
let l = ref [] and maxi = ref 0 in
|
|
seq (fun x -> l := x :: !l; maxi := max !maxi x);
|
|
let bv = create ~size:(!maxi+1) false in
|
|
List.iter (fun i -> set bv i) !l;
|
|
bv
|
|
|
|
(*$T
|
|
CCList.range 0 10 |> CCList.to_seq |> of_seq |> to_seq \
|
|
|> CCList.of_seq |> List.sort CCOrd.compare = CCList.range 0 10
|
|
*)
|
|
|