# Splitting Field of Polynomial over Rational
#
#   by Shin-ichiro Hara
#
# Version 1.11 (2002.02.23)

require "algebra/polynomial"
require "algebra/polynomial-factor"
require "algebra/algebraic-extension-field"

module Algebra
  module SplittingField
    def decompose(pre_facts = nil, var_obj = "a", no_sq = false)
      poly = self
      # poly may have duplicate roots
      ext_field = poly.ground

      polys = pre_facts ? pre_facts.dup : poly.factorize
      roots = []
      #roots == addelems + cmpelems, as set.
      #the difference is ordering.
      addelems = []
      cmpelems = []
      fac = Factors.new
      
      while fn = polys.shift
        f, n = fn #f: irreducible, n: duplication
        if f.deg <= 1
          fac.push [f, n]
	   roots.concat([-f[0]/f[1]] * n) unless f[1].zero?
	   cmpelems.concat([-f[0]/f[1]] * n) unless f[1].zero?
        else
          if !no_sq && f.deg == 2
            newF, r, r0 = Algebra.QuadraticExtensionField(ext_field, &f)
            roots.concat([r, r0] * n)

            addelems.push r
            cmpelems.concat([r] * (n-1))
            cmpelems.concat([r0] * n)

            px, x = Algebra.Polynomial(newF, "x")
            fac.push [x - r, n]
            fac.push [x - r0, n]
            q = f.convert_to(px) / (x - r) / (x - r0) #scalar
          else
            newF = Algebra.AlgebraicExtensionField(ext_field, var_obj) {|v|
              f.evaluate(v)
            }
            r = newF.var
            
            roots.concat([r] * n)
            addelems.push r
            cmpelems.concat([r] * (n-1))
            
            px, x = Algebra.Polynomial(newF, "x")
            fac.push [x - r, n]
            q = f.convert_to(px) / (x - r)
          end

          polys_new = (q.factorize) ** n
          polys.each do |g, m|
            g0 = g.convert_to(px)
            fc = g0.factorize
            polys_new.concat fc ** m
          end
	  
          ext_field = newF
          polys = polys_new
          var_obj = /^[a-qs-z]\d*$/ =~ var_obj ? var_obj.succ : var_obj + "_"
        end
      end
      
      mods = if ext_field <= Algebra::AlgebraicExtensionField
	       ext_field.def_polys
	     else
	       []
	     end

      proots = addelems+cmpelems

      [ext_field, mods, fac, roots, proots]
    end

    def splitting_field(pre_facts = nil, var_obj = "a", no_sq = false)
      var_obj ||= "a"
      poly = self
      field, def_polys, fac, roots, proots =
	poly.decompose(pre_facts, var_obj, no_sq)
      roots = roots.collect{|r| field.regulate(r)}
      proots = proots.collect{|r| field.regulate(r)}

      Struct.new(:poly, :field, :roots, :def_polys, :proots)[
	poly, field, roots, def_polys, proots
      ]
    end
  end

  #for backward compatibility
  def MinimalDecompositionField(f, *a); f.decompose(*a); end
  module_function :MinimalDecompositionField
end


syntax highlighted by Code2HTML, v. 0.9.1