1 /* ---------------------------------------------------------------- */
2 /* SARAG - Root Counting */
3 /* by Fabrizio Caruso modified by Alexandre Le meur and Marie-Françoise Roy */
13 padWithZeros(seq,size) :=
14 append(seq,makelist(0,i,1,size-length(seq)));
16 /* signed pseudo-remainder of p divised by q*/
18 if degree(p,var)<degree(q,var) then p
19 else (if remainder(degree(p,var)-degree(q,var),2)=1 then
20 remainder(leadCoeff(q,var)**(degree(p,var)-degree(q,var)+1)*p,q,var)
21 else remainder(leadCoeff(q,var)**(degree(p,var)-degree(q,var)+2)*p,q,var)
25 /* Signed Remainder Sequence */
27 block([aa,bb,rOld,rNew,quo,res,seq,srsRes],
36 while not(rNew = 0) do
38 srsRes : endcons(rNew,srsRes),
40 res : remainder(rOld,rNew,var),
51 /* Extended Signed Remainder Sequence */
53 block([rOld,rNew,uOld,uNew,vOld,vNew,rRes, uRes, vRes,quo,rList,uList,vList,rAux,uAux,vAux],
66 while not(rNew = 0 ) do
68 rList : endcons(rNew,rList),
69 uList : endcons(uNew,uList),
70 vList : endcons(vNew,vList),
72 rRes : divide(rOld,rNew,var),
80 uNew : ratexpand(-uOld + quo*uNew),
81 vNew : ratexpand(-vOld + quo*vNew),
89 return([rList,endcons(uNew,uList),endcons(vNew,vList)])
93 /* It counts the sign change of a determinant after */
94 /* i consecutive row changes */
95 revRowsCount(i) := (-1)^(i*(i-1)/2);
97 /* ------------------------------------------------------- */
98 /* Part concerning the computation of signed subresultants */
101 sSubResPol(p,q,var) :=
102 block([pp,qq,degA,degB,i,j,k,h,lcA,lcB,delta,
108 degA : degree(pp,var),
109 degB : degree(qq,var),
112 print("sSubRes) Error: inconsistent degrees"),
116 SR : make_array('any,degA+1),
117 s : make_array('any,degA+1),
118 sOld : make_array('any,degA+1),
121 lcA : leadCoeff(pp,var),
122 lcB : leadCoeff(qq,var),
135 while j>=1 and not(SR[j-1]=0) do
138 k : degree(SR[j-1],var),
145 SR[k-1] : NORM_ALGORITHM(-remainder(s[j-1]^2*SR[i-1],SR[j-1],var)/
150 /* Computation of s[k] */
151 for delta : 1 thru min(j-k-1,j-1) do
152 sOld[j-delta-1] : (-1)^delta * (sOld[j-1]*sOld[j-delta])/s[j],
155 SR[k] : NORM_ALGORITHM(s[k]*SR[j-1]/sOld[j-1]),
157 for h : k+1 thru j-2 do
163 /* Computation of SR[k-1] */
164 /* ratexpand or expand */
166 SR[k-1] : NORM_ALGORITHM(-remainder(sOld[j-1]*s[k]*SR[i-1],
172 sOld[k-1] : leadCoeff(SR[k-1],var),
179 res : padWithZeros(makelist(SR[degA-i],i,0,degA-j),degA+1),
186 sSubResCoeff(p,q,var) :=
190 s : sSubResPol(pp,qq,var),
192 makelist(coeff(s[j],var,degree(pp,var)+1-j),j,1,degree(pp,var)+1)
198 /* Extended Signed Subresultant Sequence */
200 /* (A variation of this algorithm is used in */
201 /* Algorithm 9.47 and Algorithm 10.17 */
204 /* Extended Signed Subresultant Sequence*/
206 sSubResExt(a,b,var) :=
207 sSubResExtVerbose(a,b,var,DEFAULT_VERBOSITY);
209 sSubResExtVerbose(a,b,var,verbosity) :=
210 block([q,degA,degB,i,j,k,lcA,lcB,delta,
211 SR:make_array('any),s:make_array('any),sOld:make_array('any),
212 u:make_array('any),v:make_array('any)],
217 degA : degree(a,var),
218 degB : degree(b,var),
221 print("sSubResExt) Error: inconsistent degrees"),
226 SR : make_array('any,degA+1),
227 s : make_array('any,degA+1),
228 sOld : make_array('any,degA+1),
229 u : make_array('any,degA+2),
230 v : make_array('any,degA+2),
233 lcA : leadCoeff(a,var),
234 lcB : leadCoeff(b,var),
241 sOld[degA] : s[degA],
254 while j>=1 and not(SR[j-1]=0) do
256 k : degree(SR[j-1],var),
257 if verbosity>=EXTRA then
259 print("sSubResExt) j : ", j),
260 print("sSubResExt) k : ", k)
266 q : quotient(s[j-1]^2 * SR[i-1], SR[j-1],var),
267 if verbosity >= VERY then
268 print("sSubResExt) q : ", q),
271 SR[k-1] : NORM_ALGORITHM((-s[j-1]^2 *
272 SR[i-1] + q * SR[j-1])/(s[j]*sOld[i-1])),
275 u[k-1+1] : NORM_ALGORITHM((-s[j-1]^2 *
276 u[i-1+1] + q * u[j-1+1])/(s[j]*sOld[i-1])),
277 v[k-1+1] : NORM_ALGORITHM((-s[j-1]^2 *
278 v[i-1+1] + q * v[j-1+1])/(s[j]*sOld[i-1]))
285 /* Computation of s[k] */
286 for delta : 1 thru min(j-k-1,j-1) do
287 sOld[j-delta-1] : (-1)^delta * (sOld[j-1]*sOld[j-delta])/s[j],
293 SR[k] : NORM_ALGORITHM(s[k]*SR[j-1]/sOld[j-1])
295 u[k+1] : NORM_ALGORITHM(s[k]*u[j-1+1]/sOld[j-1]),
296 v[k+1] : NORM_ALGORITHM(s[k]*v[j-1+1]/sOld[j-1]),
299 for h : k+1 thru j-2 do
307 /* Computation of SR[k-1], u[k-1], v[k-1] */
308 q : quotient(s[k]*sOld[j-1]*SR[i-1],SR[j-1],var),
310 SR[k-1] : NORM_ALGORITHM((-sOld[j-1] * s[k] * SR[i-1] +
311 q * SR[j-1])/(s[j]*sOld[i-1])),
313 u[k-1+1] : NORM_ALGORITHM((-sOld[j-1] * s[k] * u[i-1+1] +
314 q * u[j-1+1])/(s[j]*sOld[i-1])),
315 v[k-1+1] : NORM_ALGORITHM((-sOld[j-1] * s[k] * v[i-1+1] +
316 q * v[j-1+1])/(s[j]*sOld[i-1]))
321 sOld[k-1] : leadCoeff(SR[k-1],var),
325 if verbosity>= NORMAL and k>= 1 then
326 print("sSubResExt) SR[", k-1,"] : ", SR[k-1])
328 for h : 0 thru j-2 do
340 [padWithZeros(makelist(SR[degA-i],i,0,degA-j),degA+1),
341 makelist(u[degA-i+1],i,0,degA-j+1),
342 makelist(v[degA-i+1],i,0,degA-j+1)])
346 /* last non zero element of a sequence */
350 for i:1 thru length(seq) do
351 if not(seq[length(seq)-i+1] = 0) then
352 return(seq[length(seq)-i+1])
355 /* index of last non zero element of a sequence */
356 lastNonZeroIndex(seq) :=
358 for i:1 thru length(seq) do
359 if not(seq[length(seq)-i+1] = 0) then
360 return(length(seq)-i+1)
363 /* index of first non zero element of a sequence */
364 firstNonZeroIndex(seq) :=
366 for i:1 thru length(seq) do
367 if not(seq[i] = 0) then
371 /* Gcd and Gcd-Free part computation */
373 /* gcd(p,q) and p/gcd(p,q) */
375 gcdFreePart(p,q,var) :=
377 r:sSubResExt(ratexpand(p),sPsRem(ratexpand(q),ratexpand(p),var),var),
378 return ([lastNonZero(r[1]),lastNonZero(r[3])])
382 gcdFreePartWithZ(p,q,var) :=
384 r:sSubResExt(p,sPsRem(ratexpand(q),ratexpand(p),var),var),
385 c:leadCoeff(expand(p),var),
387 return ([ratexpand(c*lastNonZero(r[1])/leadCoeff(lastNonZero(r[1]),var)),ratexpand(c*v/leadCoeff(v,var))])
392 sSubResCoeffLast(p,q,var) :=
393 last(sSubResCoeff(p,q,var));
395 sylvesterResultant(p,q,var) :=
399 if degree(pp,var)>degree(qq,var) then
400 return(epsilon(degree(pp,var))*sSubResCoeffLast(pp,qq,var))
402 if degree(pp,var)<degree(qq,var) then
403 return((-1)^(degree(pp,var)*degree(pp,var))*sylvesterResultant(qq,pp,var))
406 aux : expand(leadCoeff(pp,var)*qq-leadCoeff(qq,var)*pp),
408 return(expand(epsilon(degree(pp,var))*sSubResCoeffLast(pp,aux,var)/leadCoeff(pp,var)^degree(aux,var)))
415 block([pcRes,pRes,cRes],
416 pcRes : sSubRes(p,diff(p,var),var),
417 pRes : first(pcRes/leadCoeff(expand(p),var)),
418 pRes : cons(first(pRes),expand(rest(pRes)/leadCoeff(expand(p),var))),
419 cRes : second(pcRes),
420 cRes : cons(first(cRes),expand(rest(cRes)/leadCoeff(expand(p),var))),
424 subDiscrCoeff(p,var):=
426 cRes : sSubResCoeff(p,diff(p,var),var),
427 cRes : cons(first(cRes)/leadCoeff(expand(p),var),expand(rest(cRes)/leadCoeff(expand(p),var))),
431 discriminant(p,var):=
432 subDiscrCoeff(p,var)[degree(expand(p),var)+1];
435 /* --------------------------------------------- */
436 /* Part concerning the signed remainder sequence */
438 /* In the following S(P,Q) is the signed remainder sequence of P and Q */
440 /* Number of sign changes (notation "V") */
441 /* of sequence containing non-zero elements */
444 nonZeroSignChanges(seq) :=
445 block([variations,i],
447 if length(seq) > 1 then
448 for i:1 thru length(seq)-1 do
449 if seq[i]*seq[i+1] < 0 then
450 variations:variations+1,
455 nonZeroSignChanges(trimZeros(seq));
458 /* Number of sign changes of a polynomial at a certain value */
459 /* Notation "V(P;a)" */
460 /* We use proposition 2.4 for the signs at infinities */
461 signChangesAt(seq,var,a) :=
463 signChanges(makelist(leadCoeff(seq[i],var),i,1,length(seq)))
466 signChanges(makelist((-1)^(degree(seq[i],var))*
467 leadCoeff(seq[i],var),i,1,length(seq)))
469 signChanges(makelist(subst(a,var,seq[i]),i,1,length(seq)));
473 /* Difference between the no. of changes a two points */
474 /* Notation "V(P;a,b)" */
475 signChangesDiff(seq,var,a,b) :=
476 signChangesAt(seq,var,a) - signChangesAt(seq,var,b);
478 /* Sign changes of the i-th coefficients of a polynomial */
479 /* Notation "V(P)" (P is a polynomial) */
480 signChangesPolyCoeff(poly,var) :=
481 signChanges(makelist(coeff(poly,var,i),i,0,degree(poly,var)));
483 /* Sequence of the derivatives of a polynomial */
484 /* Notation "Der(P)" */
486 makelist(diff(f,var,i),i,0,degree(f,var));
490 /* V(S(P,Q);a,b) = Ind(Q/P;a,b) */
492 sRemCauchyIndexBetween(num,den,var,a,b) :=
493 signChangesDiff(sRem(den,num,var),var,a,b);
495 sRemCauchyIndex(num,den,var) :=
496 signChangesDiff(sRem(den,num,var),
499 /* Tarski query computed by Sylvester's theorem's formula */
501 /* V(S(P,P'Q);a,b) = SQ(Q,P;a,b) */
503 sRemTarskiQueryBetween(q,p,var,a,b) :=
504 signChangesDiff(sRem(p,diff(p,var)*q,var),var,a,b);
506 sRemTarskiQuery(q,p,var) :=
507 signChangesDiff(sRem(p,diff(p,var)*q,var),
511 /* Number of roots counted by Tarski's theorem's formula */
514 sRemNumberOfRootsBetween(p,var,a,b) :=
515 sRemTarskiQueryBetween(1,p,var,a,b);
517 sRemNumberOfRoots(p,var) :=
518 sRemTarskiQuery(1,p,var);
520 /* Tarski sequence */
521 /* defined as: S(P,P') */
522 sturmSequence(p,var) :=
523 sRem(p,diff(p,var),var);
526 /* ------------------------------------------------- */
527 /* Part concerning signed subresultants coefficients */
528 /* and the Cauchy Index in all R(Subsection 9.1.2) */
531 /* Notation taken from Remark 4.36 and used by Notation 9.4 */
535 /* It counts and removes the trailing zeros out of a sequence */
537 trimZeroCount(seq,count) :=
538 block([list,count,i],
541 if length(seq)>0 then
542 for i:1 thru length(seq) do
546 list:append(list,[seq[i]]),
550 /* Interface function */
552 trimZeroCount(seq,0);
556 /* Notation 9.4 ("D(s)") */
557 /* When there are no zeros this gives the difference between */
558 /* the sign permanencies and the number of sign changes */
563 ( while not(i>length(seq)-1) do
564 if not(seq[i]=0) then
566 (while (seq[i+count]=0 and not(i+count>length(seq)-1)) do count:count+1), /* end while */
567 ( if remainder(count,2)=1 then
568 res:res+(-1)^((count)*(count-1)/2)*sgn(seq[i]*seq[i+count])),
569 i:i+count) /* end if */
570 else i:i+1), /* end while */
575 /* Cauchy Index by signed subresultant */
578 sSubResCauchyIndex(q,p,var) :=
579 genPermMVar(sSubResCoeff(p,sPsRem(ratexpand(q),ratexpand(p),var),var));
581 /* Tarski Query by signed subresultant */
583 sSubResTarskiQuery(q,p,var) :=
584 genPermMVar(sSubResCoeff(p,sPsRem(ratexpand(diff(p,var)*q),p,var),var));
586 sSubResNumberOfRoots(p,var) :=
587 sSubResTarskiQuery(1,p,var);
591 /* ---------------------------------------------- */
592 /* Part concerning the Bezoutian (subsection 9.1.3) */
594 /* Bezoutian (Notation 9.14) */
596 (subst(y,var,q) * subst(x,var,p) -
597 subst(x,var,q) * subst(y,var,p))/(x-y); /* end proc */
601 /* ----------------------------------------------- */
602 /* Part concerning the Cauchy Index on an interval */
603 /* (Subsection 9.1.5) */
605 /* Modified number of sign changes */
606 /* Notation 9.29, ("W(s)") */
607 /* Modified Sign Changes: */
608 /* counting as two sign changes the groups +,0,0,+ and -,0,0,- */
611 modifiedSignChanges(seq) :=
612 block([variations,i],
613 variations:signChanges(seq),
614 if length(seq) > 1 then
615 for i:1 thru length(seq)-3 do
616 if (seq[i+1]=0 and seq[i+2]=0 and seq[i]*seq[i+3] > 0) then
617 variations:variations+2,
622 /* Remove identically zero elements from a list */
628 if length(seq)>0 then
629 for i:1 thru length(seq) do
630 if not (seq[i] = 0) then
631 list:append(list,[seq[i]]),
636 /* Modified Sign Changes at a certain value */
637 /* Notation "W(P;a)" */
638 modifiedSignChangesAt(seq,var,value) :=
640 signChanges(makelist(leadCoeff(seq[i],var),i,1,length(seq)))
643 signChanges(makelist((-1)^(degree(seq[i],var))*
644 leadCoeff(seq[i],var),i,1,length(seq)))
646 modifiedSignChanges(subst(value,var,removeZeros(seq))); /* end proc */
649 /* Modified Sign Changes Difference */
650 /* Notation "W(P;a,b)" */
651 modifiedSignChangesDiff(seq,var,a,b) :=
652 modifiedSignChangesAt(seq,var,a)-
653 modifiedSignChangesAt(seq,var,b);
657 /* Cauchy Index in an interval computed by subresultants */
659 /* W(SR(P,Q);a,b) = Ind(Q/P;a,b) */
661 sSubResCauchyIndexBetween(num,den,var,a,b) :=
662 modifiedSignChangesDiff(sSubResPol(den,sPsRem(num,den,var),var),var,a,b);
664 /* Tarski Query by subresultants */
666 /* W(SR(P,Remainder(P,Q));a,b) = SQ(Q,P;a,b) */
667 sSubResTarskiQueryBetween(q,p,var,a,b) :=
668 modifiedSignChangesDiff(sSubResPol(p,
669 sPsRem(ratexpand(diff(p,var)*q),p,var),var),var,a,b);
671 /* Number of roots on an interval by signed subresultants */
672 /* Consequence of Corollary 9.32 */
673 sSubResNumberOfRootsBetween(p,var,a,b) :=
674 sSubResCauchyIndexBetween(diff(p,var),p,var,a,b);
677 /* ------------------------------- */
678 /* Part concerning Hankel Matrices */
681 /* It computes the signature of a Hankel quadratic form */
685 hankelSignature(seq) :=
686 block([m,n,sLen,trimRes,trimmed,count,c,p,q,t,i,SR,j,defective],
690 print("hankelSignature) Number of elements must be odd"),
695 trimRes : trimZero(seq),
697 if length(trimRes) = 0 then
700 c:firstNonZeroIndex(seq),
705 return(0)) /* end if */
706 else m:lastNonZeroIndex(seq),
711 return(0)) /* end if */
715 q : sum(seq[i]*t^(m-i),i,1,m),
717 SR : sSubResPol(p,q,t),
719 /* check of defectiveness */
723 if degree(SR[m-j+1],t) = j then
729 makelist(sSubResCoeff(p,q,t)[i],
740 /* It build a Hankel matrix out of an odd-numbered sequence */
755 /* ----------------------------------------------------- */
756 /* Part concerning complex roots with negative real part */
760 sum(ratcoeff(pol,var,i*2)*var^i,
761 i,0,floor(degree(pol,var)/2));
764 sum(ratcoeff(pol,var,i*2+1)*var^i,
765 i,0,ceiling(degree(pol,var)/2));
767 lienardChipartConditions(pol,var) :=
768 block([F,G,SR,degP,m],
769 F : evenPart(pol,var),
771 G : oddPart(pol,var),
773 degP : degree(pol,var),
778 SR : sSubResCoeff(F,G,var),
780 return(setify(append(poly2list(pol,var),SR)))
785 SR : sSubResCoeff(var*G,F,var),
787 return(setify(append(poly2list(pol,var),SR)))
794 /* It computes the difference between the number of roots of P */
795 /* with positive real parts and the number of roots with negative real parts */
796 /* Theorem 9.48 (notation "n(P)") */
798 block([p,degP,m,f,g,i],
800 degP : degree(p,var),
805 f : sum(coeff(p,var,i*2)* var^i,i,0,m),
806 g : sum(coeff(p,var,i*2+1)*var^i,i,0,m-1),
809 return(-sSubResCauchyIndex(g,f,var)+
810 sSubResCauchyIndexBetween(sPsRem(var*g,f,x),f,var))
815 f : sum(coeff(p,var,i*2)* var^i,i,0,m),
816 g : sum(coeff(p,var,2*i+1)* var^i,i,0,m),
819 return(-sRemCauchyIndexBetween(f,NORM_ALGORITHM(var*g),
821 sRemCauchyIndexBetween(f,g,
826 /* New name for Horner evaluation */
828 hornerEval(pol,var,value) :=saragHorner(pol,var,value) ;
830 saragHorner(pol,var,value) :=
833 degP : degree(pol,var),
835 res : leadCoeff(pol,var),
837 for i : 1 thru degP do
838 res : value*res+ratcoeff(pol,var,degP-i),