From b70a8d875e43251c3d1aa60c3d603d8b4d9fd3b5 Mon Sep 17 00:00:00 2001 From: Leonid Rozenberg Date: Mon, 17 Apr 2017 19:00:39 -0400 Subject: [PATCH 1/6] One is _a_ megalomaniac --- README.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.adoc b/README.adoc index 669e979a..d9451e23 100644 --- a/README.adoc +++ b/README.adoc @@ -23,7 +23,7 @@ Containers is: - A usable, reasonably well-designed library that extends OCaml's standard library (in 'src/core/', packaged under `containers` in ocamlfind. Modules are totally independent and are prefixed with `CC` (for "containers-core" - or "companion-cube" because I'm megalomaniac). This part should be + or "companion-cube" because I'm a megalomaniac). This part should be usable and should work. For instance, `CCList` contains functions and lists including safe versions of `map` and `append`. It also provides a drop-in replacement to the standard library, in the module From d8a55a98b9ef052d3c7cff150c78ed40a79cab33 Mon Sep 17 00:00:00 2001 From: Leonid Rozenberg Date: Wed, 19 Apr 2017 02:15:09 -0400 Subject: [PATCH 2/6] Size tracking implementation --- src/data/CCBV.ml | 313 +++++++++++++++++++++++++++++++--------------- src/data/CCBV.mli | 38 ++++-- 2 files changed, 240 insertions(+), 111 deletions(-) diff --git a/src/data/CCBV.ml b/src/data/CCBV.ml index f00d69f2..144661d4 100644 --- a/src/data/CCBV.ml +++ b/src/data/CCBV.ml @@ -1,80 +1,117 @@ -(* This file is free software, part of containers. See file "license" for more details. *) - (** {2 Imperative Bitvectors} *) -let __width = Sys.word_size - 2 +let __width = Sys.word_size - 1 -(* int with [n] ones *) -let rec __shift bv n = - if n = 0 - then bv - else __shift ((bv lsl 1) lor 1) (n-1) +(** We use OCamls ints to store the bits. We index them from the + least significant bit. We create masks to zero out the most significant + bits that aren't used to store values. *) +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 -(* 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 +let __all_ones = __lsb_masks.(__width) (* 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 + recurse 0 n + +(* Can I access the "private" members in testing? $Q + (Q.int_bound (Sys.word_size - 1)) (fun i -> __count_bits __lsb_masks.(i) = i) +*) + +type t = { + mutable a : int array; + mutable size : int; +} + +let length t = t.size + +let empty () = { a = [| |] ; size = 0 } + +let __to_array_legnth size = + if size mod __width = 0 then size / __width else (size / __width) + 1 + +let create ~size default = + if size = 0 then { a = [| |] ; size } + else begin + let n = __to_array_legnth size in + let arr = if default + then Array.make n __all_ones + else Array.make n 0 + in + (* adjust last bits *) + let r = size mod __width in + if default && r <> 0 + then Array.unsafe_set arr (n-1) __lsb_masks.(r); + { a = arr; size } + end + +(*$Q + (Q.pair Q.small_int Q.bool) (fun (size, b) -> create ~size b |> length = size) +*) + +(*$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 ; size = bv.size } + +(*$Q + (Q.list Q.small_int) (fun l -> \ + let bv = of_list l in to_list bv = to_list (copy bv)) +*) + +let capacity bv = __width * Array.length bv.a let cardinal bv = let n = ref 0 in - for i = 0 to length bv - 1 do + for i = 0 to Array.length bv.a - 1 do n := !n + __count_bits bv.a.(i) done; !n +(*$Q + Q.small_int (fun size -> create ~size true |> cardinal = size) +*) + +let __really_resize bv ~desired ~current size = + let a' = Array.make desired 0 in + Array.blit bv.a 0 a' 0 current; + bv.a <- a'; + bv.size <- size + +let __grow bv size = + if size <= capacity bv (* within capacity *) + then bv.size <- size + else (* beyond capacity *) + let desired = __to_array_legnth size in + let current = Array.length bv.a in + __really_resize bv ~desired ~current size + +let __shrink bv size = + let desired = __to_array_legnth size in + let current = Array.length bv.a in + __really_resize bv ~desired ~current size + +let resize bv size = + if size < 0 then invalid_arg "resize: negative size" else + if size < bv.size (* shrink *) + then __shrink bv size + else if size = bv.size + then () + else __grow bv size + (*$R let bv1 = CCBV.create ~size:87 true in assert_equal ~printer:string_of_int 87 (CCBV.cardinal bv1); @@ -94,12 +131,12 @@ let is_empty bv = 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 + if i < 0 then invalid_arg "get: negative index" else + let n = i / __width in + let i = i mod __width in + if n < Array.length bv.a + then (Array.unsafe_get bv.a n) land (1 lsl i) <> 0 + else false (*$R let bv = CCBV.create ~size:99 false in @@ -118,11 +155,11 @@ let get bv i = *) 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) + if i < 0 then invalid_arg "set: negative index" else + let n = i / __width in + let j = i mod __width in + if i >= bv.size then __grow bv i; + Array.unsafe_set bv.a n ((Array.unsafe_get bv.a n) lor (1 lsl j)) (*$T let bv = create ~size:3 false in set bv 0; get bv 0 @@ -130,22 +167,22 @@ let set bv i = *) 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)) + if i < 0 then invalid_arg "reset: negative index" else + let n = i / __width in + let j = i mod __width in + if i >= bv.size then __grow bv i; + Array.unsafe_set bv.a n ((Array.unsafe_get bv.a n) land (lnot (1 lsl j))) (*$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) + if i < 0 then invalid_arg "reset: negative index" else + let n = i / __width in + let j = i mod __width in + if i >= bv.size then __grow bv i; + Array.unsafe_set bv.a n ((Array.unsafe_get bv.a n) lxor (1 lsl j)) (*$R let bv = of_list [1;10; 11; 30] in @@ -163,7 +200,7 @@ let flip bv i = *) let clear bv = - Array.iteri (fun i _ -> bv.a.(i) <- 0) bv.a + Array.fill bv.a 0 (Array.length bv.a) 0 (*$T let bv = create ~size:37 true in cardinal bv = 37 && (clear bv; cardinal bv= 0) @@ -242,8 +279,9 @@ let to_list bv = let to_sorted_list bv = List.rev (to_list bv) +(* Interpret these as indices. *) let of_list l = - let size = List.fold_left max 0 l in + let size = (List.fold_left max 0 l) + 1 in let bv = create ~size false in List.iter (fun i -> set bv i) l; bv @@ -276,18 +314,61 @@ let filter bv p = to_sorted_list bv = [2;4;6] *) +let negate_self b = + let len = Array.length b.a in + for n = 0 to len - 1 do + Array.unsafe_set b.a n (lnot (Array.unsafe_get b.a n)) + 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)) + +(*$T + let v = of_list [1;2;5;7;] in negate_self v; \ + cardinal v = (List.length [0;3;4;6]) +*) + +let negate b = + let a = Array.map (lnot) b.a in + let r = b.size mod __width in + if r <> 0 then begin + let l = Array.length b.a - 1 in + Array.unsafe_set a l (__lsb_masks.(r) land (Array.unsafe_get a l)) + end; + { a ; size = b.size } + +(*$Q + Q.small_int (fun size -> create ~size false |> negate |> cardinal = size) +*) + +(* Underlying size grows for union. *) 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) + if into.size < bv.size + then __grow into bv.size; + for i = 0 to (Array.length into.a) - 1 do + Array.unsafe_set into.a i + ((Array.unsafe_get into.a i) lor (Array.unsafe_get bv.a i)) done -let union bv1 bv2 = - let bv = copy bv1 in - union_into ~into:bv bv2; - bv +(* To avoid potentially 2 passes, figure out what we need to copy. *) +let union b1 b2 = + if b1.size <= b2.size + then begin + let into = copy b2 in + for i = 0 to (Array.length b1.a) - 1 do + Array.unsafe_set into.a i + ((Array.unsafe_get into.a i) lor (Array.unsafe_get b1.a i)) + done; + into + end else begin + let into = copy b1 in + for i = 0 to (Array.length b1.a) - 1 do + Array.unsafe_set into.a i + ((Array.unsafe_get into.a i) lor (Array.unsafe_get b2.a i)) + done; + into + end (*$R let bv1 = CCBV.of_list [1;2;3;4] in @@ -302,22 +383,32 @@ let union bv1 bv2 = union (of_list [1;2;3;4;5]) (of_list [7;3;5;6]) |> to_sorted_list = CCList.range 1 7 *) +(* Underlying size shrinks for inter. *) 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) + if into.size > bv.size + then __shrink into bv.size; + for i = 0 to (Array.length into.a) - 1 do + Array.unsafe_set into.a i + ((Array.unsafe_get into.a i) land (Array.unsafe_get 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 +let inter b1 b2 = + if b1.size <= b2.size + then begin + let into = copy b1 in + for i = 0 to (Array.length b1.a) - 1 do + Array.unsafe_set into.a i + ((Array.unsafe_get into.a i) land (Array.unsafe_get b2.a i)) + done; + into + end else begin + let into = copy b2 in + for i = 0 to (Array.length b2.a) - 1 do + Array.unsafe_set into.a i + ((Array.unsafe_get into.a i) land (Array.unsafe_get b1.a i)) + done; + into + end (*$T inter (of_list [1;2;3;4]) (of_list [2;4;6;1]) |> to_sorted_list = [1;2;4] @@ -331,6 +422,20 @@ let inter bv1 bv2 = assert_equal [3;4] l; *) +(* Underlying size depends on the 'in_' set for diff, so we don't change + it's size! *) +let diff_into ~into bv = + let n = min (Array.length into.a) (Array.length bv.a) in + for i = 0 to n - 1 do + Array.unsafe_set into.a i + ((Array.unsafe_get into.a i) land (lnot (Array.unsafe_get bv.a i))) + done + +let diff ~in_ not_in = + let into = copy in_ in + diff_into ~into not_in; + into + let select bv arr = let l = ref [] in begin try diff --git a/src/data/CCBV.mli b/src/data/CCBV.mli index 36bb217f..414d50c2 100644 --- a/src/data/CCBV.mli +++ b/src/data/CCBV.mli @@ -21,14 +21,19 @@ val copy : t -> t (** Copy of bitvector *) val cardinal : t -> int -(** Number of bits set *) +(** Number of set bits. *) val length : t -> int -(** Length of underlying array *) +(** Length of underlying bitvector. *) + +val capacity : t -> int +(** The number of bits this bitvector can store without resizing. *) val resize : t -> int -> unit -(** Resize the BV so that it has at least the given physical length - [resize bv n] should make [bv] able to store [(Sys.word_size - 2)* n] bits *) +(** Resize the BV so that it has the specified length. This can grow or shrink + the underlying bitvector. + + @raise Invalid_arg on negative sizes. *) val is_empty : t -> bool (** Any bit set? *) @@ -62,7 +67,10 @@ val to_sorted_list : t -> int list increasing order *) val of_list : int list -> t -(** From a list of true bits *) +(** From a list of true bits. + + The bits are interpreted as indices into the returned bitvector, so the final + bitvector will have [length t] equal to 1 more than max of list indices. *) val first : t -> int (** First set bit, or @@ -72,11 +80,21 @@ val filter : t -> (int -> bool) -> unit (** [filter bv p] only keeps the true bits of [bv] whose [index] satisfies [p index] *) +val negate_self : t -> unit +(** [negate_self t] flips all of the bits in [t]. *) + +val negate : t -> t +(** [negate t] returns a copy of [t] with all of the bits flipped. *) + val union_into : into:t -> t -> unit -(** [union ~into bv] sets [into] to the union of itself and [bv]. *) +(** [union ~into bv] sets [into] to the union of itself and [bv]. + + Note that [into] will grow to accammodate the union. *) val inter_into : into:t -> t -> unit -(** [inter ~into bv] sets [into] to the intersection of itself and [bv] *) +(** [inter ~into bv] sets [into] to the intersection of itself and [bv] + + Note that [into] will shrink to accammodate the union. *) val union : t -> t -> t (** [union bv1 bv2] returns the union of the two sets *) @@ -84,6 +102,12 @@ val union : t -> t -> t val inter : t -> t -> t (** [inter bv1 bv2] returns the intersection of the two sets *) +val diff_into : into:t -> t -> unit +(** [diff ~into t] Modify [into] with only the bits set but not in [t]. *) + +val diff : in_:t -> t -> t +(** [diff ~in_ t] Return those bits found [in_] but not in [t]. *) + val select : t -> 'a array -> 'a list (** [select arr bv] selects the elements of [arr] whose index corresponds to a true bit in [bv]. If [bv] is too short, elements of [arr] From f90f73f671dc82af197d010f4ed23c7dda033e24 Mon Sep 17 00:00:00 2001 From: Leonid Rozenberg Date: Wed, 19 Apr 2017 12:26:47 -0400 Subject: [PATCH 3/6] Incorporate reviewier feedback. Also added style elements from PR#116. --- src/data/CCBV.ml | 212 ++++++++++++++++++++++++++-------------------- src/data/CCBV.mli | 58 ++++++++----- 2 files changed, 160 insertions(+), 110 deletions(-) diff --git a/src/data/CCBV.ml b/src/data/CCBV.ml index 144661d4..179e482b 100644 --- a/src/data/CCBV.ml +++ b/src/data/CCBV.ml @@ -1,29 +1,30 @@ (** {2 Imperative Bitvectors} *) -let __width = Sys.word_size - 1 +let width_ = Sys.word_size - 1 (** We use OCamls ints to store the bits. We index them from the least significant bit. We create masks to zero out the most significant - bits that aren't used to store values. *) -let __lsb_masks = - let a = Array.make (__width + 1) 0 in - for i = 1 to __width do + 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) +let all_ones_ = lsb_masks_.(width_) (* count the 1 bits in [n]. See https://en.wikipedia.org/wiki/Hamming_weight *) -let __count_bits n = +let count_bits_ n = let rec recurse count n = if n = 0 then count else recurse (count+1) (n land (n-1)) in recurse 0 n (* Can I access the "private" members in testing? $Q - (Q.int_bound (Sys.word_size - 1)) (fun i -> __count_bits __lsb_masks.(i) = i) + (Q.int_bound (Sys.word_size - 1)) (fun i -> count_bits_ lsb_masks_.(i) = i) *) type t = { @@ -35,23 +36,24 @@ let length t = t.size let empty () = { a = [| |] ; size = 0 } -let __to_array_legnth size = - if size mod __width = 0 then size / __width else (size / __width) + 1 +let array_length_of_size size = + if size mod width_ = 0 then size / width_ else (size / width_) + 1 let create ~size default = - if size = 0 then { a = [| |] ; size } - else begin - let n = __to_array_legnth size in - let arr = if default - then Array.make n __all_ones + if size = 0 then { a = [| |]; size } + else ( + let n = capa_of_size size in + let a = if default + then Array.make n all_ones_ else Array.make n 0 in (* adjust last bits *) - let r = size mod __width in - if default && r <> 0 - then Array.unsafe_set arr (n-1) __lsb_masks.(r); - { a = arr; size } - end + let r = size mod width_ in + if default && r <> 0 then ( + Array.unsafe_set a (n-1) lsb_masks_.(r); + ); + { a; size } + ) (*$Q (Q.pair Q.small_int Q.bool) (fun (size, b) -> create ~size b |> length = size) @@ -65,52 +67,69 @@ let create ~size default = create ~size:29 true |> to_sorted_list = CCList.range 0 28 *) -let copy bv = { a = Array.copy bv.a ; size = bv.size } +let copy bv = { bv with 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 capacity bv = __width * Array.length bv.a +let capacity bv = width_ * Array.length bv.a + +(* iterate on words of width (at most) [width_] *) +let iter_words ~f bv: unit = + if bv.size = 0 then () + else ( + let len = array_length_of_size bv.size in + assert (len>0); + for i = 0 to len-1 do + let word = Array.unsafe_get a i in + f i word + done; + if r <> 0 then f (len-1) (Array.unsafe_get a (len-1) land lsb_masks_.(r)); + ) let cardinal bv = - let n = ref 0 in - for i = 0 to Array.length bv.a - 1 do - n := !n + __count_bits bv.a.(i) - done; - !n + if bv.size = 0 then 0 + else ( + let n = ref 0 in + for i = 0 to Array.length bv.a - 1 do + n := !n + count_bits_ bv.a.(i) (* MSB of last element are all 0 *) + done; + !n + ) (*$Q Q.small_int (fun size -> create ~size true |> cardinal = size) *) -let __really_resize bv ~desired ~current size = +let really_resize_ bv ~desired ~current size = let a' = Array.make desired 0 in Array.blit bv.a 0 a' 0 current; bv.a <- a'; bv.size <- size -let __grow bv size = +let grow_ bv size = if size <= capacity bv (* within capacity *) then bv.size <- size - else (* beyond capacity *) - let desired = __to_array_legnth size in + else ( (* beyond capacity *) + let desired = array_length_of_size size in let current = Array.length bv.a in - __really_resize bv ~desired ~current size + really_resize_ bv ~desired ~current size + ) -let __shrink bv size = - let desired = __to_array_legnth size in +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 + really_resize_ bv ~desired ~current size let resize bv size = if size < 0 then invalid_arg "resize: negative size" else if size < bv.size (* shrink *) - then __shrink bv size + then shrink_ bv size else if size = bv.size then () - else __grow bv size + else grow_ bv size (*$R let bv1 = CCBV.create ~size:87 true in @@ -124,19 +143,19 @@ let resize bv size = let is_empty bv = try for i = 0 to Array.length bv.a - 1 do - if bv.a.(i) <> 0 then raise Exit + if bv.a.(i) <> 0 then raise Exit (* MSB of last element are all 0 *) done; true with Exit -> false let get bv i = - if i < 0 then invalid_arg "get: negative index" else - let n = i / __width in - let i = i mod __width in - if n < Array.length bv.a - then (Array.unsafe_get bv.a n) land (1 lsl i) <> 0 - else false + if i < 0 then invalid_arg "get: negative index"; + let n = i / width_ in + let i = i mod width_ in + if n < Array.length bv.a + then (Array.unsafe_get bv.a n) land (1 lsl i) <> 0 + else false (*$R let bv = CCBV.create ~size:99 false in @@ -155,11 +174,13 @@ let get bv i = *) let set bv i = - if i < 0 then invalid_arg "set: negative index" else - let n = i / __width in - let j = i mod __width in - if i >= bv.size then __grow bv i; + if i < 0 then invalid_arg "set: negative index" + else ( + let n = i / width_ in + let j = i mod width_ in + if i >= bv.size then grow_ bv i; Array.unsafe_set bv.a n ((Array.unsafe_get bv.a n) lor (1 lsl j)) + ) (*$T let bv = create ~size:3 false in set bv 0; get bv 0 @@ -167,36 +188,40 @@ let set bv i = *) let reset bv i = - if i < 0 then invalid_arg "reset: negative index" else - let n = i / __width in - let j = i mod __width in - if i >= bv.size then __grow bv i; + if i < 0 then invalid_arg "reset: negative index" + else ( + let n = i / width_ in + let j = i mod width_ in + if i >= bv.size then grow_ bv i; Array.unsafe_set bv.a n ((Array.unsafe_get bv.a n) land (lnot (1 lsl j))) + ) (*$T let bv = create ~size:3 false in set bv 0; reset bv 0; not (get bv 0) *) let flip bv i = - if i < 0 then invalid_arg "reset: negative index" else - let n = i / __width in - let j = i mod __width in - if i >= bv.size then __grow bv i; + if i < 0 then invalid_arg "reset: negative index" + else ( + let n = i / width_ in + let j = i mod width_ in + if i >= bv.size then grow_ bv i; Array.unsafe_set bv.a n ((Array.unsafe_get bv.a n) lxor (1 lsl j)) + ) (*$R let bv = of_list [1;10; 11; 30] in flip bv 10; - assert_equal [1;11;30] (to_sorted_list bv); - assert_equal false (get bv 10); + assert_equal ~printer:Q.Print.(list int) [1;11;30] (to_sorted_list bv); + assert_equal ~printer:Q.Print.bool false (get bv 10); flip bv 10; - assert_equal true (get bv 10); + assert_equal ~printer:Q.Print.bool true (get bv 10); flip bv 5; - assert_equal [1;5;10;11;30] (to_sorted_list bv); - assert_equal true (get bv 5); + assert_equal ~printer:Q.Print.(list int) [1;5;10;11;30] (to_sorted_list bv); + assert_equal ~printer:Q.Print.bool true (get bv 5); flip bv 100; - assert_equal [1;5;10;11;30;100] (to_sorted_list bv); - assert_equal true (get bv 100); + assert_equal ~printer:Q.Print.(list int) [1;5;10;11;30;100] (to_sorted_list bv); + assert_equal ~printer:Q.Print.bool true (get bv 100); *) let clear bv = @@ -216,11 +241,17 @@ let clear bv = 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 + for n = 0 to len - 2 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 j = max 0 (width_ * (len - 2)) in + let r = size mod width_ in + let final_length = if r = 0 then width_ else r in + for i = 0 to final_length - 1 do + f (j + i) (bv.a.(len - 1) land (i lsl i) <> 0) done (*$R @@ -232,14 +263,7 @@ let iter bv f = *) 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 + iter bv (fun i b -> if b then f i else ()) (*$T of_list [1;5;7] |> iter_true |> Sequence.to_list |> List.sort CCOrd.compare = [1;5;7] @@ -294,15 +318,19 @@ let of_list l = exception FoundFirst of int -let first bv = +let first_exn bv = try iter_true bv (fun i -> raise (FoundFirst i)); raise Not_found with FoundFirst i -> i +let first bv = + try Some (first_exn bv) + with Not_found -> None + (*$T - of_list [50; 10; 17; 22; 3; 12] |> first = 3 + of_list [50; 10; 17; 22; 3; 12] |> first = Some 3 *) let filter bv p = @@ -319,10 +347,10 @@ let negate_self b = for n = 0 to len - 1 do Array.unsafe_set b.a n (lnot (Array.unsafe_get b.a n)) done; - let r = b.size mod __width in + 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)) + Array.unsafe_set b.a l (lsb_masks_.(r) land (Array.unsafe_get b.a l)) (*$T let v = of_list [1;2;5;7;] in negate_self v; \ @@ -331,10 +359,10 @@ let negate_self b = let negate b = let a = Array.map (lnot) b.a in - let r = b.size mod __width in + let r = b.size mod width_ in if r <> 0 then begin let l = Array.length b.a - 1 in - Array.unsafe_set a l (__lsb_masks.(r) land (Array.unsafe_get a l)) + Array.unsafe_set a l (lsb_masks_.(r) land (Array.unsafe_get a l)) end; { a ; size = b.size } @@ -345,7 +373,7 @@ let negate b = (* Underlying size grows for union. *) let union_into ~into bv = if into.size < bv.size - then __grow into bv.size; + then grow_ into bv.size; for i = 0 to (Array.length into.a) - 1 do Array.unsafe_set into.a i ((Array.unsafe_get into.a i) lor (Array.unsafe_get bv.a i)) @@ -354,21 +382,21 @@ let union_into ~into bv = (* To avoid potentially 2 passes, figure out what we need to copy. *) let union b1 b2 = if b1.size <= b2.size - then begin + then ( let into = copy b2 in for i = 0 to (Array.length b1.a) - 1 do Array.unsafe_set into.a i ((Array.unsafe_get into.a i) lor (Array.unsafe_get b1.a i)) done; into - end else begin + ) else ( let into = copy b1 in for i = 0 to (Array.length b1.a) - 1 do Array.unsafe_set into.a i ((Array.unsafe_get into.a i) lor (Array.unsafe_get b2.a i)) done; into - end + ) (*$R let bv1 = CCBV.of_list [1;2;3;4] in @@ -386,7 +414,7 @@ let union b1 b2 = (* Underlying size shrinks for inter. *) let inter_into ~into bv = if into.size > bv.size - then __shrink into bv.size; + then shrink_ into bv.size; for i = 0 to (Array.length into.a) - 1 do Array.unsafe_set into.a i ((Array.unsafe_get into.a i) land (Array.unsafe_get bv.a i)) @@ -394,21 +422,21 @@ let inter_into ~into bv = let inter b1 b2 = if b1.size <= b2.size - then begin + then ( let into = copy b1 in for i = 0 to (Array.length b1.a) - 1 do Array.unsafe_set into.a i ((Array.unsafe_get into.a i) land (Array.unsafe_get b2.a i)) done; into - end else begin + ) else ( let into = copy b2 in for i = 0 to (Array.length b2.a) - 1 do Array.unsafe_set into.a i ((Array.unsafe_get into.a i) land (Array.unsafe_get b1.a i)) done; into - end + ) (*$T inter (of_list [1;2;3;4]) (of_list [2;4;6;1]) |> to_sorted_list = [1;2;4] @@ -431,7 +459,7 @@ let diff_into ~into bv = ((Array.unsafe_get into.a i) land (lnot (Array.unsafe_get bv.a i))) done -let diff ~in_ not_in = +let diff in_ not_in = let into = copy in_ in diff_into ~into not_in; into @@ -474,10 +502,12 @@ let selecti bv arr = assert_equal [("b",1); ("c",2); ("f",5)] l; *) -(*$T +(*$= & ~printer:Q.Print.(list (pair int int)) selecti (of_list [1;4;3]) [| 0;1;2;3;4;5;6;7;8 |] \ + [1,1; 3,3; 4,4] (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] -*) + |> List.sort CCOrd.compare) + *) type 'a sequence = ('a -> unit) -> unit diff --git a/src/data/CCBV.mli b/src/data/CCBV.mli index 414d50c2..4d0e4bae 100644 --- a/src/data/CCBV.mli +++ b/src/data/CCBV.mli @@ -3,9 +3,13 @@ (** {2 Imperative Bitvectors} - The size of the bitvector is rounded up to the multiple of 30 or 62. - In other words some functions such as {!iter} might iterate on more - bits than what was originally asked for. + {b BREAKING CHANGES} since NEXT_RELEASE: + size is now stored along with the bitvector. Some functions have + a new signature. + + The size of the bitvector used to be rounded up to the multiple of 30 or 62. + In other words some functions such as {!iter} would iterate on more + bits than what was originally asked for. This is not the case anymore. *) type t @@ -21,13 +25,18 @@ val copy : t -> t (** Copy of bitvector *) val cardinal : t -> int -(** Number of set bits. *) +(** Number of bits set to one, seen as a set of bits. *) val length : t -> int -(** Length of underlying bitvector. *) +(** Size of underlying bitvector. + This is not related to the underlying implementation. + Changed at NEXT_RELEASE +*) val capacity : t -> int -(** The number of bits this bitvector can store without resizing. *) +(** The number of bits this bitvector can store without resizing. + + @since NEXT_RELEASE *) val resize : t -> int -> unit (** Resize the BV so that it has the specified length. This can grow or shrink @@ -36,19 +45,19 @@ val resize : t -> int -> unit @raise Invalid_arg on negative sizes. *) val is_empty : t -> bool -(** Any bit set? *) +(** Are there any true bits? *) val set : t -> int -> unit -(** Set i-th bit. *) +(** Set i-th bit, extending the bitvector if needed. *) val get : t -> int -> bool (** Is the i-th bit true? Returns false if the index is too high*) val reset : t -> int -> unit -(** Set i-th bit to 0 *) +(** Set i-th bit to 0, extending the bitvector if needed. *) val flip : t -> int -> unit -(** Flip i-th bit *) +(** Flip i-th bit, extending the bitvector if needed. *) val clear : t -> unit (** Set every bit to 0 *) @@ -72,16 +81,23 @@ val of_list : int list -> t The bits are interpreted as indices into the returned bitvector, so the final bitvector will have [length t] equal to 1 more than max of list indices. *) -val first : t -> int -(** First set bit, or - @raise Not_found if all bits are 0 *) +val first : t -> int option +(** First set bit, or return None. + changed type at NEXT_RELEASE *) + +val first_exn : t -> int + (** First set bit, or + @raise Not_found if all bits are 0 + @since NEXT_RELEASE *) val filter : t -> (int -> bool) -> unit (** [filter bv p] only keeps the true bits of [bv] whose [index] satisfies [p index] *) 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]. + + @since NEXT_RELEASE *) val negate : t -> t (** [negate t] returns a copy of [t] with all of the bits flipped. *) @@ -89,12 +105,12 @@ val negate : t -> t val union_into : into:t -> t -> unit (** [union ~into bv] sets [into] to the union of itself and [bv]. - Note that [into] will grow to accammodate the union. *) + Also updates the length of [into] to be at least [length bv]. *) val inter_into : into:t -> t -> unit (** [inter ~into bv] sets [into] to the intersection of itself and [bv] - Note that [into] will shrink to accammodate the union. *) + Also updates the length of [into] to be at most [length bv]. *) val union : t -> t -> t (** [union bv1 bv2] returns the union of the two sets *) @@ -103,10 +119,14 @@ val inter : t -> t -> t (** [inter bv1 bv2] returns the intersection of the two sets *) val diff_into : into:t -> t -> unit -(** [diff ~into t] Modify [into] with only the bits set but not in [t]. *) +(** [diff ~into t] Modify [into] with only the bits set but not in [t]. -val diff : in_:t -> t -> t -(** [diff ~in_ t] Return those bits found [in_] but not in [t]. *) + @since NEXT_RELEASE *) + +val diff : t -> t -> t +(** [diff t1 t2] Return those bits found [t1] but not in [t2]. + + @since NEXT_RELEASE *) val select : t -> 'a array -> 'a list (** [select arr bv] selects the elements of [arr] whose index From 2b148f0055827dd3dac4e110c6a360216d14d2d9 Mon Sep 17 00:00:00 2001 From: Leonid Rozenberg Date: Wed, 19 Apr 2017 12:29:23 -0400 Subject: [PATCH 4/6] Add self to authors --- AUTHORS.adoc | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.adoc b/AUTHORS.adoc index fd3da2dc..9ac9544e 100644 --- a/AUTHORS.adoc +++ b/AUTHORS.adoc @@ -19,3 +19,4 @@ - Malcolm Matalka (`orbitz`) - David Sheets (@dsheets) - Glenn Slotte (glennsl) +- Leonid Rozenberg (@rleonid) From 0b53ed01a33599b2718142eefda4c362b404f2c5 Mon Sep 17 00:00:00 2001 From: Leonid Rozenberg Date: Wed, 19 Apr 2017 14:52:13 -0400 Subject: [PATCH 5/6] Merge and bug fix pass --- src/data/CCBV.ml | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/src/data/CCBV.ml b/src/data/CCBV.ml index 179e482b..94decfdb 100644 --- a/src/data/CCBV.ml +++ b/src/data/CCBV.ml @@ -42,7 +42,7 @@ let array_length_of_size size = let create ~size default = if size = 0 then { a = [| |]; size } else ( - let n = capa_of_size size in + let n = array_length_of_size size in let a = if default then Array.make n all_ones_ else Array.make n 0 @@ -76,19 +76,6 @@ let copy bv = { bv with a = Array.copy bv.a } let capacity bv = width_ * Array.length bv.a -(* iterate on words of width (at most) [width_] *) -let iter_words ~f bv: unit = - if bv.size = 0 then () - else ( - let len = array_length_of_size bv.size in - assert (len>0); - for i = 0 to len-1 do - let word = Array.unsafe_get a i in - f i word - done; - if r <> 0 then f (len-1) (Array.unsafe_get a (len-1) land lsb_masks_.(r)); - ) - let cardinal bv = if bv.size = 0 then 0 else ( @@ -178,7 +165,7 @@ let set bv i = else ( let n = i / width_ in let j = i mod width_ in - if i >= bv.size then grow_ bv i; + if i >= bv.size then grow_ bv (i+1); Array.unsafe_set bv.a n ((Array.unsafe_get bv.a n) lor (1 lsl j)) ) @@ -192,7 +179,7 @@ let reset bv i = else ( let n = i / width_ in let j = i mod width_ in - if i >= bv.size then grow_ bv i; + if i >= bv.size then grow_ bv (i+1); Array.unsafe_set bv.a n ((Array.unsafe_get bv.a n) land (lnot (1 lsl j))) ) @@ -205,7 +192,7 @@ let flip bv i = else ( let n = i / width_ in let j = i mod width_ in - if i >= bv.size then grow_ bv i; + if i >= bv.size then grow_ bv (i+1); Array.unsafe_set bv.a n ((Array.unsafe_get bv.a n) lxor (1 lsl j)) ) @@ -247,11 +234,11 @@ let iter bv f = f (j+i) (bv.a.(n) land (1 lsl i) <> 0) done done; - let j = max 0 (width_ * (len - 2)) in - let r = size mod width_ in + let j = width_ * (len - 1) in + let r = bv.size mod width_ in let final_length = if r = 0 then width_ else r in for i = 0 to final_length - 1 do - f (j + i) (bv.a.(len - 1) land (i lsl i) <> 0) + f (j + i) (bv.a.(len - 1) land (1 lsl i) <> 0) done (*$R @@ -503,9 +490,7 @@ let selecti bv arr = *) (*$= & ~printer:Q.Print.(list (pair int int)) - selecti (of_list [1;4;3]) [| 0;1;2;3;4;5;6;7;8 |] \ [1,1; 3,3; 4,4] (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] |> List.sort CCOrd.compare) *) From 84a537efbd861669948ad0586989d4c62ab28a5b Mon Sep 17 00:00:00 2001 From: Leonid Rozenberg Date: Wed, 19 Apr 2017 15:27:35 -0400 Subject: [PATCH 6/6] Add test for diff --- src/data/CCBV.ml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/data/CCBV.ml b/src/data/CCBV.ml index 94decfdb..e8e6d043 100644 --- a/src/data/CCBV.ml +++ b/src/data/CCBV.ml @@ -451,6 +451,14 @@ let diff in_ not_in = diff_into ~into not_in; into +(*$T + diff (of_list [1;2;3]) (of_list [1;2;3]) |> to_list = []; + diff (of_list [1;2;3]) (of_list [1;2;3;4]) |> to_list = []; + diff (of_list [1;2;3;4]) (of_list [1;2;3]) |> to_list = [4]; + diff (of_list [1;2;3]) (of_list [1;2;3;400]) |> to_list = []; + diff (of_list [1;2;3;400]) (of_list [1;2;3]) |> to_list = [400]; +*) + let select bv arr = let l = ref [] in begin try