# Localization of Ring
#
#   by Shin-ichiro Hara
#
# Version 1.1 (2001.04.10)

require "euclidian-ring"
require "algebraic-system"

module Algebra
def LocalizedRing(ring)
  LocalizedRing.create(ring)
end

def RationalFunctionField(field, obj)
  require "polynomial"
  k = LocalizedRing(Polynomial(field, obj))
  def k.var
    self[ground.var]
  end
  k
end
module_function :LocalizedRing, :RationalFunctionField

class LocalizedRing
  extend AlgebraCreator
  include AlgebraBase
  
  attr :numerator
  attr :denominator

  def monomial?; true; end

  def self.[](num, den = nil)
    den ? reduce(num, den) : unity * num
  end

  def self.reduce(num, den)
    raise ZeroDivisionError, "denometor is 0" if den.zero?
    return zero if num.zero?

    if ground.field?
      num = num / den
      den = ground.unit
    elsif ground.euclidian?
      gcd = num.gcd(den)
      num = num / gcd
      den = den / gcd
#      num = num.div(gcd)
#      den = den.div(gcd)
    end

    if defined?(Polynomial) && ground <= Polynomial
      if ground.ground.field?
	m =  den.lc
	num /= m
        den /= m
      elsif ground.ground.euclidian? # high cost!
	m = num.cont.gcd(den.cont)
	num /= m
        den /= m
      end
    end

#    if den.respond_to?(:<) and den < 0
#      num = -num
#      den = -den
#    end
    new(num, den)
  end
  
  def initialize(num, den = ground.unity)
    if den.respond_to?(:<=>) and den < ground.zero
      num = -num
      den = -den
    end
    @numerator = num
    @denominator = den
  end

  def == (o)
    super{ |o|
      @numerator * o.denominator == o.numerator * @denominator
    }
  end

  def + (o)
    super { |o|
      num = @numerator * o.denominator
      num_o = o.numerator * @denominator
      type.reduce(num + num_o, @denominator * o.denominator)
    }
  end
  
  def - (o)
    super{ |o|
      num = @numerator * o.denominator
      num_o = o.numerator * @denominator
      type.reduce(num - num_o, @denominator * o.denominator)
    }
  end
  
  def * (o)
    super{ |o|
      num = @numerator * o.numerator
      den = @denominator * o.denominator
      type.reduce(num, den)
    }
  end
  
  def / (o)
    raise ZeroDivisionError, "devided by 0" if o.zero?
    super{ |o|
      num = @numerator * o.denominator
      den = @denominator * o.numerator
      type.reduce(num, den)
    }
  end

  def unit?
    not zero? #some about
  end

  def pdivmod(other); [self / other, zero]; end

#  def % (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 ** (other)
    case other
    when Integer
      if other > 0
	num = @numerator ** other
	den = @denominator ** other
	type.new(num, den)
      elsif other < 0
	num = @denominator ** -other
	den = @numerator ** -other
	type.new(num, den)
      elsif other.zero?
	unity
      end
    else
      x , y = other.coerce(self)
      x ** y
    end
  end
  
  def to_s
    n = if @numerator.respond_to?(:monomial?) && @numerator.monomial?
	  "#@numerator"
	else
	  "(#@numerator)"
	end
    d = if @denominator.respond_to?(:monomial?) && @denominator.monomial? &&
	    @denominator.kind_of?(Integer)
	  "#@denominator"
	else
	  "(#@denominator)"
	end
    if @denominator.unity?
      n.to_s
    else
      n + "/" + d
    end
  end
  
  def inspect
    to_s
#    sprintf("%s(%s/%s)", type, @numerator.inspect, @denominator.inspect)
  end

  def hash
    raise "hash is undefined"
  end
  
end
end

if __FILE__ == $0
  require "polynomial"
  require "rational"
  require "residue-class-ring"
  include Algebra

  Q = LocalizedRing(Integer)
  a = Q.new(3, 5)
  b = Q.new(5, 3)
  p [a + b, a - b, a * b, a / b, a + 3, 1 + a]

  Z13 = ResidueClassRing(Integer, 13)

  Z13x = Polynomial(Z13, "x")
  x = Z13x.var
  QZ13x = LocalizedRing(Z13x)
  a = QZ13x[x**2 + x + 1, x**2 - 1]
  b = QZ13x[x + 1, x**2 + 3*x + 2]
  p a + b
  p( (a + b) ** 4)
  puts

  Rx = Polynomial(Rational, "x")
#  Rx = Polynomial(Z13, "x")
  x = Rx.var
  QRx = LocalizedRing(Rx)
  x = QRx[x]
  a = (x**2 + 1)/(x**3 + x + 1)
  QRxy = Polynomial(QRx, "y")
  y = QRxy.var
  AFF = ResidueClassRing(QRxy, y**3 + a*y + 1)
  y = AFF[y]
  p( (y+x) ** 3 )

  F = RationalFunctionField(Rational, "x")
  x = F.var
  p ( 1 / (x**2 - 1) - 1 / (x**3 - 1) )

  require "mathn"
  require "algebraic-extension-field"
  S = AlgebraicExtensionField(Rational, "a") {|a| a**2 - 2}
  QSx = RationalFunctionField(S, "x")
  x, a = QSx.var, S.var
  p( (a/4*x + 1/2)/(x**2 + a*x + 1) + (-a/4*x + 1/2)/(x**2 - a*x + 1) )
end
