# G.C.D. and something for Euclidian ring (equipping 'divmod' and 'zero?')
#
#   by Shin-ichiro Hara
#
# Version 1.01 (2001.03.22)

require "algebra/algebraic-system"
module Algebra
module EuclidianRing
  def lcm(b)
    self * b / gcd(b)
  end

  def lcm_all(*a)
    t = self
    a.each do |x|
      t = t.lcm(x)
    end
    t
  end

  def gcd3(b)
    x, y = self, b
    until y.zero?
      q, r = x.divmod y
      x, y = y, r
    end
    x
  end

  def gcd(n)
    m = self
    while n != 0
      m %= n
      tmp = m; m = n; n = tmp
    end
    m
  end

  def gcd_all(*a)
    t = self
    a.each do |x|
      t = t.gcd(x)
    end
    t
  end

  def gcd_ext(g)
    h, s = self, g
    a, b = self.class.unity, self.class.zero
    c, d = self.class.zero, self.class.unity
    loop do
      q, r = h.divmod s
      return [s, c, d] if r.zero?
      h, s = s, r # r == h - q * s
      a, c = c, a - q * c
      b, d = d, b - q * d
    end
  end

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

  def gcd_coeff_all(*a0)
    return [self, self] if a0.empty?
    t, *a = a0
    d, x, y = gcd_ext(t)
    r = [x, y]
    a.each do |u|
      d, x, y = d.gcd_coeff(u)
      r = r.collect{|v| x*v} + [y]
    end
    [d] + r
  end

  alias gcd_ext_all gcd_coeff_all

  def pgcd(b)
    x, y = self, b
    until y.zero?
      q, r = x.pdivmod y
      x, y = y, r
    end
    x
  end

  def gcd_rec(b)
    return self if b.zero?
    return b if zero?
    if is_a?(Polynomial) && !ground.field?
      ac, ap = cont_pp
      bc, bp = b.cont_pp
      c = ac.gcd_rec(bc)
      x, y = ap, bp
      until y.zero?
	q, r = x.pdivmod y
	x, y = y, r
      end
      x.pp * c
    else
      x, y = self, b
      until y.zero?
	q, r = x.pdivmod y
	x, y = y, r
      end
      x
    end
  end

  def cont
    t = nil
    each do |x|
      break if t && t.unit?
      t = t ? t.gcd_rec(x) : x
    end
    t ? t : self
  end

  def pp; self / cont; end
  def cont_pp; [c = cont, self / c]; end
end
end

class Integer# < Numeric
  include Algebra::EuclidianRing
end

if $0 == __FILE__
  include Algebra
  a, b = 108, 221
  d, x, y = a.gcd_coeff(b)
  puts "#{x}*#{a} + #{y}*#{b} = #{x * a + y * b} = #{d}"
  a, b, c, e = 36, 126, 81, 12
  p a.gcd_all(b, c, e)
  d, x, y, z, w = a.gcd_coeff_all(b, c, e)
  p d == x*a + y*b + z*c + w*e
  puts "#{d} == #{x}*#{a} + #{y}*#{b} + #{z}*#{c} + #{w}*#{e}"

  require "algebra/polynomial"
  require "algebra/residue-class-ring"
  Z7 = ResidueClassRing(Integer, 7)
#  P = Polynomial(Z7, "x", "y")
  P = Polynomial(Integer, "x", "y")
  x, y = P.vars
  f = 6*(x + 1)*(x + 2)*(y - 1)* (y + 2)
  g = 9*(x + 1)*(x - 3)*(y - 1)* (y - 1)
  p [f, g, f.gcd_rec(g)]
end
