polys.sa
Generated by gen_html_sa_files from ICSI. Contact gomes@icsi.berkeley.edu for details
--
-- This module is distributed freely in the sence of
-- GPL(GNU General Public License).
--
-- Class of 1 variable polynomial.
-- Coefficients admit Integer,Rationall,Complex and Float.
--
-- Class of single variable polynomial of ring "R" coefficient.
-- K.Kodama 2000-07-01. Port from ruby polynomial class.
-- refinement checkDivZe,checkZp and setPoly
-- by toyofuku@jiuce.or.jp 2000-03-10
-- Ruby. [] []= by Masaki Suketa 2000-01-22
--
-- Thaks to Hideto ISHIBASHI and Masaki Suketa for their suggestion.
--
-- K.Kodama(kodama@kobe-kosen.ac.jp) 2000-01-09
-- first version(Ruby)
--
class POLYS_INTI < $IS_LT{POLYS_INTI},$STR
class POLYS_INTI < $IS_LT{POLYS_INTI},$STR is
include COMPARABLE;
include POLYS{INTI} is_SqrFree->POLYS_is_SqrFree,
squareFreeDecomposition->POLYS_squareFreeDecomposition;
include PID_GCD;
init is
r_0:=0.inti; r_1:=1.inti; divmodForce0:=false;
POLYS_RAT::init;
end;
gcd_coeff:INTI is
-- gcd of coefficients
g:INTI:=0.inti; -- gcd
loop c::=arr.elt!;
if c.is_zero.not then g:=g.gcd(c);
if g=1.inti then return g; end;
end;
end;
return g;
end;
lcm_coeff:INTI is
-- lcm of of coefficients
l:INTI:=1.inti; -- lcm
loop c::=arr.elt!; if c.is_zero.not then l:=l.lcm(c); end; end;
return l;
end;
remove_gcd is
-- self/gcd_coeff
g::=gcd_coeff;
if g>1.inti then loop i::=arr.ind!; arr[i]:=arr[i]/g; end; end;
end;
remove_gcd:SAME is
-- self/gcd_coeff
f::=self.copy; f.remove_gcd; return f;
end;
mod(n:INTI):SAME is
-- mod for each coeff.
r::=copy; loop i::=r.arr.ind!; r.arr[i]:=r.arr[i]%n; end;
r.normalize; return r;
end;
mod_n(n:INTI):SAME is
-- mod( -n/2< coeff <= n/2 ) for each coeff.
c:INTI;
r::=copy; n2::=(n+1.inti)/2.inti;
loop i::=r.arr.ind!;
c:=r.arr[i]%n; if c>n2 then c:=c-n; end;
r.arr[i]:=c;
end;
r.normalize; return r;
end;
divmod_Zp(p:INTI, divisor:SAME, out q:SAME, out r:SAME) is
-- as Zp coefficient polynomial.
r:=mod(p); degR:INT:=r.degree;
d_poly::=divisor.mod(p); degD:INT:=d_poly.degree;
pd::=INTI_EXT::inv(p,d_poly.lc);
q:=#; -- Quotient
loop dq::=(degR-degD).downto!(0);
if (r.arr[dq+degD]/=r_0) then ;-- q.arr[dq]:=r_0
q1::=(r[dq+degD]*pd)%p;
q[dq]:=q1;
loop i::=0.upto!(degD); j::=dq.up!;
r.arr[j] := (r.arr[j]-q1*d_poly.arr[i])%p;
end;
end;
end;
q.normalize; r.normalize;
end;
gcd_Zp(prime:INTI, o:SAME):SAME is
-- Euclidean algorithm.
a::=copy; b::=o.copy; q:SAME;
loop
if b.is_zero then return a; end;
a.divmod_Zp(prime, b,out q, out a);
if a.is_zero then return b; end;
b.divmod_Zp(prime, a,out q, out b);
end;
end;
extended_gcd_Zp(prime:INTI,o:SAME, out f1:SAME, out f2:SAME):SAME is
a::=copy; b::=o.copy;
x::=one; y::=zero; u::=zero; v::=one;
q:SAME;
loop
if b.is_zero then f1:=x; f2:=y; return a; end;
a.divmod_Zp(prime,b, out q, out a);
x:=(x-q*u)%prime; y:=(y-q*v)%prime;
if a.is_zero then f1:=u; f2:=v; return b; end;
b.divmod_Zp(prime,a,out q, out b);
u:=(u-q*x)%prime; v:=(v-q*y)%prime;
end;
end;
polys_rat:POLYS_RAT is
g:POLYS_RAT:=#;
loop i::=arr.ind!; g[i]:=#RAT(arr[i]); end;
return g;
end;
is_SqrFree(prime:INTI):BOOL is
return self.gcd_Zp(prime, self.derivative).degree=0;
end;
is_SqrFree:BOOL is
return polys_rat.is_SqrFree;
end;
squareFreeDecomposition:ARRAY{SAME} is
f_rat::=polys_rat;
sqf_rat::=f_rat.squareFreeDecomposition;
sqf:ARRAY{SAME}:=#(sqf_rat.size);
loop i::=sqf_rat.ind!; sqf[i]:=sqf_rat[i].polys_inti; end;
return sqf;
end;
constructionHensel(g,h:SAME, n,prime,pn:INTI, out g1,out h1:SAME) is
-- Hensel's construction.
-- Input: self,g,h,n,prime,pn s.t. self==g*h (mod pn), pn=prime^n
-- Output: g,h s.t. f=g*h (mod prime^(n+1))
d0:SAME:=self-g*h;
d:SAME:=(d0/#SAME(pn))%prime;
a,b,a0,b0,a1,b1,q:SAME;
gcd_gh:SAME:=g.extended_gcd_Zp(prime,h,out a0,out b0);
a1:=(a0*d*INTI_EXT::inv(prime,gcd_gh[0]));
b1:=(b0*d*INTI_EXT::inv(prime,gcd_gh[0]));
-- Must be a1.degree<h.degree, b1.degree<=g.degree.
a1.divmod_Zp(prime, h,out q, out a);
b:=(b1+q*g)%prime;
g1:=(g+b*pn).mod_n(pn*prime);
h1:=(h+a*pn).mod_n(pn*prime);
-- Now, a*g1+b*h1==d mod prime and self==g*h mod prime^(n+1)
end;
factorize:ARRAY{SAME} is
return FACTORIZATION_ALG::factorize(self);
end;
countSolution(a,b:INTI , countRedundancy:BOOL):INT is
return polys_rat.countSolution(#RAT(a),#RAT(b),countRedundancy);
end;
end;
class POLYS_GAUSS_INTI < $IS_LT{POLYS_GAUSS_INTI},$STR
class POLYS_GAUSS_INTI < $IS_LT{POLYS_GAUSS_INTI},$STR is
include COMPARABLE;
include POLYS{GAUSS_INTI};
init is
r_0:=GAUSS_INTI::zero; r_1:=GAUSS_INTI::one; divmodForce0:=false;
end;
mod(n:GAUSS_INTI):SAME is
r::=copy; loop i::=r.arr.ind!; r.arr[i]:=r.arr[i]%n; end;
r.normalize; return r;
end;
end;
class POLYS_RAT < $STR
class POLYS_RAT < $STR is
-- POLYS_RAT is PID.
include POLYS{RAT};
include PID_GCD;
include STRUM{RAT};
init is
r_0:=#RAT(0); r_1:=#RAT(1); divmodForce0:=false;
end;
gcd_coeff_num:INTI is
-- gcd of numerator of coefficients as Rational
g:INTI:=0.inti; -- gcd
loop c::=arr.elt!; if c.is_zero.not then g:=g.gcd(c.u); end; end;
return g;
end;
lcm_coeff_den:INTI is
-- lcm of of denominator of coefficients as Rarional
l:INTI:=1.inti; -- lcm
loop c::=arr.elt!; if c.is_zero.not then l:=l.lcm(c.v); end; end;
return l;
end;
polys_inti:POLYS_INTI is
f::=self*#RAT(lcm_coeff_den); -- make coefficients integer.
g:POLYS_INTI:=#;
loop i::=(f.arr.size-1).downto!(0); g[i]:=f[i].floor; end;
g.remove_gcd;
return g;
end;
is_SqrFree:BOOL is
return self.gcd(self.derivative).degree=0;
end;
end;
class POLYS_FLTD < $STR
class POLYS_FLTD < $STR is
-- POLYS_RAT is PID.
include POLYS{FLTD};
include PID_GCD;
init is
r_0:=0.0d; r_1:=1.0d; divmodForce0:=true;
end;
end;
partial class POLYS{R}
partial class POLYS{R} is
attr arr: ARRAY{R};
shared r_0:R;
shared r_1:R;
clear is
loop arr.set!(r_0); end;
end;
allocate(d:INT):SAME pre (d>=0) is
-- degree "d"
res:SAME:=new; res.arr:=#(d+1); return res;
end;
create(c:R,d:INT): SAME pre (d>=0) is
-- c*x^(d)
res:SAME:=allocate(d); res.clear; res.arr[d]:=c; return res;
end;
create(c:R): SAME is
-- same as create(c,0)
res:SAME:=new; res.arr:= #(1); res.arr[0] := c;
return res;
end;
create: SAME is
-- same as create(r_0,c)
res:SAME:=new; res.arr:= #(1); res.arr[0] := r_0;
return res;
end;
create(a:ARRAY{R}):SAME is
res:SAME:=new; res.arr:=a.copy; return res;
end;
gen_func(a:ARRAY{R}):SAME is
-- generating function.
return #(a);
end;
exp_gen_func(a:ARRAY{R}):SAME is
-- exponential generating function
res:SAME:=allocate(a.size-1);
factorial:R:= r_1;
loop i::=1.upto!(a.size-1);
factorial:=factorial*#R(i);
res.arr[i]:=res.arr[i]/factorial;
end;
return res;
end;
array:ARRAY{R} is
return normalize.arr;
end;
aget(i:INT): R is
if arr.has_ind(i) then return arr[i]; else return r_0; end;
end;
aset(i:INT, v:R) pre i>=0 is
if (i>=arr.size) then
s:INT:=arr.size;
arr:=arr.resize(i+1); loop arr.set!(s, r_0); end;
end;
arr[i]:=v;
end;
copy:SAME is
p:SAME:=#; p.arr:=arr.copy; return p;
end;
deep_copy:SAME is
raise "POLYS::deep_copy is not implemented.\n";
end;
degree:INT is
d:INT:=arr.size-1;
loop while!((d>0)and(arr[d]=r_0)); d:=d-1; end;
return d;
end;
mindeg:INT is
--minimum degree of non-zero term
i::=0; d::=degree;
loop while!( (i<degree)and(arr[i]=r_0)); i:=i+1; end;
return i;
end;
normalize is
-- destructive. ### Note that arr[0]==0 for polynomial "0".
arr:=arr.resize(degree+1);
end;
normalize:SAME is
p:SAME:=copy; p.normalize; return p;
end;
-- Polynomial(poly_arg1=[0],*poly_arg2)
-- Polynomial.term(c=0,n=0)
str:STR is return str("text","x",true); end;
str(format:STR, v:STR, r:BOOL): STR is
-- needed "is_lt" in R.
-- format: "text","tex", "texm", "prog"
-- v: variable,
-- r: switch of order. true:decreasing order. false:increasing order
timeS,power1,power2,ms,me:STR;
case format
when "text" then timeS:="";power1:="^(";power2:=")"; ms:=""; me:="";
when "tex" then timeS:="";power1:="^{";power2:="}"; ms:=""; me:="";
when "texm" then timeS:="";power1:="^{";power2:="}"; ms:="$"; me:="$";
when "prog" then timeS:="*"; power1:="**(";power2:=")"; ms:=""; me:="";
end;
s:STR:="";
addS:STR:="";
deg::=degree;
d:INT; -- degree
c:R; -- coefficient
loop i::=0.upto!(deg);
if r then d:=deg-i; else d:=i; end;
c:=arr[d];
if c/=r_0 then
if c<r_0 then s:=s+"-"; c:=-c; else; s:=s+addS; end;
addS:="+";
--if c.kind_of?(Rational)&&(c.denominator != 1);
-- den="/"+c.denominator.to_s; c=c.numerator
-- else
-- end
if (c /= r_1)or(d=0) then s:=s+c.str; end;
if (c /= r_1)and(d/=0) then s:=s+timeS; end;
if d /= 0 then s:=s+v;
if d /= 1 then s:=s+power1+(d.str)+power2; end;
end;
end;
end;
if s="" then s:="0"; end;
s:=ms+s+me; -- math start and math end
return s;
end;
lc: R is
-- lc: leading coefficient
return arr[degree];
end;
lp:SAME is
-- leading power product
d:INT:=degree; return #SAME(r_1,d);
end;
lt:SAME is
-- leading term
d:INT:=degree; return #SAME(arr[d],d);
end;
low_deg:INT is
-- (mlowest degree of non-zero term) or 0.
i:INT:=0;
loop while!( (arr.has_ind(i))and(arr[i]=r_0) ); i:=i+1; end;
if arr.has_ind(i) then return i; else return 0; end;
end;
zero:SAME is return #SAME(r_0,0); end;
one:SAME is return #SAME(r_1,0); end;
x:SAME is return #SAME(r_1,1); end;
is_zero: BOOL is
return ((degree=0)and(arr[0]=r_0));
end;
is_one:BOOL is
return ((degree=0)and(arr[0]=r_1));
end;
negate:SAME is
p:SAME:=allocate(arr.size-1);
loop i::=arr.ind!; p.arr[i]:=-arr[i]; end;
return p;
end;
plus(other:R):SAME is
p:SAME:=copy; p.arr[0]:=p.arr[0]+other; return p.normalize;
end;
plus(other:INT):SAME is
p:SAME:=copy; p.arr[0]:=p.arr[0]+#R(other); return p.normalize;
end;
plus(other:SAME):SAME is
s0::=(arr.size).min(other.arr.size);
s1::=(arr.size).max(other.arr.size);
p:SAME:=allocate(s1-1);
loop i::=0.upto!(s0-1); p.arr[i]:=arr[i]+other.arr[i]; end;
if (arr.size)>(other.arr.size) then
loop i::=s0.upto!(s1-1); p.arr[i]:=arr[i]; end;
elsif (arr.size)<(other.arr.size) then
loop i::=s0.upto!(s1-1); p.arr[i]:=other.arr[i]; end;
end;
return p.normalize;
end;
minus(other:R):SAME is
p:SAME:=copy; p.arr[0]:=p.arr[0]-other; return p.normalize;
end;
minus(other:INT):SAME is
p:SAME:=copy; p.arr[0]:=p.arr[0]-#R(other); return p.normalize;
end;
minus(other:SAME):SAME is
s0::=(arr.size).min(other.arr.size);
s1::=(arr.size).max(other.arr.size);
p:SAME:=allocate(s1-1);
loop i::=0.upto!(s0-1); p.arr[i]:=arr[i]-other.arr[i]; end;
if (arr.size)>(other.arr.size) then
loop i::=s0.upto!(s1-1); p.arr[i]:=arr[i]; end;
elsif (arr.size)<(other.arr.size) then
loop i::=s0.upto!(s1-1); p.arr[i]:=-other.arr[i]; end;
end;
-- #OUT+"minus: self="+arr.str+" o="+other.arr.str+" p="+p.arr.str+"\n";
return p.normalize;
end;
times(other:R):SAME is
p:SAME:=copy.normalize;
loop i::=p.arr.ind!; p.arr[i]:=p.arr[i]*other; end;
return p;
end;
times(other:INT):SAME is
oR::=#R(other);
p:SAME:=copy.normalize;
loop i::=p.arr.ind!; p.arr[i]:=p.arr[i]*oR; end;
return p;
end;
times(other:SAME): SAME is
d0:INT:=degree; d1:INT:=other.degree;
d2:INT:=d0+d1;
p:SAME:=allocate(d2); p.clear;
loop i::=0.upto!(d0);
loop j::=0.upto!(d1);
p.arr[i+j]:=p.arr[i+j]+arr[i]*other.arr[j];
end;
end;
return p;
end;
shift_deg(d:INT):SAME is
-- self * (x^d)
d0::=degree;
if d=0 then return copy.normalize;
elsif d>0 then
if is_zero then return zero; end;
res:SAME:=allocate(d0+d); res.clear;
loop i::=0.upto!(d0); j::=d.up!;
res.arr[j]:=arr[i];
end;
return res;
elsif d+d0>=0 then -- d<0
res:SAME:=allocate(d0+d);
loop i::=(-d).upto!(d0); j::=0.up!;
res.arr[j]:=arr[i];
end;
return res;
else -- d<0, d+d0<0
return zero;
end;
end;
shared divmodForce0:BOOL;
divmod(divisor:SAME, out q:SAME, out r:SAME) is
divmod(divmodForce0, divisor, out q, out r);
end;
divmod(force0:BOOL, divisor:SAME, out q:SAME, out r:SAME) is
-- R be field or PID
-- "force0" be true to force "0" at head of remainder.
-- true: continuous field e.g.FLT,FLTD, CPX,CPXD...
-- false: PID or discrete field or RAT e.g. INT,INTI,RAT,finite field
r:=copy; degR:INT:=r.degree;
degD:INT:=divisor.degree;
q:=#; -- Quotient
loop dq::=(degR-degD).downto!(0);
if (r.arr[dq+degD]/=r_0) then ; --q.arr[dq]:=r_0
q1::=r.arr[dq+degD].div(divisor.arr[degD]);
q[dq]:=q1;
loop i::=0.upto!(degD); j::=dq.up!;
r.arr[j] := r.arr[j]-q1*divisor.arr[i];
end;
if force0 then r[dq+degD]:=r_0; end;
end;
end;
q.normalize; r.normalize;
end;
div(other:SAME): SAME is
q,r:SAME;
divmod(other,out q,out r);
return q;
end;
mod(other:SAME): SAME is
q,r:SAME;
divmod(other,out q,out r);
return r;
end;
pow(power:INT): SAME pre power>=0 is
s:SAME:=one;
if (power>0) then
p:SAME:=copy;
loop while!(power>0); -- with binary notation of "power".
if power.is_odd then s:=s*p; end;
p:=p*p; power:=power/2;
end;
end;
return s;
end;
pow(power:INTI): SAME pre power.is_non_neg is
s:SAME:=one;
if (power.is_pos) then
p:SAME:=copy; -- with binary notation of "power".
loop while!(power.is_pos);
if power.is_odd then s:=s*p; end;
p:=p*p; power:=power/2.inti;
end;
end;
return s;
end;
derivative:SAME is return derivative(1); end;
derivative(n:INT):SAME pre n>=0 is
-- n-th derivative
p:SAME:=copy;
d:INT:=p.degree;
loop n.times!;
loop i::=1.upto!(d); p[i-1] := p[i]*#R(i); end;
p[d]:=r_0;
if d>0 then d:=d-1; end;
end;
return p.normalize;
end;
integral:SAME is return integral(1); end;
integral(n:INT):SAME pre n>=0 is
-- integral n-times
p:SAME:=copy;
d:INT:=p.degree;
if n>1 then p.arr:=p.arr.resize(d+n+1); end;
loop n.times!;
loop i::=d.downto!(0); p.arr[i+1]:=p.arr[i]/#R(i+1); end;
p.arr[0]:=r_0; d:=d+1;
end;
return p.normalize;
end;
substitute(x:R):R is
d:INT:=degree; s:R:=arr[d];
loop i::=(d-1).downto!(0); s:= (x*s)+ arr[i]; end;
return s;
end;
substitute(x:SAME):SAME is
d:INT:=degree; s:SAME:=#(arr[d]);
loop i::=(d-1).downto!(0); s:= (x*s)+arr[i]; end;
return s;
end;
is_eq(other:SAME): BOOL is
d:INT:=degree;
if ( d/= other.degree) then return false; end;
loop i::=0.upto!(d);
if (arr[i] /= other.arr[i]) then return false; end;
end;
return true;
end;
compare(other:SAME):INT is
-- <=>: -1 if "<", 0 if "=", 1 if ">" .
d0::=degree; d1::=other.degree;
if d0=d1 then
loop i::=d0.downto!(0);
if arr[i]/=other.arr[i] then
return (arr[i]-other.arr[i]).sign;
end;
end;
return 0;
else return (d0-d1).sign;
end;
end;
is_lt(other:SAME): BOOL is
-- expr1 < expr2 expr1.is_lt(expr2)
-- return degree<other.degree;
return compare(other)<0;
end;
squareFreeDecomposition:ARRAY{SAME} is
g:ARRAY{SAME}:=|self.copy|;
gm:SAME;
loop s:INT:=g.size; while!( g[s-1].degree>0 );
gm:=g[s-1].copy;
gm:=gm.gcd(gm.derivative);
g:=g.append(|gm|);
end;
s::=g.size;
h:ARRAY{SAME}:=#(s+1);
h[0]:=one;
loop i::=1.upto!(s-1); h[i]:=g[i-1]/g[i]; end;
h[s]:=one;
fd:ARRAY{SAME}:=#(s);
fd[0]:=one;
loop i::=1.upto!(s-1); fd[i]:=h[i]/h[i+1]; end;
return fd;
end;
is_SqrFree:BOOL is
return self.gcd(self.derivative).degree=0;
end;
monic is
-- make monic i.e. f(x/lc)*(lc^(degree-1))
d::=degree; c::=lc; c1::=c;
arr[d]:=r_1;
loop i::=(d-2).downto!(0); arr[i]:=arr[i]*c1; c1:=c1*c; end;
end;
pseudo_monic(c:R) is
-- f(x/c)*(c^degree)
d::=degree; c1::=c;
loop i::=(d-1).downto!(0); arr[i]:=arr[i]*c1; c1:=c1*c; end;
end;
-- coeff_to_real
-- coeff_to_Z
-- coeff_truncate # truncate each coefficient to Integer
-- coeff_to_f # converts each element to Float
-- Polynomial.factor2s(f,s="")
-- cnv_prog_format(str)
end; -- POLYS
partial class STRUM{R}
partial class STRUM{R} is
getSturm(out gcd:SAME,out sturm:ARRAY{SAME}) is
-- return GCD, Sturm_sequence
s:INT;
sturm0:ARRAY{SAME}:=|self.copy,self.derivative|;
loop s:=sturm0.size; while!(sturm0[s-1].is_zero);
sturm0:=sturm0.append(| -(sturm0[s-2]%sturm0[s-1]) |);
end;
s:=sturm0.size;
gcd:=sturm0[ s-2 ];
sturm:=#(s-1);
loop i::=0.upto!(s-2); sturm[i]:=sturm0[i]/gcd; end
end;
countSturmChange(a:R, sturm:ARRAY{SAME}):INT is
sturmA:ARRAY{R}; c:R;
loop p::=sturm.elt!;
-- if a=Infinity;sturmA.push(p.lc)
-- elsif a==-Infinity;sturmA.push(p.lc*((-1)^p.degree))
c:=p.substitute(a);
if c.is_zero.not then sturmA:=sturmA.append(|c|); end;
end;
-- v=#of sign change
v:INT:=0;
loop i::=0.upto!(sturmA.size-2);
if (sturmA[i]*sturmA[i+1]).is_neg then v:=v+1; end;
end;
return v;
end;
countSolution(a,b: R , countRedundancy:BOOL):INT is
-- count # of solution between a and b using Sturm's algorithm.
-- R Integer,Rational,Float
gcd_s:SAME;
sturm:ARRAY{SAME};
getSturm(out gcd_s, out sturm);
n1:INT;
if (countRedundancy=true)and(gcd_s.degree>0)then
n1:=gcd_s.countSolution(a,b,countRedundancy);
else n1:=0;
end;
return countSturmChange(a,sturm)-countSturmChange(b,sturm)+n1;
end;
end;