mirror of
https://github.com/mistergibson/gxg-web-client.git
synced 2026-08-15 03:26:00 -07:00
383 lines
7.3 KiB
Ruby
383 lines
7.3 KiB
Ruby
#
|
|
# rational.rb -
|
|
# $Release Version: 0.5 $
|
|
# $Revision: 1.7 $
|
|
# $Date: 1999/08/24 12:49:28 $
|
|
# by Keiju ISHITSUKA(SHL Japan Inc.)
|
|
#
|
|
|
|
class Rational < Numeric
|
|
attr_reader :numerator
|
|
attr_reader :denominator
|
|
|
|
def *(other)
|
|
case other
|
|
when Rational
|
|
num = @numerator * other.numerator
|
|
den = @denominator * other.denominator
|
|
Rational(num, den)
|
|
when Integer
|
|
Rational(@numerator * other, @denominator)
|
|
when Float
|
|
to_f * other
|
|
else
|
|
a, b = other.coerce(self)
|
|
a * b
|
|
end
|
|
end
|
|
|
|
def **(other)
|
|
if other.kind_of?(Rational) && other.denominator == 1
|
|
other = other.numerator
|
|
end
|
|
|
|
case other
|
|
when Fixnum
|
|
if other > 0
|
|
Rational(@numerator ** other, @denominator ** other)
|
|
elsif other < 0
|
|
raise ZeroDivisionError, "divided by 0" if self == 0
|
|
Rational(@denominator ** -other, @numerator ** -other)
|
|
elsif other == 0
|
|
Rational.new(1, 1)
|
|
end
|
|
when Bignum
|
|
if self == 0
|
|
if other < 0
|
|
raise ZeroDivisionError, "divided by 0"
|
|
elsif other > 0
|
|
Rational.new(0, 1)
|
|
end
|
|
elsif self == 1
|
|
Rational.new(1, 1)
|
|
elsif self == -1
|
|
Rational.new(other.even? ? 1 : -1, 1)
|
|
else
|
|
to_f ** other
|
|
end
|
|
when Float
|
|
to_f ** other
|
|
when Rational
|
|
if self == 0 && other < 0
|
|
raise ZeroDivisionError, "divided by 0"
|
|
end
|
|
|
|
to_f ** other
|
|
else
|
|
a, b = other.coerce(self)
|
|
a ** b
|
|
end
|
|
end
|
|
|
|
def +(other)
|
|
case other
|
|
when Rational
|
|
num = @numerator * other.denominator + @denominator * other.numerator
|
|
den = @denominator * other.denominator
|
|
Rational(num, den)
|
|
when Integer
|
|
Rational(@numerator + other * @denominator, @denominator)
|
|
when Float
|
|
to_f + other
|
|
else
|
|
a, b = other.coerce(self)
|
|
a + b
|
|
end
|
|
end
|
|
|
|
def -(other)
|
|
case other
|
|
when Rational
|
|
num = @numerator * other.denominator - @denominator * other.numerator
|
|
den = @denominator * other.denominator
|
|
Rational(num, den)
|
|
when Integer
|
|
Rational(@numerator - other * @denominator, @denominator)
|
|
when Float
|
|
to_f - other
|
|
else
|
|
a, b = other.coerce(self)
|
|
a - b
|
|
end
|
|
end
|
|
|
|
def /(other)
|
|
case other
|
|
when Rational
|
|
num = @numerator * other.denominator
|
|
den = @denominator * other.numerator
|
|
Rational(num, den)
|
|
when Integer
|
|
raise ZeroDivisionError, "divided by 0" if other == 0
|
|
Rational(@numerator, @denominator * other)
|
|
when Float
|
|
to_f / other
|
|
else
|
|
redo_coerced :/, other
|
|
end
|
|
end
|
|
alias_method :divide, :/
|
|
alias_method :quo, :/
|
|
|
|
def <=>(other)
|
|
case other
|
|
when Rational
|
|
diff = @numerator * other.denominator - @denominator * other.numerator
|
|
diff <=> 0
|
|
when Integer
|
|
diff = @numerator - @denominator * other
|
|
diff <=> 0
|
|
when Float
|
|
to_f <=> other
|
|
else
|
|
if defined?(other.coerce)
|
|
a, b = other.coerce(self)
|
|
a <=> b
|
|
end
|
|
end
|
|
end
|
|
|
|
def ==(other)
|
|
case other
|
|
when Rational
|
|
@numerator == other.numerator && @denominator == other.denominator
|
|
when Integer
|
|
@numerator == other && @denominator == 1
|
|
when Float
|
|
to_f == other
|
|
else
|
|
other == self
|
|
end
|
|
end
|
|
|
|
def abs
|
|
return self if @numerator >= 0
|
|
|
|
Rational.new(-@numerator, @denominator)
|
|
end
|
|
|
|
def ceil(precision = 0)
|
|
if precision == 0
|
|
-(-@numerator / @denominator)
|
|
else
|
|
with_precision(:ceil, precision)
|
|
end
|
|
end
|
|
|
|
def coerce(other)
|
|
case other
|
|
when Integer
|
|
return Rational.new(other, 1), self
|
|
when Float
|
|
return other, self.to_f
|
|
else
|
|
super
|
|
end
|
|
end
|
|
|
|
def floor(precision = 0)
|
|
if precision == 0
|
|
@numerator / @denominator
|
|
else
|
|
with_precision(:floor, precision)
|
|
end
|
|
end
|
|
|
|
def hash
|
|
@numerator.hash ^ @denominator.hash
|
|
end
|
|
|
|
def inspect
|
|
"(#{to_s})"
|
|
end
|
|
|
|
def rationalize(eps = undefined)
|
|
return self if undefined.equal?(eps)
|
|
|
|
e = eps.abs
|
|
a = self - e
|
|
b = self + e
|
|
|
|
p0 = 0
|
|
p1 = 1
|
|
q0 = 1
|
|
q1 = 0
|
|
|
|
while true
|
|
c = a.ceil
|
|
|
|
break if c < b
|
|
|
|
k = c - 1
|
|
p2 = k * p1 + p0
|
|
q2 = k * q1 + q0
|
|
t = 1 / (b - k)
|
|
b = 1 / (a - k)
|
|
a = t
|
|
|
|
p0 = p1
|
|
q0 = q1
|
|
p1 = p2
|
|
q1 = q2
|
|
end
|
|
|
|
# The rational number is guaranteed to be in lowest terms.
|
|
Rational.new(c * p1 + p0, c * q1 + q0)
|
|
end
|
|
|
|
def round(precision = 0)
|
|
return with_precision(:round, precision) unless precision == 0
|
|
return 0 if @numerator == 0
|
|
return @numerator if @denominator == 1
|
|
|
|
num = @numerator.abs * 2 + @denominator
|
|
den = @denominator * 2
|
|
|
|
approx = num / den
|
|
|
|
if @numerator < 0
|
|
-approx
|
|
else
|
|
approx
|
|
end
|
|
end
|
|
|
|
def to_f
|
|
@numerator.to_f / @denominator.to_f
|
|
end
|
|
|
|
def to_i
|
|
truncate
|
|
end
|
|
|
|
def to_r
|
|
self
|
|
end
|
|
|
|
def to_s
|
|
"#{@numerator.to_s}/#{@denominator.to_s}"
|
|
end
|
|
|
|
def truncate(precision = 0)
|
|
if precision == 0
|
|
@numerator < 0 ? ceil : floor
|
|
else
|
|
with_precision(:truncate, precision)
|
|
end
|
|
end
|
|
|
|
def self.convert(num, den, mathn = true)
|
|
if num.nil? || den.nil?
|
|
raise TypeError, "cannot convert nil into Rational"
|
|
end
|
|
|
|
if num.kind_of?(Integer) && den.kind_of?(Integer)
|
|
return reduce(num, den, mathn)
|
|
end
|
|
|
|
case num
|
|
when Integer
|
|
# nothing
|
|
when Float, String, Complex
|
|
num = num.to_r
|
|
end
|
|
|
|
case den
|
|
when Integer
|
|
# nothing
|
|
when Float, String, Complex
|
|
den = den.to_r
|
|
end
|
|
|
|
if den.equal?(1) && !num.kind_of?(Integer)
|
|
return Rubinius::Type.coerce_to(num, Rational, :to_r)
|
|
elsif num.kind_of?(Numeric) && den.kind_of?(Numeric) &&
|
|
!(num.kind_of?(Integer) && den.kind_of?(Integer))
|
|
return num / den
|
|
end
|
|
|
|
reduce(num, den)
|
|
end
|
|
|
|
def self.reduce(num, den, mathn = true)
|
|
case num
|
|
when Integer
|
|
# nothing
|
|
when Numeric
|
|
num = num.to_i
|
|
else
|
|
raise TypeError, "numerator is not an Integer"
|
|
end
|
|
|
|
case den
|
|
when Integer
|
|
if den == 0
|
|
raise ZeroDivisionError, "divided by 0"
|
|
elsif den < 0
|
|
num = -num
|
|
den = -den
|
|
end
|
|
|
|
if den == 1
|
|
return (mathn && Rubinius.mathn_loaded?) ? num : new(num, den)
|
|
end
|
|
when Numeric
|
|
den = den.to_i
|
|
else
|
|
raise TypeError, "denominator is not an Integer"
|
|
end
|
|
|
|
gcd = num.gcd(den)
|
|
num = num / gcd
|
|
den = den / gcd
|
|
|
|
return num if mathn && Rubinius.mathn_loaded? && den == 1
|
|
|
|
new(num, den)
|
|
end
|
|
|
|
class << self
|
|
private :convert
|
|
private :reduce
|
|
end
|
|
|
|
def initialize(num, den)
|
|
@numerator = num
|
|
@denominator = den
|
|
end
|
|
private :initialize
|
|
|
|
def marshal_dump
|
|
ary = [@numerator, @denominator]
|
|
|
|
instance_variables.each do |ivar|
|
|
ary.instance_variable_set(ivar, instance_variable_get(ivar))
|
|
end
|
|
|
|
ary
|
|
end
|
|
private :marshal_dump
|
|
|
|
def marshal_load(ary)
|
|
@numerator, @denominator = ary
|
|
|
|
ary.instance_variables.each do |ivar|
|
|
instance_variable_set(ivar, ary.instance_variable_get(ivar))
|
|
end
|
|
|
|
self
|
|
end
|
|
private :marshal_load
|
|
|
|
def with_precision(method, n)
|
|
raise TypeError, "not an Integer" unless n.kind_of?(Integer)
|
|
|
|
p = 10 ** n
|
|
s = self * p
|
|
|
|
r = Rational(s.send(method), p)
|
|
|
|
n < 1 ? r.to_i : r
|
|
end
|
|
private :with_precision
|
|
end
|