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