feat(Int64): complete CCInt64 with regards to CCInt

This commit is contained in:
Fardale 2020-05-22 23:21:18 +02:00
parent b7dcc7ed2a
commit 576ee2ede8
2 changed files with 336 additions and 38 deletions

View file

@ -3,6 +3,200 @@
open CCShims_
include Int64
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
| 1L -> acc
| n ->
if equal (rem n 2L) zero
then aux (mul acc acc) (div n 2L)
else mul acc (aux (mul acc acc) (div n 2L))
in
match b with
| 0L -> if equal a 0L then raise (Invalid_argument "pow: undefined value 0^0") else 1L
| b when compare b 0L < 0 -> raise (Invalid_argument "pow: can't raise int to negative power")
| b -> aux a b
(*$T
pow 2L 10L = 1024L
pow 2L 15L = 32768L
pow 10L 5L = 100000L
pow 42L 0L = 1L
pow 0L 1L = 0L
*)
let floor_div a n =
if compare a 0L < 0 && compare n 0L >= 0 then
sub (div (add a 1L) n) 1L
else if compare a 0L > 0 && compare n 0L < 0 then
sub (div (sub a 1L) n) 1L
else
div a n
(*$T
(floor_div 3L 5L = 0L)
(floor_div 5L 5L = 1L)
(floor_div 20L 5L = 4L)
(floor_div 12L 5L = 2L)
(floor_div 0L 5L = 0L)
(floor_div (-1L) 5L = -1L)
(floor_div (-5L) 5L = -1L)
(floor_div (-12L) 5L = -3L)
(floor_div 0L (-5L) = 0L)
(floor_div 3L (-5L) = -1L)
(floor_div 5L (-5L) = -1L)
(floor_div 9L (-5L) = -2L)
(floor_div 20L (-5L) = -4L)
(floor_div (-2L) (-5L) = 0L)
(floor_div (-8L) (-5L) = 1L)
(floor_div (-35L) (-5L) = 7L)
try ignore (floor_div 12L 0L); false with Division_by_zero -> true
try ignore (floor_div (-12L) 0L); 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 + 1L 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 + 1L 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 1L) j yield
)
and down i j yield =
if equal i j then yield i
else (
yield i;
down (sub i 1L) j yield
)
in
if compare i j <= 0 then up i j yield else down i j yield
(*$= & ~printer:Q.Print.(list to_string)
[0L;1L;2L;3L;4L;5L] (range 0L 5L |> Iter.to_list)
[0L] (range 0L 0L |> Iter.to_list)
[5L;4L;3L;2L] (range 5L 2L |> Iter.to_list)
*)
let range' i j yield =
if compare i j < 0 then range i (sub j 1L) yield
else if equal i j then ()
else range i (add j 1L) 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 0L then
raise (Invalid_argument "CCInt64.range_by")
else if (if compare step 0L > 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)
[0L] (range_by ~step:1L 0L 0L |> Iter.to_list)
[] (range_by ~step:1L 5L 0L |> Iter.to_list)
[] (range_by ~step:2L 1L 0L |> Iter.to_list)
[0L;2L;4L] (range_by ~step:2L 0L 4L |> Iter.to_list)
[0L;2L;4L] (range_by ~step:2L 0L 5L |> Iter.to_list)
[0L] (range_by ~step:(neg 1L) 0L 0L |> Iter.to_list)
[] (range_by ~step:(neg 1L) 0L 5L |> Iter.to_list)
[] (range_by ~step:(neg 2L) 0L 1L |> Iter.to_list)
[5L;3L;1L] (range_by ~step:(neg 2L) 5L 1L |> Iter.to_list)
[5L;3L;1L] (range_by ~step:(neg 2L) 5L 0L |> Iter.to_list)
[0L] (range_by ~step:max_int 0L 2L |> 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 CCInt64.equal \
(CCInt64.range_by ~step:1L i j |> Iter.to_list) \
(CCInt64.range i j |> Iter.to_list) )
*)
let random n st = Random.State.int64 st n
let random_small = random 100L
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 1L) (shift_right_logical (neg 1L) 1)
type output = char -> unit
(* abstract printer *)
let to_binary_gen (out:output) n =
let n = if compare n 0L <0 then (out '-'; neg n) else n in
out '0'; out 'b';
let rec loop started bit n =
if equal bit 0L then (
if not started then out '0'
) else (
let b = logand n bit in
if equal b 0L 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 7L)
"-0b111" (to_string_binary (-7L))
"0b0" (to_string_binary 0L)
*)
(** {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
@ -14,6 +208,12 @@ module Infix = struct
let (/) = div
let ( ** ) = pow
let (--) = range
let (--^) = range'
let (mod) = rem
let (land) = logand
@ -38,14 +238,4 @@ module Infix = struct
let (>) = Stdlib.(>)
let (>=) = Stdlib.(>=)
end
include Infix
let hash x = Stdlib.abs (to_int x)
(** {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

View file

@ -45,6 +45,14 @@ val ( mod ) : t -> t -> t
[x = ((x / y) * y) + (x mod y)].
If [y = 0], [x mod y] raises [Division_by_zero]. *)
val min : t -> t -> t
(** [min x y] returns the minimum of the two integers [x] and [y].
@since NEXT_RELEASE *)
val max : t -> t -> t
(** [max x y] returns the maximum of the two integers [x] and [y].
@since NEXT_RELEASE *)
val ( land ) : t -> t -> t
(** [x land y] is the bitwise logical and of [x] and [y]. *)
@ -73,40 +81,83 @@ val ( asr ) : t -> int -> t
and inserted in the vacated bits.
The result is unspecified if [y < 0] or [y >= 64]. *)
(** Infix operators
@since 2.1 *)
module Infix : sig
val (+) : t -> t -> t
val (-) : t -> t -> t
val (~-) : 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
val (=) : t -> t -> bool
val (<>) : t -> t -> bool
val (>) : t -> t -> bool
val (>=) : t -> t -> bool
val (<=) : t -> t -> bool
val (<) : t -> t -> bool
end
include module type of Infix
val hash : t -> int
(** [hash x] computes the hash of [x].
Like {!Stdlib.abs (to_int x)}. *)
val sign : t -> int
(** [sign x] return [0] if [x = 0], [-1] if [x < 0] and [1] if [x > 0].
Same as [compare x zero].
@since NEXT_RELEASE*)
val pow : t -> t -> t
(** [pow base exponent] returns [base] raised to the power of [exponent].
[pow x y = x^y] for positive integers [x] and [y].
Raises [Invalid_argument] if [x = y = 0] or [y] < 0.
@since 0.11 *)
val floor_div : t -> t -> t
(** [floor_div x n] is integer division rounding towards negative infinity.
It satisfies [x = m * floor_div x n + rem x n].
@since NEXT_RELEASE *)
type 'a printer = Format.formatter -> 'a -> unit
type 'a random_gen = Random.State.t -> 'a
type 'a iter = ('a -> unit) -> unit
val range_by : step:t -> t -> t -> t iter
(** [range_by ~step i j] iterates on integers from [i] to [j] included,
where the difference between successive elements is [step].
Use a negative [step] for a decreasing list.
@raise Invalid_argument if [step=0].
@since NEXT_RELEASE *)
val range : t -> t -> t iter
(** [range i j] iterates on integers from [i] to [j] included . It works
both for decreasing and increasing ranges.
@since NEXT_RELEASE *)
val range' : t -> t -> t iter
(** [range' i j] is like {!range} but the second bound [j] is excluded.
For instance [range' 0 5 = Iter.of_list [0;1;2;3;4]].
@since NEXT_RELEASE *)
val random : t -> t random_gen
val random_small : t random_gen
val random_range : t -> t -> t random_gen
(** {2 Conversion} *)
val to_int : t -> int
(** [to_int x] converts the given 64-bit integer [x] (type [int64]) into an
integer (type [int]). On 64-bit platforms, the 64-bit integer
is taken modulo 2{^31}, i.e. the high-order bit is lost
during the conversion. On 64-bit platforms, the conversion is exact. *)
val of_int : int -> t
(** [of_int x] converts the given integer [x] (type [int]) into an
64-bit integer (type [int64]).
Alias to {!Int64.of_int}. *)
val to_float : t -> float
(** [to_float x] converts the given 64-bit integer [x]
into a floating-point number (type [float]). *)
val of_float : float -> t
(** [of_float x] converts the given floating-point number [x] into a 64-bit integer,
discarding the fractional part (truncate towards 0).
The result of the conversion is undefined if, after truncation, the number
is outside the range \[{!CCInt64.min_int}, {!CCInt64.max_int}\].
Alias to {!Int64.of_float}. *)
val to_string : t -> string
(** [to_string x] returns the string representation of its argument [x], in signed decimal. *)
val of_string : string -> t option
(** [of_string s] is the safe version of {!of_string_exn}. *)
(** [of_string s] is the safe version of {!of_string_exn}.
Like {!of_string_exn}, but return [None] instead of raising. *)
val of_string_opt : string -> t option
(** [of_string_opt s] is an alias to {!of_string}.
@ -129,3 +180,60 @@ val of_string_exn : string -> t
Raise [Failure "Int64.of_string"] if the given string is not
a valid representation of an integer, or if the integer represented
exceeds the range of integers representable in type [int64]. *)
val to_string_binary : t -> string
(** [to_string_binary x] returns the string representation of the integer [x], in binary.
@since NEXT_RELEASE *)
(** {2 Printing} *)
val pp : t printer
(** [pp ppf x] prints the integer [x] on [ppf].
@since NEXT_RELEASE *)
val pp_binary : t printer
(** [pp_binary ppf x] prints [x] on [ppf].
Print as "0b00101010".
@since NEXT_RELEASE *)
(** {2 Infix Operators} *)
(** Infix operators
@since 2.1 *)
module Infix : sig
val (+) : t -> t -> t
val (-) : t -> t -> t
val (~-) : t -> t
val ( * ) : t -> t -> t
val (/) : t -> t -> t
val ( ** ) : t -> t -> t
(** Alias to {!pow}
@since NEXT_RELEASE *)
val (--) : t -> t -> t iter
(** Alias to {!range}.
@since NEXT_RELEASE *)
val (--^) : t -> t -> t iter
(** Alias to {!range'}.
@since NEXT_RELEASE *)
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
val (=) : t -> t -> bool
val (<>) : t -> t -> bool
val (>) : t -> t -> bool
val (>=) : t -> t -> bool
val (<=) : t -> t -> bool
val (<) : t -> t -> bool
end
include module type of Infix