mirror of
https://github.com/c-cube/ocaml-containers.git
synced 2025-12-07 11:45:31 -05:00
Merge pull request #394 from c-cube/ccbv_bytes2
(continued) use bytes for CCBV
This commit is contained in:
commit
484aa3a1e7
12 changed files with 969 additions and 248 deletions
2
Makefile
2
Makefile
|
|
@ -6,7 +6,7 @@ build:
|
||||||
dune build @install -p $(PACKAGES)
|
dune build @install -p $(PACKAGES)
|
||||||
|
|
||||||
test: build
|
test: build
|
||||||
dune runtest --cache=disabled --no-buffer --force
|
dune runtest --display=quiet --cache=disabled --no-buffer --force
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
dune clean
|
dune clean
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,20 @@ let pow a b =
|
||||||
raise (Invalid_argument "pow: can't raise int to negative power")
|
raise (Invalid_argument "pow: can't raise int to negative power")
|
||||||
| b -> aux a b
|
| b -> aux a b
|
||||||
|
|
||||||
|
(* see {!CCInt.popcount} for more details *)
|
||||||
|
let[@inline] popcount (b : t) : int =
|
||||||
|
let m1 = 0x55555555l in
|
||||||
|
let m2 = 0x33333333l in
|
||||||
|
let m4 = 0x0f0f0f0fl in
|
||||||
|
|
||||||
|
let b = sub b (logand (shift_right_logical b 1) m1) in
|
||||||
|
let b = add (logand b m2) (logand (shift_right_logical b 2) m2) in
|
||||||
|
let b = logand (add b (shift_right_logical b 4)) m4 in
|
||||||
|
let b = add b (shift_right_logical b 8) in
|
||||||
|
let b = add b (shift_right_logical b 16) in
|
||||||
|
let b = logand b 0x7fl in
|
||||||
|
to_int b
|
||||||
|
|
||||||
let floor_div a n =
|
let floor_div a n =
|
||||||
if compare a 0l < 0 && compare n 0l >= 0 then
|
if compare a 0l < 0 && compare n 0l >= 0 then
|
||||||
sub (div (add a 1l) n) 1l
|
sub (div (add a 1l) n) 1l
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,10 @@ val pow : t -> t -> t
|
||||||
Raises [Invalid_argument] if [x = y = 0] or [y] < 0.
|
Raises [Invalid_argument] if [x = y = 0] or [y] < 0.
|
||||||
@since 0.11 *)
|
@since 0.11 *)
|
||||||
|
|
||||||
|
val popcount : t -> int
|
||||||
|
(** Number of bits set to 1.
|
||||||
|
@since NEXT_RELEASE *)
|
||||||
|
|
||||||
val floor_div : t -> t -> t
|
val floor_div : t -> t -> t
|
||||||
(** [floor_div x n] is integer division rounding towards negative infinity.
|
(** [floor_div x n] is integer division rounding towards negative infinity.
|
||||||
It satisfies [x = m * floor_div x n + rem x n].
|
It satisfies [x = m * floor_div x n + rem x n].
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,21 @@ let max : t -> t -> t = Stdlib.max
|
||||||
let hash x = Stdlib.abs (to_int x)
|
let hash x = Stdlib.abs (to_int x)
|
||||||
let sign i = compare i zero
|
let sign i = compare i zero
|
||||||
|
|
||||||
|
(* see {!CCInt.popcount} for more details *)
|
||||||
|
let[@inline] popcount (b : t) : int =
|
||||||
|
let m1 = 0x5555555555555555L in
|
||||||
|
let m2 = 0x3333333333333333L in
|
||||||
|
let m4 = 0x0f0f0f0f0f0f0f0fL in
|
||||||
|
|
||||||
|
let b = sub b (logand (shift_right_logical b 1) m1) in
|
||||||
|
let b = add (logand b m2) (logand (shift_right_logical b 2) m2) in
|
||||||
|
let b = logand (add b (shift_right_logical b 4)) m4 in
|
||||||
|
let b = add b (shift_right_logical b 8) in
|
||||||
|
let b = add b (shift_right_logical b 16) in
|
||||||
|
let b = add b (shift_right_logical b 32) in
|
||||||
|
let b = logand b 0x7fL in
|
||||||
|
to_int b
|
||||||
|
|
||||||
let pow a b =
|
let pow a b =
|
||||||
let rec aux acc = function
|
let rec aux acc = function
|
||||||
| 1L -> acc
|
| 1L -> acc
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,10 @@ val hash : t -> int
|
||||||
(** [hash x] computes the hash of [x].
|
(** [hash x] computes the hash of [x].
|
||||||
Like {!Stdlib.abs (to_int x)}. *)
|
Like {!Stdlib.abs (to_int x)}. *)
|
||||||
|
|
||||||
|
val popcount : t -> int
|
||||||
|
(** Number of bits set to 1.
|
||||||
|
@since NEXT_RELEASE *)
|
||||||
|
|
||||||
val sign : t -> int
|
val sign : t -> int
|
||||||
(** [sign x] return [0] if [x = 0], [-1] if [x < 0] and [1] if [x > 0].
|
(** [sign x] return [0] if [x = 0], [-1] if [x < 0] and [1] if [x > 0].
|
||||||
Same as [compare x zero].
|
Same as [compare x zero].
|
||||||
|
|
|
||||||
394
src/data/CCBV.ml
394
src/data/CCBV.ml
|
|
@ -1,186 +1,271 @@
|
||||||
(** {2 Imperative Bitvectors} *)
|
let width_ = 8
|
||||||
|
|
||||||
(* TODO: move to [bytes] and replace all [mod] and [/] with bitshifts
|
(* Helper functions *)
|
||||||
because width_=8 *)
|
let[@inline] get_ b i = Char.code (Bytes.get b i)
|
||||||
|
let[@inline] unsafe_get_ b i = Char.code (Bytes.unsafe_get b i)
|
||||||
|
let[@inline] set_ b i v = Bytes.set b i (Char.unsafe_chr v)
|
||||||
|
let[@inline] unsafe_set_ b i v = Bytes.unsafe_set b i (Char.unsafe_chr v)
|
||||||
|
let[@inline] mod_ n = n land 0b111
|
||||||
|
let[@inline] div_ n = n lsr 3
|
||||||
|
let[@inline] mul_ n = n lsl 3
|
||||||
|
let zero = Char.unsafe_chr 0
|
||||||
|
|
||||||
let width_ = Sys.word_size - 1
|
(* 0b11111111 *)
|
||||||
|
let all_ones_ = Char.unsafe_chr ((1 lsl width_) - 1)
|
||||||
|
let () = assert (all_ones_ = Char.chr 0b1111_1111)
|
||||||
|
|
||||||
(** We use OCamls ints to store the bits. We index them from the
|
(* [lsb_mask_ n] is [0b111111] with [n] ones. *)
|
||||||
least significant bit. We create masks to zero out the most significant
|
let[@inline] __lsb_mask n = (1 lsl n) - 1
|
||||||
bits that aren't used to store values. This is necessary when we are
|
|
||||||
constructing or negating a bit vector. *)
|
|
||||||
let lsb_masks_ =
|
|
||||||
let a = Array.make (width_ + 1) 0 in
|
|
||||||
for i = 1 to width_ do
|
|
||||||
a.(i) <- a.(i - 1) lor (1 lsl (i - 1))
|
|
||||||
done;
|
|
||||||
a
|
|
||||||
|
|
||||||
let all_ones_ = lsb_masks_.(width_)
|
(*
|
||||||
|
from https://en.wikipedia.org/wiki/Hamming_weight
|
||||||
|
|
||||||
(* count the 1 bits in [n]. See https://en.wikipedia.org/wiki/Hamming_weight *)
|
//This uses fewer arithmetic operations than any other known
|
||||||
let count_bits_ n =
|
//implementation on machines with slow multiplication.
|
||||||
let rec recurse count n =
|
//It uses 17 arithmetic operations.
|
||||||
if n = 0 then
|
int popcount_2(uint64_t x) {
|
||||||
count
|
x -= (x >> 1) & m1; //put count of each 2 bits into those 2 bits
|
||||||
else
|
x = (x & m2) + ((x >> 2) & m2); //put count of each 4 bits into those 4 bits
|
||||||
recurse (count + 1) (n land (n - 1))
|
x = (x + (x >> 4)) & m4; //put count of each 8 bits into those 8 bits
|
||||||
in
|
|
||||||
recurse 0 n
|
|
||||||
|
|
||||||
(* Can I access the "private" members in testing? $Q
|
// not necessary for int8
|
||||||
(Q.int_bound (Sys.word_size - 1)) (fun i -> count_bits_ lsb_masks_.(i) = i)
|
// x += x >> 8; //put count of each 16 bits into their lowest 8 bits
|
||||||
|
// x += x >> 16; //put count of each 32 bits into their lowest 8 bits
|
||||||
|
// x += x >> 32; //put count of each 64 bits into their lowest 8 bits
|
||||||
|
|
||||||
|
return x & 0x7f;
|
||||||
|
}
|
||||||
|
|
||||||
|
m1 = 0x5555555555555555
|
||||||
|
m2 = 0x3333333333333333
|
||||||
|
m4 = 0x0f0f0f0f0f0f0f0f
|
||||||
*)
|
*)
|
||||||
|
let[@inline] __popcount8 (b : int) : int =
|
||||||
|
let m1 = 0x55 in
|
||||||
|
let m2 = 0x33 in
|
||||||
|
let m4 = 0x0f in
|
||||||
|
|
||||||
type t = { mutable a: int array; mutable size: int }
|
let b = b - ((b lsr 1) land m1) in
|
||||||
|
let b = (b land m2) + ((b lsr 2) land m2) in
|
||||||
|
let b = (b + (b lsr 4)) land m4 in
|
||||||
|
b land 0x7f
|
||||||
|
|
||||||
|
(*
|
||||||
|
invariants for [v:t]:
|
||||||
|
|
||||||
|
- [Bytes.length v.b >= div_ v.size] (enough storage)
|
||||||
|
- all bits above [size] are 0 in [v.b]
|
||||||
|
*)
|
||||||
|
type t = { mutable b: bytes; mutable size: int }
|
||||||
|
|
||||||
let length t = t.size
|
let length t = t.size
|
||||||
let empty () = { a = [||]; size = 0 }
|
let empty () = { b = Bytes.empty; size = 0 }
|
||||||
|
|
||||||
let array_length_of_size size =
|
let bytes_length_of_size size =
|
||||||
if size mod width_ = 0 then
|
if mod_ size = 0 then
|
||||||
size / width_
|
div_ size
|
||||||
else
|
else
|
||||||
(size / width_) + 1
|
div_ size + 1
|
||||||
|
|
||||||
let create ~size default =
|
let create ~size default : t =
|
||||||
if size = 0 then
|
if size = 0 then
|
||||||
{ a = [||]; size }
|
empty ()
|
||||||
else (
|
else (
|
||||||
let n = array_length_of_size size in
|
let n = bytes_length_of_size size in
|
||||||
let a =
|
let b =
|
||||||
if default then
|
if default then
|
||||||
Array.make n all_ones_
|
Bytes.make n all_ones_
|
||||||
else
|
else
|
||||||
Array.make n 0
|
Bytes.make n zero
|
||||||
in
|
in
|
||||||
(* adjust last bits *)
|
(* adjust last bits *)
|
||||||
let r = size mod width_ in
|
let r = mod_ size in
|
||||||
if default && r <> 0 then Array.unsafe_set a (n - 1) lsb_masks_.(r);
|
if default && r <> 0 then unsafe_set_ b (n - 1) (__lsb_mask r);
|
||||||
{ a; size }
|
{ b; size }
|
||||||
)
|
)
|
||||||
|
|
||||||
let copy bv = { bv with a = Array.copy bv.a }
|
let copy bv = { bv with b = Bytes.sub bv.b 0 (bytes_length_of_size bv.size) }
|
||||||
let capacity bv = width_ * Array.length bv.a
|
let[@inline] capacity bv = mul_ (Bytes.length bv.b)
|
||||||
|
|
||||||
|
(* call [f i width(byte[i]) (byte[i])] on each byte.
|
||||||
|
The last byte might have a width of less than 8. *)
|
||||||
|
let iter_bytes_ (b : t) ~f : unit =
|
||||||
|
for n = 0 to div_ b.size - 1 do
|
||||||
|
f (mul_ n) width_ (unsafe_get_ b.b n)
|
||||||
|
done;
|
||||||
|
let r = mod_ b.size in
|
||||||
|
if r <> 0 then (
|
||||||
|
let last = div_ b.size in
|
||||||
|
f (mul_ last) r (__lsb_mask r land unsafe_get_ b.b last)
|
||||||
|
)
|
||||||
|
|
||||||
|
(* set [byte[i]] to [f(byte[i])] *)
|
||||||
|
let map_bytes_ (b : t) ~f : unit =
|
||||||
|
for n = 0 to div_ b.size - 1 do
|
||||||
|
unsafe_set_ b.b n (f (unsafe_get_ b.b n))
|
||||||
|
done;
|
||||||
|
let r = mod_ b.size in
|
||||||
|
if r <> 0 then (
|
||||||
|
let last = div_ b.size in
|
||||||
|
let mask = __lsb_mask r in
|
||||||
|
unsafe_set_ b.b last (mask land f (mask land unsafe_get_ b.b last))
|
||||||
|
)
|
||||||
|
|
||||||
let cardinal bv =
|
let cardinal bv =
|
||||||
if bv.size = 0 then
|
if bv.size = 0 then
|
||||||
0
|
0
|
||||||
else (
|
else (
|
||||||
let n = ref 0 in
|
let n = ref 0 in
|
||||||
for i = 0 to Array.length bv.a - 1 do
|
iter_bytes_ bv ~f:(fun _ _ b -> n := !n + __popcount8 b);
|
||||||
n := !n + count_bits_ bv.a.(i) (* MSB of last element are all 0 *)
|
|
||||||
done;
|
|
||||||
!n
|
!n
|
||||||
)
|
)
|
||||||
|
|
||||||
let really_resize_ bv ~desired ~current size =
|
let really_resize_ bv ~desired ~current size =
|
||||||
let a' = Array.make desired 0 in
|
bv.size <- size;
|
||||||
Array.blit bv.a 0 a' 0 current;
|
if desired <> current then (
|
||||||
bv.a <- a';
|
let b = Bytes.make desired zero in
|
||||||
bv.size <- size
|
Bytes.blit bv.b 0 b 0 (min desired current);
|
||||||
|
bv.b <- b
|
||||||
|
)
|
||||||
|
|
||||||
let grow_ bv size =
|
(* set bits above [n] to 0 *)
|
||||||
if size <= capacity bv (* within capacity *) then
|
let[@inline never] clear_bits_above_ bv top =
|
||||||
bv.size <- size
|
let n = div_ top in
|
||||||
else (
|
let j = mod_ top in
|
||||||
|
Bytes.fill bv.b (n + 1)
|
||||||
|
(bytes_length_of_size bv.size - n - 1)
|
||||||
|
(Char.unsafe_chr 0);
|
||||||
|
unsafe_set_ bv.b n (unsafe_get_ bv.b n land __lsb_mask j)
|
||||||
|
|
||||||
|
let[@inline never] grow_to_at_least_real_ bv size =
|
||||||
(* beyond capacity *)
|
(* beyond capacity *)
|
||||||
let desired = array_length_of_size size in
|
let current = Bytes.length bv.b in
|
||||||
let current = Array.length bv.a in
|
let desired = bytes_length_of_size size in
|
||||||
|
let desired =
|
||||||
|
min Sys.max_string_length (max desired (current + (current / 2)))
|
||||||
|
in
|
||||||
assert (desired > current);
|
assert (desired > current);
|
||||||
really_resize_ bv ~desired ~current size
|
really_resize_ bv ~desired ~current size
|
||||||
|
|
||||||
|
let grow_to_at_least_ bv size =
|
||||||
|
if size <= capacity bv then
|
||||||
|
(* within capacity *)
|
||||||
|
bv.size <- size
|
||||||
|
else
|
||||||
|
(* resize. This is a separate function so it's easier to
|
||||||
|
inline the happy path. *)
|
||||||
|
grow_to_at_least_real_ bv size
|
||||||
|
|
||||||
|
let shrink_ bv size =
|
||||||
|
assert (size <= bv.size);
|
||||||
|
if size < bv.size then (
|
||||||
|
let desired = bytes_length_of_size size in
|
||||||
|
let current = Bytes.length bv.b in
|
||||||
|
clear_bits_above_ bv size;
|
||||||
|
really_resize_ bv ~desired ~current size
|
||||||
)
|
)
|
||||||
|
|
||||||
let shrink_ bv size =
|
|
||||||
let desired = array_length_of_size size in
|
|
||||||
let current = Array.length bv.a in
|
|
||||||
really_resize_ bv ~desired ~current size
|
|
||||||
|
|
||||||
let resize bv size =
|
let resize bv size =
|
||||||
if size < 0 then invalid_arg "resize: negative size";
|
if size < 0 then invalid_arg "resize: negative size";
|
||||||
if size < bv.size (* shrink *) then
|
if size < bv.size then (
|
||||||
|
clear_bits_above_ bv size;
|
||||||
|
bv.size <- size
|
||||||
|
) else if size > bv.size then
|
||||||
|
grow_to_at_least_ bv size
|
||||||
|
|
||||||
|
let resize_minimize_memory bv size =
|
||||||
|
if size < 0 then invalid_arg "resize: negative size";
|
||||||
|
if size < bv.size then
|
||||||
shrink_ bv size
|
shrink_ bv size
|
||||||
else if size = bv.size then
|
else if size > bv.size then
|
||||||
()
|
grow_to_at_least_ bv size
|
||||||
else
|
|
||||||
grow_ bv size
|
|
||||||
|
|
||||||
let is_empty bv =
|
let is_empty bv =
|
||||||
|
bv.size = 0
|
||||||
|
||
|
||||||
try
|
try
|
||||||
for i = 0 to Array.length bv.a - 1 do
|
for i = 0 to bytes_length_of_size bv.size - 1 do
|
||||||
if bv.a.(i) <> 0 then raise Exit (* MSB of last element are all 0 *)
|
if unsafe_get_ bv.b i <> 0 then raise_notrace Exit
|
||||||
done;
|
done;
|
||||||
true
|
true
|
||||||
with Exit -> false
|
with Exit -> false
|
||||||
|
|
||||||
let get bv i =
|
let[@inline] get bv i =
|
||||||
if i < 0 then invalid_arg "get: negative index";
|
if i < 0 then invalid_arg "get: negative index";
|
||||||
let n = i / width_ in
|
let idx_bucket = div_ i in
|
||||||
let i = i mod width_ in
|
let idx_in_byte = mod_ i in
|
||||||
if n < Array.length bv.a then
|
if idx_bucket < Bytes.length bv.b then
|
||||||
Array.unsafe_get bv.a n land (1 lsl i) <> 0
|
unsafe_get_ bv.b idx_bucket land (1 lsl idx_in_byte) <> 0
|
||||||
else
|
else
|
||||||
false
|
false
|
||||||
|
|
||||||
let set bv i =
|
let[@inline] set bv i =
|
||||||
if i < 0 then
|
if i < 0 then
|
||||||
invalid_arg "set: negative index"
|
invalid_arg "set: negative index"
|
||||||
else (
|
else (
|
||||||
let n = i / width_ in
|
let idx_bucket = div_ i in
|
||||||
let j = i mod width_ in
|
let idx_in_byte = mod_ i in
|
||||||
if i >= bv.size then grow_ bv (i + 1);
|
if i >= bv.size then grow_to_at_least_ bv (i + 1);
|
||||||
Array.unsafe_set bv.a n (Array.unsafe_get bv.a n lor (1 lsl j))
|
unsafe_set_ bv.b idx_bucket
|
||||||
|
(unsafe_get_ bv.b idx_bucket lor (1 lsl idx_in_byte))
|
||||||
)
|
)
|
||||||
|
|
||||||
let reset bv i =
|
let init size f : t =
|
||||||
|
let v = create ~size false in
|
||||||
|
for i = 0 to size - 1 do
|
||||||
|
if f i then set v i
|
||||||
|
done;
|
||||||
|
v
|
||||||
|
|
||||||
|
let[@inline] reset bv i =
|
||||||
if i < 0 then
|
if i < 0 then
|
||||||
invalid_arg "reset: negative index"
|
invalid_arg "reset: negative index"
|
||||||
else (
|
else (
|
||||||
let n = i / width_ in
|
let n = div_ i in
|
||||||
let j = i mod width_ in
|
let j = mod_ i in
|
||||||
if i >= bv.size then grow_ bv (i + 1);
|
if i >= bv.size then grow_to_at_least_ bv (i + 1);
|
||||||
Array.unsafe_set bv.a n (Array.unsafe_get bv.a n land lnot (1 lsl j))
|
unsafe_set_ bv.b n (unsafe_get_ bv.b n land lnot (1 lsl j))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
let[@inline] set_bool bv i b =
|
||||||
|
if b then
|
||||||
|
set bv i
|
||||||
|
else
|
||||||
|
reset bv i
|
||||||
|
|
||||||
let flip bv i =
|
let flip bv i =
|
||||||
if i < 0 then
|
if i < 0 then
|
||||||
invalid_arg "reset: negative index"
|
invalid_arg "reset: negative index"
|
||||||
else (
|
else (
|
||||||
let n = i / width_ in
|
let n = div_ i in
|
||||||
let j = i mod width_ in
|
let j = mod_ i in
|
||||||
if i >= bv.size then grow_ bv (i + 1);
|
if i >= bv.size then grow_to_at_least_ bv (i + 1);
|
||||||
Array.unsafe_set bv.a n (Array.unsafe_get bv.a n lxor (1 lsl j))
|
unsafe_set_ bv.b n (unsafe_get_ bv.b n lxor (1 lsl j))
|
||||||
)
|
)
|
||||||
|
|
||||||
let clear bv = Array.fill bv.a 0 (Array.length bv.a) 0
|
let clear bv = Bytes.fill bv.b 0 (Bytes.length bv.b) zero
|
||||||
let equal x y : bool = x.size = y.size && x.a = y.a
|
|
||||||
|
let clear_and_shrink bv =
|
||||||
|
clear bv;
|
||||||
|
bv.size <- 0
|
||||||
|
|
||||||
|
let equal_bytes_ size b1 b2 =
|
||||||
|
try
|
||||||
|
for i = 0 to bytes_length_of_size size - 1 do
|
||||||
|
if Bytes.get b1 i <> Bytes.get b2 i then raise_notrace Exit
|
||||||
|
done;
|
||||||
|
true
|
||||||
|
with Exit -> false
|
||||||
|
|
||||||
|
let equal x y : bool = x.size = y.size && equal_bytes_ x.size x.b y.b
|
||||||
|
|
||||||
let iter bv f =
|
let iter bv f =
|
||||||
let len = array_length_of_size bv.size in
|
iter_bytes_ bv ~f:(fun off width_n word_n ->
|
||||||
assert (len <= Array.length bv.a);
|
for i = 0 to width_n - 1 do
|
||||||
for n = 0 to len - 2 do
|
f (off + i) (word_n land (1 lsl i) <> 0)
|
||||||
let j = width_ * n in
|
done)
|
||||||
let word_n = Array.unsafe_get bv.a n in
|
|
||||||
for i = 0 to width_ - 1 do
|
|
||||||
f (j + i) (word_n land (1 lsl i) <> 0)
|
|
||||||
done
|
|
||||||
done;
|
|
||||||
if bv.size > 0 then (
|
|
||||||
let j = width_ * (len - 1) in
|
|
||||||
let r = bv.size mod width_ in
|
|
||||||
let final_length =
|
|
||||||
if r = 0 then
|
|
||||||
width_
|
|
||||||
else
|
|
||||||
r
|
|
||||||
in
|
|
||||||
let final_word = Array.unsafe_get bv.a (len - 1) in
|
|
||||||
for i = 0 to final_length - 1 do
|
|
||||||
f (j + i) (final_word land (1 lsl i) <> 0)
|
|
||||||
done
|
|
||||||
)
|
|
||||||
|
|
||||||
let[@inline] iter_true bv f =
|
let iter_true bv f =
|
||||||
iter bv (fun i b ->
|
iter bv (fun i b ->
|
||||||
if b then
|
if b then
|
||||||
f i
|
f i
|
||||||
|
|
@ -196,7 +281,11 @@ let to_sorted_list bv = List.rev (to_list bv)
|
||||||
|
|
||||||
(* Interpret these as indices. *)
|
(* Interpret these as indices. *)
|
||||||
let of_list l =
|
let of_list l =
|
||||||
let size = List.fold_left max 0 l + 1 in
|
let size =
|
||||||
|
match l with
|
||||||
|
| [] -> 0
|
||||||
|
| _ -> List.fold_left max 0 l + 1
|
||||||
|
in
|
||||||
let bv = create ~size false in
|
let bv = create ~size false in
|
||||||
List.iter (fun i -> set bv i) l;
|
List.iter (fun i -> set bv i) l;
|
||||||
bv
|
bv
|
||||||
|
|
@ -205,43 +294,28 @@ exception FoundFirst of int
|
||||||
|
|
||||||
let first_exn bv =
|
let first_exn bv =
|
||||||
try
|
try
|
||||||
iter_true bv (fun i -> raise (FoundFirst i));
|
iter_true bv (fun i -> raise_notrace (FoundFirst i));
|
||||||
raise Not_found
|
raise Not_found
|
||||||
with FoundFirst i -> i
|
with FoundFirst i -> i
|
||||||
|
|
||||||
let first bv = try Some (first_exn bv) with Not_found -> None
|
let first bv = try Some (first_exn bv) with Not_found -> None
|
||||||
let filter bv p = iter_true bv (fun i -> if not (p i) then reset bv i)
|
let filter bv p = iter_true bv (fun i -> if not (p i) then reset bv i)
|
||||||
|
let negate_self bv = map_bytes_ bv ~f:(fun b -> lnot b)
|
||||||
|
|
||||||
let negate_self b =
|
let negate a =
|
||||||
let len = Array.length b.a in
|
let b = copy a in
|
||||||
for n = 0 to len - 1 do
|
negate_self b;
|
||||||
Array.unsafe_set b.a n (lnot (Array.unsafe_get b.a n))
|
b
|
||||||
done;
|
|
||||||
let r = b.size mod width_ in
|
|
||||||
if r <> 0 then (
|
|
||||||
let l = Array.length b.a - 1 in
|
|
||||||
Array.unsafe_set b.a l (lsb_masks_.(r) land Array.unsafe_get b.a l)
|
|
||||||
)
|
|
||||||
|
|
||||||
let negate b =
|
|
||||||
let a = Array.map lnot b.a in
|
|
||||||
let r = b.size mod width_ in
|
|
||||||
if r <> 0 then (
|
|
||||||
let l = Array.length b.a - 1 in
|
|
||||||
Array.unsafe_set a l (lsb_masks_.(r) land Array.unsafe_get a l)
|
|
||||||
);
|
|
||||||
{ a; size = b.size }
|
|
||||||
|
|
||||||
let union_into_no_resize_ ~into bv =
|
let union_into_no_resize_ ~into bv =
|
||||||
assert (Array.length into.a >= Array.length bv.a);
|
assert (Bytes.length into.b >= bytes_length_of_size bv.size);
|
||||||
for i = 0 to Array.length bv.a - 1 do
|
for i = 0 to bytes_length_of_size bv.size - 1 do
|
||||||
Array.unsafe_set into.a i
|
unsafe_set_ into.b i (unsafe_get_ into.b i lor unsafe_get_ bv.b i)
|
||||||
(Array.unsafe_get into.a i lor Array.unsafe_get bv.a i)
|
|
||||||
done
|
done
|
||||||
|
|
||||||
(* Underlying size grows for union. *)
|
(* Underlying size grows for union. *)
|
||||||
let union_into ~into bv =
|
let union_into ~into bv =
|
||||||
if into.size < bv.size then grow_ into bv.size;
|
if into.size < bv.size then grow_to_at_least_ into bv.size;
|
||||||
union_into_no_resize_ ~into bv
|
union_into_no_resize_ ~into bv
|
||||||
|
|
||||||
(* To avoid potentially 2 passes, figure out what we need to copy. *)
|
(* To avoid potentially 2 passes, figure out what we need to copy. *)
|
||||||
|
|
@ -257,10 +331,9 @@ let union b1 b2 =
|
||||||
)
|
)
|
||||||
|
|
||||||
let inter_into_no_resize_ ~into bv =
|
let inter_into_no_resize_ ~into bv =
|
||||||
assert (Array.length into.a <= Array.length bv.a);
|
assert (into.size <= bv.size);
|
||||||
for i = 0 to Array.length into.a - 1 do
|
for i = 0 to bytes_length_of_size into.size - 1 do
|
||||||
Array.unsafe_set into.a i
|
unsafe_set_ into.b i (unsafe_get_ into.b i land unsafe_get_ bv.b i)
|
||||||
(Array.unsafe_get into.a i land Array.unsafe_get bv.a i)
|
|
||||||
done
|
done
|
||||||
|
|
||||||
(* Underlying size shrinks for inter. *)
|
(* Underlying size shrinks for inter. *)
|
||||||
|
|
@ -279,13 +352,12 @@ let inter b1 b2 =
|
||||||
into
|
into
|
||||||
)
|
)
|
||||||
|
|
||||||
(* Underlying size depends on the 'in_' set for diff, so we don't change
|
(* Underlying size depends on the [in_] set for diff, so we don't change
|
||||||
it's size! *)
|
its size! *)
|
||||||
let diff_into ~into bv =
|
let diff_into ~into bv =
|
||||||
let n = min (Array.length into.a) (Array.length bv.a) in
|
let n = min (Bytes.length into.b) (Bytes.length bv.b) in
|
||||||
for i = 0 to n - 1 do
|
for i = 0 to n - 1 do
|
||||||
Array.unsafe_set into.a i
|
unsafe_set_ into.b i (unsafe_get_ into.b i land lnot (unsafe_get_ bv.b i))
|
||||||
(Array.unsafe_get into.a i land lnot (Array.unsafe_get bv.a i))
|
|
||||||
done
|
done
|
||||||
|
|
||||||
let diff in_ not_in =
|
let diff in_ not_in =
|
||||||
|
|
@ -298,7 +370,7 @@ let select bv arr =
|
||||||
(try
|
(try
|
||||||
iter_true bv (fun i ->
|
iter_true bv (fun i ->
|
||||||
if i >= Array.length arr then
|
if i >= Array.length arr then
|
||||||
raise Exit
|
raise_notrace Exit
|
||||||
else
|
else
|
||||||
l := arr.(i) :: !l)
|
l := arr.(i) :: !l)
|
||||||
with Exit -> ());
|
with Exit -> ());
|
||||||
|
|
@ -309,7 +381,7 @@ let selecti bv arr =
|
||||||
(try
|
(try
|
||||||
iter_true bv (fun i ->
|
iter_true bv (fun i ->
|
||||||
if i >= Array.length arr then
|
if i >= Array.length arr then
|
||||||
raise Exit
|
raise_notrace Exit
|
||||||
else
|
else
|
||||||
l := (arr.(i), i) :: !l)
|
l := (arr.(i), i) :: !l)
|
||||||
with Exit -> ());
|
with Exit -> ());
|
||||||
|
|
@ -338,4 +410,24 @@ let pp out bv =
|
||||||
'0'));
|
'0'));
|
||||||
Format.pp_print_string out "}"
|
Format.pp_print_string out "}"
|
||||||
|
|
||||||
let __to_word_l bv = Array.to_list bv.a
|
module Internal_ = struct
|
||||||
|
let __to_word_l bv =
|
||||||
|
let l = ref [] in
|
||||||
|
Bytes.iter (fun c -> l := c :: !l) bv.b;
|
||||||
|
List.rev !l
|
||||||
|
|
||||||
|
let __popcount8 = __popcount8
|
||||||
|
let __lsb_mask = __lsb_mask
|
||||||
|
|
||||||
|
let __check_invariant self =
|
||||||
|
let n = div_ self.size in
|
||||||
|
let j = mod_ self.size in
|
||||||
|
assert (Bytes.length self.b >= n);
|
||||||
|
if j > 0 then
|
||||||
|
assert (
|
||||||
|
let c = get_ self.b n in
|
||||||
|
c land __lsb_mask j = c);
|
||||||
|
for i = n + 1 to Bytes.length self.b - 1 do
|
||||||
|
assert (get_ self.b i = 0)
|
||||||
|
done
|
||||||
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
(* This file is free software, part of containers. See file "license" for more details. *)
|
(** Imperative Bitvectors.
|
||||||
|
|
||||||
(** Imperative Bitvectors
|
A bitvector is stored in some form of internal array (on the heap).
|
||||||
|
Is it a bit similar to a more storage-efficient version of [bool
|
||||||
|
CCVector.vector], with additional operations.
|
||||||
|
|
||||||
{b BREAKING CHANGES} since 1.2:
|
{b BREAKING CHANGES} since 1.2:
|
||||||
size is now stored along with the bitvector. Some functions have
|
size is now stored along with the bitvector. Some functions have
|
||||||
|
|
@ -15,10 +17,16 @@ type t
|
||||||
(** A resizable bitvector *)
|
(** A resizable bitvector *)
|
||||||
|
|
||||||
val empty : unit -> t
|
val empty : unit -> t
|
||||||
(** Empty bitvector. *)
|
(** Empty bitvector. Length is 0. *)
|
||||||
|
|
||||||
val create : size:int -> bool -> t
|
val create : size:int -> bool -> t
|
||||||
(** Create a bitvector of given size, with given default value. *)
|
(** Create a bitvector of given size, with given default value.
|
||||||
|
Length of result is [size]. *)
|
||||||
|
|
||||||
|
val init : int -> (int -> bool) -> t
|
||||||
|
(** [init len f] initializes a bitvector of length [len], where bit [i]
|
||||||
|
is true iff [f i] is.
|
||||||
|
@since NEXT_RELEASE *)
|
||||||
|
|
||||||
val copy : t -> t
|
val copy : t -> t
|
||||||
(** Copy of bitvector. *)
|
(** Copy of bitvector. *)
|
||||||
|
|
@ -38,10 +46,16 @@ val capacity : t -> int
|
||||||
@since 1.2 *)
|
@since 1.2 *)
|
||||||
|
|
||||||
val resize : t -> int -> unit
|
val resize : t -> int -> unit
|
||||||
(** Resize the BV so that it has the specified length. This can grow or shrink
|
(** Resize the BV so that it has the specified length. This can grow
|
||||||
the underlying bitvector.
|
the underlying array, but it will not shrink it, to minimize
|
||||||
|
memory traffic.
|
||||||
|
@raise Invalid_argument on negative sizes. *)
|
||||||
|
|
||||||
@raise Invalid_arg on negative sizes. *)
|
val resize_minimize_memory : t -> int -> unit
|
||||||
|
(** Same as {!resize}, but this can also shrink the underlying
|
||||||
|
array if this reduces the size.
|
||||||
|
@raise Invalid_argument on negative sizes.
|
||||||
|
@since NEXT_RELEASE *)
|
||||||
|
|
||||||
val is_empty : t -> bool
|
val is_empty : t -> bool
|
||||||
(** Are there any true bits? *)
|
(** Are there any true bits? *)
|
||||||
|
|
@ -55,11 +69,19 @@ val get : t -> int -> bool
|
||||||
val reset : t -> int -> unit
|
val reset : t -> int -> unit
|
||||||
(** Set i-th bit to 0, extending the bitvector if needed. *)
|
(** Set i-th bit to 0, extending the bitvector if needed. *)
|
||||||
|
|
||||||
|
val set_bool : t -> int -> bool -> unit
|
||||||
|
(** Set or reset [i]-th bit.
|
||||||
|
@since NEXT_RELEASE *)
|
||||||
|
|
||||||
val flip : t -> int -> unit
|
val flip : t -> int -> unit
|
||||||
(** Flip i-th bit, extending the bitvector if needed. *)
|
(** Flip i-th bit, extending the bitvector if needed. *)
|
||||||
|
|
||||||
val clear : t -> unit
|
val clear : t -> unit
|
||||||
(** Set every bit to 0. *)
|
(** Set every bit to 0. Does not change the length. *)
|
||||||
|
|
||||||
|
val clear_and_shrink : t -> unit
|
||||||
|
(** Set every bit to 0, and set length to 0.
|
||||||
|
@since NEXT_RELEASE *)
|
||||||
|
|
||||||
val iter : t -> (int -> bool -> unit) -> unit
|
val iter : t -> (int -> bool -> unit) -> unit
|
||||||
(** Iterate on all bits. *)
|
(** Iterate on all bits. *)
|
||||||
|
|
@ -92,15 +114,17 @@ val first_exn : t -> int
|
||||||
|
|
||||||
val filter : t -> (int -> bool) -> unit
|
val filter : t -> (int -> bool) -> unit
|
||||||
(** [filter bv p] only keeps the true bits of [bv] whose [index]
|
(** [filter bv p] only keeps the true bits of [bv] whose [index]
|
||||||
satisfies [p index]. *)
|
satisfies [p index].
|
||||||
|
Length is unchanged. *)
|
||||||
|
|
||||||
val negate_self : t -> unit
|
val negate_self : t -> unit
|
||||||
(** [negate_self t] flips all of the bits in [t].
|
(** [negate_self t] flips all of the bits in [t]. Length is unchanged.
|
||||||
|
|
||||||
@since 1.2 *)
|
@since 1.2 *)
|
||||||
|
|
||||||
val negate : t -> t
|
val negate : t -> t
|
||||||
(** [negate t] returns a copy of [t] with all of the bits flipped. *)
|
(** [negate t] returns a copy of [t] with all of the bits flipped.
|
||||||
|
Length is unchanged. *)
|
||||||
|
|
||||||
val union_into : into:t -> t -> unit
|
val union_into : into:t -> t -> unit
|
||||||
(** [union_into ~into bv] sets [into] to the union of itself and [bv].
|
(** [union_into ~into bv] sets [into] to the union of itself and [bv].
|
||||||
|
|
@ -108,13 +132,20 @@ val union_into : into:t -> t -> unit
|
||||||
|
|
||||||
val inter_into : into:t -> t -> unit
|
val inter_into : into:t -> t -> unit
|
||||||
(** [inter_into ~into bv] sets [into] to the intersection of itself and [bv].
|
(** [inter_into ~into bv] sets [into] to the intersection of itself and [bv].
|
||||||
Also updates the length of [into] to be at most [length bv]. *)
|
Also updates the length of [into] to be at most [length bv].
|
||||||
|
|
||||||
|
After executing:
|
||||||
|
- [length ~into' = min (length into) (length bv)].
|
||||||
|
- [for all i: get into' ==> get into i /\ get bv i]
|
||||||
|
*)
|
||||||
|
|
||||||
val union : t -> t -> t
|
val union : t -> t -> t
|
||||||
(** [union bv1 bv2] returns the union of the two sets. *)
|
(** [union bv1 bv2] returns the union of the two sets. The length
|
||||||
|
of the result is the max of the inputs' lengths. *)
|
||||||
|
|
||||||
val inter : t -> t -> t
|
val inter : t -> t -> t
|
||||||
(** [inter bv1 bv2] returns the intersection of the two sets. *)
|
(** [inter bv1 bv2] returns the intersection of the two sets. The length
|
||||||
|
of the result is the min of the inputs' lengths. *)
|
||||||
|
|
||||||
val diff_into : into:t -> t -> unit
|
val diff_into : into:t -> t -> unit
|
||||||
(** [diff_into ~into t] modifies [into] with only the bits set but not in [t].
|
(** [diff_into ~into t] modifies [into] with only the bits set but not in [t].
|
||||||
|
|
@ -142,7 +173,10 @@ val equal : t -> t -> bool
|
||||||
type 'a iter = ('a -> unit) -> unit
|
type 'a iter = ('a -> unit) -> unit
|
||||||
|
|
||||||
val to_iter : t -> int iter
|
val to_iter : t -> int iter
|
||||||
|
(** Iterate over the true bits. *)
|
||||||
|
|
||||||
val of_iter : int iter -> t
|
val of_iter : int iter -> t
|
||||||
|
(** Build from true bits. *)
|
||||||
|
|
||||||
val pp : Format.formatter -> t -> unit
|
val pp : Format.formatter -> t -> unit
|
||||||
(** Print the bitvector as a string of bits.
|
(** Print the bitvector as a string of bits.
|
||||||
|
|
@ -150,6 +184,11 @@ val pp : Format.formatter -> t -> unit
|
||||||
|
|
||||||
(**/**)
|
(**/**)
|
||||||
|
|
||||||
val __to_word_l : t -> int list
|
module Internal_ : sig
|
||||||
|
val __to_word_l : t -> char list
|
||||||
|
val __popcount8 : int -> int
|
||||||
|
val __lsb_mask : int -> int
|
||||||
|
val __check_invariant : t -> unit
|
||||||
|
end
|
||||||
|
|
||||||
(**/**)
|
(**/**)
|
||||||
|
|
|
||||||
|
|
@ -7,21 +7,29 @@ type 'a print = 'a -> string
|
||||||
|
|
||||||
module Test = struct
|
module Test = struct
|
||||||
type run =
|
type run =
|
||||||
| T of (unit -> bool)
|
| T of { prop: unit -> bool }
|
||||||
| Eq : { eq: 'a eq option; print: 'a print option; lhs: 'a; rhs: 'a } -> run
|
| Eq : { eq: 'a eq option; print: 'a print option; lhs: 'a; rhs: 'a } -> run
|
||||||
| Q : {
|
| Q : {
|
||||||
count: int option;
|
count: int option;
|
||||||
arb: 'a Q.arbitrary;
|
arb: 'a Q.arbitrary;
|
||||||
prop: 'a -> bool;
|
prop: 'a -> bool;
|
||||||
long_factor: int option;
|
long_factor: int option;
|
||||||
|
max_gen: int option;
|
||||||
|
max_fail: int option;
|
||||||
|
if_assumptions_fail: ([ `Fatal | `Warning ] * float) option;
|
||||||
}
|
}
|
||||||
-> run
|
-> run
|
||||||
|
|
||||||
type t = { run: run; __FILE__: string; n: int }
|
type t = { name: string option; run: run; __FILE__: string; n: int }
|
||||||
|
|
||||||
(** Location for this test *)
|
(** Location for this test *)
|
||||||
let str_loc (self : t) : string =
|
let str_loc (self : t) : string =
|
||||||
Printf.sprintf "(test :file '%s' :n %d)" self.__FILE__ self.n
|
let what =
|
||||||
|
match self.name with
|
||||||
|
| None -> ""
|
||||||
|
| Some f -> spf " :name %S" f
|
||||||
|
in
|
||||||
|
Printf.sprintf "(test :file '%s'%s :n %d)" self.__FILE__ what self.n
|
||||||
|
|
||||||
[@@@ifge 4.08]
|
[@@@ifge 4.08]
|
||||||
|
|
||||||
|
|
@ -36,14 +44,17 @@ module Test = struct
|
||||||
|
|
||||||
[@@@endif]
|
[@@@endif]
|
||||||
|
|
||||||
let run ~seed (self : t) : _ result =
|
let run ?(long = false) ~seed (self : t) : _ result =
|
||||||
match
|
match
|
||||||
|
let what = CCOption.map_or ~default:"" (fun s -> s ^ " ") self.name in
|
||||||
match self.run with
|
match self.run with
|
||||||
| T f ->
|
| T { prop } ->
|
||||||
if f () then
|
let fail msg = Error (spf "%sfailed: %s" what msg) in
|
||||||
Ok ()
|
|
||||||
else
|
(match prop () with
|
||||||
Error "failed: returns false"
|
| exception e -> fail (spf "raised %s" (Printexc.to_string e))
|
||||||
|
| true -> Ok ()
|
||||||
|
| false -> fail "returns false")
|
||||||
| Eq { eq; print; lhs; rhs } ->
|
| Eq { eq; print; lhs; rhs } ->
|
||||||
let eq =
|
let eq =
|
||||||
match eq with
|
match eq with
|
||||||
|
|
@ -55,12 +66,22 @@ module Test = struct
|
||||||
else (
|
else (
|
||||||
let msg =
|
let msg =
|
||||||
match print with
|
match print with
|
||||||
| None -> "failed: not equal"
|
| None -> spf "%sfailed: not equal" what
|
||||||
| Some p -> spf "failed: not equal:\nlhs=%s\nrhs=%s" (p lhs) (p rhs)
|
| Some p ->
|
||||||
|
spf "%s failed: not equal:\nlhs=%s\nrhs=%s" what (p lhs) (p rhs)
|
||||||
in
|
in
|
||||||
Error msg
|
Error msg
|
||||||
)
|
)
|
||||||
| Q { count; arb; prop; long_factor } ->
|
| Q
|
||||||
|
{
|
||||||
|
count;
|
||||||
|
arb;
|
||||||
|
prop;
|
||||||
|
long_factor;
|
||||||
|
max_fail;
|
||||||
|
max_gen;
|
||||||
|
if_assumptions_fail;
|
||||||
|
} ->
|
||||||
(* create a random state from the seed *)
|
(* create a random state from the seed *)
|
||||||
let rand =
|
let rand =
|
||||||
let bits =
|
let bits =
|
||||||
|
|
@ -71,7 +92,8 @@ module Test = struct
|
||||||
|
|
||||||
let module Fmt = CCFormat in
|
let module Fmt = CCFormat in
|
||||||
let cell =
|
let cell =
|
||||||
Q.Test.make_cell ?count ?long_factor ~name:(str_loc self) arb prop
|
Q.Test.make_cell ?if_assumptions_fail ?max_gen ?max_fail ?count
|
||||||
|
?long_factor ~name:(str_loc self) arb prop
|
||||||
in
|
in
|
||||||
|
|
||||||
let pp_cex out (cx : _ Q.TestResult.counter_ex) =
|
let pp_cex out (cx : _ Q.TestResult.counter_ex) =
|
||||||
|
|
@ -91,24 +113,24 @@ module Test = struct
|
||||||
in
|
in
|
||||||
|
|
||||||
(* TODO: if verbose, print stats, etc. *)
|
(* TODO: if verbose, print stats, etc. *)
|
||||||
let res = Q.Test.check_cell ~rand cell in
|
let res = Q.Test.check_cell ~long ~rand cell in
|
||||||
|
|
||||||
(match get_state res with
|
(match get_state res with
|
||||||
| QCheck.TestResult.Success -> Ok ()
|
| QCheck.TestResult.Success -> Ok ()
|
||||||
| QCheck.TestResult.Failed { instances } ->
|
| QCheck.TestResult.Failed { instances } ->
|
||||||
let msg =
|
let msg =
|
||||||
Format.asprintf "@[<v2>failed on instances:@ %a@]"
|
Format.asprintf "@[<v2>%sfailed on instances:@ %a@]" what
|
||||||
(Fmt.list ~sep:(Fmt.return ";@ ") pp_cex)
|
(Fmt.list ~sep:(Fmt.return ";@ ") pp_cex)
|
||||||
instances
|
instances
|
||||||
in
|
in
|
||||||
Error msg
|
Error msg
|
||||||
| QCheck.TestResult.Failed_other { msg } ->
|
| QCheck.TestResult.Failed_other { msg } ->
|
||||||
let msg = spf "failed: %s" msg in
|
let msg = spf "%sfailed: %s" what msg in
|
||||||
Error msg
|
Error msg
|
||||||
| QCheck.TestResult.Error { instance; exn; backtrace } ->
|
| QCheck.TestResult.Error { instance; exn; backtrace } ->
|
||||||
let msg =
|
let msg =
|
||||||
Format.asprintf "@[<v2>raised %s@ on instance %a@ :backtrace %s@]"
|
Format.asprintf "@[<v2>%sraised %s@ on instance %a@ :backtrace %s@]"
|
||||||
(Printexc.to_string exn) pp_cex instance backtrace
|
what (Printexc.to_string exn) pp_cex instance backtrace
|
||||||
in
|
in
|
||||||
Error msg)
|
Error msg)
|
||||||
with
|
with
|
||||||
|
|
@ -119,11 +141,19 @@ end
|
||||||
module type S = sig
|
module type S = sig
|
||||||
module Q = QCheck
|
module Q = QCheck
|
||||||
|
|
||||||
val t : (unit -> bool) -> unit
|
val t : ?name:string -> (unit -> bool) -> unit
|
||||||
val eq : ?cmp:'a eq -> ?printer:'a print -> 'a -> 'a -> unit
|
val eq : ?name:string -> ?cmp:'a eq -> ?printer:'a print -> 'a -> 'a -> unit
|
||||||
|
|
||||||
val q :
|
val q :
|
||||||
?count:int -> ?long_factor:int -> 'a Q.arbitrary -> ('a -> bool) -> unit
|
?name:string ->
|
||||||
|
?count:int ->
|
||||||
|
?long_factor:int ->
|
||||||
|
?max_gen:int ->
|
||||||
|
?max_fail:int ->
|
||||||
|
?if_assumptions_fail:[ `Fatal | `Warning ] * float ->
|
||||||
|
'a Q.arbitrary ->
|
||||||
|
('a -> bool) ->
|
||||||
|
unit
|
||||||
|
|
||||||
val assert_equal :
|
val assert_equal :
|
||||||
?printer:('a -> string) -> ?cmp:('a -> 'a -> bool) -> 'a -> 'a -> unit
|
?printer:('a -> string) -> ?cmp:('a -> 'a -> bool) -> 'a -> 'a -> unit
|
||||||
|
|
@ -144,18 +174,29 @@ struct
|
||||||
let add_ t = all_ := t :: !all_
|
let add_ t = all_ := t :: !all_
|
||||||
let n_ = ref 0
|
let n_ = ref 0
|
||||||
|
|
||||||
let mk run : Test.t =
|
let mk ?name run : Test.t =
|
||||||
let n = !n_ in
|
let n = !n_ in
|
||||||
incr n_;
|
incr n_;
|
||||||
{ __FILE__ = X.file; n; run }
|
{ __FILE__ = X.file; name; n; run }
|
||||||
|
|
||||||
let t f : unit = add_ @@ mk @@ Test.T f
|
let t ?name f : unit = add_ @@ mk ?name @@ Test.T { prop = f }
|
||||||
|
|
||||||
let eq ?cmp ?printer lhs rhs : unit =
|
let eq ?name ?cmp ?printer lhs rhs : unit =
|
||||||
add_ @@ mk @@ Test.Eq { eq = cmp; print = printer; lhs; rhs }
|
add_ @@ mk ?name @@ Test.Eq { eq = cmp; print = printer; lhs; rhs }
|
||||||
|
|
||||||
let q ?count ?long_factor arb prop : unit =
|
let q ?name ?count ?long_factor ?max_gen ?max_fail ?if_assumptions_fail arb
|
||||||
add_ @@ mk @@ Test.Q { arb; prop; count; long_factor }
|
prop : unit =
|
||||||
|
add_ @@ mk ?name
|
||||||
|
@@ Test.Q
|
||||||
|
{
|
||||||
|
arb;
|
||||||
|
prop;
|
||||||
|
count;
|
||||||
|
long_factor;
|
||||||
|
max_gen;
|
||||||
|
max_fail;
|
||||||
|
if_assumptions_fail;
|
||||||
|
}
|
||||||
|
|
||||||
let assert_equal ?printer ?(cmp = ( = )) x y : unit =
|
let assert_equal ?printer ?(cmp = ( = )) x y : unit =
|
||||||
if not @@ cmp x y then (
|
if not @@ cmp x y then (
|
||||||
|
|
@ -188,7 +229,12 @@ let make ~__FILE__ () : (module S) =
|
||||||
|
|
||||||
let getenv_opt s = try Some (Sys.getenv s) with _ -> None
|
let getenv_opt s = try Some (Sys.getenv s) with _ -> None
|
||||||
|
|
||||||
let run_all ?seed:seed_hex ~descr (l : Test.t list list) : unit =
|
let long =
|
||||||
|
match getenv_opt "LONG" with
|
||||||
|
| Some ("true" | "1") -> true
|
||||||
|
| _ -> false
|
||||||
|
|
||||||
|
let run_all ?seed:seed_hex ?(long = long) ~descr (l : Test.t list list) : unit =
|
||||||
let start = Unix.gettimeofday () in
|
let start = Unix.gettimeofday () in
|
||||||
|
|
||||||
(* generate or parse seed *)
|
(* generate or parse seed *)
|
||||||
|
|
@ -223,7 +269,7 @@ let run_all ?seed:seed_hex ~descr (l : Test.t list list) : unit =
|
||||||
NOTE: we probably want this to be silent?
|
NOTE: we probably want this to be silent?
|
||||||
Format.printf "> run %s@." (Test.str_loc t);
|
Format.printf "> run %s@." (Test.str_loc t);
|
||||||
*)
|
*)
|
||||||
match Test.run ~seed t with
|
match Test.run ~long ~seed t with
|
||||||
| Ok () -> ()
|
| Ok () -> ()
|
||||||
| Error msg ->
|
| Error msg ->
|
||||||
Format.printf "FAILED: %s@." (Test.str_loc t);
|
Format.printf "FAILED: %s@." (Test.str_loc t);
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,19 @@ end
|
||||||
module type S = sig
|
module type S = sig
|
||||||
module Q = QCheck
|
module Q = QCheck
|
||||||
|
|
||||||
val t : (unit -> bool) -> unit
|
val t : ?name:string -> (unit -> bool) -> unit
|
||||||
val eq : ?cmp:'a eq -> ?printer:'a print -> 'a -> 'a -> unit
|
val eq : ?name:string -> ?cmp:'a eq -> ?printer:'a print -> 'a -> 'a -> unit
|
||||||
|
|
||||||
val q :
|
val q :
|
||||||
?count:int -> ?long_factor:int -> 'a Q.arbitrary -> ('a -> bool) -> unit
|
?name:string ->
|
||||||
|
?count:int ->
|
||||||
|
?long_factor:int ->
|
||||||
|
?max_gen:int ->
|
||||||
|
?max_fail:int ->
|
||||||
|
?if_assumptions_fail:[ `Fatal | `Warning ] * float ->
|
||||||
|
'a Q.arbitrary ->
|
||||||
|
('a -> bool) ->
|
||||||
|
unit
|
||||||
|
|
||||||
val assert_equal :
|
val assert_equal :
|
||||||
?printer:('a -> string) -> ?cmp:('a -> 'a -> bool) -> 'a -> 'a -> unit
|
?printer:('a -> string) -> ?cmp:('a -> 'a -> bool) -> 'a -> 'a -> unit
|
||||||
|
|
@ -24,4 +32,6 @@ module type S = sig
|
||||||
end
|
end
|
||||||
|
|
||||||
val make : __FILE__:string -> unit -> (module S)
|
val make : __FILE__:string -> unit -> (module S)
|
||||||
val run_all : ?seed:string -> descr:string -> Test.t list list -> unit
|
|
||||||
|
val run_all :
|
||||||
|
?seed:string -> ?long:bool -> descr:string -> Test.t list list -> unit
|
||||||
|
|
|
||||||
|
|
@ -83,4 +83,20 @@ q
|
||||||
|
|
||||||
eq ~printer:CCFun.id "0b111" (to_string_binary 7l);;
|
eq ~printer:CCFun.id "0b111" (to_string_binary 7l);;
|
||||||
eq ~printer:CCFun.id "-0b111" (to_string_binary (-7l));;
|
eq ~printer:CCFun.id "-0b111" (to_string_binary (-7l));;
|
||||||
eq ~printer:CCFun.id "0b0" (to_string_binary 0l)
|
eq ~printer:CCFun.id "0b0" (to_string_binary 0l);;
|
||||||
|
|
||||||
|
q
|
||||||
|
Q.(0 -- (Int32.max_int |> Int32.to_int))
|
||||||
|
(fun i ->
|
||||||
|
let n1 = CCInt.popcount i in
|
||||||
|
let n2 = CCInt32.popcount (Int32.of_int i) in
|
||||||
|
CCInt.(n1 = n2))
|
||||||
|
|
||||||
|
let eq' = eq ~printer:CCInt.to_string;;
|
||||||
|
|
||||||
|
eq' 0 (popcount 0l);;
|
||||||
|
eq' 1 (popcount 1l);;
|
||||||
|
eq' 31 (popcount max_int);;
|
||||||
|
eq' 1 (popcount min_int);;
|
||||||
|
eq' 10 (popcount 0b1110010110110001010l);;
|
||||||
|
eq' 5 (popcount 0b1101110000000000l)
|
||||||
|
|
|
||||||
|
|
@ -92,3 +92,12 @@ q
|
||||||
eq ~printer:CCFun.id "0b111" (to_string_binary 7L);;
|
eq ~printer:CCFun.id "0b111" (to_string_binary 7L);;
|
||||||
eq ~printer:CCFun.id "-0b111" (to_string_binary (-7L));;
|
eq ~printer:CCFun.id "-0b111" (to_string_binary (-7L));;
|
||||||
eq ~printer:CCFun.id "0b0" (to_string_binary 0L)
|
eq ~printer:CCFun.id "0b0" (to_string_binary 0L)
|
||||||
|
|
||||||
|
let eq' = eq ~printer:CCInt.to_string;;
|
||||||
|
|
||||||
|
eq' 0 (popcount 0L);;
|
||||||
|
eq' 1 (popcount 1L);;
|
||||||
|
eq' 63 (popcount max_int);;
|
||||||
|
eq' 1 (popcount min_int);;
|
||||||
|
eq' 10 (popcount 0b1110010110110001010L);;
|
||||||
|
eq' 5 (popcount 0b1101110000000000L)
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,35 @@
|
||||||
module Test = (val Containers_testlib.make ~__FILE__ ())
|
module Test = (val Containers_testlib.make ~__FILE__ ())
|
||||||
open Test
|
open Test
|
||||||
open CCBV
|
open CCBV
|
||||||
|
open Internal_
|
||||||
|
|
||||||
let ppli = CCFormat.(Dump.list int);;
|
let spf = Printf.sprintf
|
||||||
|
let ppli = CCFormat.(Dump.list int)
|
||||||
|
|
||||||
|
module Intset = CCSet.Make (CCInt);;
|
||||||
|
|
||||||
q (Q.pair Q.small_int Q.bool) (fun (size, b) -> create ~size b |> length = size)
|
q (Q.pair Q.small_int Q.bool) (fun (size, b) -> create ~size b |> length = size)
|
||||||
;;
|
;;
|
||||||
t @@ fun () -> create ~size:17 true |> cardinal = 17;;
|
|
||||||
t @@ fun () -> create ~size:32 true |> cardinal = 32;;
|
t ~name:(spf "line %d" __LINE__) @@ fun () ->
|
||||||
t @@ fun () -> create ~size:132 true |> cardinal = 132;;
|
create ~size:17 true |> cardinal = 17
|
||||||
t @@ fun () -> create ~size:200 false |> cardinal = 0;;
|
;;
|
||||||
t @@ fun () -> create ~size:29 true |> to_sorted_list = CCList.range 0 28;;
|
|
||||||
|
t ~name:(spf "line %d" __LINE__) @@ fun () ->
|
||||||
|
create ~size:32 true |> cardinal = 32
|
||||||
|
;;
|
||||||
|
|
||||||
|
t ~name:(spf "line %d" __LINE__) @@ fun () ->
|
||||||
|
create ~size:132 true |> cardinal = 132
|
||||||
|
;;
|
||||||
|
|
||||||
|
t ~name:(spf "line %d" __LINE__) @@ fun () ->
|
||||||
|
create ~size:200 false |> cardinal = 0
|
||||||
|
;;
|
||||||
|
|
||||||
|
t ~name:(spf "line %d" __LINE__) @@ fun () ->
|
||||||
|
create ~size:29 true |> to_sorted_list = CCList.range 0 28
|
||||||
|
;;
|
||||||
|
|
||||||
q (Q.list Q.small_int) (fun l ->
|
q (Q.list Q.small_int) (fun l ->
|
||||||
let bv = of_list l in
|
let bv = of_list l in
|
||||||
|
|
@ -19,7 +38,11 @@ q (Q.list Q.small_int) (fun l ->
|
||||||
|
|
||||||
q Q.small_int (fun size -> create ~size true |> cardinal = size);;
|
q Q.small_int (fun size -> create ~size true |> cardinal = size);;
|
||||||
|
|
||||||
t @@ fun () ->
|
q Q.small_int (fun size ->
|
||||||
|
create ~size true |> to_sorted_list = CCList.init size CCFun.id)
|
||||||
|
;;
|
||||||
|
|
||||||
|
t ~name:(spf "line %d" __LINE__) @@ fun () ->
|
||||||
let bv1 = CCBV.create ~size:87 true in
|
let bv1 = CCBV.create ~size:87 true in
|
||||||
assert_equal ~printer:string_of_int 87 (CCBV.cardinal bv1);
|
assert_equal ~printer:string_of_int 87 (CCBV.cardinal bv1);
|
||||||
true
|
true
|
||||||
|
|
@ -27,7 +50,7 @@ true
|
||||||
|
|
||||||
q Q.small_int (fun n -> CCBV.cardinal (CCBV.create ~size:n true) = n);;
|
q Q.small_int (fun n -> CCBV.cardinal (CCBV.create ~size:n true) = n);;
|
||||||
|
|
||||||
t @@ fun () ->
|
t ~name:(spf "line %d" __LINE__) @@ fun () ->
|
||||||
let bv = CCBV.create ~size:99 false in
|
let bv = CCBV.create ~size:99 false in
|
||||||
assert_bool "32 must be false" (not (CCBV.get bv 32));
|
assert_bool "32 must be false" (not (CCBV.get bv 32));
|
||||||
assert_bool "88 must be false" (not (CCBV.get bv 88));
|
assert_bool "88 must be false" (not (CCBV.get bv 88));
|
||||||
|
|
@ -44,26 +67,26 @@ assert_bool "1 must be false" (not (CCBV.get bv 1));
|
||||||
true
|
true
|
||||||
;;
|
;;
|
||||||
|
|
||||||
t @@ fun () ->
|
t ~name:(spf "line %d" __LINE__) @@ fun () ->
|
||||||
let bv = create ~size:3 false in
|
let bv = create ~size:3 false in
|
||||||
set bv 0;
|
set bv 0;
|
||||||
get bv 0
|
get bv 0
|
||||||
;;
|
;;
|
||||||
|
|
||||||
t @@ fun () ->
|
t ~name:(spf "line %d" __LINE__) @@ fun () ->
|
||||||
let bv = create ~size:3 false in
|
let bv = create ~size:3 false in
|
||||||
set bv 1;
|
set bv 1;
|
||||||
not (get bv 0)
|
not (get bv 0)
|
||||||
;;
|
;;
|
||||||
|
|
||||||
t @@ fun () ->
|
t ~name:(spf "line %d" __LINE__) @@ fun () ->
|
||||||
let bv = create ~size:3 false in
|
let bv = create ~size:3 false in
|
||||||
set bv 0;
|
set bv 0;
|
||||||
reset bv 0;
|
reset bv 0;
|
||||||
not (get bv 0)
|
not (get bv 0)
|
||||||
;;
|
;;
|
||||||
|
|
||||||
t @@ fun () ->
|
t ~name:(spf "line %d" __LINE__) @@ fun () ->
|
||||||
let bv = of_list [ 1; 10; 11; 30 ] in
|
let bv = of_list [ 1; 10; 11; 30 ] in
|
||||||
flip bv 10;
|
flip bv 10;
|
||||||
assert_equal ~printer:Q.Print.(list int) [ 1; 11; 30 ] (to_sorted_list bv);
|
assert_equal ~printer:Q.Print.(list int) [ 1; 11; 30 ] (to_sorted_list bv);
|
||||||
|
|
@ -83,7 +106,7 @@ assert_equal ~printer:Q.Print.bool true (get bv 100);
|
||||||
true
|
true
|
||||||
;;
|
;;
|
||||||
|
|
||||||
t @@ fun () ->
|
t ~name:(spf "line %d" __LINE__) @@ fun () ->
|
||||||
let bv = create ~size:37 true in
|
let bv = create ~size:37 true in
|
||||||
cardinal bv = 37
|
cardinal bv = 37
|
||||||
&&
|
&&
|
||||||
|
|
@ -91,7 +114,7 @@ cardinal bv = 37
|
||||||
cardinal bv = 0)
|
cardinal bv = 0)
|
||||||
;;
|
;;
|
||||||
|
|
||||||
t @@ fun () ->
|
t ~name:(spf "line %d" __LINE__) @@ fun () ->
|
||||||
let bv = CCBV.of_list [ 1; 5; 200 ] in
|
let bv = CCBV.of_list [ 1; 5; 200 ] in
|
||||||
assert_equal ~printer:string_of_int 3 (CCBV.cardinal bv);
|
assert_equal ~printer:string_of_int 3 (CCBV.cardinal bv);
|
||||||
CCBV.clear bv;
|
CCBV.clear bv;
|
||||||
|
|
@ -100,13 +123,25 @@ assert_bool "must be empty" (CCBV.is_empty bv);
|
||||||
true
|
true
|
||||||
;;
|
;;
|
||||||
|
|
||||||
t @@ fun () -> equal (of_list [ 1; 3; 4 ]) (of_list [ 1; 3; 4 ]);;
|
t ~name:(spf "line %d" __LINE__) @@ fun () ->
|
||||||
t @@ fun () -> equal (empty ()) (empty ());;
|
equal (of_list [ 1; 3; 4 ]) (of_list [ 1; 3; 4 ])
|
||||||
t @@ fun () -> not (equal (empty ()) (of_list [ 1 ]));;
|
;;
|
||||||
t @@ fun () -> not (equal (empty ()) (of_list [ 2; 5 ]));;
|
|
||||||
t @@ fun () -> not (equal (of_list [ 1; 3 ]) (of_list [ 2; 3 ]));;
|
|
||||||
|
|
||||||
t @@ fun () ->
|
t ~name:(spf "line %d" __LINE__) @@ fun () -> equal (empty ()) (empty ());;
|
||||||
|
|
||||||
|
t ~name:(spf "line %d" __LINE__) @@ fun () ->
|
||||||
|
not (equal (empty ()) (of_list [ 1 ]))
|
||||||
|
;;
|
||||||
|
|
||||||
|
t ~name:(spf "line %d" __LINE__) @@ fun () ->
|
||||||
|
not (equal (empty ()) (of_list [ 2; 5 ]))
|
||||||
|
;;
|
||||||
|
|
||||||
|
t ~name:(spf "line %d" __LINE__) @@ fun () ->
|
||||||
|
not (equal (of_list [ 1; 3 ]) (of_list [ 2; 3 ]))
|
||||||
|
;;
|
||||||
|
|
||||||
|
t ~name:(spf "line %d" __LINE__) @@ fun () ->
|
||||||
List.iter
|
List.iter
|
||||||
(fun size ->
|
(fun size ->
|
||||||
let bv = create ~size false in
|
let bv = create ~size false in
|
||||||
|
|
@ -142,7 +177,7 @@ q
|
||||||
List.length l = n && List.for_all (fun (_, b) -> b) l)
|
List.length l = n && List.for_all (fun (_, b) -> b) l)
|
||||||
;;
|
;;
|
||||||
|
|
||||||
t @@ fun () ->
|
t ~name:(spf "line %d" __LINE__) @@ fun () ->
|
||||||
of_list [ 1; 5; 7 ]
|
of_list [ 1; 5; 7 ]
|
||||||
|> iter_true |> Iter.to_list |> List.sort CCOrd.poly = [ 1; 5; 7 ]
|
|> iter_true |> Iter.to_list |> List.sort CCOrd.poly = [ 1; 5; 7 ]
|
||||||
|
|
||||||
|
|
@ -157,7 +192,7 @@ q gen_bv (fun bv ->
|
||||||
CCBV.cardinal bv = CCBV.cardinal bv')
|
CCBV.cardinal bv = CCBV.cardinal bv')
|
||||||
;;
|
;;
|
||||||
|
|
||||||
t @@ fun () ->
|
t ~name:(spf "line %d" __LINE__) @@ fun () ->
|
||||||
let bv = CCBV.of_list [ 1; 5; 156; 0; 222 ] in
|
let bv = CCBV.of_list [ 1; 5; 156; 0; 222 ] in
|
||||||
assert_equal ~printer:string_of_int 5 (CCBV.cardinal bv);
|
assert_equal ~printer:string_of_int 5 (CCBV.cardinal bv);
|
||||||
CCBV.set bv 201;
|
CCBV.set bv 201;
|
||||||
|
|
@ -190,7 +225,7 @@ q
|
||||||
let l1 = bv |> to_sorted_list in
|
let l1 = bv |> to_sorted_list in
|
||||||
let l2 =
|
let l2 =
|
||||||
CCList.init (length bv) (get bv)
|
CCList.init (length bv) (get bv)
|
||||||
|> List.mapi (fun i b -> i, b)
|
|> CCList.mapi (fun i b -> i, b)
|
||||||
|> CCList.filter_map (function
|
|> CCList.filter_map (function
|
||||||
| i, true -> Some i
|
| i, true -> Some i
|
||||||
| _ -> None)
|
| _ -> None)
|
||||||
|
|
@ -208,12 +243,23 @@ eq ~cmp:equal ~printer:(CCFormat.to_string pp)
|
||||||
bv)
|
bv)
|
||||||
;;
|
;;
|
||||||
|
|
||||||
t @@ fun () -> of_list [ 1; 32; 64 ] |> CCFun.flip get 64;;
|
t ~name:(spf "line %d" __LINE__) @@ fun () ->
|
||||||
t @@ fun () -> of_list [ 1; 32; 64 ] |> CCFun.flip get 32;;
|
of_list [ 1; 32; 64 ] |> CCFun.flip get 64
|
||||||
t @@ fun () -> of_list [ 1; 31; 63 ] |> CCFun.flip get 63;;
|
;;
|
||||||
t @@ fun () -> of_list [ 50; 10; 17; 22; 3; 12 ] |> first = Some 3;;
|
|
||||||
|
|
||||||
t @@ fun () ->
|
t ~name:(spf "line %d" __LINE__) @@ fun () ->
|
||||||
|
of_list [ 1; 32; 64 ] |> CCFun.flip get 32
|
||||||
|
;;
|
||||||
|
|
||||||
|
t ~name:(spf "line %d" __LINE__) @@ fun () ->
|
||||||
|
of_list [ 1; 31; 63 ] |> CCFun.flip get 63
|
||||||
|
;;
|
||||||
|
|
||||||
|
t ~name:(spf "line %d" __LINE__) @@ fun () ->
|
||||||
|
of_list [ 50; 10; 17; 22; 3; 12 ] |> first = Some 3
|
||||||
|
;;
|
||||||
|
|
||||||
|
t ~name:(spf "line %d" __LINE__) @@ fun () ->
|
||||||
let bv = of_list [ 1; 2; 3; 4; 5; 6; 7 ] in
|
let bv = of_list [ 1; 2; 3; 4; 5; 6; 7 ] in
|
||||||
filter bv (fun x -> x mod 2 = 0);
|
filter bv (fun x -> x mod 2 = 0);
|
||||||
to_sorted_list bv = [ 2; 4; 6 ]
|
to_sorted_list bv = [ 2; 4; 6 ]
|
||||||
|
|
@ -227,7 +273,7 @@ eq ~printer:(CCFormat.to_string ppli) [ 0; 3; 4; 6 ]
|
||||||
|
|
||||||
q Q.small_int (fun size -> create ~size false |> negate |> cardinal = size);;
|
q Q.small_int (fun size -> create ~size false |> negate |> cardinal = size);;
|
||||||
|
|
||||||
t @@ fun () ->
|
t ~name:(spf "line %d" __LINE__) @@ fun () ->
|
||||||
let bv1 = CCBV.of_list [ 1; 2; 3; 4 ] in
|
let bv1 = CCBV.of_list [ 1; 2; 3; 4 ] in
|
||||||
let bv2 = CCBV.of_list [ 4; 200; 3 ] in
|
let bv2 = CCBV.of_list [ 4; 200; 3 ] in
|
||||||
let bv = CCBV.union bv1 bv2 in
|
let bv = CCBV.union bv1 bv2 in
|
||||||
|
|
@ -236,7 +282,20 @@ assert_equal ~printer:CCFormat.(to_string (Dump.list int)) [ 1; 2; 3; 4; 200 ] l
|
||||||
true
|
true
|
||||||
;;
|
;;
|
||||||
|
|
||||||
t @@ fun () ->
|
q ~name:"union"
|
||||||
|
Q.(pair (small_list small_nat) (small_list small_nat))
|
||||||
|
(fun (l1, l2) ->
|
||||||
|
let bv1 = of_list l1 in
|
||||||
|
let bv2 = of_list l2 in
|
||||||
|
let l' = CCList.sort_uniq ~cmp:CCInt.compare (l1 @ l2) in
|
||||||
|
let bv = union bv1 bv2 in
|
||||||
|
let bv' = of_list l' in
|
||||||
|
if not (equal bv bv') then
|
||||||
|
Q.Test.fail_reportf "union (%a, %a) <> %a" ppli l1 ppli l2 ppli l';
|
||||||
|
true)
|
||||||
|
;;
|
||||||
|
|
||||||
|
t ~name:(spf "line %d" __LINE__) @@ fun () ->
|
||||||
let bv1 = CCBV.of_list [ 1; 2; 3; 4; 64; 130 ] in
|
let bv1 = CCBV.of_list [ 1; 2; 3; 4; 64; 130 ] in
|
||||||
let bv2 = CCBV.of_list [ 4; 64; 3; 120 ] in
|
let bv2 = CCBV.of_list [ 4; 64; 3; 120 ] in
|
||||||
let bv = CCBV.union bv1 bv2 in
|
let bv = CCBV.union bv1 bv2 in
|
||||||
|
|
@ -246,7 +305,7 @@ assert_equal ~cmp:equal ~printer:(CCFormat.to_string pp)
|
||||||
true
|
true
|
||||||
;;
|
;;
|
||||||
|
|
||||||
t @@ fun () ->
|
t ~name:(spf "line %d" __LINE__) @@ fun () ->
|
||||||
let bv1 = CCBV.of_list [ 1; 2; 3; 4 ] in
|
let bv1 = CCBV.of_list [ 1; 2; 3; 4 ] in
|
||||||
let bv2 = CCBV.of_list [ 4; 200; 3 ] in
|
let bv2 = CCBV.of_list [ 4; 200; 3 ] in
|
||||||
let bv = CCBV.union bv1 bv2 in
|
let bv = CCBV.union bv1 bv2 in
|
||||||
|
|
@ -256,7 +315,7 @@ assert_equal ~cmp:equal ~printer:(CCFormat.to_string pp)
|
||||||
true
|
true
|
||||||
;;
|
;;
|
||||||
|
|
||||||
t @@ fun () ->
|
t ~name:(spf "line %d" __LINE__) @@ fun () ->
|
||||||
let v1 = CCBV.empty () in
|
let v1 = CCBV.empty () in
|
||||||
let () = CCBV.set v1 64 in
|
let () = CCBV.set v1 64 in
|
||||||
let v2 = CCBV.diff (CCBV.empty ()) (CCBV.empty ()) in
|
let v2 = CCBV.diff (CCBV.empty ()) (CCBV.empty ()) in
|
||||||
|
|
@ -265,17 +324,17 @@ assert_equal ~printer:(CCFormat.to_string pp) ~cmp:CCBV.equal v1 v3;
|
||||||
true
|
true
|
||||||
;;
|
;;
|
||||||
|
|
||||||
t @@ fun () ->
|
t ~name:(spf "line %d" __LINE__) @@ fun () ->
|
||||||
union (of_list [ 1; 2; 3; 4; 5 ]) (of_list [ 7; 3; 5; 6 ])
|
union (of_list [ 1; 2; 3; 4; 5 ]) (of_list [ 7; 3; 5; 6 ])
|
||||||
|> to_sorted_list = CCList.range 1 7
|
|> to_sorted_list = CCList.range 1 7
|
||||||
;;
|
;;
|
||||||
|
|
||||||
t @@ fun () ->
|
t ~name:(spf "line %d" __LINE__) @@ fun () ->
|
||||||
inter (of_list [ 1; 2; 3; 4 ]) (of_list [ 2; 4; 6; 1 ])
|
inter (of_list [ 1; 2; 3; 4 ]) (of_list [ 2; 4; 6; 1 ])
|
||||||
|> to_sorted_list = [ 1; 2; 4 ]
|
|> to_sorted_list = [ 1; 2; 4 ]
|
||||||
;;
|
;;
|
||||||
|
|
||||||
t @@ fun () ->
|
t ~name:(spf "line %d" __LINE__) @@ fun () ->
|
||||||
let bv1 = CCBV.of_list [ 1; 2; 3; 4; 200; 201 ] in
|
let bv1 = CCBV.of_list [ 1; 2; 3; 4; 200; 201 ] in
|
||||||
let bv2 = CCBV.of_list [ 4; 200; 3 ] in
|
let bv2 = CCBV.of_list [ 4; 200; 3 ] in
|
||||||
let bv = CCBV.inter bv1 bv2 in
|
let bv = CCBV.inter bv1 bv2 in
|
||||||
|
|
@ -284,7 +343,25 @@ assert_equal ~printer:CCFormat.(to_string (Dump.list int)) [ 3; 4; 200 ] l;
|
||||||
true
|
true
|
||||||
;;
|
;;
|
||||||
|
|
||||||
t @@ fun () ->
|
q ~name:"inter" ~count:10_000
|
||||||
|
Q.(pair (small_list small_nat) (small_list small_nat))
|
||||||
|
(fun (l1, l2) ->
|
||||||
|
let bv1 = of_list l1 in
|
||||||
|
let bv2 = of_list l2 in
|
||||||
|
let l' = CCList.inter ~eq:CCInt.equal l1 l2 in
|
||||||
|
let bv = inter bv1 bv2 in
|
||||||
|
let bv' = of_list l' in
|
||||||
|
(* make sure both are of the same length before comparing *)
|
||||||
|
let len = max (length bv) (length bv') in
|
||||||
|
resize bv len;
|
||||||
|
resize bv' len;
|
||||||
|
if not (equal bv bv') then
|
||||||
|
Q.Test.fail_reportf "inter (%a, %a) <> %a@ (@[<hv>bv= %a,@ bv'=%a@])" ppli
|
||||||
|
l1 ppli l2 ppli l' pp bv pp bv';
|
||||||
|
true)
|
||||||
|
;;
|
||||||
|
|
||||||
|
t ~name:(spf "line %d" __LINE__) @@ fun () ->
|
||||||
let bv1 = CCBV.of_list [ 1; 2; 3; 4 ] in
|
let bv1 = CCBV.of_list [ 1; 2; 3; 4 ] in
|
||||||
let bv2 = CCBV.of_list [ 4; 200; 3 ] in
|
let bv2 = CCBV.of_list [ 4; 200; 3 ] in
|
||||||
CCBV.inter_into ~into:bv1 bv2;
|
CCBV.inter_into ~into:bv1 bv2;
|
||||||
|
|
@ -293,33 +370,52 @@ assert_equal [ 3; 4 ] l;
|
||||||
true
|
true
|
||||||
;;
|
;;
|
||||||
|
|
||||||
t @@ fun () -> diff (of_list [ 1; 2; 3 ]) (of_list [ 1; 2; 3 ]) |> to_list = []
|
t ~name:(spf "line %d" __LINE__) @@ fun () ->
|
||||||
|
diff (of_list [ 1; 2; 3 ]) (of_list [ 1; 2; 3 ]) |> to_list = []
|
||||||
;;
|
;;
|
||||||
|
|
||||||
t @@ fun () ->
|
q ~name:"diff" ~count:10_000
|
||||||
|
Q.(pair (small_list small_nat) (small_list small_nat))
|
||||||
|
(fun (l1, l2) ->
|
||||||
|
let bv1 = of_list l1 in
|
||||||
|
let bv2 = of_list l2 in
|
||||||
|
let bv = diff bv1 bv2 in
|
||||||
|
let l' = Intset.(diff (of_list l1) (of_list l2) |> to_list) in
|
||||||
|
let bv' = of_list l' in
|
||||||
|
(* make sure both are of the same length before comparing *)
|
||||||
|
let len = max (length bv) (length bv') in
|
||||||
|
resize bv len;
|
||||||
|
resize bv' len;
|
||||||
|
if not (equal bv bv') then
|
||||||
|
Q.Test.fail_reportf "idff (%a, %a) <> %a@ (@[<hv>bv= %a,@ bv'=%a@])" ppli
|
||||||
|
l1 ppli l2 ppli l' pp bv pp bv';
|
||||||
|
true)
|
||||||
|
;;
|
||||||
|
|
||||||
|
t ~name:(spf "line %d" __LINE__) @@ fun () ->
|
||||||
diff (of_list [ 1; 2; 3 ]) (of_list [ 1; 2; 3; 4 ]) |> to_list = []
|
diff (of_list [ 1; 2; 3 ]) (of_list [ 1; 2; 3; 4 ]) |> to_list = []
|
||||||
;;
|
;;
|
||||||
|
|
||||||
t @@ fun () ->
|
t ~name:(spf "line %d" __LINE__) @@ fun () ->
|
||||||
diff (of_list [ 1; 2; 3; 4 ]) (of_list [ 1; 2; 3 ]) |> to_list = [ 4 ]
|
diff (of_list [ 1; 2; 3; 4 ]) (of_list [ 1; 2; 3 ]) |> to_list = [ 4 ]
|
||||||
;;
|
;;
|
||||||
|
|
||||||
t @@ fun () ->
|
t ~name:(spf "line %d" __LINE__) @@ fun () ->
|
||||||
diff (of_list [ 1; 2; 3 ]) (of_list [ 1; 2; 3; 400 ]) |> to_list = []
|
diff (of_list [ 1; 2; 3 ]) (of_list [ 1; 2; 3; 400 ]) |> to_list = []
|
||||||
;;
|
;;
|
||||||
|
|
||||||
t @@ fun () ->
|
t ~name:(spf "line %d" __LINE__) @@ fun () ->
|
||||||
diff (of_list [ 1; 2; 3; 400 ]) (of_list [ 1; 2; 3 ]) |> to_list = [ 400 ]
|
diff (of_list [ 1; 2; 3; 400 ]) (of_list [ 1; 2; 3 ]) |> to_list = [ 400 ]
|
||||||
;;
|
;;
|
||||||
|
|
||||||
t @@ fun () ->
|
t ~name:(spf "line %d" __LINE__) @@ fun () ->
|
||||||
let v1 = CCBV.empty () in
|
let v1 = CCBV.empty () in
|
||||||
set v1 65;
|
set v1 65;
|
||||||
let v2 = CCBV.diff v1 v1 in
|
let v2 = CCBV.diff v1 v1 in
|
||||||
CCBV.is_empty v2
|
CCBV.is_empty v2
|
||||||
;;
|
;;
|
||||||
|
|
||||||
t @@ fun () ->
|
t ~name:(spf "line %d" __LINE__) @@ fun () ->
|
||||||
let bv = CCBV.of_list [ 1; 2; 5; 400 ] in
|
let bv = CCBV.of_list [ 1; 2; 5; 400 ] in
|
||||||
let arr = [| "a"; "b"; "c"; "d"; "e"; "f" |] in
|
let arr = [| "a"; "b"; "c"; "d"; "e"; "f" |] in
|
||||||
let l = List.sort compare (CCBV.select bv arr) in
|
let l = List.sort compare (CCBV.select bv arr) in
|
||||||
|
|
@ -327,7 +423,7 @@ assert_equal [ "b"; "c"; "f" ] l;
|
||||||
true
|
true
|
||||||
;;
|
;;
|
||||||
|
|
||||||
t @@ fun () ->
|
t ~name:(spf "line %d" __LINE__) @@ fun () ->
|
||||||
let bv = CCBV.of_list [ 1; 2; 5; 400 ] in
|
let bv = CCBV.of_list [ 1; 2; 5; 400 ] in
|
||||||
let arr = [| "a"; "b"; "c"; "d"; "e"; "f" |] in
|
let arr = [| "a"; "b"; "c"; "d"; "e"; "f" |] in
|
||||||
let l = List.sort compare (CCBV.selecti bv arr) in
|
let l = List.sort compare (CCBV.selecti bv arr) in
|
||||||
|
|
@ -350,9 +446,385 @@ q
|
||||||
i = (to_iter bv |> Iter.length))
|
i = (to_iter bv |> Iter.length))
|
||||||
;;
|
;;
|
||||||
|
|
||||||
t @@ fun () ->
|
t ~name:(spf "line %d" __LINE__) @@ fun () ->
|
||||||
CCList.range 0 10 |> CCList.to_iter |> of_iter |> to_iter |> CCList.of_iter
|
CCList.range 0 10 |> CCList.to_iter |> of_iter |> to_iter |> CCList.of_iter
|
||||||
|> List.sort CCOrd.poly = CCList.range 0 10
|
|> List.sort CCOrd.poly = CCList.range 0 10
|
||||||
;;
|
;;
|
||||||
|
|
||||||
eq ~printer:CCFun.id "bv {00001}" (CCFormat.to_string pp (of_list [ 4 ]))
|
eq ~printer:CCFun.id "bv {00001}" (CCFormat.to_string pp (of_list [ 4 ]))
|
||||||
|
|
||||||
|
let eq' = eq ~printer:CCInt.to_string;;
|
||||||
|
|
||||||
|
eq' 0b0 (__lsb_mask 0);;
|
||||||
|
eq' 0b1 (__lsb_mask 1);;
|
||||||
|
eq' 0b11 (__lsb_mask 2);;
|
||||||
|
eq' 0b111 (__lsb_mask 3);;
|
||||||
|
eq' 0b1111 (__lsb_mask 4);;
|
||||||
|
eq' 0b1_1111 (__lsb_mask 5);;
|
||||||
|
eq' 0b11_1111 (__lsb_mask 6);;
|
||||||
|
eq' 0b111_1111 (__lsb_mask 7);;
|
||||||
|
eq' 0b1111_1111 (__lsb_mask 8)
|
||||||
|
|
||||||
|
let popcount8_ref n =
|
||||||
|
let rec loop n =
|
||||||
|
if n = 0 then
|
||||||
|
0
|
||||||
|
else if n land 1 = 0 then
|
||||||
|
loop (n lsr 1)
|
||||||
|
else
|
||||||
|
1 + loop (n lsr 1)
|
||||||
|
in
|
||||||
|
loop n
|
||||||
|
;;
|
||||||
|
|
||||||
|
(* test __popcount8 just to be sure. *)
|
||||||
|
t ~name:(spf "line %d" __LINE__) (fun () ->
|
||||||
|
for i = 0 to 255 do
|
||||||
|
let n = __popcount8 i in
|
||||||
|
let n2 = popcount8_ref i in
|
||||||
|
if n <> n2 then (
|
||||||
|
Printf.printf "bad: i=%d => %d,%d\n" i n n2;
|
||||||
|
assert false
|
||||||
|
)
|
||||||
|
done;
|
||||||
|
true)
|
||||||
|
;;
|
||||||
|
|
||||||
|
t ~name:(spf "line %d" __LINE__) (fun () ->
|
||||||
|
let b = create ~size:10 false in
|
||||||
|
assert_equal 10 (length b);
|
||||||
|
set b 9;
|
||||||
|
for i = 0 to 9 do
|
||||||
|
assert (i = 9 || not (get b i))
|
||||||
|
done;
|
||||||
|
|
||||||
|
resize b 42;
|
||||||
|
assert_equal 42 (length b);
|
||||||
|
for i = 0 to 41 do
|
||||||
|
assert (i = 9 || not (get b i))
|
||||||
|
done;
|
||||||
|
resize b 11;
|
||||||
|
assert_equal 11 (length b);
|
||||||
|
for i = 0 to 11 do
|
||||||
|
assert (i = 9 || not (get b i))
|
||||||
|
done;
|
||||||
|
|
||||||
|
true)
|
||||||
|
;;
|
||||||
|
|
||||||
|
t ~name:(spf "line %d" __LINE__) (fun () ->
|
||||||
|
let v = empty () in
|
||||||
|
resize v 9;
|
||||||
|
inter_into ~into:v (of_list []);
|
||||||
|
true)
|
||||||
|
;;
|
||||||
|
|
||||||
|
t ~name:(spf "line %d" __LINE__) (fun () ->
|
||||||
|
let l = [ 1; 3; 10; 29; 55 ] in
|
||||||
|
let v = init 120 (fun i -> List.mem i l) in
|
||||||
|
assert_equal ~printer:(CCFormat.to_string ppli) l (to_sorted_list v);
|
||||||
|
true)
|
||||||
|
;;
|
||||||
|
|
||||||
|
q ~name:(spf "line %d" __LINE__)
|
||||||
|
Q.(small_list small_nat)
|
||||||
|
(fun l ->
|
||||||
|
let l = CCList.sort_uniq ~cmp:CCInt.compare l in
|
||||||
|
let max = 1 + List.fold_left max 0 l in
|
||||||
|
let v = init max (fun i -> List.mem i l) in
|
||||||
|
assert_equal ~printer:(CCFormat.to_string ppli) l (to_sorted_list v);
|
||||||
|
true)
|
||||||
|
;;
|
||||||
|
|
||||||
|
t ~name:(spf "line %d" __LINE__) (fun () ->
|
||||||
|
let bv = empty () in
|
||||||
|
flip bv 0;
|
||||||
|
resize bv 0;
|
||||||
|
negate_self bv;
|
||||||
|
union_into ~into:bv (of_list [ 2 ]);
|
||||||
|
assert_equal ~printer:(CCFormat.to_string ppli) [ 2 ] (to_list bv);
|
||||||
|
true)
|
||||||
|
;;
|
||||||
|
|
||||||
|
t ~name:(spf "line %d" __LINE__) (fun () ->
|
||||||
|
let bv = empty () in
|
||||||
|
flip bv 0;
|
||||||
|
inter_into ~into:bv (of_list []);
|
||||||
|
negate_self bv;
|
||||||
|
assert_equal ~printer:(CCFormat.to_string ppli) [] (to_list bv);
|
||||||
|
true)
|
||||||
|
;;
|
||||||
|
|
||||||
|
t ~name:(spf "line %d" __LINE__) (fun () ->
|
||||||
|
let v = empty () in
|
||||||
|
union_into ~into:v (of_list [ 9; 16 ]);
|
||||||
|
resize_minimize_memory v 9;
|
||||||
|
Internal_.__check_invariant v;
|
||||||
|
is_empty v)
|
||||||
|
|
||||||
|
module Op = struct
|
||||||
|
type t =
|
||||||
|
| Resize of int
|
||||||
|
| Resize_min_mem of int
|
||||||
|
| Set of int
|
||||||
|
| Reset of int
|
||||||
|
| Set_bool of int * bool
|
||||||
|
| Flip of int
|
||||||
|
| Clear
|
||||||
|
| Clear_and_shrink
|
||||||
|
| Filter_is_odd
|
||||||
|
| Negate
|
||||||
|
| Inter of int list
|
||||||
|
| Union of int list
|
||||||
|
| Diff of int list
|
||||||
|
|
||||||
|
let apply (self : CCBV.t) (op : t) : unit =
|
||||||
|
match op with
|
||||||
|
| Resize n -> resize self n
|
||||||
|
| Resize_min_mem n -> resize_minimize_memory self n
|
||||||
|
| Set i -> set self i
|
||||||
|
| Reset i -> reset self i
|
||||||
|
| Set_bool (i, b) -> set_bool self i b
|
||||||
|
| Flip i -> flip self i
|
||||||
|
| Clear -> clear self
|
||||||
|
| Clear_and_shrink -> clear_and_shrink self
|
||||||
|
| Filter_is_odd -> filter self (fun i -> i mod 2 = 1)
|
||||||
|
| Negate -> negate_self self
|
||||||
|
| Inter l ->
|
||||||
|
let bv' = of_list l in
|
||||||
|
inter_into ~into:self bv'
|
||||||
|
| Union l ->
|
||||||
|
let bv' = of_list l in
|
||||||
|
union_into ~into:self bv'
|
||||||
|
| Diff l ->
|
||||||
|
let bv' = of_list l in
|
||||||
|
diff_into ~into:self bv'
|
||||||
|
|
||||||
|
let post_size sz (self : t) =
|
||||||
|
match self with
|
||||||
|
| Resize i -> i
|
||||||
|
| Resize_min_mem i -> i
|
||||||
|
| Set j | Reset j | Set_bool (j, _) | Flip j -> max sz (j + 1)
|
||||||
|
| Clear -> sz
|
||||||
|
| Clear_and_shrink -> 0
|
||||||
|
| Filter_is_odd | Negate -> sz
|
||||||
|
| Diff _ -> sz
|
||||||
|
| Inter [] | Union [] -> sz
|
||||||
|
| Union l -> max sz (succ (List.fold_left max 0 l))
|
||||||
|
| Inter l -> min sz (succ (List.fold_left max 0 l))
|
||||||
|
|
||||||
|
let gen_ size : t Q.Gen.t =
|
||||||
|
let open Q.Gen in
|
||||||
|
let nonzero =
|
||||||
|
[
|
||||||
|
(3, 0 -- size >|= fun x -> Set x);
|
||||||
|
(3, 0 -- size >|= fun x -> Reset x);
|
||||||
|
( 3,
|
||||||
|
0 -- size >>= fun x ->
|
||||||
|
bool >|= fun y -> Set_bool (x, y) );
|
||||||
|
(3, 0 -- size >|= fun x -> Flip x);
|
||||||
|
]
|
||||||
|
in
|
||||||
|
|
||||||
|
(* random list of integers *)
|
||||||
|
let rand_list =
|
||||||
|
0 -- 200 >>= fun n st ->
|
||||||
|
CCList.init n (fun i ->
|
||||||
|
if bool st then
|
||||||
|
Some i
|
||||||
|
else
|
||||||
|
None)
|
||||||
|
|> CCList.keep_some
|
||||||
|
in
|
||||||
|
|
||||||
|
frequency
|
||||||
|
@@ List.flatten
|
||||||
|
[
|
||||||
|
(if size > 0 then
|
||||||
|
nonzero
|
||||||
|
else
|
||||||
|
[]);
|
||||||
|
[
|
||||||
|
1, return Clear;
|
||||||
|
1, return Clear_and_shrink;
|
||||||
|
1, return Negate;
|
||||||
|
1, return Filter_is_odd;
|
||||||
|
(1, rand_list >|= fun l -> Inter l);
|
||||||
|
(1, rand_list >|= fun l -> Union l);
|
||||||
|
(1, rand_list >|= fun l -> Diff l);
|
||||||
|
(1, 0 -- 100 >|= fun x -> Resize x);
|
||||||
|
(1, 0 -- 100 >|= fun x -> Resize_min_mem x);
|
||||||
|
];
|
||||||
|
]
|
||||||
|
|
||||||
|
let shrink =
|
||||||
|
let open Q.Iter in
|
||||||
|
let module S = Q.Shrink in
|
||||||
|
function
|
||||||
|
| Resize i -> S.int i >|= fun i -> Resize i
|
||||||
|
| Resize_min_mem i -> S.int i >|= fun i -> Resize_min_mem i
|
||||||
|
| Set i -> S.int i >|= fun i -> Resize i
|
||||||
|
| Reset i -> S.int i >|= fun i -> Resize i
|
||||||
|
| Set_bool (i, b) ->
|
||||||
|
S.int i
|
||||||
|
>|= (fun i -> Set_bool (i, b))
|
||||||
|
<+>
|
||||||
|
if b then
|
||||||
|
return @@ Set_bool (i, b)
|
||||||
|
else
|
||||||
|
empty
|
||||||
|
| Flip i -> S.int i >|= fun i -> Flip i
|
||||||
|
| Clear | Clear_and_shrink | Filter_is_odd | Negate -> empty
|
||||||
|
| Inter l -> S.list ~shrink:S.int l >|= fun l -> Inter l
|
||||||
|
| Union l -> S.list ~shrink:S.int l >|= fun l -> Union l
|
||||||
|
| Diff l -> S.list ~shrink:S.int l >|= fun l -> Diff l
|
||||||
|
|
||||||
|
let pp out =
|
||||||
|
let fpf = Format.fprintf in
|
||||||
|
function
|
||||||
|
| Resize i -> fpf out "resize %d" i
|
||||||
|
| Resize_min_mem i -> fpf out "resize_minimize_memory %d" i
|
||||||
|
| Set i -> fpf out "set %d" i
|
||||||
|
| Reset i -> fpf out "reset %d" i
|
||||||
|
| Set_bool (i, b) -> fpf out "set_bool(%d,%b)" i b
|
||||||
|
| Flip i -> fpf out "flip %d" i
|
||||||
|
| Clear -> fpf out "clear"
|
||||||
|
| Clear_and_shrink -> fpf out "clear_and_shrink"
|
||||||
|
| Filter_is_odd -> fpf out "filter_is_odd"
|
||||||
|
| Negate -> fpf out "negate"
|
||||||
|
| Inter l -> fpf out "inter %a" ppli l
|
||||||
|
| Union l -> fpf out "union %a" ppli l
|
||||||
|
| Diff l -> fpf out "diff %a" ppli l
|
||||||
|
|
||||||
|
let arb_l =
|
||||||
|
let rec gen_l sz n =
|
||||||
|
let open Q.Gen in
|
||||||
|
if n = 0 then
|
||||||
|
return []
|
||||||
|
else
|
||||||
|
gen_ sz >>= fun op ->
|
||||||
|
let sz' = post_size sz op in
|
||||||
|
gen_l sz' (n - 1) >|= fun tl -> op :: tl
|
||||||
|
in
|
||||||
|
|
||||||
|
Q.make
|
||||||
|
~print:CCFormat.(to_string @@ Dump.list pp)
|
||||||
|
~shrink:(Q.Shrink.list ~shrink)
|
||||||
|
Q.Gen.(0 -- 30 >>= fun len -> gen_l 0 len)
|
||||||
|
end
|
||||||
|
|
||||||
|
module Ref_ = struct
|
||||||
|
type t = { mutable set: Intset.t; mutable size: int }
|
||||||
|
|
||||||
|
let empty () = { size = 0; set = Intset.empty }
|
||||||
|
|
||||||
|
let to_list self =
|
||||||
|
Intset.to_list self.set |> List.filter (fun x -> x < self.size)
|
||||||
|
|
||||||
|
let pp out (self : t) = ppli out (to_list self)
|
||||||
|
|
||||||
|
let equal_to_bv (self : t) (bv : CCBV.t) : bool =
|
||||||
|
to_list self = CCBV.to_sorted_list bv
|
||||||
|
|
||||||
|
let cardinal self : int =
|
||||||
|
Intset.filter (fun x -> x < self.size) self.set |> Intset.cardinal
|
||||||
|
|
||||||
|
let get (self : t) i = Intset.mem i self.set
|
||||||
|
|
||||||
|
let rec apply_op (self : t) (op : Op.t) =
|
||||||
|
match op with
|
||||||
|
| Resize n | Resize_min_mem n ->
|
||||||
|
self.set <- Intset.filter (fun x -> x < n) self.set;
|
||||||
|
self.size <- n
|
||||||
|
| Set i ->
|
||||||
|
self.size <- max self.size (i + 1);
|
||||||
|
self.set <- Intset.add i self.set
|
||||||
|
| Reset i ->
|
||||||
|
self.size <- max self.size (i + 1);
|
||||||
|
self.set <- Intset.remove i self.set
|
||||||
|
| Set_bool (i, b) ->
|
||||||
|
apply_op self
|
||||||
|
(if b then
|
||||||
|
Set i
|
||||||
|
else
|
||||||
|
Reset i)
|
||||||
|
| Flip i ->
|
||||||
|
self.size <- max self.size (i + 1);
|
||||||
|
apply_op self
|
||||||
|
(if Intset.mem i self.set then
|
||||||
|
Reset i
|
||||||
|
else
|
||||||
|
Set i)
|
||||||
|
| Clear -> self.set <- Intset.empty
|
||||||
|
| Clear_and_shrink ->
|
||||||
|
self.set <- Intset.empty;
|
||||||
|
self.size <- 0
|
||||||
|
| Filter_is_odd -> self.set <- Intset.filter (fun x -> x mod 2 = 1) self.set
|
||||||
|
| Negate ->
|
||||||
|
let l' =
|
||||||
|
CCList.init self.size (fun x -> x)
|
||||||
|
|> List.filter (fun x -> not (Intset.mem x self.set))
|
||||||
|
in
|
||||||
|
self.set <- Intset.of_list l'
|
||||||
|
| Inter l ->
|
||||||
|
let s' = Intset.of_list l in
|
||||||
|
let sz' = List.fold_left (fun s x -> max s (x + 1)) 0 l in
|
||||||
|
self.size <- min self.size sz';
|
||||||
|
self.set <- Intset.inter self.set s'
|
||||||
|
| Union l ->
|
||||||
|
let s' = Intset.of_list l in
|
||||||
|
self.size <- List.fold_left (fun s x -> max s (x + 1)) self.size l;
|
||||||
|
self.set <- Intset.union self.set s'
|
||||||
|
| Diff l ->
|
||||||
|
let s' = Intset.of_list l in
|
||||||
|
self.set <- Intset.diff self.set s'
|
||||||
|
end
|
||||||
|
;;
|
||||||
|
|
||||||
|
q ~name:"list ops: invariant" ~max_fail:1 ~long_factor:10 ~count:20_000 Op.arb_l
|
||||||
|
(fun ops ->
|
||||||
|
let bv = empty () in
|
||||||
|
|
||||||
|
Internal_.__check_invariant bv;
|
||||||
|
List.iter
|
||||||
|
(fun op ->
|
||||||
|
Op.apply bv op;
|
||||||
|
Internal_.__check_invariant bv)
|
||||||
|
ops;
|
||||||
|
true)
|
||||||
|
;;
|
||||||
|
|
||||||
|
q ~name:"list ops: compare to ref" ~max_fail:1 ~long_factor:10 ~count:2_000
|
||||||
|
Op.arb_l (fun ops ->
|
||||||
|
let bv = empty () in
|
||||||
|
let bv' = Ref_.empty () in
|
||||||
|
|
||||||
|
List.iter
|
||||||
|
(fun op ->
|
||||||
|
Op.apply bv op;
|
||||||
|
Ref_.apply_op bv' op;
|
||||||
|
|
||||||
|
if cardinal bv <> Ref_.cardinal bv' then
|
||||||
|
Q.Test.fail_reportf
|
||||||
|
"@[<v2>different cardinal:@ actual=%a@ ref=%a@ @[<v2>actual.card \
|
||||||
|
%d@]@ @[<v2>ref.cardinal %d@]@]"
|
||||||
|
pp bv Ref_.pp bv' (cardinal bv) (Ref_.cardinal bv');
|
||||||
|
|
||||||
|
let bad_idx =
|
||||||
|
Iter.(0 -- CCBV.length bv)
|
||||||
|
|> Iter.find_pred (fun i -> get bv i <> Ref_.get bv' i)
|
||||||
|
in
|
||||||
|
(match bad_idx with
|
||||||
|
| None -> ()
|
||||||
|
| Some idx ->
|
||||||
|
Q.Test.fail_reportf
|
||||||
|
"at idx %d, not same `get`@ actual.get=%b,@ ref.get=%b" idx
|
||||||
|
(get bv idx) (Ref_.get bv' idx));
|
||||||
|
|
||||||
|
if not (Ref_.equal_to_bv bv' bv) then
|
||||||
|
Q.Test.fail_reportf
|
||||||
|
"@[<v2>not equal:@ actual=%a@ ref=%a@ @[<v2>actual.to_list@ %a@]@ \
|
||||||
|
@[<v2>ref.to_list@ %a@]@]"
|
||||||
|
pp bv Ref_.pp bv' ppli (to_sorted_list bv) ppli (Ref_.to_list bv'))
|
||||||
|
ops;
|
||||||
|
true)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue