ocaml-containers/src/core/CCNativeint.ml

241 lines
6 KiB
OCaml

(* This file is free software, part of containers. See file "license" for more details. *)
open CCShims_
include Nativeint
let min : t -> t -> t = Stdlib.min
let max : t -> t -> t = Stdlib.max
let hash x = Stdlib.abs (to_int x)
let sign i = compare i zero
let pow a b =
let rec aux acc = function
| 1n -> acc
| n ->
if equal (rem n 2n) zero
then aux (mul acc acc) (div n 2n)
else mul acc (aux (mul acc acc) (div n 2n))
in
match b with
| 0n -> if equal a 0n then raise (Invalid_argument "pow: undefined value 0^0") else 1n
| b when compare b 0n < 0 -> raise (Invalid_argument "pow: can't raise int to negative power")
| b -> aux a b
(*$T
pow 2n 10n = 1024n
pow 2n 15n = 32768n
pow 10n 5n = 100000n
pow 42n 0n = 1n
pow 0n 1n = 0n
*)
let floor_div a n =
if compare a 0n < 0 && compare n 0n >= 0 then
sub (div (add a 1n) n) 1n
else if compare a 0n > 0 && compare n 0n < 0 then
sub (div (sub a 1n) n) 1n
else
div a n
(*$T
(floor_div 3n 5n = 0n)
(floor_div 5n 5n = 1n)
(floor_div 20n 5n = 4n)
(floor_div 12n 5n = 2n)
(floor_div 0n 5n = 0n)
(floor_div (-1n) 5n = -1n)
(floor_div (-5n) 5n = -1n)
(floor_div (-12n) 5n = -3n)
(floor_div 0n (-5n) = 0n)
(floor_div 3n (-5n) = -1n)
(floor_div 5n (-5n) = -1n)
(floor_div 9n (-5n) = -2n)
(floor_div 20n (-5n) = -4n)
(floor_div (-2n) (-5n) = 0n)
(floor_div (-8n) (-5n) = 1n)
(floor_div (-35n) (-5n) = 7n)
try ignore (floor_div 12n 0n); false with Division_by_zero -> true
try ignore (floor_div (-12n) 0n); false with Division_by_zero -> true
*)
(*$Q
(Q.pair (Q.map of_int Q.small_signed_int) (Q.map of_int Q.small_nat)) \
(fun (n, m) -> let m = m + 1n in \
floor_div n m = of_float @@ floor (to_float n /. to_float m))
(Q.pair (Q.map of_int Q.small_signed_int) (Q.map of_int Q.small_nat)) \
(fun (n, m) -> let m = m + 1n in \
floor_div n (-m) = of_float @@ floor (to_float n /. to_float (-m)))
*)
type 'a printer = Format.formatter -> 'a -> unit
type 'a random_gen = Random.State.t -> 'a
type 'a iter = ('a -> unit) -> unit
let range i j yield =
let rec up i j yield =
if equal i j then yield i
else (
yield i;
up (add i 1n) j yield
)
and down i j yield =
if equal i j then yield i
else (
yield i;
down (sub i 1n) j yield
)
in
if compare i j <= 0 then up i j yield else down i j yield
(*$= & ~printer:Q.Print.(list to_string)
[0n;1n;2n;3n;4n;5n] (range 0n 5n |> Iter.to_list)
[0n] (range 0n 0n |> Iter.to_list)
[5n;4n;3n;2n] (range 5n 2n |> Iter.to_list)
*)
let range' i j yield =
if compare i j < 0 then range i (sub j 1n) yield
else if equal i j then ()
else range i (add j 1n) yield
let range_by ~step i j yield =
let rec range i j yield =
if equal i j then yield i
else (
yield i;
range (add i step) j yield
)
in
if equal step 0n then
raise (Invalid_argument "CCNativeint.range_by")
else if (if compare step 0n > 0 then compare i j > 0 else compare i j < 0) then ()
else range i (add (mul (div (sub j i) step) step) i) yield
(* note: the last test checks that no error occurs due to overflows. *)
(*$= & ~printer:Q.Print.(list to_string)
[0n] (range_by ~step:1n 0n 0n |> Iter.to_list)
[] (range_by ~step:1n 5n 0n |> Iter.to_list)
[] (range_by ~step:2n 1n 0n |> Iter.to_list)
[0n;2n;4n] (range_by ~step:2n 0n 4n |> Iter.to_list)
[0n;2n;4n] (range_by ~step:2n 0n 5n |> Iter.to_list)
[0n] (range_by ~step:(neg 1n) 0n 0n |> Iter.to_list)
[] (range_by ~step:(neg 1n) 0n 5n |> Iter.to_list)
[] (range_by ~step:(neg 2n) 0n 1n |> Iter.to_list)
[5n;3n;1n] (range_by ~step:(neg 2n) 5n 1n |> Iter.to_list)
[5n;3n;1n] (range_by ~step:(neg 2n) 5n 0n |> Iter.to_list)
[0n] (range_by ~step:max_int 0n 2n |> Iter.to_list)
*)
(*$Q
Q.(pair (map of_int small_int) (map of_int small_int)) (fun (i,j) -> \
let i = min i j and j = max i j in \
CCList.equal CCNativeint.equal \
(CCNativeint.range_by ~step:1n i j |> Iter.to_list) \
(CCNativeint.range i j |> Iter.to_list) )
*)
let random n st = Random.State.nativeint st n
let random_small = random 100n
let random_range i j st = add i (random (sub j i) st)
(** {2 Conversion} *)
let of_string_exn = of_string
let of_string x = try Some (of_string_exn x) with Failure _ -> None
let of_string_opt = of_string
let most_significant_bit =
logxor (neg 1n) (shift_right_logical (neg 1n) 1)
type output = char -> unit
(* abstract printer *)
let to_binary_gen (out:output) n =
let n = if compare n 0n <0 then (out '-'; neg n) else n in
out '0'; out 'b';
let rec loop started bit n =
if equal bit 0n then (
if not started then out '0'
) else (
let b = logand n bit in
if equal b 0n then (
if started then out '0';
loop started (shift_right_logical bit 1) n
) else (
out '1';
loop true (shift_right_logical bit 1) n
)
)
in
loop false most_significant_bit n
let to_string_binary n =
let buf = Buffer.create 16 in
to_binary_gen (Buffer.add_char buf) n;
Buffer.contents buf
(*$= & ~printer:CCFun.id
"0b111" (to_string_binary 7n)
"-0b111" (to_string_binary (-7n))
"0b0" (to_string_binary 0n)
*)
(** {2 Printing} *)
let pp out n = Format.pp_print_string out (to_string n)
let pp_binary out n =
to_binary_gen (Format.pp_print_char out) n
(** {2 Infix Operators} *)
module Infix = struct
let (+) = add
let (-) = sub
let (~-) = neg
let ( * ) = mul
let (/) = div
let ( ** ) = pow
let (--) = range
let (--^) = range'
let (mod) = rem
let (land) = logand
let (lor) = logor
let (lxor) = logxor
let lnot = lognot
let (lsl) = shift_left
let (lsr) = shift_right_logical
let (asr) = shift_right
let (=) = equal
let (<>) = Stdlib.(<>)
let (<) = Stdlib.(<)
let (<=) = Stdlib.(<=)
let (>) = Stdlib.(>)
let (>=) = Stdlib.(>=)
end
include Infix