# Numeric Supplements
#
#   by Shin-ichiro Hara
#
# Version 1.01 (2001.04.10)

class Numeric
  def self.unity; 1; end
  def self.zero; 0; end
  def zero; type.zero; end
  def unity; type.unity; end
  def self.indeterminate(x)
    eval(x)
  end
  def self.const(x); x; end
  def each; end
  def pdivmod(other); divmod(other); end
  def unity?; self == 1; end
  def unit?; self == unity or self == -unity; end
  def monomial?; true; end
  def inverse; self == -1 ? -1 : 1; end
  def self.field?; true; end
  def self.euclidian?; true; end

  def self.regulate(x)
    if x.is_a? self
      x
    else
      nil
    end
  end
  def regulate(x)
    type.regulate(x)
  end
  private :regulate
end

class Integer# < Numeric
  def self.ground; self; end
  def self.field?; false; end
#  def self.field?; respond_to?(:from_prime_division); end #mathn loaded
  def self.euclidian?; true; end
  def devide?(other)
    case other
    when Integer
      (other % self).zero?
    else
      # this case will occur when mathn is required
      (other / self) * self == other
#      raise "devide?: unkown type(#{other})"
    end
  end
end

class Float# < Numeric
  def self.unity; 1.0; end
  def self.zero; 0.0; end
end

class Rational < Numeric
  def self.unity; new(1,1); end
  def self.zero; new(0,1); end
  def inverse; 1 / self; end
  def pdivmod(other); [self / other, zero]; end
  def self.ground; Integer; end
  def self.regulate(o)
    case o
    when type
      o
    when Numeric
      Rational(o)
    else
      nil
    end
  end

  def devide?(other); true; end

  def gcd_coeff(b)
    if b.zero?
      [self, type.unity, type.zero]
    else
      q, r = divmod b
      d, x, y = b.gcd_coeff(r)
      [d, y, x - y * q]
    end
  end

  def mod(o)
    raise ZeroDivisionError, "devided by 0" if o.zero?
    den, a, = @denominator.gcd_coeff(o)
    num = (@numerator * a) % o
    q, r = num.divmod den
    raise "#@denominator can not divide #@numerator mod #{o}" unless r.zero?
    q
  end

#  def inspect; to_s; end
end

class Complex# < Numeric
  def self.unity; new(1); end
  def self.zero; new(0); end
end
