# Galois Group
#
#   by Shin-ichiro Hara
#
# Version 1.12 (2002.03.12)

require "permutation-group"

module Algebra
  module Galois
    def galois_group(pre_facts = nil, sw = nil)
      poly = sqfree

      sf = poly.splitting_field(pre_facts)
      gens = []
      reps0 = []
      n = poly.deg
      dpn = sf.def_polys.size
      for i in 0...dpn do
	reps = []
	for j in (sw ? i : i+1)...n do
	  if g = prolong((0...i).to_a + [j], sf)
	    reps << g
	  end
	end
	gens.unshift reps
	#      gens.push reps
      end
      
      for i in dpn...n; gens << [(0...n).to_a]; end if sw
      
      gs = gens.map{|a| a.map{|g| complete(g, sf)}}
      ary_of_gens = gs.collect{|a| a.collect{|x| Permutation.new(x)}}
      PermutationGroup.generate_strong(Permutation.unity(n), *ary_of_gens)
    end
    
    def prolong(ary, sf)
      t = ary.size
      roots = sf.roots
      pn = sf.def_polys.size
      defs = sf.def_polys
      
      for i in 0...t do
	r = (roots.indices(*ary))[0..i]
	unless defs[i].abs_lift.evaluate(*r).zero?
	  return nil
	end
      end
      
      ary0 = ary.dup
      for k in t...defs.size do
	sw = false
	for c in (0...roots.size).to_a - ary0 do
	  r = (roots.indices(* ary0+[c]))[0..k]
	  if defs[k].abs_lift.evaluate(*r).zero?
	    ary0.push c
	    sw = true
	    break
	  end
	end
	return nil unless sw
      end
      ary0
    end
    
    def complete(ary, sf)
      ary = ary.dup
      roots = sf.roots
      n = roots.size
      m = sf.def_polys.size
      for i in m...n
	ri = roots[i].abs_lift
	h = if ri.is_a? Algebra::Polynomial
	      ri.evaluate(*roots.indices(*ary[0...m]))
	    else
	      ri
	    end
	
	for k in 0...n
	  if !ary.include?(k) && roots[k] == h
	    ary.push k
	  else
	  end
	end
      end
      ary
    end
  end
end

if __FILE__ == $0
  require "rational"
  require "polynomial"
  require "splitting-field"

  P = Algebra.Polynomial(Rational, "x")
  x = P.var
  f = [
    x**3 - 2,             #0
    (x**2 - 2)*(x**2 - 3),#1
    x**3 - 3*x + 1,       #2
    x**3 - x + 1,         #3
    (x**3 - 2)**2,        #4
    x**4  + 1,
    x**4 - x + 1,
  ][ARGV.shift.to_i]

  puts "Galois Group of #{f} is:"
  f.galois_group.each do |reps|
    p reps
  end
end
