# Algebraic Extension Field
#
#   by Shin-ichiro Hara
#
# Version 1.00 (2002.02.27)

require "algebra/polynomial"
#require "algebra/m-polynomial"
require "algebra/residue-class-ring"
require "algebra/polynomial"

module Algebra
  def AlgebraicExtensionField(field, var = "x", &b)
    AlgebraicExtensionField.create(field, var, &b)
  end
  module_function :AlgebraicExtensionField

  class AlgebraicExtensionField < ResidueClassRing
    def self.create(field, var_obj)
      poly_ring = Algebra.Polynomial(field, var_obj)

      modulus = yield(poly_ring.var)
      klass = super(poly_ring, modulus)

#      def klass.var; self[ground.var]; end
      klass.sysvar :var, klass[klass.ground.var]
      klass.sysvar :base, field

      if Algebra::AlgebraicExtensionField >= field
	env_ring = Algebra.Polynomial(field.env_ring, var_obj)
	klass.sysvar :def_polys, field.def_polys + [modulus]
      else
	env_ring = poly_ring
	klass.sysvar :def_polys, [modulus]
      end

      poly_ring.class_eval <<-__DEF__
      def abs_lift
	if Algebra::AlgebraicExtensionField >= ground
	  project(self.class.env_ring) {|c, n| c.abs_lift }
	else
	  project(self.class.env_ring) {|c, n| c }
	end
      end
      __DEF__
      poly_ring.sysvar :env_ring, env_ring
      klass.sysvar :env_ring, env_ring

      klass
    end

    def [](n)
      lift[n]
    end

    def abs_lift
      Algebra::AlgebraicExtensionField >= self.class.base ? lift.abs_lift : lift
    end

    def self.to_ary
      [self, var]
    end
  end

################# experimental ###################

  def QuadraticExtensionField(field, var_obj = nil)
    poly_ring = Algebra.Polynomial(field, 'x')
    modulus = yield(poly_ring.var)
    unless modulus.deg == 2
      raise "give deg 2 polynomial to QuadraticicExtensionField."
    end

    fact = modulus.factorize
    if o = fact.find{|x| x[0].deg == 1}
      b, a = o[0][0], o[0][1]
      c = b / a
      return [field, - c, c]
    end

    c, b, a = modulus[0], modulus[1], modulus[2]
    a2 = a * 2
    b0 = a2.zero? ? b :  (b / a2)
    c1 = c / a - b0**2
    cs = (-c1).to_s
    r = "r"
    var_obj ||= r + (/^\d+$/ !~ cs ? "(" +  cs + ")" : cs)

    klass, v = AlgebraicExtensionField(field, var_obj) {|x| x**2 + c1}
    
    r1 = v - b0
    r2 = - v - b0
    [klass, r1, r2]
  end
  module_function :QuadraticExtensionField

  def Sqrt(field, a, name = nil)
    QuadraticExtensionField(field, nil) {|x| x**2 - a}
  end

  module_function :Sqrt

  def Root(field, a = nil, deg = 2, cs = nil, &b)
    r, x = Polynomial(field)
    if b
      f = b.call(x)
      as = a ? a.to_s : f.to_s
    else
      f = x ** deg - a
      as = a.to_s
    end

    r = deg == 2 ? "r" : "r[#{deg}]"
    cs ||= r + (/^\d+$/ !~ as ? "(" +  as + ")" : as)
    k = f.splitting_field(nil, cs)
    [k.field, *k.roots]
  end

  module_function :Root
end

if $0 == __FILE__
R2, r2, r2_ = Root(Rational, 2)
R3, r3, r3_ = Root(R2, 3)
R6, r6, r6_ = Root(R3, 6)

p r6
p (r6 + r2 + r3)*(r6 + r2 - r3)

P, x = Polynomial(R6, x)
p (x**4 - 60*x**2 + 36).factorize
  #=> (x - 2r3 - 3r2)(x + 2r3 - 3r2)(x - 2r3 + 3r2)(x + 2r3 + 3r2)
end


syntax highlighted by Code2HTML, v. 0.9.1