# 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")
      poly = self
      # poly may have duplicate roots
      ext_field = poly.ground
      
      polys = pre_facts ? pre_facts.dup : poly.factorize
      roots = []
      addelems = []
      cmpelems = []
      fac = Factors.new
      
      while fn = polys.shift
	f, n = fn
	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
	  newF = Algebra.AlgebraicExtensionField(ext_field, var_obj) {|v|
	    f.evaluate(v)
	  }

	  roots.concat([newF.var] * n)
	  addelems.push newF.var
	  cmpelems.concat([newF.var] * (n-1))

	  px = Algebra.Polynomial(newF, "x")
	  fac.push [px.var - newF.var, n]
	  q = f.convert_to(px) / (px.var - newF.var)
	  polys_new = q.factorize ** n
	  polys.each do |g, m|
	    fc = g.convert_to(px).factorize
	    polys_new.concat fc ** m
	  end
	  
	  ext_field = newF
	  polys = polys_new
	  var_obj = var_obj.succ
	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")
      poly = self
      field, def_polys, fac, roots, proots =
	poly.decompose(pre_facts, var_obj)
      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
