ocaml-containers/src/core/CCInt.ml
2022-09-30 20:53:40 +02:00

268 lines
6 KiB
OCaml

(* This file is free software, part of containers. See file "license" for more details. *)
[@@@if ge 4.07]
[@@@else_]
module Stdlib = Pervasives
[@@@endif]
[@@@if ge 4.08]
include Int
[@@@endif]
type t = int
type 'a iter = ('a -> unit) -> unit
let zero = 0
let one = 1
let minus_one = -1
let add = ( + )
let sub = ( - )
let mul = ( * )
let div = ( / )
let succ = succ
let pred = pred
let abs = abs
let max_int = max_int
let min_int = min_int
let equal (a : int) b = Stdlib.( = ) a b
let compare (a : int) b = compare a b
(* use FNV:
https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function *)
let hash (n : int) : int =
let offset_basis = 0xcbf29ce484222325L in
let prime = 0x100000001b3L in
let h = ref offset_basis in
for k = 0 to 7 do
(h := Int64.(mul !h prime));
(* h := h xor (k-th byte of n) *)
h := Int64.(logxor !h (of_int ((n lsr (k * 8)) land 0xff)))
done;
Int64.to_int !h land max_int
(* truncate back to int and remove sign *)
let range i j yield =
let rec up i j yield =
if i = j then
yield i
else (
yield i;
up (i + 1) j yield
)
and down i j yield =
if i = j then
yield i
else (
yield i;
down (i - 1) j yield
)
in
if i <= j then
up i j yield
else
down i j yield
let range' i j yield =
if i < j then
range i (j - 1) yield
else if i = j then
()
else
range i (j + 1) yield
let sign i = compare i 0
let neg i = -i
let pow a b =
let rec aux acc = function
| 1 -> acc
| n ->
if n mod 2 = 0 then
aux (acc * acc) (n / 2)
else
acc * aux (acc * acc) (n / 2)
in
match b with
| 0 ->
if a = 0 then
raise (Invalid_argument "pow: undefined value 0^0")
else
1
| b when b < 0 ->
raise (Invalid_argument "pow: can't raise int to negative power")
| b -> aux a b
module Infix : sig
val ( = ) : t -> t -> bool
val ( <> ) : t -> t -> bool
val ( < ) : t -> t -> bool
val ( > ) : t -> t -> bool
val ( <= ) : t -> t -> bool
val ( >= ) : t -> t -> bool
val ( -- ) : t -> t -> t iter
val ( --^ ) : t -> t -> t iter
val ( + ) : t -> t -> t
val ( - ) : t -> t -> t
val ( ~- ) : t -> t
val ( * ) : t -> t -> t
val ( / ) : t -> t -> t
val ( ** ) : t -> t -> t
val ( mod ) : t -> t -> t
val ( land ) : t -> t -> t
val ( lor ) : t -> t -> t
val ( lxor ) : t -> t -> t
val lnot : t -> t
val ( lsl ) : t -> int -> t
val ( lsr ) : t -> int -> t
val ( asr ) : t -> int -> t
end = struct
include Stdlib
let ( -- ) = range
let ( --^ ) = range'
let ( ** ) = pow
end
include Infix
let min : t -> t -> t = Stdlib.min
let max : t -> t -> t = Stdlib.max
let floor_div a n =
if a < 0 && n >= 0 then
((a + 1) / n) - 1
else if a > 0 && n < 0 then
((a - 1) / n) - 1
else
a / n
let bool_neq (a : bool) b = Stdlib.( <> ) a b
let rem a n =
let y = a mod n in
if bool_neq (y < 0) (n < 0) && y <> 0 then
y + n
else
y
type 'a printer = Format.formatter -> 'a -> unit
type 'a random_gen = Random.State.t -> 'a
let random n st = Random.State.int st n
let random_small = random 100
let random_range i j st = i + random (j - i) st
let pp fmt = Format.pp_print_int fmt
let most_significant_bit = -1 lxor (-1 lsr 1)
let to_string = string_of_int
let of_string s = try Some (int_of_string s) with Failure _ -> None
let of_string_exn = Stdlib.int_of_string
let to_float = float_of_int
let of_float = int_of_float
type output = char -> unit
(* abstract printer *)
let to_binary_gen (out : output) n =
let n =
if n < 0 then (
out '-';
-n
) else
n
in
out '0';
out 'b';
let rec loop started bit n =
if bit = 0 then (
if not started then out '0'
) else (
let b = n land bit in
if b = 0 then (
if started then out '0';
loop started (bit lsr 1) n
) else (
out '1';
loop true (bit lsr 1) n
)
)
in
loop false most_significant_bit n
let pp_binary out n = to_binary_gen (Format.pp_print_char out) n
let to_string_binary n =
let buf = Buffer.create 16 in
to_binary_gen (Buffer.add_char buf) n;
Buffer.contents buf
let range_by ~step i j yield =
let rec range i j yield =
if i = j then
yield i
else (
yield i;
range (i + step) j yield
)
in
if step = 0 then
raise (Invalid_argument "CCInt.range_by")
else if
if step > 0 then
i > j
else
i < j
then
()
else
range i (((j - i) / step * step) + i) yield
(*
from https://en.wikipedia.org/wiki/Hamming_weight
//This uses fewer arithmetic operations than any other known
//implementation on machines with slow multiplication.
//It uses 17 arithmetic operations.
int popcount_2(uint64_t x) {
x -= (x >> 1) & m1; //put count of each 2 bits into those 2 bits
x = (x & m2) + ((x >> 2) & m2); //put count of each 4 bits into those 4 bits
x = (x + (x >> 4)) & m4; //put count of each 8 bits into those 8 bits
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 popcount (b : int) : int =
let m1 = 0x5555555555555555L in
let m2 = 0x3333333333333333L in
let m4 = 0x0f0f0f0f0f0f0f0fL in
let open Int64 in
let b = of_int b in
(* int->int64 *)
let b = logand b 0x7fffffffffffffffL in
(* remove sign bit, we deal with uint64 here *)
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 logand = ( land )
let logor = ( lor )
let logxor = ( lxor )
let lognot = lnot
let shift_left = ( lsl )
let shift_right = ( asr )
let shift_right_logical = ( lsr )