(** 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)