diff --git a/src/core/CCInt64.ml b/src/core/CCInt64.ml index 1a769c94..ab290798 100644 --- a/src/core/CCInt64.ml +++ b/src/core/CCInt64.ml @@ -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 diff --git a/src/core/CCInt64.mli b/src/core/CCInt64.mli index 8d47ce4f..977e5035 100644 --- a/src/core/CCInt64.mli +++ b/src/core/CCInt64.mli @@ -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