rat.sa


Generated by gen_html_sa_files from ICSI. Contact gomes@icsi.berkeley.edu for details
 
------------------------->  GNU Sather - sourcefile  <-------------------------
-- Copyright (C) 2000 by K Hopper, University of Waikato, New Zealand        --
-- This file is part of the GNU Sather library. It is free software; you may --
-- redistribute  and/or modify it under the terms of the GNU Library General --
-- Public  License (LGPL)  as published  by the  Free  Software  Foundation; --
-- either version 2 of the license, or (at your option) any later version.   --
-- This  library  is distributed  in the  hope that it will  be  useful, but --
-- WITHOUT ANY WARRANTY without even the implied warranty of MERCHANTABILITY --
-- or FITNESS FOR A PARTICULAR PURPOSE. See Doc/LGPL for more details.       --
-- The license text is also available from:  Free Software Foundation, Inc., --
-- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA                     --
-------------->  Please email comments to <bug-sather@gnu.org>  <--------------


immutable class RAT < $RATIONAL{RAT}, $NIL, $OPTION, $ANCHORED_FMT

immutable class RAT < $RATIONAL{RAT}, $NIL, $OPTION, $ANCHORED_FMT is -- This class implements rational numbers as a numerator and denominator -- which are 'infinite' precision integers. Numbers are held in -- normalised form such that -- num.gcd(denom) = 1 AND denom > 0 -- Version 1.2 Oct 98. Copyright K Hopper, U of Waikato -- Development History -- ------------------- -- Date Who By Detail -- ---- ------ ------ -- 13 Jul 96 bg Original for Sather 1.1 Dist -- 5 Sep 97 kh Modified for style -- 1 Oct 98 kh Factored out representation and added binary. include COMPARABLE ; include BINARY ; include RAT_STR ; const negatable : BOOL := true ; const is_signed : BOOL := true ; const is_limited : BOOL := false ; readonly attr num:INTI; readonly attr denom :INTI; zero : SAME is -- This routine returns a 'constant' value of 0. return num(INTI::zero).denom(INTI::one) end ; one : SAME is -- This routine returns a 'constant' value of 1. return num(INTI::one).denom(INTI::one) end ; two : SAME is -- This routine returns a 'constant' value of 2. return num(INTI::two).denom(INTI::one) end ; nil : SAME is -- This routine returns a nil value which may not be used in arithmetic. return num(INTI::zero).denom(INTI::zero) end ; build(cursor : BIN_CURSOR) : SAME pre ~void(cursor) and ~cursor.is_done and (cursor.remaining > cursor.item.card) post true is -- This routine creates a rational number which is obtained from the -- next octets in the binary string indicated. loc_num : INTI := INTI::build(cursor) ; loc_denom : INTI := INTI::build(cursor) ; return num(loc_num).denom(loc_denom) end ; create(val : CARD) : SAME is -- This routine creates a rational number which has the same value as the argument. return create(INTI::create(val)) end ; create(val : FIELD) : SAME is -- This routine creates a rational number which has the same value as the argument. return create(INTI::create(val.card)) end ; create(val : INT) : SAME is -- This routine creates a rational number which has the same value as the argument. return create(INTI::create(val)) end ; create(val : INTI) : SAME is -- This routine creates a rational number which has the same value as the argument. me : SAME ; return me.num(val).denom(INTI::one) end ; create(val : RAT) : SAME is -- This routine creates a rational number which has the same value as -- the argument - merely returning the argument. me : SAME ; return me.num(val.num).denom(val.denom) end ; create(val : FLT) : SAME is -- This creation routine creates a rational number with the same value as the given argument. if val = FLT::zero then return zero elsif val = FLT::one then return one end ; neg : BOOL ; loc_exp : INT ; loc_mantissa : INT ; val.get_representation(out neg,out loc_exp,out loc_mantissa) ; mantissa:INTI:=INTI::create(loc_mantissa)+INTI::two.pow(FLT::mantissa_bits-1); loc_exp:=loc_exp-FLT::max_exp-FLT::mantissa_bits.int+2; me:SAME; if loc_exp.is_neg then me := create(mantissa,INTI::two.pow(-loc_exp)) ; else me := create(mantissa * INTI::two.pow(loc_exp), INTI::one) ; end; if neg then return -me; else return me; end; end; create(val : FLTD) : SAME is -- This creation routine creates a rational number with the same value -- as the given argument. if val = FLTD::zero then return zero elsif val = FLTD::one then return one end ; loc_res : INTI ; neg : BOOL ; loc_exp : INT ; loc_mantissa_lo : CARD ; loc_mantissa_hi : CARD ; val.get_representation(out neg,out loc_exp,out loc_mantissa_lo, out loc_mantissa_hi) ; mantissa:INTI:=INTI::two.pow(FLTD::mantissa_bits-1) +INTI::create(loc_mantissa_hi)*INTI::two.pow(CARD::Num_Bits) +INTI::create(loc_mantissa_lo); loc_exp:=loc_exp-FLTD::max_exp-FLTD::mantissa_bits.int+2; me:SAME; if loc_exp.is_neg then me := create(mantissa,INTI::two.pow(-loc_exp)) ; else me := create(mantissa * INTI::two.pow(loc_exp),INTI::one); end; if neg then return -me; else return me; end; end; create( numerator, denominator : INTI) : SAME is --This routine creates a rational number which has the same value as -- the result of dividing the numerator by the denominator. assert ~denominator.is_zero ; -- fatal if it is!! if numerator.is_zero then denominator := INTI::one else gcdenom : INTI := numerator.gcd(denominator) ; if denominator.is_neg then gcdenom := -gcdenom end ; numerator := numerator / gcdenom ; denominator := denominator / gcdenom end ; assert denominator.is_pos ; me : SAME ; return me.num(numerator).denom(denominator) end ; create(numerator, denominator : CARD) : SAME is -- This routine creates a rational number which has the same value as -- the result of dividing the numerator by the denominator. return create(INTI::create(numerator),INTI::create(denominator)) end ; create(numerator, denominator : INT) : SAME is -- This routine creates a rational number which has the same value as -- the result of dividing the numerator by the denominator. return create(INTI::create(numerator),INTI::create(denominator)) end ; card : CARD pre is_card is -- This routine returns the nearest cardinal value to that represented -- by self, providing that it is within the range of numbers representable -- by a cardinal number. return num.card end ; field : FIELD pre ~num.is_neg and is_int post result = num.field is -- This routine returns the nearest field value to that represented -- by self. This does not mean that create(result) = self, due to the closed -- field nature of the result! return num.field end ; int : INT pre is_int is -- This routine returns the nearest integer value to that represented -- by self, providing that it is within the range of numbers representable -- by an integer number. return num.int end ; inti : INTI pre is_inti is -- This routine returns the nearest integer value to that represented -- by self, providing that it is within the range of numbers representable -- by an integer number. return num end ; rat : SAME pre true post result = self is -- This routine returns the same value. return num(self.num).denom(self.denom) end ; flt : FLT pre (num <= INTI::create(FLT::maxval.truncate)) and (denom <= INTI::create(FLT::maxval.truncate)) post result = num.flt / denom.flt is -- This routine returns the nearest approximate value to that represented -- by self, providing that it is within the range of numbers representable -- by a single precision floating point number. return num.flt / denom.flt end ; fltd : FLTD pre (num <= INTI::create(FLTD::maxval.truncate)) and (denom <= INTI::create(FLTD::maxval.truncate)) post result = num.fltd / denom.fltd is -- This routine returns the nearest approximate value to that represented -- by self, providing that it is within the range of numbers representable -- by a double precision floating point number. return num.fltd / denom.fltd end ; binstr : BINSTR pre ~(denom < INTI::one) post result.size > 0 is -- This routine returns the binary string corresponding to the value of self. return num.binstr + denom.binstr end ; plus(other : SAME ) : SAME pre ~void(self.num) and ~void(other.num) and (self.denom > INTI::zero) and (other.denom > INTI::zero) --post (result.num = ((num * other.denom) + (denom * other.num))) -- and (result.denom = (denom * other.denom)) is -- This routine returns the arithmetic sum of self and other. return create(num * other.denom + denom * other.num, denom * other.denom) end ; minus(other : SAME ) : SAME pre ~void(self.num) and ~void(other.num) and (self.denom > INTI::zero) and (other.denom > INTI::zero) --post (result.num = ((num * other.denom) - (denom * other.num))) --and (result.denom = (denom * other.denom)) is -- This routine returns the arithmetic difference formed by subtracting -- other from self. return create(num * other.denom - denom * other.num, denom * other.denom) end ; times(other : SAME) : SAME pre ~void(self.num) and ~void(other.num) and (self.denom > INTI::zero) and (other.denom > INTI::zero) --post (result.num = (num * other.num)) --and (result.denom = (denom * other.denom)) is -- This routine returns the product formed by multiplying other and self. return create(num * other.num, denom * other.denom) end ; div(other : SAME) : SAME pre ~void(self.num) and ~void(other.num) and (self.denom > INTI::zero) and (other.denom > INTI::zero) and (other.num.is_non_zero) --post (result.num = (num * other.denom)) --and (result.denom = (denom * other.num)) is -- This routine returns the dividend formed by dividing self by other. assert ~other.num.is_zero ; return create(num * other.denom, denom * other.num) end ; divmod(other, out quotient, out residue: SAME) pre other.is_non_zero is quotient:=(self / other).floor; residue:=self - quotient * other; end; mod(other : SAME) : SAME pre other.is_non_zero --post (result = (self - ((self / other).floor) * other)) is -- This routine returns the remainder formed when dividing self by other. q,r:SAME; divmod(other, out q, out r); return r; end ; inverse:SAME pre num.is_non_zero is -- 1/self return create(denom,num); end; pow(x:INTI):SAME is -- This routine returns the result of raising self to the power exp. t::=self; if x.is_neg then x:=-x; t:=t.inverse; end; s:SAME:=one; i2:INTI:=2.inti; loop while!(x.is_pos) ; if x.is_odd then s := s * t; end ; t := t*t; x := x/i2; end ; return s; end; pow(x : CARD) : SAME is return pow(x.inti) end ; pow(x : INT) : SAME is return pow(x.inti) end ; pow(exp : SAME) : SAME is -- This routine returns the result of raising self to the power exp. if exp = zero then return one elsif exp = one then return self else if exp.denom = INTI::one then -- integral exponent return pow(exp.num); else return create(self.fltd.pow(exp.fltd)); end end end ; exp : SAME pre (num <= INTI::create(FLTD::maxval.truncate)) and (denom <= INTI::create(FLTD::maxval.truncate)) is -- This routine returns the result of raising the base of natural -- logarithms (e) to the power of self. return create(self.fltd.exp) end ; log : SAME pre (num <= INTI::create(FLTD::maxval.truncate)) and (denom <= INTI::create(FLTD::maxval.truncate)) and self > zero post true -- result.exp = self is -- This routine returns the natural logarithm of self. return create(self.fltd.log) end ; is_exact : BOOL is -- This predicate returns true if and only if self is an exact(integer) number. return denom = INTI::one end ; is_pos : BOOL is -- This predicate returns true if and only if self is greater than zero. return num.is_pos end ; is_neg : BOOL is -- This predicate returns true if and only if self is less than zero. return num.is_neg end ; is_zero : BOOL is -- This predicate returns true if and only if self is zero. return num.is_zero end ; is_non_pos : BOOL is return num.is_non_pos end; is_non_neg : BOOL is return num.is_non_neg end; is_non_zero : BOOL is return num.is_non_zero end; is_nil : BOOL is -- This predicate returns true if and only if self has the value nil. return self = nil end ; is_card : BOOL is -- This predicate returns true if and only if self is a non-negative -- exact number in the range of the class CARD. return (denom = INTI::one) and (num >= INTI::zero) and (num <= INTI::create(CARD::maxval)) end ; is_int : BOOL is -- This predicate returns true if and only if self is an exact number -- in the range of the class INT. return (denom = INTI::one) and (num <= INTI::create(INT::maxval))and (num >= INTI::create(INT::minval)) end ; is_inti : BOOL is -- This predicate returns true if and only if self is an exact number -- in the range of the class INT. return (denom = INTI::one); end ; is_eq(other : SAME) : BOOL is -- This predicate returns true if and only if self and other are the same value. return (num = other.num) and (denom = other.denom) end ; is_lt(other : SAME) : BOOL is -- This predicate returns true if and only if self is less than other. return num * other.denom < denom * other.num end ; in_range(lower,upper : SAME) : BOOL is -- This predicate returns true if and only if self has a value between -- lower and upper inclusive. return ((lower <= self) and (self <= upper)) or ((upper <= self) and (self <= lower)) end ; in_range(rng : $RANGE{RAT} ) : BOOL is -- This predicate returns true if and only if self has a value within -- the given range. return rng.contains(self) end ; in_tolerance(tolerance, val : SAME) : BOOL is -- This predicate returns true if and only if self is within the given -- tolerance of val. return (self - val).abs <= tolerance end ; abs : SAME pre ~void(self) post (result.num = num.abs) and (result.denom = denom) is -- This routine returns the absolute value of self. res : SAME ; return res.num(num.abs).denom(denom) end ; negate : SAME pre ~void(self) post ( (num.is_neg and (result.num = num.abs)) or (num.is_pos and (num = result.abs.num)) or num = result.num ) and (result.denom = denom) is -- This routine returns the absolute value of self. res : SAME ; return res.num(-num).denom(denom) end ; sign : NUM_SIGNS pre ~void(self) post (result = num.sign) is -- This routine returns the absolute value of self. return num.sign end ; sgn:SAME is -- 1/0/-1 return #(num.sgn); end; max(other : SAME ) : SAME pre true post ((self > other) and (result = self)) or (result = other) is -- This routine returns the larger of self and arg. if self < other then return other else return self end end ; min(other : SAME) : SAME pre true post ((self > other) and (result = other)) or (result = self) is -- This routine returns the smaller of self and arg. if self < other then return self else return other end end ; square : SAME pre ~void(self) post (result.num = num.square) and (result.denom = denom.square) is -- self^2 res : SAME ; return res.num(num.square).denom(denom.square) end ; cube : SAME pre ~void(self) post (result.num = num.cube) and (result.denom = denom.cube) is -- self^3 res : SAME ; return res.num(num.cube).denom(denom.cube) end ; sqrt : SAME pre self >= zero is if self=zero then return zero; end; w:SAME:=self; scale:SAME:=1.rat; f:SAME:=4.rat; t:SAME:=2.rat; loop while!(w<t); w:=w*f; scale:=scale/t; end; loop while!(w>f); w:=w/f; scale:=scale*t; end; x:SAME:=w.fltd.sqrt.rat; -- Newton's method. loop 3.times!; x:=x/t+w/(t*x); end; return x*scale; end ; floor : SAME pre ~void(self) post (result.num = num / denom) and (result.denom = INTI::one) is -- same as truncate return create(num / denom,INTI::one) end ; ceiling : SAME pre ~void(self) post (result.num = num + (denom - (num % denom))) and (result.denom = denom) is -- max integer less than or equal to self res : SAME ; return res.num(num + (denom - (num % denom))).denom(denom) end ; round : SAME pre ~void(self) post (result.num >= (num / denom)) and (result.num < ((num + denom) / denom)) and (result.denom = INTI::one) is -- This routine rounds the value of self to the nearest whole number, -- which it returns. if denom = INTI::one then return num(self.num).denom(self.denom) else loc_num : INTI := num + (denom / (INTI::one + INTI::one)) ; return create(loc_num / denom,INTI::one) end end ; truncate : SAME pre ~void(self) post (result.num >= (num / denom)) and (result.num < ((num + denom) / denom)) and (result.denom = INTI::one) is -- This routine rounds the value which is the integral component of -- self, ignoring any fractional part. return create(num / denom,INTI::one) end ; hash : CARD pre true -- irrespective of value of self post true -- irrespective of value of result is -- This routine returns a suitable hash value for keys, indexing, etc return (NUM_BITS::create(num.hash).convolve(NUM_BITS::create(denom.hash))).card end ; sum!(val : SAME) : SAME pre true post true -- could be anything! is -- This iter yields the sum of all previous values of val. Note that -- other is re-evaluated on each re-entry of the iter. Dependent on the -- value provided this iter may result in an out of memory condition arising -- which is fatal. res : SAME := zero ; loop res := res + val ; yield res end end ; product!(val : SAME) : SAME pre true post true -- could be anything! is -- This iter yields the product of all previous values of val. Note that -- other is re-evaluated on each re-entry of the iter. Dependent on the -- value provided this iter may result in an out of memory condition arising -- which is fatal. res : SAME := one ; loop res := res * val ; yield res end end ; end ; -- RAT