mte/unikernel/duniverse/Zarith/q.ml

575 lines
15 KiB
OCaml
Raw Normal View History

2025-11-11 02:07:51 +01:00
(**
Rationals.
This file is part of the Zarith library
http://forge.ocamlcore.org/projects/zarith .
It is distributed under LGPL 2 licensing, with static linking exception.
See the LICENSE file included in the distribution.
Copyright (c) 2010-2011 Antoine Miné, Abstraction project.
Abstraction is part of the LIENS (Laboratoire d'Informatique de l'ENS),
a joint laboratory by:
CNRS (Centre national de la recherche scientifique, France),
ENS (École normale supérieure, Paris, France),
INRIA Rocquencourt (Institut national de recherche en informatique, France).
*)
type t = {
num: Z.t; (** Numerator. *)
den: Z.t; (** Denominator, >= 0 *)
}
(* Type of rationals.
Invariants:
- den is always >= 0;
- num and den have no common factor;
- if den=0, then num is -1, 0 or 1.
- if num=0, then den is -1, 0 or 1.
*)
(* creation *)
(* -------- *)
(* make *)
let mk n d =
{ num = n; den = d; }
(* make and normalize n/d, assuming d > 0 *)
let make_real n d =
if n == Z.zero || d == Z.one then mk n Z.one
else
let g = Z.gcd n d in
if g == Z.one
then mk n d
else mk (Z.divexact n g) (Z.divexact d g)
(* make and normalize any fraction *)
let make n d =
let sd = Z.sign d in
if sd = 0 then mk (Z.of_int (Z.sign n)) Z.zero else
if sd > 0 then make_real n d else
make_real (Z.neg n) (Z.neg d)
let of_bigint n = mk n Z.one
(* n/1 *)
let of_int n = of_bigint (Z.of_int n)
let of_int32 n = of_bigint (Z.of_int32 n)
let of_int64 n = of_bigint (Z.of_int64 n)
let of_nativeint n = of_bigint (Z.of_nativeint n)
let of_ints n d = make (Z.of_int n) (Z.of_int d)
let zero = of_bigint Z.zero
(* 0/1 *)
let one = of_bigint Z.one
(* 1/1 *)
let minus_one = of_bigint Z.minus_one
(* -1/1 *)
let inf = mk Z.one Z.zero
(* 1/0 *)
let minus_inf = mk Z.minus_one Z.zero
(* -1/0 *)
let undef = mk Z.zero Z.zero
(* 0/0 *)
let of_float d =
if d = infinity then inf else
if d = neg_infinity then minus_inf else
if classify_float d = FP_nan then undef else
let m,e = frexp d in
(* put into the form m * 2^e, where m is an integer *)
let m,e = Z.of_float (ldexp m 53), e-53 in
if e >= 0 then of_bigint (Z.shift_left m e)
else make_real m (Z.shift_left Z.one (-e))
(* queries *)
(* ------- *)
type kind =
| ZERO (* 0 *)
| INF (* 1/0 *)
| MINF (* -1/0 *)
| UNDEF (* 0/0 *)
| NZERO (* non-special, non-0 *)
let classify n =
if n.den == Z.zero then
match Z.sign n.num with
| 1 -> INF
| -1 -> MINF
| _ -> UNDEF
else
if n.num == Z.zero
then ZERO
else NZERO
let is_real n = (n.den != Z.zero)
let num x = x.num
let den x = x.den
let sign x = Z.sign x.num
(* sign undef = 0
sign inf = 1
sign -inf = -1
*)
let equal x y =
(Z.equal x.num y.num) && (Z.equal x.den y.den) && (classify x <> UNDEF)
let compare x y =
match classify x, classify y with
| UNDEF,UNDEF | INF,INF | MINF,MINF -> 0
| UNDEF,_ -> -1
| _,UNDEF -> 1
| MINF,_ | _,INF -> -1
| INF,_ | _,MINF -> 1
| _ ->
if x.den = y.den (* implies equality,
especially if immediate value and not a pointer,
in particular in the case den = 1 *)
then Z.compare x.num y.num
else
Z.compare
(Z.mul x.num y.den)
(Z.mul y.num x.den)
let min a b = if compare a b <= 0 then a else b
let max a b = if compare a b >= 0 then a else b
let leq x y =
match classify x, classify y with
| UNDEF,_ | _,UNDEF -> false
| MINF,_ | _,INF -> true
| INF,_ | _,MINF -> false
| _ ->
if x.den = y.den
then Z.leq x.num y.num
else
Z.leq
(Z.mul x.num y.den)
(Z.mul y.num x.den)
let lt x y =
match classify x, classify y with
| UNDEF,_ | _,UNDEF -> false
| INF,_ | _,MINF -> false
| MINF,_ | _,INF -> true
| _ ->
if x.den = y.den
then Z.lt x.num y.num
else
Z.lt
(Z.mul x.num y.den)
(Z.mul y.num x.den)
let geq x y = leq y x
let gt x y = lt y x
let to_string n =
match classify n with
| UNDEF -> "undef"
| INF -> "+inf"
| MINF -> "-inf"
| ZERO -> "0"
| NZERO ->
if Z.equal n.den Z.one then Z.to_string n.num
else (Z.to_string n.num) ^ "/" ^ (Z.to_string n.den)
let to_bigint x = Z.div x.num x.den
(* raises a Division by zero in case x is undefined or infinity *)
let to_int x = Z.to_int (to_bigint x)
let to_int32 x = Z.to_int32 (to_bigint x)
let to_int64 x = Z.to_int64 (to_bigint x)
let to_nativeint x = Z.to_nativeint (to_bigint x)
let to_float x =
match classify x with
| ZERO -> 0.0
| INF -> infinity
| MINF -> neg_infinity
| UNDEF -> nan
| NZERO ->
let p = x.num and q = x.den in
let np = Z.numbits p and nq = Z.numbits q in
if np <= 53 && nq <= 53 then
(* p and q convert to floats exactly; use FP division to get the
correctly-rounded result. *)
Int64.to_float (Z.to_int64 p) /. Int64.to_float (Z.to_int64 q)
else begin
let negat =
if Z.sign p < 0 then -1 else 1
in
(* p is in [2^(np-1), 2^np)
q is in [2^(nq-1), 2^nq)
We define n,p',q' such that p'/q'*2^n=p/q and |p'/q'| is in [1, 2). *)
let n = np - nq in
(* Scaling p/q by 2^n *)
let (p', q') =
if n >= 0
then (p, Z.shift_left q n)
else (Z.shift_left p (-n), q)
in
let (p', n) =
if Z.geq (Z.abs p') q'
then (p', n)
else (Z.shift_left p' 1, pred n)
in
(* If we divided p' by q' now, the resulting quotient would
have one significant digit. *)
let p' = Z.shift_left p' 54 in
(* When we divide p' by q' next, the resulting quotient will
have 55 significant digits. The strategy is:
- First, compute the quotient with 55 significant digits in
round-to-odd, and
- Second, round that number to the number of effective
significant digits we desire for the result, which is 53
for a normal result and less than 53 for a subnormal result.
We cannot afford an intermediate rounding at 53 significant digits
if the end-result is subnormal. See
https://github.com/ocaml/Zarith/issues/29 *)
(* Euclidean division of p' by q' *)
let (quo, rem) = Z.ediv_rem p' q' in
if n <= -1080
then
(* The end result is +0.0 or -0.0 (depending on negat)
or perhaps the next floating-point number of the same
sign (depending on the current rounding mode. *)
ldexp (float_of_int negat) (-1080)
else
let offset =
if n <= -1023
then
(* The end result will be subnormal, add an offset
to make the rounding happen directly at the place
where it should happend.
quo has the form: 1xxxx...
we add: 1000000...
so as to end up with: 101xxxx... *)
Z.shift_left (Z.of_int negat) (55 + (-1023 - n))
else
Z.zero
in
let quo = Z.add offset quo in
let quo =
if Z.sign rem = 0
then quo
else Z.logor Z.one quo (* round to odd *)
in
(* The FPU rounding mode affects the Z.to_float that comes next,
making the rounding computed according to the current FPU rounding
mode. *)
let f = Z.to_float quo in
(* The subtraction that comes next is exact, so that the rounding
mode does not change what it does. *)
let f = f -. (Z.to_float offset)
in
(* ldexp is also exact and unaffected by the rounding mode.
We have made sure that if the end result is going to be subnormal,
then f has exactly the correct number of significant digits for
no rounding to happen here. *)
ldexp f (n - 54)
end
(* operations *)
(* ---------- *)
let neg x =
mk (Z.neg x.num) x.den
(* neg undef = undef
neg inf = -inf
neg -inf = inf
*)
let abs x =
mk (Z.abs x.num) x.den
(* abs undef = undef
abs inf = abs -inf = inf
*)
(* addition or substraction (zaors) of finite numbers *)
let aors zaors x y =
if x.den == y.den then (* implies equality,
especially if immediate value and not a pointer,
in particular in the case den = 1 *)
make_real (zaors x.num y.num) x.den
else
make_real
(zaors
(Z.mul x.num y.den)
(Z.mul y.num x.den))
(Z.mul x.den y.den)
let add x y =
if x.den == Z.zero || y.den == Z.zero then match classify x, classify y with
| ZERO,_ -> y
| _,ZERO -> x
| UNDEF,_ | _,UNDEF -> undef
| INF,MINF | MINF,INF -> undef
| INF,_ | _,INF -> inf
| MINF,_ | _,MINF -> minus_inf
| NZERO,NZERO -> failwith "impossible case"
else
aors Z.add x y
(* undef + x = x + undef = undef
inf + -inf = -inf + inf = undef
inf + x = x + inf = inf
-inf + x = x + -inf = -inf
*)
let sub x y =
if x.den == Z.zero || y.den == Z.zero then match classify x, classify y with
| ZERO,_ -> neg y
| _,ZERO -> x
| UNDEF,_ | _,UNDEF -> undef
| INF,INF | MINF,MINF -> undef
| INF,_ | _,MINF -> inf
| MINF,_ | _,INF -> minus_inf
| NZERO,NZERO -> failwith "impossible case"
else
aors Z.sub x y
(* sub x y = add x (neg y) *)
let mul x y =
if x.den == Z.zero || y.den == Z.zero then
mk
(Z.of_int ((Z.sign x.num) * (Z.sign y.num)))
Z.zero
else
make_real (Z.mul x.num y.num) (Z.mul x.den y.den)
(* undef * x = x * undef = undef
0 * inf = inf * 0 = 0 * -inf = -inf * 0 = undef
inf * x = x * inf = sign x * inf
-inf * x = x * -inf = - sign x * inf
*)
let inv x =
match Z.sign x.num with
| 1 -> mk x.den x.num
| -1 -> mk (Z.neg x.den) (Z.neg x.num)
| _ -> if x.den == Z.zero then undef else inf
(* 1 / undef = undef
1 / inf = 1 / -inf = 0
1 / 0 = inf
note that: inv (inv -inf) = inf <> -inf
*)
let div x y =
if Z.sign y.num >= 0
then mul x (mk y.den y.num)
else mul x (mk (Z.neg y.den) (Z.neg y.num))
(* undef / x = x / undef = undef
0 / 0 = undef
inf / inf = inf / -inf = -inf / inf = -inf / -inf = undef
0 / inf = 0 / -inf = x / inf = x / -inf = 0
inf / x = sign x * inf
-inf / x = - sign x * inf
inf / 0 = inf
-inf / 0 = -inf
x / 0 = sign x * inf
we have div x y = mul x (inv y)
*)
let mul_2exp x n =
if x.den == Z.zero then x
else make_real (Z.shift_left x.num n) x.den
let div_2exp x n =
if x.den == Z.zero then x
else make_real x.num (Z.shift_left x.den n)
type supported_base =
| B2 | B8 | B10 | B16
let int_of_base = function
| B2 -> 2
| B8 -> 8
| B10 -> 10
| B16 -> 16
(* [find_in_string s ~pos ~last pred] find the first index in the string between [pos]
(inclusive) and [last] (exclusive) that satisfy the predicate [pred] *)
let rec find_in_string s ~pos ~last p =
if pos >= last
then None
else if p s.[pos]
then Some pos
else find_in_string s ~pos:(pos + 1) ~last p
(* The current implementation supports plain decimals, decimal points,
scientific notation ('e' or 'E' for base 10 litteral and 'p' or 'P'
for base 16), and fraction of integers (eg. 1/2). In particular it
accepts any numeric literal accepted by OCaml's lexer.
Restrictions:
- exponents in scientific notation should fit on an integer
- scientific notation only available in hexa and decimal (as in OCaml) *)
let of_string =
(* return a boolean (true for negative) and the next offset to read *)
let parse_sign s i j =
if j < i + 1
then false, i
else
match s.[i] with
| '-' -> true , i + 1
| '+' -> false, i + 1
| _ -> false ,i
in
(* return the base and the next offset to read *)
let parse_base s i j =
if j < i + 2
then B10, i
else
match s.[i],s.[i+1] with
| '0',('x'|'X') -> B16, i + 2
| '0',('o'|'O') -> B8, i + 2
| '0',('b'|'B') -> B2, i + 2
| _ -> B10, i
in
let find_exponent_mark = function
| B10 -> (function 'e' | 'E' -> true | _ -> false)
| B16 -> (function 'p' | 'P' -> true | _ -> false)
| B8 | B2 -> (fun _ -> false)
in
let of_scientific_notation s =
let i = 0 in
let j = String.length s in
let sign,i = parse_sign s i j in
let base,i = parse_base s i j in
(* shift left due to the exponent *)
let shift_left, j =
match find_in_string s ~pos:i ~last:j (find_exponent_mark base) with
| None -> 0, j
| Some ei ->
let pos = ei + 1 in
let ez = Z.of_substring_base 10 s ~pos ~len:(j - pos) in
Z.to_int ez, ei
in
(* shift right due to the radix *)
let z, shift_right =
match base with
| B2 | B8 -> Z.of_substring_base (int_of_base base) s ~pos:i ~len:(j - i), 0
| B10 | B16 ->
match find_in_string s ~pos:i ~last:j ((=) '.') with
| None -> Z.of_substring_base (int_of_base base) s ~pos:i ~len:(j - i), 0
| Some k ->
(* shift_right_factor correspond to the shift to apply when we move the decimal
point one position to the left.
0x1.1p1 = 0x11p-3 = 0x0.11p5
1.1e1 = 11e0 = 0.11e2 *)
let shift_right_factor =
match base with
| B10 -> 1
| B16 -> 4
| B2 | B8 -> assert false
in
(* We should only consider actual digits to perform the shift. *)
let num_digits = ref 0 in
for h = k + 1 to j - 1 do
match s.[h] with
| '0' .. '9' | 'A' .. 'F' | 'a' .. 'f' ->
incr num_digits
| '_' -> ()
| _ ->
(* '-' and '+' could wrongly be accepted by Z.of_string_base *)
invalid_arg "Q.of_string: invalid digit"
done;
let first_digit_after_dot =
match find_in_string s ~pos:(k+1) ~last:j ((<>) '_') with
| None -> j
| Some x -> x
in
let shift = !num_digits * shift_right_factor in
let without_dot =
String.sub s i (k-i)
^ (String.sub s first_digit_after_dot (j - first_digit_after_dot))
in
Z.of_string_base (int_of_base base) without_dot, shift
in
let shift = shift_left - shift_right in
let exponent_pow =
match base with
| B10 -> 10
| B16 -> 2
| B8 | B2 -> 1
in
let abs =
if shift < 0 then
make z (Z.pow (Z.of_int exponent_pow) (~- shift))
else
of_bigint (Z.mul z (Z.pow (Z.of_int exponent_pow) shift))
in
if sign
then neg abs
else abs
in
function
| "" -> zero
| "inf" | "+inf" -> inf
| "-inf" -> minus_inf
| "undef" -> undef
| s ->
try
let i = String.index s '/' in
make
(Z.of_substring s ~pos:0 ~len:i)
(Z.of_substring s ~pos:(i+1) ~len:(String.length s-i-1))
with Not_found ->
of_scientific_notation s
(* printing *)
(* -------- *)
let print x = print_string (to_string x)
let output chan x = output_string chan (to_string x)
let sprint () x = to_string x
let bprint b x = Buffer.add_string b (to_string x)
let pp_print f x = Format.pp_print_string f (to_string x)
(* prefix and infix *)
(* ---------------- *)
let (~-) = neg
let (~+) x = x
let (+) = add
let (-) = sub
let ( * ) = mul
let (/) = div
let (lsl) = mul_2exp
let (asr) = div_2exp
let (~$) = of_int
let (//) = of_ints
let (~$$) = of_bigint
let (///) = make
let (=) = equal
let (<) = lt
let (>) = gt
let (<=) = leq
let (>=) = geq
let (<>) a b = not (equal a b)