factorization.sa
Generated by gen_html_sa_files from ICSI. Contact gomes@icsi.berkeley.edu for details
class SET_POLYI_HENSEL
class SET_POLYI_HENSEL is
-- Expand DPolyM in Zp[x] to PolyM in Z[x] with PolyM|PolyN
-- DpolyMs to Z-coefficient witn Hensel's construction
setgL1(g:POLYS_INTI):INTI is
l::=0.inti;
loop c::=g.arr.elt!; l:=l+c.abs; end;
return l;
end;
setPolyRHensel(prime:INTI,inout PolyN, DpolyM:POLYS_INTI, out factor:POLYS_INTI):BOOL is
factor:=#;
h,q,r:POLYS_INTI;
f:POLYS_INTI:=PolyN.copy;
if f.lc.is_neg then f:=-f; end;
f_lc::=f.lc;
f.monic;
-- DpolyMs:=DpolyM.mod_n(prime)
g:POLYS_INTI:=DpolyM.mod_n(prime);
--g:=g*(lc^(g.degree)*INTI_EXT::inv(prime,g.lc));
--g:=g.substitute(Poly("x")/lc).coeff_to_Zp(prime,false);
g.pseudo_monic(f_lc);
g:=g.mod_n(prime);
f.divmod(g,out q, out r);
if r.is_zero then
g:=g.substitute(#POLYS_INTI(f_lc,1));
g.remove_gcd;
PolyN.divmod(g,out PolyN, out r);
factor:=g;
return true;
end;
-- if ! Number.checkDivZ?(f[0],g[0],prime); return false;end
f.divmod_Zp(prime, g,out h, out r);
--- To bound iteration.
-- F= fk x^k +...+f1 x^1+f0, G= gl x^l +...+gx x^1+g0
-- Let fL2= sqrt(|fk|^2+...+|f0|^2)* (gl/fk *2^l), gL1= (|gl|+..+|g0|)
-- then gL1<= fL2.
-- Iterate until |max coefficient of g| < gL1 <= fL2 < pn/2.
-- (Note thet f.lc==1, g.lg==1, pn=prime^n)
-- Let fMax=(max coefficient of f).
-- Check if divisible when pn^n>fMax,
fL2::=1.inti; fMax::=0.inti;
loop
c::=f.arr.elt!;
fL2:=fL2+c*c; if fMax<c.abs then fMax:=c.abs; end;
end;
fL2:=(fL2*(4.inti)^(g.degree)).sqrt;
--
n::=1.inti; pn::=prime; -- pn=prime^n
gL1::=setgL1(g);
loop while!( (pn<fL2*2.inti)and (gL1<=fL2));
f.constructionHensel(g,h,n,prime,pn,out g, out h);
n:=n+1.inti; pn:=pn*prime;
if (pn>fMax)and (f-g*h).is_zero then
g:=g.substitute(#POLYS_INTI(f_lc,1));
g.remove_gcd;
PolyN.divmod(g,out PolyN,out r);
factor:=g;
return true;
end;
gL1:=setgL1(g);
end;
return false;
end;
end;
class SET_POLYI_PRIM
class SET_POLYI_PRIM is
-- Expand DPolyM in Zp[x] to PolyM in Z[x] with PolyM|PolyN
-- with simple try&error.
-- 1. Make value sa if substitute integer [-PolyN.degree/4..PolyN.degree/4]
-- to expected factor.
-- 2. And construct the factor.
shared TblS:ARRAY{INTI};-- work table
shared V0,V1,V1nS:ARRAY{INTI};
shared DpolyMs:POLYS_INTI;
shared PolyN:POLYS_INTI;
shared PolyF:POLYS_INTI; -- factor
-- degD=DpolyM.degree for almost part.
setPolyR(degD:CARD,prime:INTI, out polyF:POLYS_INTI):BOOL is
-- TRUE if Set set polynomial PolyF[] from TblS[]
-- From dPolyM in Zp[x], recover polyF in Z[x].
-- local var: dv,dt,at, deg,diff,i, tbl0, tbl2
tbl0,tbl2:ARRAY{INTI};
polyF:=#; polyF.arr:=#(degD); polyF.clear;
-- polyF.array.fill(0,0..degD)
tbl0:=TblS.copy;
loop deg:CARD:=degD.downto!(1);
tbl2:=tbl0.copy;
dv:INTI:=1.inti;
loop diff::=deg.downto!(1);
loop i::=0.upto!(diff-1); tbl2[i]:=tbl2[i+1]- tbl2[i]; end;
dv:=dv*diff.inti;
end;
if ((tbl2[0] % dv) /= 0.inti) then return false; end;
at:INTI:=tbl2[0] / dv;
polyF[deg]:=DpolyMs[deg]+at*prime;
loop i::=0.upto!(deg);
dt:INTI:=at;
dv:=i.inti-(degD/2).inti;
loop deg.times!; dt:=dt*dv; end;
tbl0[i]:=tbl0[i] - dt;
end;
end;
polyF[0]:=DpolyMs[0]+tbl0[0]*prime;
polyF:=polyF.normalize;
return true;
end;
setV0Tbl(degD:CARD) is
-- make table PolyN.
-- Elements are non-zero,
-- because PolyN has no factor (x-a) in this range
V0:=#(degD+1);
loop i::=0.upto!(degD);
V0[i]:=PolyN.substitute(i.inti-(degD/2).inti).abs;
end;
end;
setV1Tbl(degD:CARD) is
-- make table DpolyMs
V1:=#(degD+1);
loop i::=0.upto!(degD);
V1[i]:=DpolyMs.substitute(i.inti-(degD/2).inti);
end;
end;
setVal(ite,degD:CARD, prime:INTI):BOOL is
-- "true" if ffind a factor.
-- Note that change PolyN and PolyM.
v0i:INTI:=V0[ite]; v1i:INTI:=V1[ite]; setFlg:BOOL:=false;
-- (v1i+prime*a)|v0i then range of a is
-- (-v0i-v1i)/prime..(v0i-v1i)/prime
a:INTI:=0.inti;
if a<(-v0i-v1i)/prime then a:=(-v0i-v1i)/prime; end;
if a>(v0i-v1i)/prime then a:=(v0i-v1i)/prime; end;
loop while!( ((-v0i-v1i)/prime<=a)and(a<=(v0i-v1i)/prime));
v1n:INTI:=(v1i+prime*a).abs;
if ((v1n /=0.inti) and ((v0i % v1n)=0.inti)) then
-- V1nS[ite]:=v1n;
TblS[ite]:=a;
if (ite>=degD) then
polyQ, polyR:POLYS_INTI;
setFlg:=setPolyR(degD,prime,out PolyF);
if setFlg then
PolyN.divmod(PolyF,out polyQ,out polyR);
if polyR.is_zero then -- Now, PolyF is factor.
-- printf "M=%s F=%s prime=%d\n",DpolyMs.to_s,polyF.to_s,prime
PolyN:=polyQ;
-- The polynomial is square free, so,
-- this is only one factor having DpolyM
-- throw(:setValTag,true);
return true;
end;
end;
else
if setVal(ite+1,degD,prime) then return true; end;
end;
end;
if a<=0.inti then a:=1.inti-a;
if a>(v0i-v1i)/prime then a:=-a; end;
else a:=-a;
if a<(-v0i-v1i)/prime then a:=1.inti-a; end;
end;
end;
return false;
end;
setPolyRPrimitive(prime:INTI, inout PolyNx, DpolyM:POLYS_INTI, out factor:POLYS_INTI):BOOL is
PolyN:=PolyNx;
loop i:INTI:=1.inti.upto!(prime);
-- adjust leading coefficient.
-- This is not needed if PolyN,DpolyM are monic.
dpolym::=(DpolyM*i).mod(prime);
if INTI_EXT::checkDivZ(PolyN.lc,dpolym.lc,prime)and
INTI_EXT::checkDivZ(PolyN[0],dpolym[0],prime) then
DpolyMs:=dpolym.mod_n(prime);
degD::=DpolyM.degree.card;
-- V1nS:=#(degD+1);
TblS:=#(degD+1);
setV0Tbl(degD);
setV1Tbl(degD);
if setVal(0,degD,prime) then
factor:=PolyF;
PolyNx:=PolyN;
return true;
end;
end;
end;
return false;
end;
end;
class FACTORIZATION_ALG
class FACTORIZATION_ALG is
-- Factorize a polynomial
-- Let p:prime.
--(1) Let Ap be A in Zp<t>.
-- if Ap=1 ; Apd:=1; Resume.
--(2) Search A'p s.t. A'p | Apd in Zp[x].
--(3) Check of A' | A in Z[x]
--
-- Contain algorithms of
-- 2 type of factorization in Zp[x] and
-- 2 type of constructing polynomials in Z[x] from Zp[x].
-- as module variables --
shared Factor:ARRAY{POLYS_INTI};
shared FactorP:ARRAY{POLYS_INTI}; -- factors in Z/(prime)Z
shared PolyN:POLYS_INTI; -- Normalized
shared PolyM:POLYS_INTI; -- PolyN in Zp
shared DpolyM:POLYS_INTI; -- divisor in Zp i.e. DpolyM|PolyM
shared DpolyMs:POLYS_INTI;-- change each coefficient to little abs
init is
Factor:=#;
FactorP:=#;
PolyN:=#;
PolyM:=#;
DpolyM:=#;
DpolyMs:=#;
end;
checkDivZp(dividend,divisor:POLYS_INTI, prime:INTI):BOOL is
-- true if divisible in Zp
--if true then -- test
-- q,r:POLYS_INTI;
-- dividend.divmod_Zp(prime, divisor,out q, out r); return r.is_zero;
--end;
dq,degR,degD:CARD;
q1,topDm:INTI;
degR:=dividend.degree.card;
degD:=divisor.degree.card;
if degR < degD then return false; end;
darray::=divisor.arr;
rarray::=dividend.array.copy; -- Remainder(dividend)
topDm:=prime-INTI_EXT::inv(prime,darray[degD]); -- -1/divisor.lc
loop while!(degR>=degD);
q1:=(rarray[degR]*topDm)%prime;
if q1.is_zero.not then
loop i::=0.upto!(degD-1); j::=(degR-degD).up!;
rarray[j] := rarray[j]+darray[i]*q1;
end;
-- rarray[degR]:=0.inti;
end;
degR:=degR - 1;
end;
loop i::=0.upto!(degD-1);
if (rarray[i] % prime).is_zero.not then return false; end;
end;
return true;
end;
-- Expand DPolyM in Zp[x] to PolyM in Z[x] with PolyM|PolyN
resume(prime:INTI):BOOL is
-- TRUE if find Factors
-- #OUT+"resume: prime="+prime.str+", d="+DpolyM.str+"\n";
findFlg::=false;
factor:POLYS_INTI;
if false then -- for test
--#OUT+"PolyN="+PolyN.str+", DpolyM="+DpolyM.str+
-- ", prime="+prime.str+"\n";
findFlg:=SET_POLYI_PRIM::setPolyRPrimitive
(prime, inout PolyN, DpolyM, out factor);
elsif DpolyM.degree>=5.int then
findFlg:=SET_POLYI_HENSEL::setPolyRHensel
(prime,inout PolyN, DpolyM, out factor);
else
findFlg:=SET_POLYI_PRIM::setPolyRPrimitive
(prime, inout PolyN, DpolyM, out factor);
end;
if findFlg then
Factor:=Factor.append(|factor|);
PolyM:=PolyN.mod(prime);
end;
return findFlg;
end;
----------------following PROCEDUREs work in Zp[t]-------------
-- Note that the polynomial PolyN is square free in Zp
-- set DpolyM with Berlekamp's algorithm --
-- DpolyM | PolyN over Zp
printQ(q:MAT_INTI) is
#OUT+"[";
loop i::=0.upto!(q.nr-1);
loop j::=0.upto!(q.nc-1);
#OUT+q[i][j].str+", ";
end;
#OUT+"\n";
end;
#OUT+"]\n";
end;
getQ(f:POLYS_INTI,prime:INTI):MAT_INTI is
q:MAT_INTI;
w1,w2,w3:POLYS_INTI;
n::=f.degree.card;
w1:=POLYS_INTI::x^prime;
w1.divmod_Zp(prime,f, out w3, out w1);
w2:=POLYS_INTI::one;
q:=#(n,n);
a:ARRAY{INTI};
loop i::=0.upto!(q.nr-1);
a:=#(n); a.to_val(0.inti);
loop j::=w2.arr.ind!;
a[j]:=w2.arr[j];
end;
q[i]:=a;
(w2*w1).divmod_Zp(prime,f,out w3,out w2);
end;
return q;
end;
solveQ1(q:MAT_INTI,prime:INTI):ARRAY{POLYS_INTI} is
n::=q.nr;
q0:MAT_INTI:=q.copy;
loop i::=0.upto!(n-1);
q[i][i]:=(q[i][i]+prime-1.inti)%prime;
end;
-- printQ(q);
pivot:ARRAY{INT}:=#(n); pivot.to_val(-1);
loop i::=0.upto!(n-1);
-- set pivot
j1:CARD;
loop j1:=0.up!;
while!((j1<n)and((q[i][j1]=0.inti)or(pivot[j1]/=-1)));
end;
if j1<n then
q.swap_col(i,j1, i,n-1);
pivot[i]:=i.int; j::=i;
pvr::=INTI_EXT::inv(prime,q[i][j]);
loop j1:=0.upto!(n-1);
if j1 /= j then
d::=((prime-q[i][j1])*pvr)%prime;
q.col_plus_scaled_col(j1,j,d);
q.col_mod(j1,prime);
end;
end;
end;
end;
-- printQ(q);
solve:ARRAY{POLYS_INTI}:=#;
loop i::=0.upto!(n-1);--(n-2);
if pivot[i]=-1 then
v:ARRAY{INTI}:=#(n); v.to_val(0.inti);
v[i]:=1.inti;
loop j::=0.upto!(n-1);
v[j]:=(v[j]+prime-(q[i][j]*INTI_EXT::inv(prime,q[j][j]))%prime)%prime;
end;
u::=POLYS_INTI::zero;
x::=POLYS_INTI::x;
loop j::=0.upto!(n-1); xj::=x^j;
loop k::=0.upto!(n-1);
u:=u+xj*v[k]*q0[k][j];
end;
end;
solve:=solve.append(|u.mod(prime)|);
end;
end;
return solve;
end;
genFactors(f:POLYS_INTI, uList:ARRAY{POLYS_INTI}, prime:INTI):ARRAY{POLYS_INTI} is
gcdList1:ARRAY{POLYS_INTI};
loop u:POLYS_INTI:=uList.elt!;
loop c::=#INTI(0).upto!(prime-1.inti);
g::=f.gcd_Zp(prime, u+c);
if g.degree.is_pos then gcdList1:=gcdList1.append(|g|); end;
end;
end;
i::=0; changeFlg::=false;
loop while!( i<gcdList1.size-1);
gcdList1.sort; -- small is top
j::=i+1; changeFlg:=false;
loop while!(j<gcdList1.size);
r,q:POLYS_INTI;
gcdList1[j].divmod_Zp(prime, gcdList1[i],out q, out r);
if r.is_zero then changeFlg:=true;
if q.degree.is_pos then gcdList1[j]:=q; j:=j+1;
else gcdList1:=ARRAY_EXT{POLYS_INTI}::delete_at(j,gcdList1);
end;
else j:=j+1;
end;
end;
if changeFlg then i:=0; else i:=i+1; end;
end;
-- printf "gcdList1:\n"; gcdList1.each{|f|printf "%s\n",f}
loop i:=0.upto!(gcdList1.size-1);
g::=gcdList1[i];
gcdList1[i]:=(g*INTI_EXT::inv(prime,g.lc)).mod(prime);
end;
return gcdList1;
end;
setPolyB(depth:CARD, dpoly:POLYS_INTI, prime:INTI):BOOL is
-- true if factor is determined
if depth<FactorP.size then
if setPolyB(depth+1,dpoly,prime) then
if dpoly/=POLYS_INTI::one then return true; end;
end;
if depth=0 then
if PolyN.degree.is_pos then
Factor:=Factor.append(|PolyN|);
PolyN:=POLYS_INTI::one;
end;
return true;
else
dpoly:=dpoly*FactorP[depth];
if setPolyB(depth+1,dpoly,prime) then
FactorP:=ARRAY_EXT{POLYS_INTI}::delete_at(depth,FactorP);return true;
end;
return false;
end;
else
if dpoly.degree.is_pos then
DpolyM:=dpoly.mod(prime);
-- DpolyMs:=dpoly.mod_n(prime);
-- #OUT+"setPolyB: prime="+prime.str+", d="+DpolyM.str+"\n";
f_r:BOOL:=resume(prime);
return f_r; -- TRUE if find Factors
else
return false;
end;
end;
end;
setPolyBerlekamp(prime:INTI) is
f::=PolyM.mod(prime);
--#OUT+"PolyM: "+PolyM.str+"\n";
q::=getQ(f,prime);
--printQ(q);
uList::=solveQ1(q,prime);
--#OUT+"uList:"+uList.str+"\n";
factors::=genFactors(f,uList,prime);
--#OUT+"factors:"+factors.str+"\n";
FactorP:=factors;
--#OUT+"FactorP:"+FactorP.str+"\n";
if setPolyB( 0, POLYS_INTI::one, prime ) then ; end;
end;
-- set DpolyM with simple try&error --
-- DpolyM | PolyN over Zp
checkZp(i:INTI,prime:INTI):BOOL is
if PolyN.degree<DpolyM.degree*2 then return true; end;
return checkDivZp(PolyM,DpolyM,prime) and resume(prime);
end;
setPoly2(d:CARD,prime:INTI,degD:CARD):BOOL is
-- true if factors are determined.
c:INTI:=0.inti; c0:INTI:=(prime/2.inti);
loop
DpolyM[d]:=c;
if (d<=0) then
--#OUT+"setPoly2: PolyN="+PolyN.str+", DpolyM="+DpolyM.str+"\n";
if (c>0.inti)and checkZp(c,prime) and (PolyN.degree<degD.int*2) then
return true;--throw(:setPolyTag)
end;
else
if setPoly2(d-1,prime,degD) then return true; end;
end;
if c<=c0 then c:=prime-c-1.inti;
if c=c0 then return false; end;
else c:=prime-c;
end;
end;
return false;
end;
setPoly(d:CARD,prime:INTI):BOOL is
loop degD::=1.upto!(d);
DpolyM[degD]:=1.inti; -- Set DpolyM as monic.
if (PolyN.degree<(degD*2).int) then return true;-- throw(:setPolyTag);
end;
if setPoly2(degD-1,prime,degD) then return true; end;
end;
return false;
end;
factorizeSqrFree(p:POLYS_INTI):ARRAY{POLYS_INTI} is
--- factorize a square free polynomial
Factor:=#;
PolyN:=p.remove_gcd;
if PolyN[0]=0.inti then PolyN.arr:=ARRAY_EXT{INTI}::delete_at(0,PolyN.arr);
Factor:=Factor.append(|POLYS_INTI::x|);
end;
degN::=PolyN.degree;
a::=1.inti;
loop while!((a<=degN.inti)and(a<=PolyN[0].abs));
loop while!(((PolyN[0]%a).is_zero)and(PolyN.substitute(a).is_zero));
polyF::=POLYS_INTI::x-a;
Factor:=Factor.append(|polyF|);
PolyN:=PolyN/polyF;
degN:=PolyN.degree;
end;
if a.is_pos then a:=-a; else a:=1.inti-a; end;
end;
if degN=1.int then Factor:=Factor.append(|PolyN|); return Factor;
elsif degN=0.int then return Factor;
end;
-- From here, we can assume
-- that the polynmial has no factor (x-a), |a|<=degN/4
-- With setPoly and setVal/setPolyR,
-- time order for setPoly may be
-- ~ k*prime^(degN/2) , k=?
-- time order for setVal
-- ~ l*(2fp/prime)^(degN/2), l=?
-- fp=geometric mean of f(x) (x in (-degN/4..degN/2-degN/4))
-- x=prime, d=degN,
-- Assume that O(x)= kx^(d/2)+ l(2fp/x)^(d/2)
-- O is minimal at x=(l/k)^{1/d} (2v)^{1/2}
fp::=1.inti;
loop x::=(-(degN/4)).upto!(degN/2-(degN/4));
fp:=fp*PolyN.substitute(x.inti).abs;
end;
--#OUT+"fp="+fp.str+"\n";
fp_fltd::=(fp.fltd)^((1.fltd)/(degN.fltd/2.fltd+1.fltd));
--#OUT+"fp="+fp_fltd.str+"\n";
fp_fltd:=(fp_fltd.fltd/30.fltd).sqrt.floor;
fp:=fp_fltd.int.inti;
--#OUT+"fp="+fp_fltd.str+"\n";
prime:INTI:=INTI_EXT::next_prime(fp-1.inti);
--#OUT+"Set prime = "+prime.str+", PolyN="+PolyN.str+"\n";
-- serach prime s.t.
-- leading coefficient and bottom coefficient does not vanish,
-- and square free over Zp
head::=PolyN.lc.abs; tail::=PolyN[0].abs;
loop while!((prime<=3.inti)or((head%prime).is_zero)
or((tail%prime).is_zero)or(PolyN.is_SqrFree(prime).not));
prime:=INTI_EXT::next_prime(prime);
end;
--#OUT+"Set prime = "+prime.str+", PolyN="+PolyN.str+"\n";
PolyM:=PolyN.mod(prime);
--#OUT+"degN="+degN.str+", PolyN="+PolyN.str+", PolyM="+PolyM.str+"\n";
-- Switch algorithms according to degree of the polynomial.
if false then -- test
--setPolyBerlekamp(prime);
DpolyM:=#;
if setPoly(degN.card/2,prime) then ; end;
elsif PolyN.degree<11.int then
DpolyM:=#;
if setPoly(degN.card/2,prime) then ; end;
else
DpolyM:=#;
if setPoly(3,prime) then ; end;
if (PolyN.degree<10.int) then
DpolyM:=#;
if setPoly(PolyN.degree.card/2,prime) then ; end;
else ; setPolyBerlekamp(prime);
end;
end;
if (PolyN.degree.is_pos) then Factor:=Factor.append(|PolyN|); end;
-- Make leading coefficients positive.
loop i::=Factor.ind!;
if Factor[i].lc.is_neg then Factor[i]:=-Factor[i]; end;
end;
Factor.sort;
return Factor;
end;
factorize(poly:POLYS_INTI):ARRAY{POLYS_INTI} is
-- factorize a polynomial
-- wrapper of factorizeSqrFree(p)
init;
p::=poly.remove_gcd;
sqrF::=p.squareFreeDecomposition;
factorA:ARRAY{POLYS_INTI}:=#;
loop i::=sqrF.ind!; f::=sqrF[i];
if f.degree.is_pos then ff:ARRAY{POLYS_INTI}:=factorizeSqrFree(f);
loop i.times!;
loop ffc::=ff.elt!.copy;
factorA:=factorA.append(|ffc|);
end;
end;
end;
end;
-- Make leading coefficients positive.
loop i::=factorA.ind!;
if factorA[i].lc.is_neg then factorA[i]:=-factorA[i]; end;
end;
factorA.sort;
init;
return factorA;
end;
end;