(***********************************************************************) (* *) (* OCaml *) (* *) (* Valerie Menissier-Morain, projet Cristal, INRIA Rocquencourt *) (* *) (* Copyright 1996 Institut National de Recherche en Informatique et *) (* en Automatique. All rights reserved. This file is distributed *) (* under the terms of the GNU Library General Public License, with *) (* the special exception on linking described in file ../../LICENSE. *) (* *) (***********************************************************************) open Int_misc open Nat open Big_int open Arith_flags (* Definition of the type ratio : Conventions : - the denominator is always a positive number - the sign of n/0 is the sign of n These convention is automatically respected when a ratio is created with the create_ratio primitive *) type ratio = { mutable numerator : big_int; mutable denominator : big_int; mutable normalized : bool} let failwith_zero name = let s = "infinite or undefined rational number" in failwith (if String.length name = 0 then s else name ^ " " ^ s) let numerator_ratio r = r.numerator and denominator_ratio r = r.denominator let null_denominator r = sign_big_int r.denominator = 0 let verify_null_denominator r = if sign_big_int r.denominator = 0 then (if !error_when_null_denominator_flag then (failwith_zero "") else true) else false let sign_ratio r = sign_big_int r.numerator (* Physical normalization of rational numbers *) (* 1/0, 0/0 and -1/0 are the normalized forms for n/0 numbers *) let normalize_ratio r = if r.normalized then r else if verify_null_denominator r then begin r.numerator <- big_int_of_int (sign_big_int r.numerator); r.normalized <- true; r end else begin let p = gcd_big_int r.numerator r.denominator in if eq_big_int p unit_big_int then begin r.normalized <- true; r end else begin r.numerator <- div_big_int (r.numerator) p; r.denominator <- div_big_int (r.denominator) p; r.normalized <- true; r end end let cautious_normalize_ratio r = if (!normalize_ratio_flag) then (normalize_ratio r) else r let cautious_normalize_ratio_when_printing r = if (!normalize_ratio_when_printing_flag) then (normalize_ratio r) else r let create_ratio bi1 bi2 = match sign_big_int bi2 with -1 -> cautious_normalize_ratio { numerator = minus_big_int bi1; denominator = minus_big_int bi2; normalized = false } | 0 -> if !error_when_null_denominator_flag then (failwith_zero "create_ratio") else cautious_normalize_ratio { numerator = bi1; denominator = bi2; normalized = false } | _ -> cautious_normalize_ratio { numerator = bi1; denominator = bi2; normalized = false } let create_normalized_ratio bi1 bi2 = match sign_big_int bi2 with -1 -> { numerator = minus_big_int bi1; denominator = minus_big_int bi2; normalized = true } | 0 -> if !error_when_null_denominator_flag then failwith_zero "create_normalized_ratio" else { numerator = bi1; denominator = bi2; normalized = true } | _ -> { numerator = bi1; denominator = bi2; normalized = true } let is_normalized_ratio r = r.normalized let report_sign_ratio r bi = if sign_ratio r = -1 then minus_big_int bi else bi let abs_ratio r = { numerator = abs_big_int r.numerator; denominator = r.denominator; normalized = r.normalized } let is_integer_ratio r = eq_big_int ((normalize_ratio r).denominator) unit_big_int (* Operations on rational numbers *) let add_ratio r1 r2 = if !normalize_ratio_flag then begin let p = gcd_big_int ((normalize_ratio r1).denominator) ((normalize_ratio r2).denominator) in if eq_big_int p unit_big_int then {numerator = add_big_int (mult_big_int (r1.numerator) r2.denominator) (mult_big_int (r2.numerator) r1.denominator); denominator = mult_big_int (r1.denominator) r2.denominator; normalized = true} else begin let d1 = div_big_int (r1.denominator) p and d2 = div_big_int (r2.denominator) p in let n = add_big_int (mult_big_int (r1.numerator) d2) (mult_big_int d1 r2.numerator) in let p' = gcd_big_int n p in { numerator = div_big_int n p'; denominator = mult_big_int d1 (div_big_int (r2.denominator) p'); normalized = true } end end else { numerator = add_big_int (mult_big_int (r1.numerator) r2.denominator) (mult_big_int (r1.denominator) r2.numerator); denominator = mult_big_int (r1.denominator) r2.denominator; normalized = false } let minus_ratio r = { numerator = minus_big_int (r.numerator); denominator = r.denominator; normalized = r.normalized } let add_int_ratio i r = ignore (cautious_normalize_ratio r); { numerator = add_big_int (mult_int_big_int i r.denominator) r.numerator; denominator = r.denominator; normalized = r.normalized } let add_big_int_ratio bi r = ignore (cautious_normalize_ratio r); { numerator = add_big_int (mult_big_int bi r.denominator) r.numerator ; denominator = r.denominator; normalized = r.normalized } let sub_ratio r1 r2 = add_ratio r1 (minus_ratio r2) let mult_ratio r1 r2 = if !normalize_ratio_flag then begin let p1 = gcd_big_int ((normalize_ratio r1).numerator) ((normalize_ratio r2).denominator) and p2 = gcd_big_int (r2.numerator) r1.denominator in let (n1, d2) = if eq_big_int p1 unit_big_int then (r1.numerator, r2.denominator) else (div_big_int (r1.numerator) p1, div_big_int (r2.denominator) p1) and (n2, d1) = if eq_big_int p2 unit_big_int then (r2.numerator, r1.denominator) else (div_big_int r2.numerator p2, div_big_int r1.denominator p2) in { numerator = mult_big_int n1 n2; denominator = mult_big_int d1 d2; normalized = true } end else { numerator = mult_big_int (r1.numerator) r2.numerator; denominator = mult_big_int (r1.denominator) r2.denominator; normalized = false } let mult_int_ratio i r = if !normalize_ratio_flag then begin let p = gcd_big_int ((normalize_ratio r).denominator) (big_int_of_int i) in if eq_big_int p unit_big_int then { numerator = mult_big_int (big_int_of_int i) r.numerator; denominator = r.denominator; normalized = true } else { numerator = mult_big_int (div_big_int (big_int_of_int i) p) r.numerator; denominator = div_big_int (r.denominator) p; normalized = true } end else { numerator = mult_int_big_int i r.numerator; denominator = r.denominator; normalized = false } let mult_big_int_ratio bi r = if !normalize_ratio_flag then begin let p = gcd_big_int ((normalize_ratio r).denominator) bi in if eq_big_int p unit_big_int then { numerator = mult_big_int bi r.numerator; denominator = r.denominator; normalized = true } else { numerator = mult_big_int (div_big_int bi p) r.numerator; denominator = div_big_int (r.denominator) p; normalized = true } end else { numerator = mult_big_int bi r.numerator; denominator = r.denominator; normalized = false } let square_ratio r = ignore (cautious_normalize_ratio r); { numerator = square_big_int r.numerator; denominator = square_big_int r.denominator; normalized = r.normalized } let inverse_ratio r = if !error_when_null_denominator_flag && (sign_big_int r.numerator) = 0 then failwith_zero "inverse_ratio" else {numerator = report_sign_ratio r r.denominator; denominator = abs_big_int r.numerator; normalized = r.normalized} let div_ratio r1 r2 = mult_ratio r1 (inverse_ratio r2) (* Integer part of a rational number *) (* Odd function *) let integer_ratio r = if null_denominator r then failwith_zero "integer_ratio" else if sign_ratio r = 0 then zero_big_int else report_sign_ratio r (div_big_int (abs_big_int r.numerator) (abs_big_int r.denominator)) (* Floor of a rational number *) (* Always less or equal to r *) let floor_ratio r = ignore (verify_null_denominator r); div_big_int (r.numerator) r.denominator (* Round of a rational number *) (* Odd function, 1/2 -> 1 *) let round_ratio r = ignore (verify_null_denominator r); let abs_num = abs_big_int r.numerator in let bi = div_big_int abs_num r.denominator in report_sign_ratio r (if sign_big_int (sub_big_int (mult_int_big_int 2 (sub_big_int abs_num (mult_big_int (r.denominator) bi))) r.denominator) = -1 then bi else succ_big_int bi) let ceiling_ratio r = if (is_integer_ratio r) then r.numerator else succ_big_int (floor_ratio r) (* Comparison operators on rational numbers *) let eq_ratio r1 r2 = ignore (normalize_ratio r1); ignore (normalize_ratio r2); eq_big_int (r1.numerator) r2.numerator && eq_big_int (r1.denominator) r2.denominator let compare_ratio r1 r2 = if verify_null_denominator r1 then let sign_num_r1 = sign_big_int r1.numerator in if (verify_null_denominator r2) then let sign_num_r2 = sign_big_int r2.numerator in if sign_num_r1 = 1 && sign_num_r2 = -1 then 1 else if sign_num_r1 = -1 && sign_num_r2 = 1 then -1 else 0 else sign_num_r1 else if verify_null_denominator r2 then -(sign_big_int r2.numerator) else match compare_int (sign_big_int r1.numerator) (sign_big_int r2.numerator) with 1 -> 1 | -1 -> -1 | _ -> if eq_big_int (r1.denominator) r2.denominator then compare_big_int (r1.numerator) r2.numerator else compare_big_int (mult_big_int (r1.numerator) r2.denominator) (mult_big_int (r1.denominator) r2.numerator) let lt_ratio r1 r2 = compare_ratio r1 r2 < 0 and le_ratio r1 r2 = compare_ratio r1 r2 <= 0 and gt_ratio r1 r2 = compare_ratio r1 r2 > 0 and ge_ratio r1 r2 = compare_ratio r1 r2 >= 0 let max_ratio r1 r2 = if lt_ratio r1 r2 then r2 else r1 and min_ratio r1 r2 = if gt_ratio r1 r2 then r2 else r1 let eq_big_int_ratio bi r = (is_integer_ratio r) && eq_big_int bi r.numerator let compare_big_int_ratio bi r = ignore (normalize_ratio r); if (verify_null_denominator r) then -(sign_big_int r.numerator) else compare_big_int (mult_big_int bi r.denominator) r.numerator let lt_big_int_ratio bi r = compare_big_int_ratio bi r < 0 and le_big_int_ratio bi r = compare_big_int_ratio bi r <= 0 and gt_big_int_ratio bi r = compare_big_int_ratio bi r > 0 and ge_big_int_ratio bi r = compare_big_int_ratio bi r >= 0 (* Coercions *) (* Coercions with type int *) let int_of_ratio r = if ((is_integer_ratio r) && (is_int_big_int r.numerator)) then (int_of_big_int r.numerator) else failwith "integer argument required" and ratio_of_int i = { numerator = big_int_of_int i; denominator = unit_big_int; normalized = true } (* Coercions with type nat *) let ratio_of_nat nat = { numerator = big_int_of_nat nat; denominator = unit_big_int; normalized = true } and nat_of_ratio r = ignore (normalize_ratio r); if not (is_integer_ratio r) then failwith "nat_of_ratio" else if sign_big_int r.numerator > -1 then nat_of_big_int (r.numerator) else failwith "nat_of_ratio" (* Coercions with type big_int *) let ratio_of_big_int bi = { numerator = bi; denominator = unit_big_int; normalized = true } and big_int_of_ratio r = ignore (normalize_ratio r); if is_integer_ratio r then r.numerator else failwith "big_int_of_ratio" let div_int_ratio i r = ignore (verify_null_denominator r); mult_int_ratio i (inverse_ratio r) let div_ratio_int r i = div_ratio r (ratio_of_int i) let div_big_int_ratio bi r = ignore (verify_null_denominator r); mult_big_int_ratio bi (inverse_ratio r) let div_ratio_big_int r bi = div_ratio r (ratio_of_big_int bi) (* Functions on type string *) (* giving floating point approximations of rational numbers *) (* Compares strings that contains only digits, have the same length, from index i to index i + l *) let rec compare_num_string s1 s2 i len = if i >= len then 0 else let c1 = int_of_char s1.[i] and c2 = int_of_char s2.[i] in match compare_int c1 c2 with | 0 -> compare_num_string s1 s2 (succ i) len | c -> c;; (* Position of the leading digit of the decimal expansion *) (* of a strictly positive rational number *) (* if the decimal expansion of a non null rational r is equal to *) (* sigma for k=-P to N of r_k*10^k then msd_ratio r = N *) (* Nota : for a big_int we have msd_ratio = nums_digits_big_int -1 *) (* Tests if s has only zeros characters from index i to index lim *) let rec only_zeros s i lim = i >= lim || s.[i] == '0' && only_zeros s (succ i) lim;; (* Nota : for a big_int we have msd_ratio = nums_digits_big_int -1 *) let msd_ratio r = ignore (cautious_normalize_ratio r); if null_denominator r then failwith_zero "msd_ratio" else if sign_big_int r.numerator == 0 then 0 else begin let str_num = string_of_big_int r.numerator and str_den = string_of_big_int r.denominator in let size_num = String.length str_num and size_den = String.length str_den in let size_min = min size_num size_den in let m = size_num - size_den in let cmp = compare_num_string str_num str_den 0 size_min in match cmp with | 1 -> m | -1 -> pred m | _ -> if m >= 0 then m else if only_zeros str_den size_min size_den then m else pred m end ;; (* Decimal approximations of rational numbers *) (* Approximation with fix decimal point *) (* This is an odd function and the last digit is round off *) (* Format integer_part . decimal_part_with_n_digits *) let approx_ratio_fix n r = (* Don't need to normalize *) if (null_denominator r) then failwith_zero "approx_ratio_fix" else let sign_r = sign_ratio r in if sign_r = 0 then "+0" (* r = 0 *) else (* r.numerator and r.denominator are not null numbers s1 contains one more digit than desired for the round off operation *) if n >= 0 then begin let s1 = string_of_nat (nat_of_big_int (div_big_int (base_power_big_int 10 (succ n) (abs_big_int r.numerator)) r.denominator)) in (* Round up and add 1 in front if needed *) let s2 = if round_futur_last_digit (Bytes.unsafe_of_string s1) 0 (String.length s1) then "1" ^ s1 else s1 in let l2 = String.length s2 - 1 in (* if s2 without last digit is xxxxyyy with n 'yyy' digits: xxxx . yyy if s2 without last digit is yy with <= n digits: 0 . 0yy *) if l2 > n then begin let s = Bytes.make (l2 + 2) '0' in Bytes.set s 0 (if sign_r = -1 then '-' else '+'); String.blit s2 0 s 1 (l2 - n); Bytes.set s (l2 - n + 1) '.'; String.blit s2 (l2 - n) s (l2 - n + 2) n; Bytes.unsafe_to_string s end else begin let s = Bytes.make (n + 3) '0' in Bytes.set s 0 (if sign_r = -1 then '-' else '+'); Bytes.set s 2 '.'; String.blit s2 0 s (n + 3 - l2) l2; Bytes.unsafe_to_string s end end else begin (* Dubious; what is this code supposed to do? *) let s = string_of_big_int (div_big_int (abs_big_int r.numerator) (base_power_big_int 10 (-n) r.denominator)) in let len = succ (String.length s) in let s' = Bytes.make len '0' in Bytes.set s' 0 (if sign_r = -1 then '-' else '+'); String.blit s 0 s' 1 (pred len); Bytes.unsafe_to_string s' end (* Number of digits of the decimal representation of an int *) let num_decimal_digits_int n = String.length (string_of_int n) (* Approximation with floating decimal point *) (* This is an odd function and the last digit is round off *) (* Format (+/-)(0. n_first_digits e msd)/(1. n_zeros e (msd+1) *) let approx_ratio_exp n r = (* Don't need to normalize *) if (null_denominator r) then failwith_zero "approx_ratio_exp" else if n <= 0 then invalid_arg "approx_ratio_exp" else let sign_r = sign_ratio r and i = ref (n + 3) in if sign_r = 0 then String.concat "" ["+0."; String.make n '0'; "e0"] else let msd = msd_ratio (abs_ratio r) in let k = n - msd in let s = (let nat = nat_of_big_int (if k < 0 then div_big_int (abs_big_int r.numerator) (base_power_big_int 10 (- k) r.denominator) else div_big_int (base_power_big_int 10 k (abs_big_int r.numerator)) r.denominator) in string_of_nat nat) in if round_futur_last_digit (Bytes.unsafe_of_string s) 0 (String.length s) then let m = num_decimal_digits_int (succ msd) in let str = Bytes.make (n + m + 4) '0' in (String.blit (if sign_r = -1 then "-1." else "+1.") 0 str 0 3); Bytes.set str !i ('e'); incr i; (if m = 0 then Bytes.set str !i '0' else String.blit (string_of_int (succ msd)) 0 str !i m); Bytes.unsafe_to_string str else let m = num_decimal_digits_int (succ msd) and p = n + 3 in let str = Bytes.make (succ (m + p)) '0' in (String.blit (if sign_r = -1 then "-0." else "+0.") 0 str 0 3); (String.blit s 0 str 3 n); Bytes.set str p 'e'; (if m = 0 then Bytes.set str (succ p) '0' else (String.blit (string_of_int (succ msd)) 0 str (succ p) m)); Bytes.unsafe_to_string str (* String approximation of a rational with a fixed number of significant *) (* digits printed *) let float_of_rational_string r = let s = approx_ratio_exp !floating_precision r in if String.get s 0 = '+' then (String.sub s 1 (pred (String.length s))) else s (* Coercions with type string *) let string_of_ratio r = ignore (cautious_normalize_ratio_when_printing r); if !approx_printing_flag then float_of_rational_string r else string_of_big_int r.numerator ^ "/" ^ string_of_big_int r.denominator (* XL: j'ai puissamment simplifie "ratio_of_string" en virant la notation scientifique. | I have strongly simplified "ratio_of_string" by deleting scientific notation *) let ratio_of_string s = try let n = String.index s '/' in create_ratio (sys_big_int_of_string s 0 n) (sys_big_int_of_string s (n+1) (String.length s - n - 1)) with Not_found -> { numerator = big_int_of_string s; denominator = unit_big_int; normalized = true } (* Coercion with type float *) let float_of_ratio r = let p = r.numerator and q = r.denominator in (* Special cases 0/0, 0/q and p/0 *) if sign_big_int q = 0 then begin match sign_big_int p with | 0 -> nan | 1 -> infinity | -1 -> neg_infinity | _ -> assert false end else if sign_big_int p = 0 then 0.0 else begin let np = num_bits_big_int p and nq = num_bits_big_int 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 (int64_of_big_int p) /. Int64.to_float (int64_of_big_int q) else begin let ap = abs_big_int p in (* |p| is in [2^(np-1), 2^np) q is in [2^(nq-1), 2^nq) hence |p|/q is in (2^(np-nq-1), 2^(np-nq+1)). We define n such that |p|/q*2^n is in [2^54, 2^56). >= 2^54 so that the round to odd technique applies. < 2^56 so that the integral part is representable as an int64. *) let n = 55 - (np - nq) in (* Scaling |p|/q by 2^n *) let (p', q') = if n >= 0 then (shift_left_big_int ap n, q) else (ap, shift_left_big_int q (-n)) in (* Euclidean division of p' by q' *) let (quo, rem) = quomod_big_int p' q' in (* quo is the integral part of |p|/q*2^n rem/q' is the fractional part. *) (* Round quo to float *) let f = round_big_int_to_float quo (sign_big_int rem = 0) in (* Apply exponent *) let f = ldexp f (-n) in (* Apply sign *) if sign_big_int p < 0 then -. f else f end end let power_ratio_positive_int r n = create_ratio (power_big_int_positive_int (r.numerator) n) (power_big_int_positive_int (r.denominator) n) let power_ratio_positive_big_int r bi = create_ratio (power_big_int_positive_big_int (r.numerator) bi) (power_big_int_positive_big_int (r.denominator) bi)