|
#############################################################################
##
## This file is part of GAP, a system for computational discrete algebra.
## This file's authors include Alexander Hulpke.
##
## Copyright of GAP belongs to its developers, whose names are too numerous
## to list here. Please refer to the COPYRIGHT file for details.
##
## SPDX-License-Identifier: GPL-2.0-or-later
##
## This file contains functions for polynomials over the rationals
##
#############################################################################
##
#F APolyProd(<a>,<b>,<p>) . . . . . . . . . . polynomial product a*b mod p
##
##return a*b mod p;
InstallGlobalFunction(APolyProd,function(a,b,p)
local ac,bc,i,j,pc,pv,ci,fam;
ci:=CIUnivPols(a,b);
fam:=FamilyObj(a);
a:=CoefficientsOfLaurentPolynomial(a);
b:=CoefficientsOfLaurentPolynomial(b);
pv:=a[2]+b[2];
#pc:=ProductCoeffs(a.coefficients,b.coefficients);
ac:=List(a[1],i->i mod p);
bc:=List(b[1],i->i mod p);
if Length(ac)>Length(bc) then
pc:=ac;
ac:=bc;
bc:=pc;
fi;
# prepare result list
pc:=[];
for i in [1..Length(ac)+Length(bc)-1] do
pc[i]:=0;
od;
for i in [1..Length(ac)] do
#pc:=SumCoeffs(pc,i*bc) mod p;
for j in [1..Length(bc)] do
pc[i+j-1]:=(pc[i+j-1]+ac[i]*bc[j]) mod p;
od;
od;
pv:=pv+RemoveOuterCoeffs(pc,fam!.zeroCoefficient);
return LaurentPolynomialByExtRepNC(fam,pc,pv,ci);
end);
#############################################################################
##
#F BPolyProd(<a>,<b>,<m>,<p>) . . . . . . polynomial product a*b mod m mod p
##
## return EuclideanRemainder(PolynomialRing(Rationals),a*b mod p,m) mod p;
InstallGlobalFunction(BPolyProd,function(a,b,m,p)
local ac,bc,mc,i,j,pc,ci,f,fam;
ci:=CIUnivPols(a,b);
fam:=FamilyObj(a);
a:=CoefficientsOfLaurentPolynomial(a);
b:=CoefficientsOfLaurentPolynomial(b);
m:=CoefficientsOfLaurentPolynomial(m);
# we shift as otherwise the mod will mess up valuations (should occur
# rarely anyhow)
ac:=List(a[1],i->i mod p);
ac:=ShiftedCoeffs(ac,a[2]);
bc:=List(b[1],i->i mod p);
bc:=ShiftedCoeffs(bc,b[2]);
mc:=List(m[1],i->i mod p);
mc:=ShiftedCoeffs(mc,m[2]);
ReduceCoeffsMod(ac,mc,p);
ShrinkRowVector(ac);
ReduceCoeffsMod(bc,mc,p);
ShrinkRowVector(bc);
if Length(ac)>Length(bc) then
pc:=ac;
ac:=bc;
bc:=pc;
fi;
pc:=[];
f:=false;
for i in ac do
if f then
# only do it 2nd time (here to avoin doing once too often)
bc:=ShiftedCoeffs(bc,1);
ReduceCoeffsMod(bc,mc,p);
ShrinkRowVector(bc);
else
f:=true;
fi;
#pc:=SumCoeffs(pc,i*bc) mod p;
for j in [Length(pc)+1..Length(bc)] do
pc[j]:=0;
od;
for j in [1..Length(bc)] do
pc[j]:=(pc[j]+i*bc[j] mod p) mod p;
od;
ShrinkRowVector(pc);
ReduceCoeffsMod(pc,mc,p);
ShrinkRowVector(pc);
od;
p:=RemoveOuterCoeffs(pc,fam!.zeroCoefficient);
return LaurentPolynomialByExtRepNC(fam,pc,p,ci);
end);
#############################################################################
##
#F ApproxRational: approximativ k"urzen
##
BindGlobal("ApproxRational",function(r,s)
local n,d,u;
n:=NumeratorRat(r);
d:=DenominatorRat(r);
u:=LogInt(d,10)-s+1;
if u>0 then
u:=10^u;
return QuoInt(n,u)/QuoInt(d,u);
else
return r;
fi;
end);
#############################################################################
##
#F ApproximateRoot(<num>,<n>[,<digits>]) . . approximate th n-th root of num
## numerically with a denominator of 'digits' digits.
##
BIND_GLOBAL( "APPROXROOTS", NEW_SORTED_CACHE(false) );
BindGlobal("ApproximateRoot",function(arg)
local r,e,f,store,maker;
r:=arg[1];
e:=arg[2];
if Length(arg)>2 then
f:=arg[3];
else
f:=10;
fi;
store:= e<=10 and IsInt(r) and 0<=r and r<=100 and f=10;
maker := function()
local x,nf,lf,c,letzt;
x:=RootInt(NumeratorRat(r),e)/RootInt(DenominatorRat(r),e);
nf:=r;
c:=0;
letzt:=[];
repeat
lf:=nf;
Add(letzt,x);
x:=ApproxRational(1/e*((e-1)*x+r/(x^(e-1))),f+6);
nf:=AbsoluteValue(x^e-r);
if nf=0 then
c:=6;
else
if nf>lf then
lf:=nf/lf;
else
lf:=lf/nf;
fi;
if lf<2 then
c:=c+1;
else
c:=0;
fi;
fi;
# until 3 times no improvement
until c>2 or x in letzt;
return x;
end;
if store then
return GET_FROM_SORTED_CACHE(APPROXROOTS, [e,r], maker);
fi;
return maker();
end);
#############################################################################
##
#F ApproxRootBound(f) Numerical approximation of RootBound (better, but
## may fail)
##
BindGlobal("ApproxRootBound",function(f)
local x,p,tp,diff,app,d,scheit,v,nkon;
x:=IndeterminateNumberOfLaurentPolynomial(f);
p:=CoefficientsOfLaurentPolynomial(f);
if p[2]<0 or not ForAll(p[1],IsRat) then
# avoid complex conjugation etc.
Error("only yet implemented for rational polynomials");
fi;
# eliminate valuation
f:=UnivariatePolynomialByCoefficients(CyclotomicsFamily,p[1],x);
x:=UnivariatePolynomialByCoefficients(CyclotomicsFamily,[0,1],x);
# probably first test, whether polynomial should be inverted. However,
# we expect roots larger than one.
d:=DegreeOfLaurentPolynomial(f);
f:=Value(f,1/x)*x^d;
app:=1/2;
diff:=1/4;
nkon:=true;
repeat
# pol, whose roots are the 1/app of the roots of f
tp:=Value(f,x*app);
tp:=CoefficientsOfLaurentPolynomial(tp)[1];
tp:=tp/tp[1];
tp:=List(tp,i->ApproxRational(i,10));
# now check, by using the Lehmer/Schur method, whether tp has a root
# in the unit circle, i.e. f has a root in the app-circle
repeat
scheit:=false;
p:=tp;
repeat
d:=Length(p);
# compute T[p]=\bar a_n p-a_0 p*, everything rational.
p:=p[1]*p-p[d]*Reversed(p);
p:=List(p,i->ApproxRational(i,10));
d:=Length(p);
while d>1 and p[d]=0 do
Unbind(p[d]);
d:=d-1;
od;
v:=p[1];
if v=0 then
scheit:=nkon;
fi;
nkon:=ForAny(p{[2..Length(p)]},i->i<>0);
until v<=0;
if scheit then
# we fail due to rounding errors
return fail;
else
if v<0 then
# zero in the unit circle, app smaller
app:=app-diff;
else
# no zero in the unit circle, app larger
app:=app+diff;
fi;
fi;
until not scheit;
diff:=diff/2;
# until good circle found, which does not contain roots.
until v=0 and (1-app/(app+diff))<1/40;
# revert last enlargement and add accuracy to be secure
app:=app-2*diff;
return 1/app+1/20;
end);
#############################################################################
##
#F RootBound(<f>) . . . . bound for absolute value of (complex) roots of f
##
InstallGlobalFunction(RootBound,function(f)
local a,b,c,d;
# valuation gives only 0 as zero, this can be neglected
f:=CoefficientsOfLaurentPolynomial(f)[1];
# normieren
f:=f/Last(f);
f:=UnivariatePolynomialByCoefficients(CyclotomicsFamily,f,1);
a:=ApproxRootBound(f);
# did the numerical part fail?
if a=fail then
c:=CoefficientsOfLaurentPolynomial(f)[1];
c:=List(c,AbsInt);
d:=Length(c);
a:=Maximum(1,Sum(c{[1..d-1]}));
b:=1+Maximum(c);
if b<a then
a:=b;
fi;
b:=Maximum(List([1..d-1],i->RootInt(d*Int(AbsInt(c[d-i])+1/2),i)+1));
if b<a then
a:=b;
fi;
if ForAll(c,i->i<>0) then
b:=List([3..d],i->2*AbsInt(c[i-1]/c[i]));
Add(b,AbsInt(c[1]/c[2]));
b:=Maximum(b);
if b<a then
a:=b;
fi;
fi;
b:=Sum([1..d-1],i->AbsInt(c[i]-c[i+1]))+AbsInt(c[1]);
if b<a then
a:=b;
fi;
b:=1/20+Maximum(List([1..d-1],
i->RootInt(Int(AbsInt(c[d-i]/Binomial(d-1,i))+1/2),i)+1))
/(ApproximateRoot(2,d-1)-1)+10^(-10);
if b<a then
a:=b;
fi;
fi;
return a;
end);
#############################################################################
##
#F BombieriNorm(<pol>) . . . . . . . . . . . . compute weighted Norm [pol]_2
##
InstallGlobalFunction(BombieriNorm,function(f)
local c,i,n,s;
c:=CoefficientsOfLaurentPolynomial(f);
c:=ShiftedCoeffs(c[1],c[2]);
n:=Length(c)-1;
s:=0;
for i in [0..n] do
s:=s+AbsInt(c[i+1])^2/Binomial(n,i);
od;
return ApproximateRoot(s,2);
end);
#############################################################################
##
#F MinimizedBombieriNorm(<pol>) . . . . . . . minimize weighted Norm [pol]_2
## by shifting roots
##
InstallMethod(MinimizedBombieriNorm,true,
[IsPolynomial and IsRationalFunctionsFamilyElement],0,
function(f)
local bn,bnf,a,b,c,d,bb,bf,bd,step,x,cnt;
step:=1;
bb:=infinity;
bf:=f;
bd:=0;
bn:=[];
# evaluation of norm, including storing it (avoids expensive double evals)
bnf := function(dis)
local p,g;
p:=Filtered(bn,i->i[1]=dis);
if p=[] then
g:=Value(f,x+dis);
p:=[dis,BombieriNorm(g)];
Add(bn,p);
if bb>p[2] then
# note record
bf:=g;
bb:=p[2];
bd:=dis;
fi;
return p[2];
else
return p[1][2];
fi;
end;
x:=UnivariatePolynomialByCoefficients(CyclotomicsFamily,[0,1],
IndeterminateNumberOfLaurentPolynomial(f));
d:=0;
cnt:=0;
repeat
cnt:=cnt+1;
Info(InfoPoly,2,"Minimizing BombieriNorm, x->x+(",d,")");
# local parabola approximation
a:=bnf(d-step);
b:=bnf(d);
c:=bnf(d+step);
if a<b and c<b then
if a<c then d:=d-step;
else d:=d+step;
fi;
elif not(a>b and c>b) and (a+c<>2*b) then
a:=-(c-a)/2/(a+c-2*b)*step;
# stets aufrunden (wir wollen weg)
a:=step*Int(AbsInt(a)/step+1)*SignInt(a);
if a=0 then
Error("???");
else
d:=d+a;
fi;
fi;
until (a>b and c>b) # no better can be reached
or cnt>6
or ForAll([d-1,d,d+1],i->Filtered(bn,j->j[1]=i)<>[]); #or loop
# best value
return [bf,bd];
end);
#############################################################################
##
#F BeauzamyBound(<pol>) . . . . . Beauzamy's Bound for Factors Coefficients
## cf. JSC 13 (1992), 463-472
##
BindGlobal("BeauzamyBound",function(f)
local n;
n:=DegreeOfLaurentPolynomial(f);
return Int(
# the strange number in the next line is an (upper) rational approximation
# for 3^{3/4}/2/\sqrt(\pi)
643038/1000000*ApproximateRoot(3^n,2)/ApproximateRoot(n,2)*BombieriNorm(f))+1;
end);
#############################################################################
##
#F OneFactorBound(<pol>) . . . . . . . . . . . . Bound for one factor of pol
##
InstallGlobalFunction(OneFactorBound,function(f)
local d,n;
n:=DegreeOfLaurentPolynomial(f);
if n>=3 then
# Single factor bound of Beauzamy, Trevisan and Wang (1993)
return Int(10912/10000*(ApproximateRoot(2^n,2)/ApproximateRoot(n^3,8)
*(ApproximateRoot(BombieriNorm(f),2))))+1;
else
# Mignotte's single factor bound
d:=QuoInt(n,2);
return
Binomial(d,QuoInt(d,2))
*(1+RootInt(Sum(CoefficientsOfLaurentPolynomial(f)[1],
i->i^2),2));
fi;
end);
#############################################################################
##
#F PrimitivePolynomial(<f>) . . . remove denominator and coefficients gcd
##
InstallMethod(PrimitivePolynomial,"univariate polynomial",true,
[IsUnivariatePolynomial],0,
function(f)
local lcm, c, fc,fac;
fc:=CoefficientsOfLaurentPolynomial(f)[1];
# compute lcm of denominator
lcm := 1;
for c in fc do
lcm := LcmInt(lcm,DenominatorRat(c));
od;
# remove all denominators
f := f*lcm;
fac:=1/lcm;
fc:=CoefficientsOfLaurentPolynomial(f)[1];
# remove gcd of coefficients
if Length(fc)>0 then
fc:=Gcd(fc);
else
fc:=1;
fi;
fac:=fac*fc;
return [f*(1/fc),fac];
end);
BindGlobal("PrimitiveFacExtRepRatPol",function(e)
local d,lcm,i,fac;
d:=e{[2,4..Length(e)]};
if not ForAll(d,IsRat) then
TryNextMethod();
fi;
lcm:=1;
for i in d do
lcm := LcmInt(lcm,DenominatorRat(i));
od;
fac:=1/lcm;
d:=d*lcm;
if Length(d)>0 then
fac:=fac*Gcd(d);
fi;
return fac;
end);
InstallMethod(PrimitivePolynomial,"rational polynomial",true,
[IsPolynomial],0,
function(f)
local e,fac;
e:=ExtRepPolynomialRatFun(f);
fac:=PrimitiveFacExtRepRatPol(e);
return [f/fac,fac];
end);
#############################################################################
##
#F BeauzamyBoundGcd(<f>,<g>) . . . . . Beauzamy's Bound for Gcd Coefficients
##
## cf. JSC 13 (1992),463-472
##
BindGlobal("BeauzamyBoundGcd",function(f,g)
local n, A, B,lf,lg;
lf:=LeadingCoefficient(f);
if not IsOne(lf) then
f:=f/lf;
fi;
lg:=LeadingCoefficient(f);
if not IsOne(lg) then
g:=g/lg;
fi;
n := DegreeOfLaurentPolynomial(f);
# the strange number in the next line is an (upper) rational
# approximation for 3^{3/4}/2/\sqrt(\pi)
A := Int(643038/1000000
* ApproximateRoot(3^n,2)/ApproximateRoot(n,2)
* BombieriNorm(f))+1;
# the strange number in the next line is an (upper) rational
# approximation for 3^{3/4}/2/\sqrt(\pi)
n := DegreeOfLaurentPolynomial(g);
B := Int(643038/1000000
* ApproximateRoot(3^n,2)/ApproximateRoot(n,2)
* BombieriNorm(g))+1;
B:=Minimum(A,B);
if not (IsOne(lf) or IsOne(lg)) then
B:=B*GcdInt(lf,lg);
fi;
return B;
end);
#############################################################################
##
#F RPGcdModPrime(<f>,<g>,<p>,<a>,<brci>) . . gcd mod <p>
##
BindGlobal("RPGcdModPrime",function(f,g,p,a,brci)
local fam,gcd, u, v, w, val, r, s;
fam:=CoefficientsFamily(FamilyObj(f));
f:=CoefficientsOfLaurentPolynomial(f);
g:=CoefficientsOfLaurentPolynomial(g);
# compute in the finite field F_<p>
val := Minimum(f[2], g[2]);
s := ShiftedCoeffs(f[1],f[2]-val);
r := ShiftedCoeffs(g[1],g[2]-val);
ReduceCoeffsMod(s,p); ShrinkRowVector(s);
ReduceCoeffsMod(r,p); ShrinkRowVector(r);
# compute the gcd
u := r;
v := s;
while 0 < Length(v) do
w := v;
ReduceCoeffsMod(u,v,p);
ShrinkRowVector(u);
v := u;
u := w;
od;
#gcd := u * (a/Last(u));
gcd:=u;
MultVector(gcd,a/Last(u));
ReduceCoeffsMod(gcd,p);
# and return the polynomial
return LaurentPolynomialByCoefficients(fam,gcd,val,brci);
end);
BindGlobal("RPGcdCRT",function(f,p,g,q,ci)
local min, cf, lf, cg, lg, i, P, m, r, fam;
fam := CoefficientsFamily(FamilyObj(f));
f:=CoefficientsOfLaurentPolynomial(f);
g:=CoefficientsOfLaurentPolynomial(g);
# remove valuation
min := Minimum(f[2],g[2]);
if f[2] <> min then
cf := ShiftedCoeffs(f[1],f[2] - min);
else
cf := ShallowCopy(f[1]);
fi;
lf := Length(cf);
if g[2] <> min then
cg := ShiftedCoeffs(g[1],g[2] - min);
else
cg := ShallowCopy(g[1]);
fi;
lg := Length(cg);
# use chinese remainder
r := [ p,q ];
P := p * q;
m := P/2;
for i in [ 1 .. Minimum(lf,lg) ] do
cf[i] := ChineseRem(r,[ cf[i],cg[i] ]);
if m < cf[i] then cf[i] := cf[i] - P; fi;
od;
if lf < lg then
for i in [ lf+1 .. lg ] do
cf[i] := ChineseRem(r,[ 0,cg[i] ]);
if m < cf[i] then cf[i] := cf[i] - P; fi;
od;
elif lg < lf then
for i in [ lg+1 .. lf ] do
cf[i] := ChineseRem(r,[ cf[i],0 ]);
if m < cf[i] then cf[i] := cf[i] - P; fi;
od;
fi;
# return the polynomial
return LaurentPolynomialByCoefficients(fam,cf,min,ci);
end);
BindGlobal("RPGcd1",function(t,a,f,g)
local G, P, l, m, i;
# <P> will hold the product of primes use so far
t.modulo := t.prime;
# <G> will hold the approximation of the gcd
G := t.gcd;
# use next prime until we reach the Beauzamy bound
while t.modulo < t.bound do
repeat t.prime := NextPrimeInt(t.prime); until a mod t.prime <> 0;
# compute modular gcd
t.gcd := RPGcdModPrime(f,g,t.prime,a,t.brci);
Info(InfoPoly,3,"gcd mod ",t.prime," = ",t.gcd);
# if the degree of <C> is smaller we started with wrong <p>
if DegreeOfLaurentPolynomial(t.gcd)
< DegreeOfLaurentPolynomial(G)
then
Info(InfoPoly,3,"found lower degree,restarting");
return false;
fi;
# if the degrees of <C> and <G> are equal use chinese remainder
if DegreeOfLaurentPolynomial(t.gcd)
= DegreeOfLaurentPolynomial(G)
then
P := G;
G := RPGcdCRT(P,t.modulo,t.gcd,t.prime,t.brci);
t.modulo := t.modulo * t.prime;
Info(InfoPoly,3,"gcd mod ",t.modulo," = ",G);
if G = P then
t.correct := IsZero(f mod G) and IsZero(g mod G);
if t.correct then
Info(InfoPoly,3,"found correct gcd");
t.gcd := G;
return true;
fi;
fi;
fi;
od;
# get <G> into the -<t.modulo>/2 to +<t.modulo> range
G:=CoefficientsOfLaurentPolynomial(G);
l := [];
m := t.modulo/2;
for i in [ 1 .. Length(G[1]) ] do
if m < G[1][i] then
l[i] := G[1][i] - t.modulo;
else
l[i] := G[1][i];
fi;
od;
G := LaurentPolynomialByExtRepNC(FamilyObj(f),l,G[2],t.brci);
Info(InfoPoly,3,"gcd mod ",t.modulo," = ",G);
# check if <G> is correct but return 'true' in any case
t.correct := IsZero(f mod G) and IsZero(g mod G);
t.gcd := G;
return true;
end);
BindGlobal("RPIGcd", function(f,g)
local a,t;
# special case zero:
if IsZero(f) then
return g;
elif IsZero(g) then
return f;
fi;
# compute the Beauzamy bound for the gcd
t := rec(prime := 1000);
t.brci:=CIUnivPols(f,g);
t.bound := 2 * Int(BeauzamyBoundGcd(f,g)+1);
Info(InfoPoly,3,"Beauzamy bound = ",t.bound/2);
# avoid gcd of leading coefficients
a := GcdInt(LeadingCoefficient(f),LeadingCoefficient(g));
repeat
# start with first prime avoiding gcd of leading coefficients
repeat t.prime := NextPrimeInt(t.prime); until a mod t.prime <> 0;
# compute modular gcd with leading coefficient <a>
t.gcd := RPGcdModPrime(f,g,t.prime,a,t.brci);
Info(InfoPoly,3,"gcd mod ",t.prime," = ",t.gcd);
# loop until we have success
repeat
if 0 = DegreeOfLaurentPolynomial(t.gcd) then
Info(InfoPoly,3,"<f> and <g> are relative prime");
return One(f);
fi;
until RPGcd1(t,a,f,g);
until t.correct;
# return the gcd
return t.gcd;
end);
#############################################################################
##
#F GcdOp( <R>, <f>, <g> ) . . . . . . . for rational univariate polynomials
##
InstallRingAgnosticGcdMethod("rational univariate polynomials",
IsCollsElmsElms,IsIdenticalObj,
[IsRationalsPolynomialRing and IsEuclideanRing,
IsUnivariatePolynomial,IsUnivariatePolynomial],0,
function(f,g)
local brci,gcd,fam,fc,gc;
fam:=FamilyObj(f);
if not (IsIdenticalObj(CoefficientsFamily(fam),CyclotomicsFamily)
and ForAll(CoefficientsOfLaurentPolynomial(f)[1],IsRat)
and ForAll(CoefficientsOfLaurentPolynomial(g)[1],IsRat)) then
TryNextMethod(); # not applicable as not rational
fi;
brci:=CIUnivPols(f,g);
if brci=fail then TryNextMethod();fi;
# check trivial cases
if -infinity = DegreeOfLaurentPolynomial(f) then
return g;
elif -infinity = DegreeOfLaurentPolynomial(g) then
return f;
elif 0 = DegreeOfLaurentPolynomial(f)
or 0 = DegreeOfLaurentPolynomial(g)
then
return One(f);
fi;
# convert polynomials into integer polynomials
f := PrimitivePolynomial(f)[1];
g := PrimitivePolynomial(g)[1];
Info(InfoPoly,3,"<f> = ",f);
Info(InfoPoly,3,"<g> = ",g);
fc:=CoefficientsOfLaurentPolynomial(f);
gc:=CoefficientsOfLaurentPolynomial(g);
# try heuristic method:
gcd:=HeuGcdIntPolsCoeffs(fc[1],gc[1]);
if gcd=fail then
# fall back to the original version:
gcd:=RPIGcd(f,g);
return StandardAssociate(gcd);
fi;
fc:=Minimum(fc[2],gc[2]);
fc:=fc+RemoveOuterCoeffs(gcd,fam!.zeroCoefficient);
if Length(gcd)>0 and not IsOne(Last(gcd)) then
gcd:=gcd/Last(gcd);
fi;
return LaurentPolynomialByExtRepNC(fam,gcd,fc,brci);
end);
InstallMethod(\mod,"reduction of univariate rational polynomial at a prime",
true,[IsUnivariatePolynomial,IsInt],0,
function(f,p)
local c;
c:=CoefficientsOfLaurentPolynomial(f);
if Length(c[1])>0 and
ForAny(c[1],i->not (IsRat(i) or IsAlgebraicElement(i))) then
TryNextMethod();
fi;
return LaurentPolynomialByCoefficients(
CoefficientsFamily(FamilyObj(f)),List(c[1],i->i mod p),c[2],
IndeterminateNumberOfLaurentPolynomial(f));
end);
InstallMethod(\mod,"reduction of general rational polynomial at a prime",
true,[IsPolynomial,IsInt],0,
function(f,p)
local c,d,i,m;
c:=ExtRepPolynomialRatFun(f);
d:=[];
for i in [2,4..Length(c)] do
if not (IsRat(c[i]) or IsAlgebraicElement(c[i])) then
TryNextMethod();
fi;
m:=c[i] mod p;
if m<>0 then
Add(d,c[i-1]);
Add(d,m);
fi;
od;
return PolynomialByExtRepNC(FamilyObj(f),d);
end);
#############################################################################
##
#F RPQuotientModPrime(<f>,<g>,<p>) . . . quotient
##
BindGlobal("RPQuotientModPrime",function(f,g,p)
local m, n, i, k, c, q, val, fc,gc,brci,fam;
# get base ring
brci:=CIUnivPols(f,g);
fam:=FamilyObj(f);
# reduce <f> and <g> mod <p>
f := f mod p;
g := g mod p;
fc:=CoefficientsOfLaurentPolynomial(f);
gc:=CoefficientsOfLaurentPolynomial(g);
# if <f> is zero return it
if 0 = Length(fc[1]) then
return f;
fi;
# check the value of the valuation of <f> and <g>
if fc[2] < gc[2] then
return false;
fi;
val := fc[2]-gc[2];
# Try to divide <f> by <g>,compute mod <p>
q := [];
n := Length(gc[1]);
m := Length(fc[1]) - n;
gc:=gc[1];
f := ShallowCopy(fc[1]);
for i in [0..m] do
c := f[m-i+n]/gc[n] mod p;
for k in [1..n] do
f[m-i+k] := (f[m-i+k] - c*gc[k]) mod p;
od;
q[m-i+1] := c;
od;
# Did the division work?
for i in [ 1 .. m+n ] do
if f[i] <> fam!.zeroCoefficient then
return false;
fi;
od;
val:=val+RemoveOuterCoeffs(q,fam!.zeroCoefficient);
return LaurentPolynomialByExtRepNC(fam,q,val,brci);
end);
#############################################################################
##
#F RPGcdRepresentationModPrime(<f>,<g>,<p>) . gcd
##
BindGlobal("RPGcdRepresentationModPrime",function(f,g,p)
local val, # the minimal valuation of <f> and <g>
s, sx, # first line of gcd algorithm
t, tx, # second line of gcd algorithm
h, hx, # temp for swapping lines
q, # quotient
n,m,r,c, # used in quotient
brci,
i,k; # loops
Info(InfoPoly,3,"f=",f,"g=",g);
# get base ring
brci:=CIUnivPols(f,g);
brci:=[CoefficientsFamily(FamilyObj(f)),brci];
# remove common x^i term
f:=CoefficientsOfLaurentPolynomial(f);
g:=CoefficientsOfLaurentPolynomial(g);
val:=Minimum(f[2],g[2]);
f :=ShiftedCoeffs(f[1],f[2]-val);
g :=ShiftedCoeffs(g[1],g[2]-val);
ReduceCoeffsMod(f,p); ShrinkRowVector(f);
ReduceCoeffsMod(g,p); ShrinkRowVector(g);
# compute the gcd and representation mod <p>
s := ShallowCopy(f); sx := [ One(brci[1]) ];
t := ShallowCopy(g); tx := [];
while 0 < Length(t) do
Info(InfoPoly,3,"<s> = ",s,", <sx> = ",sx,"\n",
"#I <t> = ",t,", <tx> = ",tx);
# compute the euclidean quotient of <s> by <t>
q := [];
n := Length(t);
m := Length(s) - n;
r := ShallowCopy(s);
for i in [ 0 .. m ] do
c := r[m-i+n] / t[n] mod p;
for k in [ 1 .. n ] do
r[m-i+k] := (r[m-i+k] - c * t[k]) mod p;
od;
q[m-i+1] := c;
od;
Info(InfoPoly,3,"<q> = ",q);
# update representation
h := t;
hx := tx;
t := s;
AddCoeffs(t,ProductCoeffs(q,h),-1);
ReduceCoeffsMod(t,p);
ShrinkRowVector(t);
tx := sx;
AddCoeffs(tx,ProductCoeffs(q,hx),-1);
ReduceCoeffsMod(tx,p);
ShrinkRowVector(tx);
s := h;
sx := hx;
od;
Info(InfoPoly,3,"<s> = ",s,", <sx> = ",sx);
# compute conversion for standard associate
q := (1/Last(s)) mod p;
# convert <s> and <x> back into polynomials
if 0 = Length(g) then
#sx := q * sx;
MultVector(sx,q);
ReduceCoeffsMod(sx,p);
return [ LaurentPolynomialByCoefficients(brci[1],sx,0,brci[2]),
Zero(brci[1]) ];
else
#hx := q * sx;
hx:=ShallowCopy(sx);
MultVector(hx,q);
ReduceCoeffsMod(hx,p);
hx := LaurentPolynomialByCoefficients(brci[1],hx,0,brci[2]);
AddCoeffs(s,ProductCoeffs(sx,f),-1);
#s := q * s;
MultVector(s,q);
ReduceCoeffsMod(s,p);
s := LaurentPolynomialByCoefficients(brci[1],s,0,brci[2]);
g := LaurentPolynomialByCoefficients(brci[1],g,0,brci[2]);
q := RPQuotientModPrime(s,g,p);
return [ hx,q ];
fi;
end);
#############################################################################
##
#F HenselBound(<pol>,[<minpol>,<den>]) . . . Bounds for Factor coefficients
## if the computation takes place over an algebraic extension, then
## minpol and denominator must be given
##
InstallGlobalFunction(HenselBound,function(arg)
local pol,n,nalpha,d,dis,rb,bound,a,i,j,k,l,w,bin,lm,bea,polc,ro,rbpow;
pol:=arg[1];
if Length(arg)>1 then
n:=arg[2];
d:=arg[3];
dis:=Discriminant(n);
nalpha:=RootBound(n); # bound for norm of \alpha.
polc:=CoefficientsOfLaurentPolynomial(pol)[1];
if not ForAll(polc,IsRat) then
# now try to bound the roots of f accordingly. As in all estimates by
# RootBound only the absolute value of the coefficients is used, we will
# estimate these first, and replace f by the polynomial
# x^n-b_{n-1}x^(n-1)-...-b_0 whose roots are certainly larger
a:=[];
for i in polc do
# bound for coefficients of pol
if IsRat(i) then
Add(a,AbsInt(i));
else
Add(a,Sum(ExtRepOfObj(i),AbsInt)*nalpha);
fi;
od;
a:=-a;
a[Length(a)]:=-a[Length(a)];
pol:=UnivariatePolynomialByCoefficients(CyclotomicsFamily,a,1);
else
pol:=UnivariatePolynomialByCoefficients(CyclotomicsFamily,polc,1);
fi;
n:=DegreeOfLaurentPolynomial(n);
else
n:=1;
fi;
bound:=[];
rb:=0;
#BeauzamyBound
bea:=BeauzamyBound(pol);
# compute Landau-Mignotte bound for absolute values of
# coefficients of any factor
polc:=CoefficientsOfLaurentPolynomial(pol)[1];
w:=Sum(polc,i->i^2);
# we want an upper bound of the root, RootInt will give a lower
# bound. So we compute the root of w-1 (in case w is a perfect square)
# and add 1. As we nowhere selected a specific galois representative,
# this bound (which is rational!) will bound all conjugactes as well.
lm:=(RootInt(Int(w)-1,2)+1);
for k in [1..DegreeOfLaurentPolynomial(pol)] do
l:=2^k*lm;
if l<bea then
w:=l;
else
w:=bea;
fi;
#if lb>10^30 or n>1 then
if bea>10^200 or n>1 then
if rb=0 then
rb:=RootBound(pol);
if rb>1000 and not IsInt(rb) then
rb:=Int(rb+1);
fi;
rbpow:=[rb];
a:=rb;
fi;
# now try factor deg k
bin:=1;
for j in [1..k] do
bin:=bin*(k-j+1)/j;
if not IsBound(rbpow[j]) then
rbpow[j]:=rbpow[j-1]*rb;
if rbpow[j]>10 and not IsInt(rbpow[j]) then
rbpow[j]:=Int(rbpow[j]+1);
fi;
fi;
w:=bin*rbpow[j];
if w>a then
a:=w;
fi;
od;
# select the better bound
if a<l then
w:=a;
else
w:=l;
fi;
fi;
if n>1 then
# algebraic Extension case
# finally we have to bound (again) the coefficients of \alpha when
# writing the coefficients of the factor as \sum c_i/d\alpha^i.
ro:=AbsInt(dis);
ro:=RootInt(NumeratorRat(ro))/(1+RootInt(DenominatorRat(ro)-1));
w:=Int(d*w*Factorial(n)/ro*nalpha^(n*(n-1)/2))+1;
fi;
bound[k]:=Int(w)+1;
od;
return bound;
end);
#############################################################################
##
#F TrialQuotientRPF(<f>,<g>,<b>) . . . . . . f/g if coeffbounds are given by b
##
InstallGlobalFunction(TrialQuotientRPF,function(f,g,b)
local fc,gc,a,m, n, i, k, c, q, val, brci,fam;
brci:=CIUnivPols(f,g);
a:=DegreeOfLaurentPolynomial(f)-DegreeOfLaurentPolynomial(g);
fam:=FamilyObj(f);
fc:=CoefficientsOfLaurentPolynomial(f);
gc:=CoefficientsOfLaurentPolynomial(g);
if a=0 then
# Special case (that has to return 0)
a:=b[1];
else
a:=b[a];
fi;
if 0=Length(fc[1]) then
return f;
fi;
if fc[2]<gc[2] then
return fail;
fi;
val:=fc[2]-gc[2];
q:=[];
n:=Length(gc[1]);
m:=Length(fc[1])-n;
f:=ShallowCopy(ShiftedCoeffs(fc[1],fc[2]));
gc:=gc[1];
# if IsField(R) then
for i in [0..m] do
c:=f[(m-i+n)]/gc[n];
if MaxNumeratorCoeffAlgElm(c)>a then
Info(InfoPoly,3,"early break");
return fail;
fi;
for k in [1..n] do
f[m-i+k]:=f[m-i+k]-c*gc[k];
od;
q[m-i+1]:=c;
od;
# else
# for i in [0..m] do
# c:=Quotient(R,f[m-i+n],gc[n]);
# if c=fail then
# return fail;
# fi;
# if MaxNumeratorCoeffAlgElm(c)>a then
# Info(InfoPoly,3,"early break\n");
# return fail;
# fi;
# for k in [1..n] do
# f[m-i+k]:=f[m-i+k]-c*g.coefficients[k];
# if MaxNumeratorCoeffAlgElm(f[m-i+k])>b then
# Info(InfoPoly,3,"early break\n");
# return fail;
# fi;
# od;
# q[m-i+1]:=c;
# od;
# fi;
k:=Zero(f[1]);
for i in [1..m+n] do
if f[i]<>k then
return fail;
fi;
od;
val:=val+RemoveOuterCoeffs(q,fam!.zeroCoefficient);
return LaurentPolynomialByExtRepNC(fam,q,val,brci);
end);
#############################################################################
##
#F TryCombinations(<f>,...) . . . . . . . . . . . . . . . . try factors
##
InstallGlobalFunction(TryCombinations,function(f,lc,l,p,t,bounds,opt,split,useonefacbound)
local p2, res, j, i,ii,o,d,b,lco,degs, step, cnew, sel, deli,
degf, good, act, da, prd, cof, q, combi,mind,binoli,alldegs;
alldegs:=t.degrees;
# <res> contains the irr/reducible factors and the remaining ones
res:=rec(irreducibles:=[],
irrFactors :=[],
reducibles :=[],
redFactors :=[],
remaining :=[ 1 .. Length(l) ]);
# coefficients should be in -<p>/2 and <p>/2
p2 :=p/2;
deli:=List(l,DegreeOfLaurentPolynomial);
# sel are the still selected indices
sel:=[ 1 .. Length(l) ];
# create List of binomial coefficients to speed up the 'Combinations' process
binoli:=[];
for i in [0..Length(l)-1] do
binoli[i+1]:=List([0..i],j->Binomial(i,j));
od;
step:=0;
act :=1;
repeat
# factors of larger than half remaining degree we will find as
# final cofactor. This cannot be used if we factor only using the one
# factor bound!
if not useonefacbound then
degf:=DegreeOfLaurentPolynomial(f);
degs:=Filtered(alldegs,i -> 2*i<=degf);
else
degs:=alldegs;
fi;
if IsBound(opt.onlydegs) then
degs:=Intersection(degs,opt.onlydegs);
Info(InfoPoly,3,"degs=",degs);
fi;
if act in sel then
# search all combinations of Length step+1 containing the act-th
# factor,that are allowed
good:=true;
da:=List(degs,i -> i-deli[act]);
# check,whether any combination will be of suitable degree
cnew:=Set(deli{Filtered(sel,i->i>act)});
if ForAny(da,i->NrRestrictedPartitions(i,cnew,step)>0) then
# as we have all combinations including < <act>,we can skip them
Info(InfoPoly,2,"trying length ",step+1," containing ",act);
cnew:=Filtered(sel,i -> i > act);
else
Info(InfoPoly,2,"length ",step+1," containing ",act," not feasible");
cnew:=[];
fi;
mind:=Sum(deli); # the maximum of the possible degrees. We surely
# will find something smaller
lco:=Binomial(Length(cnew),step);
if 0 = lco then
# fix mind to make sure,we don't erroneously eliminate the factor
mind:=0;
else
Info(InfoPoly,2,lco," combinations");
i:=1;
while good and i<=lco do
# try combination number i
# combi:=CombinationNr(cnew,step,i);
q:=i;
d:=Length(cnew); # the remaining Length
o:=0;
combi:=[];
for ii in [step-1,step-2..0] do
j:=1;
b:=binoli[d][ii+1];
while q>b do
q:=q-b;
# compute b:=Binomial(d-(j+1),ii);
b:=b*(d-j-ii)/(d-j);
j:=j+1;
od;
o:=j+o;
d:=d-j;
Add(combi,cnew[o]);
od;
# check whether this yields a minimal degree
d:=Sum(deli{combi});
if d<mind then
mind:=d;
fi;
if d in da then
AddSet(combi,act); # add the 'always' factor
# make sure that the quotient has a chance,compute the
# extremal coefficient of the product:
q:=(ProductMod(List(l{combi},
i->CoefficientsOfLaurentPolynomial(i)[1][1]),
p) * lc) mod p;
if p2 < q then
q:=q - p;
fi;
# As we don't know yet the gcd of all the products
# coefficients (to make it primitive),we do a slightly
# weaker test: (test of leading coeffs is first in
# 'TrialQuotientRPF') this just should reduce the number of
# 'ProductMod' necessary. the absolute part of the
# product must divide the absolute part of f up to a
# divisor of <lc>
q:=CoefficientsOfLaurentPolynomial(f)[1][1] / q * lc;
if not IsInt(q) then
Info(InfoPoly,3,"ignoring combination ",combi);
q:=fail;
else
Info(InfoPoly,2,"testing combination ",combi);
# compute the product and reduce
prd:=ProductMod(l{combi},p);
prd:=CoefficientsOfUnivariatePolynomial(prd);
cof:=[];
for j in [ 1 .. Length(prd) ] do
cof[j]:=(lc*prd[j]) mod p;
if p2 < cof[j] then
cof[j]:=cof[j] - p;
fi;
od;
# make the product primitive
cof:=cof * (1/Gcd(cof));
prd:=UnivariatePolynomialByCoefficients(CyclotomicsFamily,
cof,t.ind);
q:=TrialQuotientRPF(f,prd,bounds);
fi;
if q <> fail then
f:=q;
Info(InfoPoly,2,"found true factor of degree ",
DegreeOfLaurentPolynomial(prd));
if Length(combi)=1 or split then
q:=0;
else
q:=2*lc*OneFactorBound(prd);
if q <= p then
Info(InfoPoly,2,"proven irreducible by 'OneFactorBound'");
fi;
fi;
# for some reason,we know,the factor is irred.
if q <= p then
Append(res.irreducibles,combi);
Add(res.irrFactors,prd);
if IsBound(opt.stopdegs)
and DegreeOfLaurentPolynomial(prd) in opt.stopdegs then
Info(InfoPoly,2,"hit stopdegree");
Add(res.redFactors,f);
res.stop:=true;
return res;
fi;
else
Add(res.reducibles,combi);
Add(res.redFactors,prd);
fi;
SubtractSet(res.remaining,combi);
good:=false;
SubtractSet(sel,combi);
fi;
fi;
i:=i+1;
od;
fi;
# we can forget about the actual factor,as any longer combination
# is too big
if Length(degs)>1 and deli[act]+mind >= Maximum(degs) then
Info(InfoPoly,2,"factor ",act," can be further neglected");
sel:=Difference(sel,[act]);
fi;
fi;
# consider next factor
act:=act + 1;
if 0 < Length(sel) and act>Maximum(sel) then
step:=step+1;
act :=sel[1];
fi;
# until nothing is left
until 0 = Length(sel) or Length(sel)<step;
# if <split> is true we *must* find a complete factorization.
if split and 0 < Length(res.remaining) and not IsOne(f) then
#and not(IsBound(opt.onlydegs) or IsBound(opt.stopdegs)) then
# the remaining f must be an irreducible factor,larger than deg/2
Append(res.irreducibles,res.remaining);
res.remaining:=[];
Add(res.irrFactors,f);
fi;
# return the result
return res;
end);
#############################################################################
##
#F RPSquareHensel(<f>,<t>,<opt>)
##
BindGlobal("RPSquareHensel",function(f,t,opt)
local p, # prime
q, # current modulus
q1, # last modulus
l, # factorization mod <q>
lc, # leading coefficient of <f>
bounds, # Bounds for Factor Coefficients
ofb, # OneFactorBound
k, # Lift boundary
prd, # product of <l>
rep, # lifted representation of gcd(<lp>)
fcn, # index of true factor in <l>
dis, # distance of <f> and <l>
cor, # correction
max, # maximum absolute coefficient of <f>
res, # result
gcd, # used in gcd representation
i, j; # loop
# get <l> and <p>
l:=t.factors;
p:=t.prime;
# get the leading coefficient of <f>
lc:=LeadingCoefficient(f);
# and maximal coefficient
max:=Maximum(List(CoefficientsOfUnivariatePolynomial(f),AbsInt));
# compute the factor coefficient bounds
ofb:=2*AbsInt(lc)*OneFactorBound(f);
Info(InfoPoly,2,"One factor bound = ",ofb);
bounds:=2*AbsInt(lc)*HenselBound(f);
k:=bounds[Maximum(Filtered(t.degrees,
i-> 2*i<=DegreeOfLaurentPolynomial(f)))];
Info(InfoPoly,2,"Hensel bound = ",k);
# compute a representation of the 1 mod <p>
Info(InfoPoly,2,"computing gcd representation: ",Runtime());
prd:=(1/lc * f) mod p;
gcd:=RPQuotientModPrime(prd,l[1],p);
rep:=[ One(f) ];
for i in [ 2 .. Length(l) ] do
dis:=RPQuotientModPrime(prd,l[i],p);
cor:=RPGcdRepresentationModPrime(gcd,dis,p);
gcd:=(cor[1] * gcd + cor[2] * dis) mod p;
rep:=List(rep,z -> z * cor[1] mod p);
Add(rep,cor[2]);
od;
Info(InfoPoly,2,"representation computed: ",Runtime());
# <res> will hold our result
res:=rec(irrFactors:=[], redFactors:=[], remaining:=[],
bounds:=bounds);
# start Hensel until <q> is greater than k
q :=p^2;
q1 :=p;
while q1 < k do
Info(InfoPoly,2,"computing mod ",q);
for i in [ 1 .. Length(l) ] do
dis:=List(CoefficientsOfUnivariatePolynomial(f),i->i/lc mod q);
ReduceCoeffsMod(dis,CoefficientsOfUnivariatePolynomial(l[i]),q);
#dis:=EuclideanRemainder(PolynomialRing(Rationals),dis,l[i]) mod q;
#dis:=CoefficientsOfUnivariatePolynomial(dis);
dis:=UnivariatePolynomialByCoefficients(CyclotomicsFamily,dis,t.ind);
l[i]:=l[i] + BPolyProd(rep[i],dis,l[i],q);
od;
# NOCH: Assert und leading coeff
#if not ForAll(CoefficientsOfLaurentPolynomial(Product(l)-f)[1],
# i->i mod q=0) then
# Error("not product modulo q");
#fi;
# if this is not the last step update <rep> and check for factors
if q < k then
# correct the inverses
for i in [ 1 .. Length(l) ] do
if Length(l)=1 then
dis:=l[1]^0;
else
dis:=UnivariatePolynomialByCoefficients(CyclotomicsFamily,
[1],t.ind);
for j in Difference([1..Length(l)],[i]) do
dis:=BPolyProd(dis,l[j],l[i],q);
od;
fi;
rep[i]:=BPolyProd(rep[i],
(2-APolyProd(rep[i],dis,q)),
l[i],
q);
od;
# try to find true factors
if max <= q then
Info(InfoPoly,2,"searching for factors: ",Runtime());
fcn:=TryCombinations(f,lc,l,q,t,bounds,opt,false,false);
elif ofb < q then
Info(InfoPoly,2,"searching for factors: ",Runtime());
fcn:=TryCombinations(f,lc,l,q,t,bounds,opt,false,true);
#Info(InfoPoly,2,"finishing search: ",Runtime());
else
fcn:=rec(irreducibles:=[], reducibles:=[]);
fi;
# if we have found a true factor update everything
if 0 < Length(fcn.irreducibles)+Length(fcn.reducibles) then
# append irreducible factors to <res>.irrFactors
Append(res.irrFactors,fcn.irrFactors);
# append reducible factors to <res>.redFactors
Append(res.redFactors,fcn.redFactors);
# compute new <f>
prd:=Product(fcn.redFactors) * Product(fcn.irrFactors);
f :=f/prd;
if IsBound(fcn.stop) then
res.stop:=true;
return res;
fi;
lc :=LeadingCoefficient(f);
ofb:=2*AbsInt(lc)*OneFactorBound(f);
Info(InfoPoly,2,"new one factor bound = ",ofb);
# degree arguments or OFB arguments prove f irreducible
if (ForAll(t.degrees,i->i=0 or 2*i>=DegreeOfLaurentPolynomial(f))
or ofb<q) and DegreeOfLaurentPolynomial(f)>0 then
Add(fcn.irrFactors,f);
Add(res.irrFactors,f);
f:=f^0;
fi;
# if <f> is trivial return
if DegreeOfLaurentPolynomial(f) < 1 then
Info(InfoPoly,2,"found non-trivial factorization");
return res;
fi;
# compute the factor coefficient bounds
k:=HenselBound(f);
bounds:=List([ 1 .. Length(k) ],
i -> Minimum(bounds[i],k[i]));
k:=2 * AbsInt(lc)
* bounds[Maximum(Filtered(t.degrees,
i-> 2*i<=DegreeOfLaurentPolynomial(f)))];
Info(InfoPoly,2,"new Hensel bound = ",k);
# remove true factors from <l> and corresponding <rep>
prd:=(1/LeadingCoefficient(prd)) * prd mod q;
l :=l{fcn.remaining};
rep:=List(rep{fcn.remaining},x -> prd * x);
# reduce <rep>[i] mod <l>[i]
for i in [ 1 .. Length(l) ] do
#rep[i]:=rep[i] mod l[i] mod q;
rep[i]:=CoefficientsOfLaurentPolynomial(rep[i]);
rep[i]:=ShiftedCoeffs(rep[i][1],rep[i][2]);
j:=CoefficientsOfLaurentPolynomial(l[i]);
j:=ReduceCoeffsMod(rep[i],ShiftedCoeffs(j[1],j[2]),q);
# shrink the list rep[i], according to the 'j' value
rep[i]:=rep[i]{[1..j]};
rep[i]:=LaurentPolynomialByCoefficients(
CyclotomicsFamily,rep[i],0,t.ind);
od;
# if there was a factor,we ought to have found it
elif ofb < q then
Add(res.irrFactors,f);
Info(InfoPoly,2,"f irreducible,since one factor would ",
"have been found now");
return res;
fi;
fi;
# square modulus
q1:=q;
q :=q^2;
# avoid a modulus too big
if q > k then
q:=p^(LogInt(k,p)+1);
fi;
od;
# return the remaining polynomials
res.remPolynomial:=f;
res.remaining :=l;
res.primePower :=q1;
res.lc :=lc;
return res;
end);
#############################################################################
##
#F RPFactorsModPrime(<f>) find suitable prime and factor
##
## <f> must be squarefree. We test 3 "small" and 2 "big" primes.
##
BindGlobal("RPFactorsModPrime",function(f)
local i, # loops
fc, # f's coeffs
lc, # leading coefficient of <f>
p, # current prime
PR, # polynomial ring over F_<p>
fp, # <f> in <R>
lp, # factors of <fp>
min, # minimal number of factors so far
P, # best prime so far
LP, # factorization of <f> mod <P>
deg, # possible degrees of factors
t, # return record
cof, # new coefficients
tmp;
fc:=CoefficientsOfLaurentPolynomial(f)[1];
# set minimal number of factors to the degree of <f>
min:=DegreeOfLaurentPolynomial(f)+1;
lc :=LeadingCoefficient(f);
# find a suitable prime
t:=rec(ind:=IndeterminateNumberOfLaurentPolynomial(f));
p:=1;
for i in [ 1 .. 5 ] do
# reset <p> to big prime after first 3 test
if i = 4 then p:=Maximum(p,1000); fi;
# find a prime not dividing <lc> and <f>_<p> squarefree
repeat
repeat
p :=NextPrimeInt(p);
until lc mod p <> 0 and fc[1] mod p <> 0;
PR:=PolynomialRing(GF(p));
tmp:=1/lc mod p;
fp :=UnivariatePolynomialByCoefficients(FamilyObj(Z(p)),
List(fc,x->((tmp*x) mod p)* Z(p)^0),1);
until 0 = DegreeOfLaurentPolynomial(Gcd(PR,fp,Derivative(fp)));
# factorize <f> modulo <p>
Info(InfoPoly,2,"starting factorization mod p: ",Runtime());
lp:=Factors(PR,fp);
Info(InfoPoly,2,"finishing factorization mod p: ",Runtime());
# if <fp> is irreducible so is <f>
if 1 = Length(lp) then
Info(InfoPoly,2,"<f> mod ",p," is irreducible");
t.isIrreducible:=true;
return t;
else
Info(InfoPoly,2,"found ",Length(lp)," factors mod ",p);
Info(InfoPoly,2,"of degree ",List(lp,DegreeOfLaurentPolynomial));
fi;
# choose a maximal prime with minimal number of factors
if Length(lp) <= min then
min:=Length(lp);
P :=p;
LP :=lp;
fi;
# compute the possible degrees
tmp:=Set(Combinations(List(lp,DegreeOfLaurentPolynomial)),Sum);
if 1 = i then
deg:=tmp;
else
deg:=Intersection(deg,tmp);
fi;
# if there is only one possible degree != 0 then <f> is irreducible
if 2 = Length(deg) then
Info(InfoPoly,2,"<f> must be irreducible,only one degree left");
t.isIrreducible:=true;
return t;
fi;
od;
# convert factors <LP> back to the integers
lp:=ShallowCopy(LP);
for i in [ 1 .. Length(LP) ] do
cof:=CoefficientsOfUnivariatePolynomial(LP[i]);
#cof:=IntVecFFE(cof);
cof:=List(cof,IntFFE);
LP[i]:=UnivariatePolynomialByCoefficients(CyclotomicsFamily,cof,t.ind);
od;
# return the chosen prime
Info(InfoPoly,2,"choosing prime ",P," with ",Length(LP)," factors");
Info(InfoPoly,2,"possible degrees: ",deg);
t.isIrreducible:=false;
t.prime :=P;
t.factors :=LP;
t.degrees :=deg;
return t;
end);
#############################################################################
##
#M FactorsSquarefree(<R>,<f>,<opt>) . factors of <f>
##
## <f> must be square free and must have a constant term.
##
InstallMethod( FactorsSquarefree, "univariate rational poly", true,
[IsRationalsPolynomialRing,IsUnivariatePolynomial,IsRecord],0,
function(R,f,opt)
local t, h, fac, g, tmp;
# find a suitable prime,if <f> is irreducible return
t:=RPFactorsModPrime(f);
if t.isIrreducible then return [f]; fi;
Info(InfoPoly,2,"using prime ",t.prime," for factorization");
# for easy combining,we want large degree factors first
Sort(t.factors,
function(a,b)
return DegreeOfLaurentPolynomial(a) > DegreeOfLaurentPolynomial(b);
end);
# start Hensel
h:=RPSquareHensel(f,t,opt);
# combine remaining factors
fac:=[];
# first the factors found by hensel
if 0 < Length(h.remaining) then
Info(InfoPoly,2,"found ",Length(h.remaining)," remaining terms");
tmp:=TryCombinations(
h.remPolynomial,
h.lc,
h.remaining,
h.primePower,
t,
h.bounds,
opt,
true,false);
Append(fac,tmp.irrFactors);
Append(fac,tmp.redFactors);
else
tmp:=rec();
fi;
# append the irreducible ones
if 0 < Length(h.irrFactors) then
Info(InfoPoly,2,"found ",Length(h.irrFactors)," irreducibles");
Append(fac,h.irrFactors);
fi;
# and try to factorize the (possible) reducible ones
if 0 < Length(h.redFactors) then
Info(InfoPoly,2,"found ",Length(h.redFactors)," reducibles");
if not (IsBound(tmp.stop) or IsBound(h.stop)) then
# the stopping criterion has not yet been reached
for g in h.redFactors do
Append(fac,FactorsSquarefree(R,g,opt));
od;
else
Append(fac,h.redFactors);
fi;
fi;
# and return
return fac;
end);
BindGlobal("RPIFactors",function(R,f,opt)
local fc,ind, v, g, q, s, r, x,shift;
fc:=CoefficientsOfLaurentPolynomial(f);
ind:=IndeterminateNumberOfLaurentPolynomial(f);
# if <f> is trivial return
Info(InfoPoly,2,"starting integer factorization: ",Runtime());
if 0 = Length(fc[1]) then
Info(InfoPoly,2,"<f> is trivial");
return [ f ];
fi;
# remove a valuation
v:=fc[2];
f:=UnivariatePolynomialByCoefficients(CyclotomicsFamily,fc[1],ind);
x:=LaurentPolynomialByCoefficients(CyclotomicsFamily,[1],1,ind);
# if <f> is almost constant return
if Length(fc[1])=1 then
s:=List([1..fc[2]],i->x);
s[1]:=s[1]*LeadingCoefficient(f);
return s;
fi;
# if <f> is almost linear return
if 1 = DegreeOfLaurentPolynomial(f) then
Info(InfoPoly,2,"<f> is almost linear");
s:=List([1..v],f -> x);
Add(s,f);
return s;
fi;
# shift the zeros of f if appropriate
if DegreeOfLaurentPolynomial(f) > 20 then
g:=MinimizedBombieriNorm(f);
f:=g[1];
shift:=-g[2];
else
shift:=0;
fi;
# make <f> integral,primitive and square free
g:=Gcd(R,f,Derivative(f));
q:=PrimitivePolynomial(f/g)[1];
q:=q * SignInt(LeadingCoefficient(q));
Info(InfoPoly,3,"factorizing polynomial of degree ",
DegreeOfLaurentPolynomial(q));
# and factorize <q>
if DegreeOfLaurentPolynomial(q) < 2 then
Info(InfoPoly,2,"<f> is a linear power");
s:=[ q ];
else
# treat zeroes (only one possible)
s:=CoefficientsOfLaurentPolynomial(q)[2];
if s>0 then
s:=[x];
q:=q/x;
else
s:=[];
fi;
s:=Concatenation(s,FactorsSquarefree(R,q,opt));
fi;
# find factors of <g>
for r in s do
if 0 < DegreeOfLaurentPolynomial(g)
and DegreeOfLaurentPolynomial(g) >= DegreeOfLaurentPolynomial(r) then
q:=Quotient(R,g,r);
while 0 < DegreeOfLaurentPolynomial(g) and q <> fail do
Add(s,r);
g:=q;
if DegreeOfLaurentPolynomial(g)>=DegreeOfLaurentPolynomial(r) then
q:=Quotient(R,g,r);
else
q:=fail;
fi;
od;
fi;
od;
# reshift
if shift<>0 then
Info(InfoPoly,2,"shifting zeros back");
Apply(s,i->Value(i,x+shift));
fi;
# sort the factors
Append(s,List([1..v],f->x));
Sort(s);
if not (IsBound(opt.stopdegs) or IsBound(opt.onlydegs)) and Sum(s,DegreeOfLaurentPolynomial)<>DegreeOfLaurentPolynomial(f)+v then
Error("degree discrepancy!");
fi;
# return the (primitive) factors
return s;
end);
#############################################################################
##
#M Factors(<R>,<f> ) . . factors of rational polynomial
##
InstallMethod(Factors,"univariate rational polynomial",IsCollsElms,
[IsRationalsPolynomialRing,IsUnivariatePolynomial],0,
function(R,f)
local r,cr,opt,irf,i;
opt:=ValueOption("factoroptions");
PushOptions(rec(factoroptions:=rec())); # options do not hold for
# subsequent factorizations
if opt=fail then
opt:=rec();
fi;
cr:=CoefficientsRing(R);
irf:=IrrFacsPol(f);
i:=PositionProperty(irf,i->i[1]=cr);
if i<>fail then
# if we know the factors,return
PopOptions();
return ShallowCopy(irf[i][2]);
fi;
# handle trivial case
if DegreeOfLaurentPolynomial(f) < 2 then
StoreFactorsPol(cr,f,[f]);
PopOptions();
return [f];
fi;
# compute the integer factors
r:=RPIFactors(R,PrimitivePolynomial(f)[1],opt);
# convert into standard associates and sort
r:=List(r,x -> StandardAssociate(R,x));
Sort(r);
if Length(r)>0 then
# correct leading term
r[1]:=r[1]*Quotient(R,f,Product(r));
fi;
# and return
if not IsBound(opt.onlydegs) and not IsBound(opt.stopdegs) then
StoreFactorsPol(cr,f,r);
for i in r do
StoreFactorsPol(cr,i,[i]);
od;
fi;
PopOptions();
return r;
end);
#############################################################################
##
#F SymAdic( <x>, <b> ) . . . . . . . . . . symmetric <b>-adic expansion of <x>
#F (<b> and <x> integers)
##
BindGlobal( "SymAdic", function(x,b)
local l, b2, r;
b2:=QuoInt(b,2);
l:=[];
while x<>0 do
r:=x mod b;
if r>b2 then
r:=r-b;
fi;
Add(l,r);
x:=(x-r)/b;
od;
return l;
end );
#############################################################################
##
#F HeuGcdIntPolsExtRep(<fam>,<a>,<b>)
##
## takes two polynomials ext reps, primitivizes them with integer
## coefficients and tries to
## compute a Gcd expanding Gcds of specializations.
## Source: Geddes,Czapor,Labahn: Algorithm 7.4
#V MAXTRYGCDHEU: defines maximal number of attempts to find Gcd heuristically
MAXTRYGCDHEU:=6;
InstallGlobalFunction(HeuGcdIntPolsExtRep,function(fam,d,e)
local x,xd,er,i,j,k,m,p,sel,xi,gamma,G,g,loop,zero,add;
# find the indeterminates and their degrees:
x:=[];
xd:=[[],[]];
er:=[d,e];
for k in [1,2] do
for i in [1,3..Length(er[k])-1] do
m:=er[k][i];
for j in [1,3..Length(m)-1] do
p:=Position(x,m[j]);
if p=fail then
Add(x,m[j]);
p:=Length(x);
xd[1][p]:=0;
xd[2][p]:=0;
fi;
xd[k][p]:=Maximum(xd[k][p],m[j+1]);
od;
od;
od;
# discard the indeterminates which occur in only one of the polynomials
sel:=[];
for i in [1..Length(x)] do
if xd[1][i]>0 and xd[2][i]>0 then
Add(sel,i);
fi;
od;
x:=x{sel};
xd:=List(xd,i->i{sel});
# are the variables disjoint or do we have no variables at all?
if Length(x)=0 then
# return the gcd of all coefficients involved
G:=Gcd(Concatenation(d{[2,4..Length(d)]},e{[2,4..Length(e)]}));
return G;
fi;
# pick the first indeterminate
x:=x[1];
xd:=[xd[1][1],xd[2][1]];
xi:=2*Minimum(Maximum(List(d{[2,4..Length(d)]},AbsInt)),
Maximum(List(e{[2,4..Length(e)]},AbsInt)))+2;
for loop in [1..MAXTRYGCDHEU] do
if LogInt(AbsInt(Int(xi)),10)*Maximum(xd)>5000 then
return fail;
fi;
# specialize both polynomials at x=xi
# and compute their heuristic Gcd
gamma:=HeuGcdIntPolsExtRep(fam,
SpecializedExtRepPol(fam,d,x,xi),
SpecializedExtRepPol(fam,e,x,xi) );
if gamma<>fail then
# generate G from xi-adic expansion
G:=[];
i:=0;
if IsInt(gamma) then
zero:=Zero(gamma);
else
zero:=[];
fi;
while gamma<>zero do
if IsInt(gamma) then
# gamma is an integer value
g:=gamma mod xi;
if g>xi/2 then
g:=g-xi; # symmetric rep.
fi;
gamma:=(gamma-g)/xi;
if i=0 then
add:=[[],g];
else
add:=[[x,i],g];
fi;
else
# gamma is an ext rep
g:=[];
for j in [2,4..Length(gamma)] do
k:=gamma[j] mod xi;
if k>xi/2 then
k:=k - xi; #symmetric rep
fi;
if k<>0*k then
Add(g,gamma[j-1]);
Add(g,k);
fi;
od;
#gamma:=(gamma-g)/xi in ext rep;
add:=ShallowCopy(g);
add{[2,4..Length(add)]}:=-add{[2,4..Length(add)]}; #-g
gamma:=ZippedSum(gamma,add,0,fam!.zippedSum); # gamma-g
gamma{[2,4..Length(gamma)]}:=gamma{[2,4..Length(gamma)]}/xi; # /xi
#add:=g*xp^i; in extrep
add:=ZippedProduct(g,[[x,i],1],0,fam!.zippedProduct);
fi;
# G:=G+add in extrep
G:=ZippedSum(G,add,0,fam!.zippedSum);
i:=i+1;
od;
if Length(G)>0 and Length(G[1])>0 and
QuotientPolynomialsExtRep(fam,d,G)<>fail and
QuotientPolynomialsExtRep(fam,e,G)<>fail then
return G;
fi;
fi;
xi:=QuoInt(xi*73794,27011); #square of golden ratio
od;
return fail;
end);
# and the same in univariate
InstallGlobalFunction(HeuGcdIntPolsCoeffs,function(f,g)
local xi, t, h, i, lf, lg, lh,fr,gr;
if IsEmpty(f) or IsEmpty(g) then
return fail;
fi;
# first test value for heuristic gcd:
xi:=2+2*Minimum(Maximum(List(f,AbsInt)),Maximum(List(g,AbsInt)));
i:=0;
lf:=Last(f);
lg:=Last(g);
# and now the tests:
while i< MAXTRYGCDHEU do
# xi-adic expansion of Gcd(f(xi),g(xi)) (regarded as coefficient list)
h:=Gcd( ValuePol(f,xi),
ValuePol(g,xi) );
h:=SymAdic(h,xi);
# make it primitive:
t:=Gcd(h);
if t<>1 then
h:=h/t;
fi;
lh:=Last(h);
# check if it divides f and g: if yes, ready! if no, try larger xi
## this should be done more efficiently !
if RemInt(lg,lh)=0 and RemInt(lf,lh)=0 then
fr:=ShallowCopy(f);
ReduceCoeffs(fr,h);
gr:=ShallowCopy(g);
ReduceCoeffs(gr,h);
t:=Set(Concatenation(fr,gr));
else
t:=false;
fi;
if t=[0] then
Info(InfoPoly,4,"GcdHeuPrimitiveList: tried ",i+1," values");
return h;
else
i:=i+1;
xi:=QuoInt(xi*73794,27011);
fi;
od;
Info(InfoPoly,2,"GcdHeuPrimitiveList: failed after trying ",
MAXTRYGCDHEU," values");
return fail;
end);
InstallMethod(HeuristicCancelPolynomialsExtRep,"rationals",true,
[IsRationalFunctionsFamily,IsList,IsList],0,
function(fam,a,b)
local nf,df,g;
if not (HasCoefficientsFamily(fam)
and IsIdenticalObj(CoefficientsFamily(fam),CyclotomicsFamily)
and ForAll(a{[2,4..Length(a)]},IsRat)
and ForAll(b{[2,4..Length(b)]},IsRat)) then
# the coefficients are not all rationals
TryNextMethod();
fi;
# make numerator and denominator primitive with integer coefficients
nf:=PrimitiveFacExtRepRatPol(a);
if not IsOne(nf) then
a:=ShallowCopy(a);
a{[2,4..Length(a)]}:=a{[2,4..Length(a)]}/nf;
fi;
df:=PrimitiveFacExtRepRatPol(b);
if not IsOne(df) then
b:=ShallowCopy(b);
b{[2,4..Length(b)]}:=b{[2,4..Length(b)]}/df;
fi;
# the remaining common factor
nf:=nf/df;
g:=HeuGcdIntPolsExtRep(fam,a,b);
if IsList(g) and (Length(g)>2 or (Length(g)=2 and Length(g[1])>0)) then
# make g primitive
df:=PrimitiveFacExtRepRatPol(g);
if not IsOne(df) then
g:=ShallowCopy(g);
g{[2,4..Length(g)]}:=g{[2,4..Length(g)]}/df;
fi;
Info(InfoPoly,3,"Heuristic integer gcd returns ",g);
a:=QuotientPolynomialsExtRep(fam,a,g);
b:=QuotientPolynomialsExtRep(fam,b,g);
if a<>fail and b<>fail then
a{[2,4..Length(a)]}:=a{[2,4..Length(a)]}*nf;
g:=[a,b];
return g;
fi;
fi;
return fail;
end);
InstallGlobalFunction(PolynomialModP,function(pol,p)
local f, cof;
f:=GF(p);
cof:=CoefficientsOfUnivariatePolynomial(pol);
return UnivariatePolynomial(f,cof*One(f),
IndeterminateNumberOfUnivariateRationalFunction(pol));
end);
[ Dauer der Verarbeitung: 0.54 Sekunden
(vorverarbeitet)
]
|