# Factorization of Polyomial over Integer by Zassenhaus's method
#
# by Shin-ichiro Hara
#
# Version 1.0 (2001.04.19)
require "algebra/prime-gen"
require "algebra/residue-class-ring"
module Algebra
module PolynomialFactorization
module Z
def resolve_c(g, h, c) # find [a, b] s.t. a * g + b * h == c
d, a, b = g.gcd_coeff(h)
q, r = c.divmod(d)
raise "#{c} is not devided by (#{g}, #{h})" unless r.zero?
a = a * q
b = b * q
q, r = a.divmod(h)
a = r #= a - q * h
b = b + q * g
[a, b]
end
def centorize(f, n)
half = n / 2
f.class.new(*f.collect{|c| c > half ? c - n : c })
end
def try_e(g, u)
m = g.deg
c = 1.0
n = (m/2).to_i
0.upto n - 1 do |i|
c *= (m - i) / (n - i)
end
s = 0
each do |c|
s += c**2
end
(Math.log(c * Math.sqrt(s))/Math.log(u)).to_i + 1
end
def sqfree_over_integral
f = pp
g = f.derivate.pp
h = f.pgcd(g)
f / h.pp
end
def divmod_ED(o) # divmod in the ring over Euclidian Domain
raise ZeroDivisionError, "divisor is zero" if o.zero?
d0, d1 = deg, o.deg
if d1 <= 0
a = collect{|c|
q, r = (ground.field? ? [ground.zero, c/o.lc] : c.divmond(o.lc))
if r.zero?
[q, zero]
else
return [zero, self]
end
}
[self.class.new(*a), zero]
elsif d0 < d1
[zero, self]
else
q, r = (ground.field? ? [lc/o.lc, ground.zero] : lc.divmod(o.lc))
if r.zero?
tk = monomial(d0 - d1) * q
e = rt - o.rt * tk
q, r = e.divmod_ED(o)
[q + tk, r]
else
[zero, self]
end
end
end
def resultant_by_elimination(o) #ground ring must be a field
sylvester_matrix(o).determinant_by_elimination
end
def reduce_over_prime_field(f)
Primes.new.each do |prime|
if prime > 10
pf = Algebra::Polynomial.reduction(Integer, prime, "x")
f0 = pf.reduce(f)
r = f0.resultant_by_elimination(f0.derivate)
unless r.zero?
return f0
end
end
end
end
def lifting(f, ring)
f.project(ring){|c, i| c.lift}
end
def hensel_lift(g0)
ring, pf, char = self.class, g0.class, g0.class.ground.char
f0 = pf.reduce(self)
h0, r0 = f0.divmod g0
unless r0.zero?
raise "each_product does not work well"
#return nil
end
g = lifting(g0, ring)
h = lifting(h0, ring)
a, b = 0, 0
0.upto try_e(g, char) do |e|
mod = char ** e
mod1 = mod * char
g = centorize(g + b * mod, mod1)
h = centorize(h + a * mod, mod1)
q, r = divmod(g)
if r.zero?
return [g, q]
end
c = (self - g * h) / mod1
g0, h0, c0 = pf.reduce(g), pf.reduce(h), pf.reduce(c)
a0, b0 = resolve_c(g0, h0, c0)
a, b = lifting(a0, ring), lifting(b0, ring)
end
return nil
end
def _factorize
if constant?
return Algebra::Factors.new([[self, 1]]), 0
end
f = sqfree_over_integral
flc = f.lc
f = f.monic_int(flc)
f0 = reduce_over_prime_field(f)
fa = f0.factorize_modp
f1 = f
factors = Algebra::Factors.new
# fa.each_product0 do |g0|
fa.each_product do |g0|
next if g0.deg > f1.deg
# if fact_q = f1.hensel_lift(g0, f0.class)
if fact_q = f1.hensel_lift(g0)
fact, f1 = fact_q
factors.push [fact, 1]
break if f1.deg <= 0
true
end
end
factors.map!{|f2| f2.project(self.class){|c, j| c*flc**j}.pp }
facts = factors.fact_all(self)
[facts.correct_lc!(self), f0.ground.char]
end
def factorize_int
_factorize.first
end
end
module Q
def contQ
ns = collect{|q| q.numerator}
ds = collect{|q| q.denominator}
n = ns.first.gcd_all(* ns[1..-1])
d = ds.first.lcm_all(* ds[1..-1])
# ground.new(n, d)
if ground == Rational #in 1.8.0, Rational.new is private.
ground.new!(n, d)
else
raise "unknown gound type #{ground}" #20030606
ground.new(n, d)
end
end
def ppQ(ring)
(self / contQ).project(ring){|c, j| c.to_i}
end
def factorize_rational
pz = Algebra.Polynomial(Integer, "x")
fz = ppQ(pz)
a = fz.factorize
u = ground.ground.unity # ground == Rational, ground.ground == Integer
b = a.collect{|f, i|
[f.project(self.class){|c, j|
# ground.new
##in 1.8.0, Rational.new is private.
ground == Rational ? ground.new!(c, u) : ground.new(c, u)
}, i]
}
contQ == ground.unity ? b : b.unshift([self.class.const(contQ), 1])
end
end
include Z
include Q
end
end
if __FILE__ == $0
require "algebra/polynomial"
require "algebra/polynomial-factor"
require "algebra/residue-class-ring"
require "algebra/rational"
# include Algebra
def test(f)
print "#{f}\n => "
a = f.factorize
sw = (f == a.pi)
puts "#{a.inspect}, #{sw}"
raise "Test Failed" unless sw
end
PQ = Algebra.Polynomial(Rational, "y")
y = PQ.var
P = Algebra.Polynomial(Integer, "x")
x = P.var
h = x**12 - 12*x**10 - 4*x**9 + 96*x**8 - 442*x**6 + 672*x**5 + 1572*x**4 - 1412*x**3 - 2544*x**2 - 1560*x + 4225
fs = [
54*x**3 + 57*x**2 + 28*x + 15, #(9*x**2+2*x+3)*(6*x+5)
(x**2+x+1)**2*(x+1)**3,
(x**2+x+1)**2,
(x**2+x+1)*(x+1)**3,
(x**2+x+1)*(x+1)*(x+2),
(x**2+x+1)*(x+1),
(x**2+x+1),
(x+1)**3,
(3*x + 1)*(2*x + 5)*3,
(x**3 + 1)**3,
# (x**8+x**6+x**5+x**4+x**3+x**2+1)*(x**2+x+1)**3,
(y**2 + 2*y + Rational(3, 4))*(y + Rational(1, 3)),
# h
]
# fs[-1..-1].each do |f|
fs.each do |f|
test(f)
end
end
syntax highlighted by Code2HTML, v. 0.9.1