1998-01-16 15:13:05 +03:00
|
|
|
#
|
|
|
|
# rational.rb -
|
|
|
|
# $Release Version: 0.5 $
|
1999-09-01 13:48:03 +04:00
|
|
|
# $Revision: 1.7 $
|
|
|
|
# $Date: 1999/08/24 12:49:28 $
|
1998-01-16 15:13:05 +03:00
|
|
|
# by Keiju ISHITSUKA(SHL Japan Inc.)
|
|
|
|
#
|
|
|
|
# --
|
|
|
|
# Usage:
|
|
|
|
# class Rational < Numeric
|
2002-06-11 11:02:23 +04:00
|
|
|
# (include Comparable)
|
1998-01-16 15:13:05 +03:00
|
|
|
#
|
|
|
|
# Rational(a, b) --> a/b
|
|
|
|
#
|
|
|
|
# Rational::+
|
|
|
|
# Rational::-
|
|
|
|
# Rational::*
|
|
|
|
# Rational::/
|
|
|
|
# Rational::**
|
|
|
|
# Rational::%
|
|
|
|
# Rational::divmod
|
|
|
|
# Rational::abs
|
|
|
|
# Rational::<=>
|
|
|
|
# Rational::to_i
|
|
|
|
# Rational::to_f
|
|
|
|
# Rational::to_s
|
|
|
|
#
|
|
|
|
# Integer::gcd
|
|
|
|
# Integer::lcm
|
|
|
|
# Integer::gcdlcm
|
|
|
|
# Integer::to_r
|
|
|
|
#
|
|
|
|
# Fixnum::**
|
2003-01-23 09:22:50 +03:00
|
|
|
# Fixnum::quo
|
1998-01-16 15:13:05 +03:00
|
|
|
# Bignum::**
|
2003-01-23 09:22:50 +03:00
|
|
|
# Bignum::quo
|
1998-01-16 15:13:05 +03:00
|
|
|
#
|
|
|
|
|
|
|
|
def Rational(a, b = 1)
|
|
|
|
if a.kind_of?(Rational) && b == 1
|
|
|
|
a
|
|
|
|
else
|
|
|
|
Rational.reduce(a, b)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class Rational < Numeric
|
1999-09-01 13:48:03 +04:00
|
|
|
@RCS_ID='-$Id: rational.rb,v 1.7 1999/08/24 12:49:28 keiju Exp keiju $-'
|
1999-01-20 07:59:39 +03:00
|
|
|
|
1998-01-16 15:13:05 +03:00
|
|
|
def Rational.reduce(num, den = 1)
|
2004-03-29 11:54:38 +04:00
|
|
|
raise ZeroDivisionError, "denominator is zero" if den == 0
|
1999-01-20 07:59:39 +03:00
|
|
|
|
1998-01-16 15:13:05 +03:00
|
|
|
if den < 0
|
|
|
|
num = -num
|
|
|
|
den = -den
|
|
|
|
end
|
|
|
|
gcd = num.gcd(den)
|
|
|
|
num = num.div(gcd)
|
|
|
|
den = den.div(gcd)
|
|
|
|
if den == 1 && defined?(Unify)
|
|
|
|
num
|
|
|
|
else
|
|
|
|
new!(num, den)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def Rational.new!(num, den = 1)
|
|
|
|
new(num, den)
|
|
|
|
end
|
2003-04-22 12:18:19 +04:00
|
|
|
|
|
|
|
private_class_method :new
|
|
|
|
|
1998-01-16 15:13:05 +03:00
|
|
|
def initialize(num, den)
|
|
|
|
if den < 0
|
|
|
|
num = -num
|
|
|
|
den = -den
|
|
|
|
end
|
|
|
|
if num.kind_of?(Integer) and den.kind_of?(Integer)
|
|
|
|
@numerator = num
|
|
|
|
@denominator = den
|
|
|
|
else
|
|
|
|
@numerator = num.to_i
|
1999-08-13 09:45:20 +04:00
|
|
|
@denominator = den.to_i
|
1998-01-16 15:13:05 +03:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def + (a)
|
|
|
|
if a.kind_of?(Rational)
|
|
|
|
num = @numerator * a.denominator
|
|
|
|
num_a = a.numerator * @denominator
|
|
|
|
Rational(num + num_a, @denominator * a.denominator)
|
|
|
|
elsif a.kind_of?(Integer)
|
|
|
|
self + Rational.new!(a, 1)
|
|
|
|
elsif a.kind_of?(Float)
|
|
|
|
Float(self) + a
|
|
|
|
else
|
2003-02-06 12:49:12 +03:00
|
|
|
x, y = a.coerce(self)
|
1998-01-16 15:13:05 +03:00
|
|
|
x + y
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def - (a)
|
|
|
|
if a.kind_of?(Rational)
|
|
|
|
num = @numerator * a.denominator
|
|
|
|
num_a = a.numerator * @denominator
|
|
|
|
Rational(num - num_a, @denominator*a.denominator)
|
|
|
|
elsif a.kind_of?(Integer)
|
|
|
|
self - Rational.new!(a, 1)
|
|
|
|
elsif a.kind_of?(Float)
|
|
|
|
Float(self) - a
|
|
|
|
else
|
2003-02-06 12:49:12 +03:00
|
|
|
x, y = a.coerce(self)
|
1998-01-16 15:13:05 +03:00
|
|
|
x - y
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def * (a)
|
|
|
|
if a.kind_of?(Rational)
|
|
|
|
num = @numerator * a.numerator
|
|
|
|
den = @denominator * a.denominator
|
|
|
|
Rational(num, den)
|
|
|
|
elsif a.kind_of?(Integer)
|
|
|
|
self * Rational.new!(a, 1)
|
|
|
|
elsif a.kind_of?(Float)
|
|
|
|
Float(self) * a
|
|
|
|
else
|
2003-02-06 12:49:12 +03:00
|
|
|
x, y = a.coerce(self)
|
1998-01-16 15:13:05 +03:00
|
|
|
x * y
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def / (a)
|
|
|
|
if a.kind_of?(Rational)
|
|
|
|
num = @numerator * a.denominator
|
|
|
|
den = @denominator * a.numerator
|
|
|
|
Rational(num, den)
|
|
|
|
elsif a.kind_of?(Integer)
|
2004-03-29 11:54:38 +04:00
|
|
|
raise ZeroDivisionError, "division by zero" if a == 0
|
1998-01-16 15:13:05 +03:00
|
|
|
self / Rational.new!(a, 1)
|
|
|
|
elsif a.kind_of?(Float)
|
|
|
|
Float(self) / a
|
|
|
|
else
|
2003-02-06 12:49:12 +03:00
|
|
|
x, y = a.coerce(self)
|
1998-01-16 15:13:05 +03:00
|
|
|
x / y
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def ** (other)
|
|
|
|
if other.kind_of?(Rational)
|
|
|
|
Float(self) ** other
|
|
|
|
elsif other.kind_of?(Integer)
|
|
|
|
if other > 0
|
|
|
|
num = @numerator ** other
|
|
|
|
den = @denominator ** other
|
|
|
|
elsif other < 0
|
|
|
|
num = @denominator ** -other
|
|
|
|
den = @numerator ** -other
|
|
|
|
elsif other == 0
|
|
|
|
num = 1
|
|
|
|
den = 1
|
|
|
|
end
|
|
|
|
Rational.new!(num, den)
|
|
|
|
elsif other.kind_of?(Float)
|
|
|
|
Float(self) ** other
|
|
|
|
else
|
2003-02-06 12:49:12 +03:00
|
|
|
x, y = other.coerce(self)
|
1998-01-16 15:13:05 +03:00
|
|
|
x ** y
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def % (other)
|
|
|
|
value = (self / other).to_i
|
|
|
|
return self - other * value
|
|
|
|
end
|
|
|
|
|
|
|
|
def divmod(other)
|
|
|
|
value = (self / other).to_i
|
|
|
|
return value, self - other * value
|
|
|
|
end
|
|
|
|
|
|
|
|
def abs
|
|
|
|
if @numerator > 0
|
|
|
|
Rational.new!(@numerator, @denominator)
|
|
|
|
else
|
|
|
|
Rational.new!(-@numerator, @denominator)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2003-02-06 12:49:12 +03:00
|
|
|
def == (other)
|
|
|
|
if other.kind_of?(Rational)
|
|
|
|
@numerator == other.numerator and @denominator == other.denominator
|
|
|
|
elsif other.kind_of?(Integer)
|
|
|
|
self == Rational.new!(other, 1)
|
|
|
|
elsif other.kind_of?(Float)
|
|
|
|
Float(self) == other
|
|
|
|
else
|
|
|
|
other == self
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
1998-01-16 15:13:05 +03:00
|
|
|
def <=> (other)
|
|
|
|
if other.kind_of?(Rational)
|
|
|
|
num = @numerator * other.denominator
|
|
|
|
num_a = other.numerator * @denominator
|
|
|
|
v = num - num_a
|
|
|
|
if v > 0
|
|
|
|
return 1
|
|
|
|
elsif v < 0
|
|
|
|
return -1
|
|
|
|
else
|
|
|
|
return 0
|
|
|
|
end
|
|
|
|
elsif other.kind_of?(Integer)
|
|
|
|
return self <=> Rational.new!(other, 1)
|
|
|
|
elsif other.kind_of?(Float)
|
|
|
|
return Float(self) <=> other
|
2003-02-06 12:49:12 +03:00
|
|
|
elsif defined? other.coerce
|
|
|
|
x, y = other.coerce(self)
|
1998-01-16 15:13:05 +03:00
|
|
|
return x <=> y
|
2003-02-06 12:49:12 +03:00
|
|
|
else
|
|
|
|
return nil
|
1998-01-16 15:13:05 +03:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def coerce(other)
|
|
|
|
if other.kind_of?(Float)
|
|
|
|
return other, self.to_f
|
|
|
|
elsif other.kind_of?(Integer)
|
|
|
|
return Rational.new!(other, 1), self
|
|
|
|
else
|
|
|
|
super
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def to_i
|
|
|
|
Integer(@numerator.div(@denominator))
|
|
|
|
end
|
|
|
|
|
|
|
|
def to_f
|
|
|
|
@numerator.to_f/@denominator.to_f
|
|
|
|
end
|
|
|
|
|
|
|
|
def to_s
|
|
|
|
if @denominator == 1
|
|
|
|
@numerator.to_s
|
|
|
|
else
|
|
|
|
@numerator.to_s+"/"+@denominator.to_s
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def to_r
|
|
|
|
self
|
|
|
|
end
|
|
|
|
|
1999-09-01 13:48:03 +04:00
|
|
|
def inspect
|
|
|
|
sprintf("Rational(%s, %s)", @numerator.inspect, @denominator.inspect)
|
|
|
|
end
|
|
|
|
|
1998-01-16 15:13:05 +03:00
|
|
|
def hash
|
2002-08-15 11:36:35 +04:00
|
|
|
@numerator.hash ^ @denominator.hash
|
1998-01-16 15:13:05 +03:00
|
|
|
end
|
|
|
|
|
|
|
|
attr :numerator
|
|
|
|
attr :denominator
|
|
|
|
|
|
|
|
private :initialize
|
|
|
|
end
|
|
|
|
|
|
|
|
class Integer
|
|
|
|
def numerator
|
|
|
|
self
|
|
|
|
end
|
|
|
|
|
2003-05-16 21:19:41 +04:00
|
|
|
def denominator
|
1998-01-16 15:13:05 +03:00
|
|
|
1
|
|
|
|
end
|
|
|
|
|
|
|
|
def to_r
|
|
|
|
Rational(self, 1)
|
|
|
|
end
|
|
|
|
|
1999-09-01 13:48:03 +04:00
|
|
|
def gcd(n)
|
|
|
|
m = self.abs
|
|
|
|
n = n.abs
|
|
|
|
|
|
|
|
return n if m == 0
|
|
|
|
return m if n == 0
|
|
|
|
|
|
|
|
b = 0
|
|
|
|
while n[0] == 0 && m[0] == 0
|
|
|
|
b += 1; n >>= 1; m >>= 1
|
|
|
|
end
|
|
|
|
m >>= 1 while m[0] == 0
|
|
|
|
n >>= 1 while n[0] == 0
|
|
|
|
while m != n
|
|
|
|
m, n = n, m if n > m
|
|
|
|
m -= n; m >>= 1 while m[0] == 0
|
|
|
|
end
|
|
|
|
m << b
|
|
|
|
end
|
|
|
|
|
|
|
|
def gcd2(int)
|
1998-01-16 15:13:05 +03:00
|
|
|
a = self.abs
|
|
|
|
b = int.abs
|
|
|
|
|
|
|
|
a, b = b, a if a < b
|
|
|
|
|
|
|
|
while b != 0
|
|
|
|
void, a = a.divmod(b)
|
|
|
|
a, b = b, a
|
|
|
|
end
|
|
|
|
return a
|
|
|
|
end
|
1999-09-01 13:48:03 +04:00
|
|
|
|
2004-11-15 19:45:03 +03:00
|
|
|
def lcm(other)
|
|
|
|
if self.zero? or other.zero?
|
|
|
|
0
|
|
|
|
else
|
|
|
|
(self.div(self.gcd(other)) * other).abs
|
|
|
|
end
|
1998-01-16 15:13:05 +03:00
|
|
|
end
|
|
|
|
|
2004-11-15 19:45:03 +03:00
|
|
|
def gcdlcm(other)
|
|
|
|
gcd = self.gcd(other)
|
|
|
|
if self.zero? or other.zero?
|
|
|
|
[gcd, 0]
|
|
|
|
else
|
|
|
|
[gcd, (self.div(gcd) * other).abs]
|
|
|
|
end
|
1998-01-16 15:13:05 +03:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class Fixnum
|
2003-01-23 11:27:06 +03:00
|
|
|
undef quo
|
2003-01-23 09:22:50 +03:00
|
|
|
def quo(other)
|
1998-01-16 15:13:05 +03:00
|
|
|
Rational.new!(self,1) / other
|
|
|
|
end
|
2003-01-23 09:22:50 +03:00
|
|
|
alias rdiv quo
|
1998-01-16 15:13:05 +03:00
|
|
|
|
|
|
|
def rpower (other)
|
|
|
|
if other >= 0
|
|
|
|
self.power!(other)
|
|
|
|
else
|
|
|
|
Rational.new!(self,1)**other
|
|
|
|
end
|
|
|
|
end
|
2003-07-17 12:58:16 +04:00
|
|
|
|
|
|
|
unless defined? 1.power!
|
|
|
|
alias power! **
|
1998-01-16 15:13:05 +03:00
|
|
|
alias ** rpower
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class Bignum
|
2003-05-02 10:41:33 +04:00
|
|
|
unless defined? Complex
|
1998-01-16 15:13:05 +03:00
|
|
|
alias power! **
|
|
|
|
end
|
2003-01-23 11:27:06 +03:00
|
|
|
|
|
|
|
undef quo
|
2003-01-23 09:22:50 +03:00
|
|
|
def quo(other)
|
1998-01-16 15:13:05 +03:00
|
|
|
Rational.new!(self,1) / other
|
|
|
|
end
|
2003-01-23 09:22:50 +03:00
|
|
|
alias rdiv quo
|
1998-01-16 15:13:05 +03:00
|
|
|
|
|
|
|
def rpower (other)
|
|
|
|
if other >= 0
|
|
|
|
self.power!(other)
|
|
|
|
else
|
|
|
|
Rational.new!(self, 1)**other
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2003-05-02 10:41:33 +04:00
|
|
|
unless defined? Complex
|
1998-01-16 15:13:05 +03:00
|
|
|
alias ** rpower
|
|
|
|
end
|
|
|
|
end
|