10
1
mirror of https://gitlab.com/scemama/QCaml.git synced 2024-06-26 15:12:05 +02:00
QCaml/common/lib/bitstring.ml

291 lines
11 KiB
OCaml
Raw Normal View History

2020-12-27 16:36:25 +01:00
(* Single-integer implementation :noexport: *)
2020-12-26 01:47:55 +01:00
2019-03-26 01:20:17 +01:00
2020-12-28 01:55:03 +01:00
(* [[file:~/QCaml/common/bitstring.org::*Single-integer%20implementation][Single-integer implementation:1]] *)
2020-12-26 01:47:55 +01:00
module One = struct
2019-03-26 01:20:17 +01:00
let of_int x =
assert (x > 0); x
let numbits _ = 63
let zero = 0
let is_zero x = x = 0
let shift_left x i = x lsl i
let shift_right x i = x lsr i
let shift_left_one i = 1 lsl i
2020-12-27 16:36:25 +01:00
let testbit x i = ( (x lsr i) land 1 ) = 1
2019-03-26 01:20:17 +01:00
let logor a b = a lor b
2020-12-27 16:36:25 +01:00
let neg a = - a
2019-03-26 01:20:17 +01:00
let logxor a b = a lxor b
let logand a b = a land b
let lognot a = lnot a
let minus_one a = a - 1
let plus_one a = a + 1
let popcount = function
| 0 -> 0
2020-12-27 16:36:25 +01:00
| r -> Util.popcnt (Int64.of_int r)
2019-03-26 01:20:17 +01:00
2020-12-27 16:36:25 +01:00
let trailing_zeros r =
Util.trailz (Int64.of_int r)
2019-03-26 01:20:17 +01:00
let hamdist a b =
a lxor b
2020-12-27 16:36:25 +01:00
|> popcount
2019-03-26 01:20:17 +01:00
2020-12-27 16:36:25 +01:00
let pp ppf s =
Format.fprintf ppf "@[@[%a@]@]" (Util.pp_bitstring 64)
2019-03-26 01:20:17 +01:00
(Z.of_int s)
end
2020-12-26 01:47:55 +01:00
(* Single-integer implementation:1 ends here *)
2019-03-26 01:20:17 +01:00
2020-12-27 16:36:25 +01:00
(* Zarith implementation :noexport: *)
2019-03-26 01:20:17 +01:00
2020-12-28 01:55:03 +01:00
(* [[file:~/QCaml/common/bitstring.org::*Zarith%20implementation][Zarith implementation:1]] *)
2019-03-26 01:20:17 +01:00
module Many = struct
let of_z x = x
let zero = Z.zero
let is_zero x = x = Z.zero
2020-12-27 16:36:25 +01:00
let shift_left = Z.shift_left
2019-03-26 01:20:17 +01:00
let shift_right = Z.shift_right
let shift_left_one i = Z.shift_left Z.one i
let testbit = Z.testbit
let logor = Z.logor
let logxor = Z.logxor
let logand = Z.logand
let lognot = Z.lognot
2019-12-03 12:25:31 +01:00
let neg = Z.neg
2019-03-26 01:20:17 +01:00
let minus_one = Z.pred
let plus_one = Z.succ
let trailing_zeros = Z.trailing_zeros
let hamdist = Z.hamdist
let numbits i = max (Z.numbits i) 64
2020-12-27 16:36:25 +01:00
let popcount z =
2019-03-26 01:20:17 +01:00
if z = Z.zero then 0 else Z.popcount z
2020-12-27 16:36:25 +01:00
let pp ppf s =
2019-12-03 12:25:31 +01:00
Format.fprintf ppf "@[@[%a@]@]" (Util.pp_bitstring (Z.numbits s)) s
2019-03-26 01:20:17 +01:00
end
2020-12-26 01:47:55 +01:00
(* Zarith implementation:1 ends here *)
2019-03-26 01:20:17 +01:00
2020-12-28 01:55:03 +01:00
(* [[file:~/QCaml/common/bitstring.org::*Type][Type:2]] *)
2020-12-26 01:47:55 +01:00
type t =
| One of int
| Many of Z.t
(* Type:2 ends here *)
2019-03-26 01:20:17 +01:00
2020-12-27 17:38:04 +01:00
2020-12-27 23:08:12 +01:00
(* | ~of_int~ | Creates a bit string from an ~int~ |
* | ~of_z~ | Creates a bit string from an ~Z.t~ multi-precision integer |
* | ~zero~ | ~zero n~ creates a zero bit string with ~n~ bits |
* | ~is_zero~ | True if all the bits of the bit string are zero. |
* | ~numbits~ | Returns the number of bits used to represent the bit string |
* | ~testbit~ | ~testbit t n~ is true if the ~n~-th bit of the bit string ~t~ is set to ~1~ |
* | ~neg~ | Returns the negative of the integer interpretation of the bit string |
* | ~shift_left~ | ~shift_left t n~ returns a new bit strings with all the bits shifted ~n~ positions to the left |
* | ~shift_right~ | ~shift_right t n~ returns a new bit strings with all the bits shifted ~n~ positions to the right |
* | ~shift_left_one~ | ~shift_left_one size n~ returns a new bit strings with the ~n~-th bit set to one. It is equivalent as shifting ~1~ by ~n~ bits to the left, ~size~ is the total number of bits of the bit string |
* | ~logor~ | Bitwise logical or |
* | ~logxor~ | Bitwise logical exclusive or |
* | ~logand~ | Bitwise logical and |
* | ~lognot~ | Bitwise logical negation |
* | ~plus_one~ | Takes the integer representation of the bit string and adds one |
* | ~minus_one~ | Takes the integer representation of the bit string and removes one |
* | ~hamdist~ | Returns the Hamming distance, i.e. the number of bits differing between two bit strings |
* | ~trailing_zeros~ | Returns the number of trailing zeros in the bit string |
* | ~permutations~ | ~permutations m n~ generates the list of all possible ~n~-bit strings with ~m~ bits set to ~1~. Algorithm adapted from [[https://graphics.stanford.edu/~seander/bithacks.html#NextBitPermutation][Bit twiddling hacks]] |
* | ~popcount~ | Returns the number of bits set to one in the bit string |
* | ~to_list~ | Converts a bit string into a list of integers indicating the positions where the bits are set to ~1~. The first value for the position is not ~0~ but ~1~ | *)
2020-12-28 01:55:03 +01:00
(* [[file:~/QCaml/common/bitstring.org::*General%20implementation][General implementation:2]] *)
2019-03-26 01:20:17 +01:00
let of_int x =
One (One.of_int x)
2020-12-27 17:38:04 +01:00
2019-03-26 01:20:17 +01:00
let of_z x =
if Z.numbits x < 64 then One (Z.to_int x) else Many (Many.of_z x)
2020-12-29 01:08:37 +01:00
(* General implementation:2 ends here *)
2020-12-27 17:38:04 +01:00
2020-12-29 01:08:37 +01:00
(* #+begin_example
* Bitstring.of_int 15;;
* - : Bitstring.t =
* ++++------------------------------------------------------------
* #+end_example *)
(* [[file:~/QCaml/common/bitstring.org::*General%20implementation][General implementation:4]] *)
2019-03-26 01:20:17 +01:00
let zero = function
2020-12-27 17:38:04 +01:00
| n when n < 64 -> One (One.zero)
| _ -> Many (Many.zero)
2020-12-29 01:08:37 +01:00
2019-03-26 01:20:17 +01:00
let numbits = function
2020-12-27 17:38:04 +01:00
| One x -> One.numbits x
| Many x -> Many.numbits x
2020-12-29 01:08:37 +01:00
2019-03-25 19:28:38 +01:00
let is_zero = function
2020-12-26 01:47:55 +01:00
| One x -> One.is_zero x
| Many x -> Many.is_zero x
2019-03-25 19:28:38 +01:00
2020-12-29 01:08:37 +01:00
2019-12-03 12:25:31 +01:00
let neg = function
2020-12-26 01:47:55 +01:00
| One x -> One (One.neg x)
| Many x -> Many (Many.neg x)
2020-12-27 17:38:04 +01:00
2020-12-29 01:08:37 +01:00
2019-03-26 01:20:17 +01:00
let shift_left x i = match x with
2020-12-27 17:38:04 +01:00
| One x -> One (One.shift_left x i)
| Many x -> Many (Many.shift_left x i)
2019-03-25 19:28:38 +01:00
2020-12-29 01:08:37 +01:00
2019-03-26 01:20:17 +01:00
let shift_right x i = match x with
2020-12-27 17:38:04 +01:00
| One x -> One (One.shift_right x i)
| Many x -> Many (Many.shift_right x i)
2019-03-26 01:20:17 +01:00
let shift_left_one = function
2020-12-27 17:38:04 +01:00
| n when n < 64 -> fun i -> One (One.shift_left_one i)
| _ -> fun i -> Many (Many.shift_left_one i)
2019-03-25 19:28:38 +01:00
2019-03-26 01:20:17 +01:00
let testbit = function
2020-12-27 17:38:04 +01:00
| One x -> One.testbit x
| Many x -> Many.testbit x
2020-12-29 01:08:37 +01:00
(* General implementation:4 ends here *)
2020-12-27 17:38:04 +01:00
2020-12-29 01:08:37 +01:00
(* [[file:~/QCaml/common/bitstring.org::*General%20implementation][General implementation:6]] *)
2020-12-27 16:36:25 +01:00
let logor a b =
2019-03-25 19:28:38 +01:00
match a,b with
2019-03-26 01:20:17 +01:00
| One a, One b -> One (One.logor a b)
| Many a, Many b -> Many (Many.logor a b)
| _ -> invalid_arg "Bitstring.logor"
2020-12-27 17:38:04 +01:00
2020-12-29 01:08:37 +01:00
2020-12-27 16:36:25 +01:00
let logxor a b =
2019-03-25 19:28:38 +01:00
match a,b with
2019-03-26 01:20:17 +01:00
| One a, One b -> One (One.logxor a b)
| Many a, Many b -> Many (Many.logxor a b)
| _ -> invalid_arg "Bitstring.logxor"
2020-12-27 17:38:04 +01:00
2020-12-29 01:08:37 +01:00
2020-12-27 16:36:25 +01:00
let logand a b =
2019-03-25 19:28:38 +01:00
match a,b with
2019-03-26 01:20:17 +01:00
| One a, One b -> One (One.logand a b)
| Many a, Many b -> Many (Many.logand a b)
| _ -> invalid_arg "Bitstring.logand"
2020-12-27 17:38:04 +01:00
2020-12-29 01:08:37 +01:00
2019-03-25 19:28:38 +01:00
let lognot = function
2020-12-27 17:38:04 +01:00
| One x -> One (One.lognot x)
| Many x -> Many (Many.lognot x)
2020-12-29 01:08:37 +01:00
(* General implementation:6 ends here *)
2020-12-27 17:38:04 +01:00
2020-12-29 01:08:37 +01:00
(* [[file:~/QCaml/common/bitstring.org::*General%20implementation][General implementation:8]] *)
2019-03-25 19:28:38 +01:00
let minus_one = function
2020-12-27 17:38:04 +01:00
| One x -> One (One.minus_one x)
| Many x -> Many (Many.minus_one x)
2020-12-29 01:08:37 +01:00
let plus_one = function
| One x -> One (One.plus_one x)
| Many x -> Many (Many.plus_one x)
(* General implementation:8 ends here *)
2019-03-25 19:28:38 +01:00
2020-12-27 17:38:04 +01:00
2020-12-27 23:08:12 +01:00
2020-12-28 01:55:03 +01:00
(* #+begin_example
2020-12-29 01:08:37 +01:00
* Bitstring.(plus_one (of_int 15));;
* - : Bitstring.t =
* ----+-----------------------------------------------------------
*
* Bitstring.(minus_one (of_int 15));;
* - : Bitstring.t =
* -+++------------------------------------------------------------
2020-12-28 01:55:03 +01:00
* #+end_example *)
2020-12-27 17:38:04 +01:00
2020-12-29 01:08:37 +01:00
(* [[file:~/QCaml/common/bitstring.org::*General%20implementation][General implementation:9]] *)
2019-03-25 19:28:38 +01:00
let trailing_zeros = function
2020-12-27 17:38:04 +01:00
| One x -> One.trailing_zeros x
| Many x -> Many.trailing_zeros x
2020-12-29 01:08:37 +01:00
2019-03-26 01:20:17 +01:00
let hamdist a b = match a, b with
2020-12-27 17:38:04 +01:00
| One a, One b -> One.hamdist a b
| Many a, Many b -> Many.hamdist a b
| _ -> invalid_arg "Bitstring.hamdist"
2020-12-29 01:08:37 +01:00
2019-03-26 01:20:17 +01:00
let popcount = function
2020-12-27 17:38:04 +01:00
| One x -> One.popcount x
| Many x -> Many.popcount x
2020-12-29 01:08:37 +01:00
(* General implementation:9 ends here *)
2020-12-27 23:08:12 +01:00
2019-03-26 01:20:17 +01:00
2020-12-27 17:38:04 +01:00
2020-12-28 01:55:03 +01:00
(* #+begin_example
2020-12-29 01:08:37 +01:00
* Bitstring.(trailing_zeros (of_int 12));;
* - : int = 2
*
* Bitstring.(hamdist (of_int 15) (of_int 73));;
* - : int = 3
*
* Bitstring.(popcount (of_int 15));;
* - : int = 4
2020-12-28 01:55:03 +01:00
* #+end_example *)
2020-12-27 17:38:04 +01:00
2020-12-29 01:08:37 +01:00
(* [[file:~/QCaml/common/bitstring.org::*General%20implementation][General implementation:10]] *)
2019-03-26 01:20:17 +01:00
let rec to_list ?(accu=[]) = function
| t when (is_zero t) -> List.rev accu
2019-03-25 19:28:38 +01:00
| t -> let newlist =
(trailing_zeros t + 1)::accu
2020-12-27 17:38:04 +01:00
in
logand t @@ minus_one t
|> (to_list [@tailcall]) ~accu:newlist
2020-12-29 01:08:37 +01:00
(* General implementation:10 ends here *)
2019-03-25 19:28:38 +01:00
2020-12-27 17:38:04 +01:00
2020-12-28 01:55:03 +01:00
(* #+begin_example
2020-12-29 01:08:37 +01:00
* Bitstring.(to_list (of_int 45));;
* - : int list = [1; 3; 4; 6]
2020-12-28 01:55:03 +01:00
* #+end_example *)
2020-12-27 17:38:04 +01:00
2020-12-29 01:08:37 +01:00
(* [[file:~/QCaml/common/bitstring.org::*General%20implementation][General implementation:12]] *)
2020-12-27 16:36:25 +01:00
let permutations m n =
2019-03-25 19:28:38 +01:00
let rec aux k u rest =
if k=1 then
List.rev (u :: rest)
else
2019-12-03 12:25:31 +01:00
let t = logor u @@ minus_one u in
2019-03-25 19:28:38 +01:00
let t' = plus_one t in
2019-12-03 12:25:31 +01:00
let not_t = lognot t in
let neg_not_t = neg not_t in
let t'' = shift_right (minus_one @@ logand not_t neg_not_t) (trailing_zeros u + 1) in
(*
2019-03-25 19:28:38 +01:00
let t'' = shift_right (minus_one (logand (lognot t) t')) (trailing_zeros u + 1) in
2019-12-03 12:25:31 +01:00
*)
2019-09-10 18:39:14 +02:00
(aux [@tailcall]) (k-1) (logor t' t'') (u :: rest)
2019-03-25 19:28:38 +01:00
in
2019-03-26 18:02:22 +01:00
aux (Util.binom n m) (minus_one (shift_left_one n m)) []
2020-12-29 01:08:37 +01:00
(* General implementation:12 ends here *)
2019-03-25 19:28:38 +01:00
2020-12-28 01:55:03 +01:00
(* [[file:~/QCaml/common/bitstring.org::*Printers][Printers:2]] *)
2020-12-26 01:47:55 +01:00
let pp ppf = function
2020-12-27 17:38:04 +01:00
| One x -> One.pp ppf x
| Many x -> Many.pp ppf x
2020-12-26 01:47:55 +01:00
(* Printers:2 ends here *)