1 //   Copyright (c)  2005-2018  John Abbott and Anna M. Bigatti
2 //   Authors:  2005-2018  John Abbott and Anna M. Bigatti
3 
4 //   This file is part of the source of CoCoALib, the CoCoA Library.
5 
6 //   CoCoALib is free software: you can redistribute it and/or modify
7 //   it under the terms of the GNU General Public License as published by
8 //   the Free Software Foundation, either version 3 of the License, or
9 //   (at your option) any later version.
10 
11 //   CoCoALib is distributed in the hope that it will be useful,
12 //   but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 //   GNU General Public License for more details.
15 
16 //   You should have received a copy of the GNU General Public License
17 //   along with CoCoALib.  If not, see <http://www.gnu.org/licenses/>.
18 
19 
20 // Source code for abstract class SparsePolyRing and friends
21 
22 #include "CoCoA/SparsePolyOps-RingElem.H"
23 
24 #include "CoCoA/SparsePolyRing.H"
25 #include "CoCoA/BigIntOps.H"
26 #include "CoCoA/BigRat.H"
27 #include "CoCoA/DUPFp.H"
28 #include "CoCoA/MatrixOps.H" // for LinSolve
29 #include "CoCoA/MatrixView.H" // for ZeroMat
30 #include "CoCoA/NumTheory-RatReconstruct.H"
31 #include "CoCoA/OpenMath.H"
32 #include "CoCoA/PPMonoidHom.H"
33 #include "CoCoA/QuotientRing.H" // for IsQuotientRing
34 #include "CoCoA/ReductionCog.H"
35 #include "CoCoA/RingDistrMPolyClean.H" // for NewPolyRing_DMP
36 #include "CoCoA/RingFp.H" // for IsRingFp
37 #include "CoCoA/RingQQ.H" // for IsQQ
38 #include "CoCoA/RingTwinFloat.H" // for IsRingTwinFloat
39 #include "CoCoA/RingZZ.H" // for IsZZ
40 #include "CoCoA/SmallFpImpl.H"
41 #include "CoCoA/SparsePolyIter.H"
42 #include "CoCoA/SparsePolyOps-ideal.H"
43 #include "CoCoA/assert.H"
44 #include "CoCoA/convert.H"
45 #include "CoCoA/error.H"
46 #include "CoCoA/factor.H"  // for myGcd
47 #include "CoCoA/geobucket.H" // for myMul
48 #include "CoCoA/ideal.H"     // for myGcd
49 #include "CoCoA/interrupt.H"
50 #include "CoCoA/matrix.H" // for OrdMat, myDivMod
51 #include "CoCoA/module.H"    // for myGcd
52 #include "CoCoA/random.H" // for RandomLong
53 #include "CoCoA/submodule.H"  // for myGcd
54 #include "CoCoA/symbol.H"
55 
56 
57 #include <algorithm>
58 using std::max;     // for MaxExponent, StdDeg
59 #include <iostream>
60 // using std::ostream in SparsePolyRingBase::myOutput
61 #include <iterator>
62 using std::back_inserter;
63 #include <map>
64 using std::map;
65 #include <utility>
66 using std::make_pair;
67 using std::pair;
68 //#include <vector>
69 using std::vector;
70 
71 namespace CoCoA
72 {
73 
74   namespace // anonymous
75   {
76     // This fn is needed in a call to std::transform
CoeffPPCtor(const pair<PPMonoidElem,RingElem> & arg)77     CoeffPP CoeffPPCtor(const pair<PPMonoidElem, RingElem>& arg)
78     {
79       return CoeffPP(arg.second, arg.first);
80     }
81   } // end of namespace anonymous
82 
83 
84 
85   //---- Functions for creating/building polynomials
86 
monomial(const SparsePolyRing & P,ConstRefRingElem c,ConstRefPPMonoidElem pp)87   RingElem monomial(const SparsePolyRing& P, ConstRefRingElem c, ConstRefPPMonoidElem pp)
88   {
89     if (owner(c) != CoeffRing(P)) CoCoA_THROW_ERROR(ERR::MixedCoeffRings, "monomial(P,c,pp)");
90     if (owner(pp) != PPM(P)) CoCoA_THROW_ERROR(ERR::MixedPPMs, "monomial(P,c,pp)");
91     if (IsZero(c)) return zero(P);
92     return P->myMonomial(raw(c), raw(pp));
93   }
94 
monomial(const SparsePolyRing & P,ConstRefPPMonoidElem pp)95   RingElem monomial(const SparsePolyRing& P, ConstRefPPMonoidElem pp)
96   { return monomial(P, one(CoeffRing(P)), pp); }
97 
monomial(const SparsePolyRing & P,const MachineInt & n,ConstRefPPMonoidElem pp)98   RingElem monomial(const SparsePolyRing& P, const MachineInt& n, ConstRefPPMonoidElem pp)
99   { return monomial(P, RingElem(CoeffRing(P), n), pp); }
100 
monomial(const SparsePolyRing & P,const BigInt & N,ConstRefPPMonoidElem pp)101   RingElem monomial(const SparsePolyRing& P, const BigInt& N, ConstRefPPMonoidElem pp)
102   { return monomial(P, RingElem(CoeffRing(P), N), pp); }
103 
monomial(const SparsePolyRing & P,const BigRat & Q,ConstRefPPMonoidElem pp)104   RingElem monomial(const SparsePolyRing& P, const BigRat& Q, ConstRefPPMonoidElem pp)
105   { return monomial(P, RingElem(CoeffRing(P), Q), pp); }
106 
monomial(const SparsePolyRing & P,ConstRefRingElem c,const std::vector<long> & expv)107   RingElem monomial(const SparsePolyRing& P, ConstRefRingElem c, const std::vector<long>& expv)
108   { return monomial(P, c, PPMonoidElem(PPM(P), expv)); }
109 
monomial(const SparsePolyRing & P,const std::vector<long> & expv)110   RingElem monomial(const SparsePolyRing& P, const std::vector<long>& expv)
111   { return monomial(P, PPMonoidElem(PPM(P), expv)); }
112 
monomial(const SparsePolyRing & P,const MachineInt & n,const std::vector<long> & expv)113   RingElem monomial(const SparsePolyRing& P, const MachineInt& n, const std::vector<long>& expv)
114   { return monomial(P, RingElem(CoeffRing(P), n), PPMonoidElem(PPM(P), expv)); }
115 
monomial(const SparsePolyRing & P,const BigInt & N,const std::vector<long> & expv)116   RingElem monomial(const SparsePolyRing& P, const BigInt& N, const std::vector<long>& expv)
117   { return monomial(P, RingElem(CoeffRing(P), N), PPMonoidElem(PPM(P), expv)); }
118 
monomial(const SparsePolyRing & P,const BigRat & Q,const std::vector<long> & expv)119   RingElem monomial(const SparsePolyRing& P, const BigRat& Q, const std::vector<long>& expv)
120   { return monomial(P, RingElem(CoeffRing(P), Q), PPMonoidElem(PPM(P), expv)); }
121 
122 
mySymbolValue(const symbol & s)123   RingElem SparsePolyRingBase::mySymbolValue(const symbol& s) const
124   {
125     std::vector<symbol> syms = symbols(myPPM());
126     syms.push_back(s);
127     if (!AreDistinct(syms))
128       return myMonomial(raw(one(myCoeffRing())), raw(myPPM()->mySymbolValue(s)));
129     if (AreArityConsistent(syms))
130       return myCoeffEmbeddingHomCtor()(myCoeffRing()->mySymbolValue(s));
131     CoCoA_THROW_ERROR(ERR::BadIndetNames, "SparsePolyRingBase::mySymbolValue");
132     return myZero(); // just to keep the compiler quiet
133   }
134 
135 
136   //---- RandomLinearForm ---------------------
137   namespace  { // anonymous
138 
RandomLinearForm(const ring & P,long lo,long hi)139     RingElem RandomLinearForm(const ring& P, long lo, long hi)
140     {
141       if (!IsSparsePolyRing(P))
142         CoCoA_THROW_ERROR(ERR::NotSparsePolyRing,"RandomLinearForm");
143       if (lo >= hi)
144         CoCoA_THROW_ERROR("Bad range lo-hi","RandomLinearForm");
145       const long nvars = NumIndets(P);
146       while (true)
147       {
148         RingElem L(P); // SLUG:  better to use a geobucket???  Or PushFront???
149         for (long i=nvars-1; i >=0; --i) L += RandomLong(lo,hi)*indet(P,i);
150         if (!IsZero(L)) return L;
151       }
152       return zero(P); // just to keep the compiler quiet
153     }
154 
155   } // anonymous namespace -- end
156 
157 
RandomLinearForm(const ring & P)158   RingElem RandomLinearForm(const ring& P)
159   {
160     const char* fn("RandomLinearForm(P)");
161     if (!IsSparsePolyRing(P))  CoCoA_THROW_ERROR(ERR::NotSparsePolyRing, fn);
162     if (!IsRingFp(CoeffRing(P)))
163       CoCoA_THROW_ERROR("only for coefficient ring = Fp", fn);
164     return RandomLinearForm(P, 1, ConvertTo<long>(characteristic(P)));
165   }
166 
167 
RandomLinearForm(const ring & P,long N)168   RingElem RandomLinearForm(const ring& P, long N)
169   { return RandomLinearForm(P, -N, N); }
170 
171 
172   //-----------------------------------------------------------------
173   namespace // for functions local to this file/compilation unit.
174   {
CheckCompatible(ConstRefRingElem x,ConstRefRingElem y,const char * const FnName)175     inline void CheckCompatible(ConstRefRingElem x, ConstRefRingElem y, const char* const FnName)
176     {
177       if (owner(x) != owner(y))  CoCoA_THROW_ERROR(ERR::MixedRings, FnName);
178     }
179 
CheckElemSparsePolyRing(ConstRefRingElem f,const char * const FnName)180     inline void CheckElemSparsePolyRing(ConstRefRingElem f, const char* const FnName)
181     {
182       if (!IsSparsePolyRing(owner(f))) CoCoA_THROW_ERROR(ERR::NotElemSparsePolyRing, FnName);
183     }
184 
CheckCoeffExpv(const SparsePolyRing & P,ConstRefRingElem c,const std::vector<long> & expv,const char * const FnName)185     void CheckCoeffExpv(const SparsePolyRing& P,
186                         ConstRefRingElem c, const std::vector<long>& expv,
187                         const char* const FnName)
188     {
189       if (CoeffRing(P) != owner(c))    CoCoA_THROW_ERROR(ERR::MixedCoeffRings, FnName);
190       if (NumIndets(P) != len(expv))   CoCoA_THROW_ERROR(ERR::BadArraySize, FnName);
191     }
192 
CheckCoeffPP(const SparsePolyRing & P,ConstRefRingElem c,ConstRefPPMonoidElem pp,const char * const FnName)193     void CheckCoeffPP(const SparsePolyRing& P,
194                       ConstRefRingElem c, ConstRefPPMonoidElem pp,
195                       const char* const FnName)
196     {
197       if (CoeffRing(P) != owner(c))    CoCoA_THROW_ERROR(ERR::MixedCoeffRings, FnName);
198       if (PPM(P) != owner(pp))         CoCoA_THROW_ERROR(ERR::MixedPPMs, FnName);
199     }
200   }
201 
202 
PushFront(RingElem & f,ConstRefRingElem c,const std::vector<long> & expv)203   RingElem& PushFront(RingElem& f, ConstRefRingElem c, const std::vector<long>& expv) /// SHOULD BE vector<BigInt> ????
204   {
205     CheckElemSparsePolyRing(f, "PushFront(f, c, expv)");
206     const SparsePolyRing Rx = owner(f);
207     CheckCoeffExpv(Rx, c, expv, "PushFront(f, c, expv)");
208     PPMonoidElem pp(PPM(Rx), expv);
209     if (!IsZero(f) && pp <= LPP(f))
210       CoCoA_THROW_ERROR(ERR::PPOrder, "PushFront(f, c, expv)");
211     Rx->myPushFront(raw(f), raw(c), raw(pp)); // OK 'cos makes a copy of raw(pp)
212     return f;
213   }
214 
215 
PushFront(RingElem & f,ConstRefRingElem c,ConstRefPPMonoidElem pp)216   RingElem& PushFront(RingElem& f, ConstRefRingElem c, ConstRefPPMonoidElem pp)
217   {
218     CheckElemSparsePolyRing(f, "PushFront(f, c, pp)");
219     const SparsePolyRing Rx = owner(f);
220     CheckCoeffPP(Rx, c, pp, "PushFront(f, c, pp)");
221     if (!IsZero(f) && pp <= LPP(f)) CoCoA_THROW_ERROR(ERR::PPOrder, "PushFront(f, c, pp)");
222     Rx->myPushFront(raw(f), raw(c), raw(pp));
223     return f;
224   }
225 
226 
PushBack(RingElem & f,ConstRefRingElem c,const std::vector<long> & expv)227   RingElem& PushBack(RingElem& f, ConstRefRingElem c, const std::vector<long>& expv) /// SHOULD BE vector<BigInt> ????
228   {
229     CheckElemSparsePolyRing(f, "PushBack(f, c, expv)");
230     const SparsePolyRing Rx = owner(f);
231     CheckCoeffExpv(Rx, c, expv, "PushBack(f, c, expv)");
232     PPMonoidElem pp(PPM(Rx), expv);
233     Rx->myPushBack(raw(f), raw(c), raw(pp)); // OK 'cos makes a copy of raw(pp)
234     return f;
235   }
236 
237 
PushBack(RingElem & f,ConstRefRingElem c,ConstRefPPMonoidElem pp)238   RingElem& PushBack(RingElem& f, ConstRefRingElem c, ConstRefPPMonoidElem pp)
239   {
240     CheckElemSparsePolyRing(f, "PushBack(f, c, pp)");
241     const SparsePolyRing Rx = owner(f);
242     CheckCoeffPP(Rx, c, pp, "PushBack(f, c, pp)");
243     Rx->myPushBack(raw(f), raw(c), raw(pp));
244     return f;
245   }
246 
247 
ClearDenom(const SparsePolyRing & ZZx,const RingElem & f)248   RingElem ClearDenom(const SparsePolyRing& ZZx, const RingElem& f)
249   {
250     const ring& P = owner(f);
251     if (!IsSparsePolyRing(P) ||
252         !IsFractionField(CoeffRing(P)) ||
253         BaseRing(CoeffRing(P)) != CoeffRing(ZZx) ||
254         PPM(P) != PPM(ZZx))
255       CoCoA_THROW_ERROR(ERR::BadArg, "ClearDenom(NewRing, f)");
256     const RingElem D = CommonDenom(f);
257     RingElem ans(ZZx);
258     for (SparsePolyIter it=BeginIter(f); !IsEnded(it); ++it)
259       PushBack(ans, num(coeff(it))*(D/den(coeff(it))), PP(it));
260     return ans;
261   }
262 
263 
264 
265   /*----------------------------------------------------------------------
266     Member functions every concrete SparsePolyRing implementation
267     must have in addition to those of PolyRingBase.
268     ----------------------------------------------------------------------*/
269 
myIsValid(ConstRawPtr rawf)270   bool SparsePolyRingBase::myIsValid(ConstRawPtr rawf) const
271   {
272     if (myIsZero(rawf)) return true;
273     SparsePolyIter itf = myBeginIter(rawf);
274     if (IsZero(coeff(itf))) return false;
275     PPMonoidElem PrevPP = PP(itf);
276     for (++itf; !IsEnded(itf); ++itf)
277     {
278       if (IsZero(coeff(itf))) return false;
279       if (PrevPP <= PP(itf)) return false;
280       PrevPP = PP(itf);
281     }
282     return true;
283   }
284 
285 
286   // ANNA: add check if ordering is StdDeg compatible and return StdDeg(LPP)
myStdDeg(ConstRawPtr rawf)287   long SparsePolyRingBase::myStdDeg(ConstRawPtr rawf) const
288   {
289     if (myIsZero(rawf)) CoCoA_THROW_ERROR(ERR::ZeroRingElem, "myStdDeg(rawf)");
290     long PolyDegree = 0;
291     for (SparsePolyIter i=myBeginIter(rawf); !IsEnded(i); ++i)
292       PolyDegree = max(PolyDegree, StdDeg(PP(i)));
293     return PolyDegree;
294   }
295 
296 
myDeg(ConstRawPtr rawf,long index)297   long SparsePolyRingBase::myDeg(ConstRawPtr rawf, long index) const
298   {
299     if (myIsZero(rawf)) CoCoA_THROW_ERROR(ERR::ZeroRingElem, "myDeg(rawf, index)");
300     long res = 0;
301     for (SparsePolyIter i=myBeginIter(rawf); !IsEnded(i); ++i)
302       res = max(res, exponent(PP(i), index));
303     return res;
304   }
305 
306 
myContent(RawPtr rawcontent,ConstRawPtr rawf)307   void SparsePolyRingBase::myContent(RawPtr rawcontent, ConstRawPtr rawf) const
308   {
309     const ring& R = myCoeffRing();
310     CoCoA_ASSERT(IsTrueGCDDomain(R));
311     // Compute answer in local var to avoid aliasing problems; also exception clean.
312     RingElem ans(R);
313     for (SparsePolyIter i=myBeginIter(rawf); !IsEnded(i); ++i)
314     {
315       R->myGcd(raw(ans), raw(ans), raw(coeff(i))); // ans = GCD(ans, coeff(i));
316       if (IsOne(ans)) break;
317     }
318     // Finally, swap answer into rawcontent.
319     R->mySwap(rawcontent, raw(ans));
320     return;
321   }
322 
myContentFrF(RawPtr rawcontent,ConstRawPtr rawf)323   void SparsePolyRingBase::myContentFrF(RawPtr rawcontent, ConstRawPtr rawf) const
324   {
325     CoCoA_ASSERT(IsFractionFieldOfGCDDomain(myCoeffRing()));
326     const FractionField R = myCoeffRing();
327     const ring& S = BaseRing(R);
328     RingElem N(S);
329     RingElem D(S,1);
330     for (SparsePolyIter i=myBeginIter(rawf); !IsEnded(i); ++i)
331     {
332       N = gcd(N, num(coeff(i)));
333       D = lcm(D, den(coeff(i)));
334 //      S->myGcd(raw(ans), raw(ans), raw(num(coeff(i)))); // ans = GCD(ans, num(coeff(i)));
335 //      if (IsOne(ans)) break;
336     }
337     RingHom phi = EmbeddingHom(R);
338     RingElem ans = phi(N)/phi(D);
339     // Finally, swap answer into rawcontent.
340     R->mySwap(rawcontent, raw(ans));
341   }
342 
343 
myCommonDenom(RawPtr rawcontent,ConstRawPtr rawf)344   void SparsePolyRingBase::myCommonDenom(RawPtr rawcontent, ConstRawPtr rawf) const
345   {
346     CoCoA_ASSERT(IsFractionFieldOfGCDDomain(myCoeffRing()));
347     const ring& R = BaseRing(myCoeffRing());
348     // Compute result in local "ans" to avoid aliasing problems; also exception clean.
349     RingElem ans = one(R);
350     for (SparsePolyIter it=myBeginIter(rawf); !IsEnded(it); ++it)
351     {
352       const RingElem D = den(coeff(it));
353       ans *= D/gcd(ans, D);
354     }
355     // Finally, swap answer into rawcontent -- cheaper than assignment.
356     R->mySwap(rawcontent, raw(ans));
357   }
358 
359 
myClearDenom(RawPtr rawg,ConstRawPtr rawf)360   void SparsePolyRingBase::myClearDenom(RawPtr rawg, ConstRawPtr rawf) const
361   {
362     CoCoA_ASSERT(IsFractionFieldOfGCDDomain(myCoeffRing()));
363     const ring& R = BaseRing(myCoeffRing());
364     RingElem c(R);
365     myCommonDenom(raw(c), rawf);
366     const RingElem coeff = EmbeddingHom(myCoeffRing())(c);
367     RingElem ans = RingElemAlias(ring(this),rawf);
368     myMulByCoeff(raw(ans), raw(coeff));
369     // Finally, swap answer into rawg -- cheaper than assignment.
370     mySwap(rawg, raw(ans));
371   }
372 
373 
myRemoveBigContent(RawPtr rawf)374   void SparsePolyRingBase::myRemoveBigContent(RawPtr rawf) const
375   {
376     CoCoA_ASSERT(IsTrueGCDDomain(myCoeffRing()));
377     CoCoA_ASSERT(!myIsZero(rawf));
378     RingElem cont(myCoeffRing());
379     myContent(raw(cont), rawf);
380     myDivByCoeff(rawf, raw(cont));
381   }
382 
383 
myMul(RawPtr rawlhs,ConstRawPtr rawf,ConstRawPtr rawg)384   void SparsePolyRingBase::myMul(RawPtr rawlhs, ConstRawPtr rawf, ConstRawPtr rawg) const
385   {
386     if (myIsZero(rawf) || myIsZero(rawg)) { myAssignZero(rawlhs); return; }
387     if (myIsConstant(rawf))
388     {
389       RingElem ans(RingElemAlias(ring(this), rawg));
390       myMulByCoeff(raw(ans), raw(myLC(rawf)));  // weak exc guarantee, but not a problem here
391       mySwap(rawlhs, raw(ans));
392       return;
393     }
394     if (myIsConstant(rawg) && myCoeffRing()->IamCommutative())  // CoeffRing should be comm
395     {
396       myMul(rawlhs, rawg, rawf);
397       return;
398     }
399     const long gLen = myNumTerms(rawg);
400     if (IamCommutative() && myNumTerms(rawf) > gLen) { myMul(rawlhs, rawg, rawf); return; }
401     const SparsePolyRing P(this);
402     RingElemAlias g(P, rawg);
403 
404     if (myIsMonomial(rawf))
405     {
406       RingElem ans(P);
407       myAddMulLM(raw(ans), rawf, rawg);
408       mySwap(raw(ans), rawlhs);
409       return;
410     }
411     geobucket gbk(P);
412     for (SparsePolyIter itf=myBeginIter(rawf); !IsEnded(itf); ++itf)
413       gbk.myAddMulLM(monomial(P, coeff(itf), PP(itf)), g, gLen);
414     RingElem ans(P);
415     AddClear(ans, gbk); // this is for exception safety
416     mySwap(raw(ans), rawlhs); // really an assignment
417   }
418 
419 
myIsDivisible(RawPtr rawlhs,ConstRawPtr rawx,ConstRawPtr rawy)420   bool SparsePolyRingBase::myIsDivisible(RawPtr rawlhs, ConstRawPtr rawx, ConstRawPtr rawy) const
421   {
422     if (myIsZero(rawy)) { return false; }
423     if (myIsZero(rawx)) { myAssignZero(rawlhs); return true; }
424     if (myIsConstant(rawy))
425     {
426       RingElem ans(RingElemAlias(ring(this), rawx));
427       if (!myDivByCoeff(raw(ans), raw(myLC(rawy)))) // exc safe?
428         return false;
429       mySwap(rawlhs, raw(ans));
430       return true;
431     }
432     if (!IamCommutative()) { CoCoA_THROW_ERROR(ERR::NYI, "SparsePolyRingBase::myDiv non commutative"); }
433     const SparsePolyRing P(this);
434     RingElem xCopy(RingElemAlias(P, rawx));
435     geobucket gbk(P);
436     gbk.myAddClear(xCopy, NumTerms(xCopy));
437     RingElemAlias y(P, rawy);
438     const long yLen = NumTerms(y);
439     RingElem ans(P);
440     const ring& R = myCoeffRing();
441     RingElem coeff(R);
442     // if not divisible will throw ERR::BadQuot
443     while ( !IsZero(gbk) )
444     {
445       if (!R->myIsDivisible(raw(coeff), raw(LC(gbk)), raw(myLC(rawy)))) return false;
446       if (!IsDivisible(LPP(gbk), LPP(y))) return false;
447       RingElem m(monomial(P, coeff, LPP(gbk)/LPP(y)));
448       // xCopy -= m*y;
449       gbk.myAddMulLM(-m, y, yLen);
450       // ans += m;
451       myAppendClear(raw(ans), raw(m));
452     }
453     mySwap(raw(ans), rawlhs); // really an assignment
454     return true;
455   }
456 
457 
myDeriv(RawPtr rawlhs,ConstRawPtr rawf,ConstRawPtr rawx)458   void SparsePolyRingBase::myDeriv(RawPtr rawlhs, ConstRawPtr rawf, ConstRawPtr rawx) const
459   {
460     if (myIsOne(rawx)) { myAssign(rawlhs, rawf); return; }
461     const long n = myNumIndets();
462     ring R = myCoeffRing();
463     const SparsePolyRing P(this);
464     vector<long> expv(n);
465     exponents(expv, myLPP(rawx));
466 
467     RingElem ans(P);
468     for (SparsePolyIter itf=myBeginIter(rawf); !IsEnded(itf); ++itf)
469     {
470       BigInt scale(1);
471       for (long indet=0; indet < n; ++indet)
472         if (expv[indet] != 0)
473         {
474           const long d = exponent(PP(itf), indet);
475           if (d < expv[indet]) { scale = 0; break; }
476           scale *= RangeFactorial(d-expv[indet]+1, d);
477         }
478       if (IsZero(scale)) continue;
479       RingElem m(monomial(P, scale*coeff(itf), PP(itf)/myLPP(rawx)));
480       if (!IsZero(m)) myAppendClear(raw(ans), raw(m));
481     }
482     mySwap(raw(ans), rawlhs); // really an assignment
483   }
484 
485 
myOutput(std::ostream & out,ConstRawPtr rawf)486   void SparsePolyRingBase::myOutput(std::ostream& out, ConstRawPtr rawf) const
487   {
488     if (!out) return;  // short-cut for bad ostreams
489 
490     if (myIsZero(rawf)) { out << '0'; return; }
491 
492     const ring& R = myCoeffRing();
493     const PPMonoid PPM = myPPM();
494     const bool IsQuotientOfZZ = IsQuotientRing(R) && IsZZ(BaseRing(R));
495     //const bool IsQuotientOfZ = IsRingFp(R);
496     const bool IsNumberRing = IsZZ(R) || IsQuotientOfZZ || IsRingTwinFloat(R); // || IsQQ(R)
497 
498     bool IsFirstCoeff = true;
499     for (SparsePolyIter itf=myBeginIter(rawf); !IsEnded(itf) ; ++itf, IsFirstCoeff = false)
500     {
501       bool PrintStar = true;
502       RingElemAlias c = coeff(itf);
503       ConstRefPPMonoidElem pp = PP(itf);
504       const bool IsWithMinus = R->myIsPrintedWithMinus(raw(c));
505 
506       // ---- coefficient ----
507       if (!IsFirstCoeff)
508       {
509         out << ' ';
510         if (!IsWithMinus)  out << '+';
511       }
512       if (IsOne(pp)) { out << c; continue; }
513       // ---------- PP != 1 ----------
514       if (IsOne(c))
515       { // Do not print "1 * "...
516         PrintStar = false;
517         goto PrintPP;
518       }
519       if ( IsWithMinus && IsMinusOne(c) ) // in some Z/(n) "-1" prints as n-1
520       { // Do not print "-1 * "...
521         out << '-';
522         PrintStar = false;
523         goto PrintPP;
524       }
525       // General case: coeff is neither +1 nor -1
526       if (IsNumberRing || R->myIsPrintAtom(raw(c)) ||
527           (IsWithMinus && R->myIsPrintAtom(raw(-c))) )
528       {
529         out << c;
530         goto PrintPP;
531       }
532       if (!IsFirstCoeff && IsWithMinus) out << '+'; // not printed before
533       out << '(' << c << ')';
534 
535     PrintPP:      // ---- PP ----
536       if (PrintStar)  out << '*';
537       out << pp;
538     }
539   }
540 
541 
myIsPrintAtom(ConstRawPtr rawx)542   bool SparsePolyRingBase::myIsPrintAtom(ConstRawPtr rawx) const
543   {
544     long NoUse;
545     if (myIsIndet(NoUse, rawx)) return true;
546     if (!myIsConstant(rawx)) return false;
547     return myCoeffRing()->myIsPrintAtom(raw(myLC(rawx)));
548   }
549 
550 
myIsPrintedWithMinus(ConstRawPtr rawx)551   bool SparsePolyRingBase::myIsPrintedWithMinus(ConstRawPtr rawx) const
552   {
553     //    if (IsMinusOne(myLC(rawx)) && IsIndet(myLPP(rawx))) return true;
554     //    if (!myIsConstant(rawx)) return false;
555     return myCoeffRing()->myIsPrintedWithMinus(raw(myLC(rawx)));
556   }
557 
558 
myOutput(OpenMathOutput & OMOut,ConstRawPtr rawx)559   void SparsePolyRingBase::myOutput(OpenMathOutput& OMOut, ConstRawPtr rawx) const
560   {
561     OMOut->mySendApplyStart();
562     OMOut << OpenMathSymbol("cocoa", "DMPSummands");
563     OMOut << myNumTerms(rawx);
564 
565     for (SparsePolyIter itf=myBeginIter(rawx); !IsEnded(itf) ; ++itf)
566     {
567       OMOut << coeff(itf);
568       OMOut << PP(itf);
569 //      R->myOutput(OMOut, it->myCoeff);
570 //      ordering(PPM)->myOutput(OMOut, it->myOrdv);
571     }
572     OMOut->mySendApplyEnd();
573   }
574 
575 
myIsOne(ConstRawPtr rawf)576   bool SparsePolyRingBase::myIsOne(ConstRawPtr rawf) const
577   {
578     if (!myIsMonomial(rawf)) return false;
579     if (!IsOne(myLPP(rawf))) return false;
580     return IsOne(myLC(rawf));
581   }
582 
583 
myIsMinusOne(ConstRawPtr rawf)584   bool SparsePolyRingBase::myIsMinusOne(ConstRawPtr rawf) const
585   {
586     if (!myIsMonomial(rawf)) return false;
587     if (!IsOne(myLPP(rawf))) return false;
588     return IsMinusOne(myLC(rawf));
589   }
590 
591 
myIsConstant(ConstRawPtr rawf)592   bool SparsePolyRingBase::myIsConstant(ConstRawPtr rawf) const
593   {
594     if (myIsZero(rawf)) return true;
595     if (!myIsMonomial(rawf)) return false;
596     return (IsOne(myLPP(rawf)));
597   }
598 
599 
myIsIndet(long & IndetIndex,ConstRawPtr rawf)600   bool SparsePolyRingBase::myIsIndet(long& IndetIndex, ConstRawPtr rawf) const
601   {
602     if (!myIsMonomial(rawf)) return false;
603     if (!IsOne(myLC(rawf))) return false;
604     return IsIndet(IndetIndex, myLPP(rawf));
605   }
606 
607 
myIsIndetPosPower(long & index,BigInt & EXP,ConstRawPtr rawf)608   bool SparsePolyRingBase::myIsIndetPosPower(long& index, BigInt& EXP, ConstRawPtr rawf) const
609   {
610     if (!myIsMonomial(rawf)) return false;
611     if (!IsOne(myLC(rawf))) return false;
612     return IsIndetPosPower(index, EXP, myLPP(rawf));
613   }
614 
615 
myIsIndetPosPower(ConstRawPtr rawf)616   bool SparsePolyRingBase::myIsIndetPosPower(ConstRawPtr rawf) const
617   {
618     if (!myIsMonomial(rawf)) return false;
619     if (!IsOne(myLC(rawf))) return false;
620     return IsIndetPosPower(myLPP(rawf));
621   }
622 
623 
myIsEvenPoly(ConstRawPtr rawf)624   bool SparsePolyRingBase::myIsEvenPoly(ConstRawPtr rawf) const
625   {
626     if (myIsZero(rawf)) { return true; }
627     for (SparsePolyIter itf=myBeginIter(rawf); !IsEnded(itf); ++itf)
628     {
629       if ((deg(PP(itf))&1) != 0) return false;
630     }
631     return true;
632   }
633 
myIsOddPoly(ConstRawPtr rawf)634   bool SparsePolyRingBase::myIsOddPoly(ConstRawPtr rawf) const
635   {
636     if (myIsZero(rawf)) { return true; }
637     for (SparsePolyIter itf=myBeginIter(rawf); !IsEnded(itf); ++itf)
638     {
639       if ((deg(PP(itf))&1) != 1) return false;
640     }
641     return true;
642   }
643 
644 
myIsInvertible(ConstRawPtr rawx)645   bool SparsePolyRingBase::myIsInvertible(ConstRawPtr rawx) const
646   {
647     return !myIsZero(rawx) && myIsConstant(rawx) && IsInvertible(myLC(rawx));
648   }
649 
650 
myIsZeroDivisor(ConstRawPtr rawx)651   bool SparsePolyRingBase::myIsZeroDivisor(ConstRawPtr rawx) const
652   {
653     if (myIsZero(rawx)) return true;
654     if (IsTrue3(IamIntegralDomain3(true/*quick*/))) return false;
655     if (myIsConstant(rawx)) return myCoeffRing()->myIsZeroDivisor(raw(myLC(rawx)));
656     if (!myCoeffRing()->myIsZeroDivisor(raw(myLC(rawx)))) return false; // LC is not zd
657     CoCoA_THROW_ERROR(ERR::NYI, "SparsePolyRingBase::myIsZeroDivisor when coeffs are not int dom,and LC(poly) is zd ");
658     return true; // just to kep compiler quiet
659   }
660 
661 
662   // code for R[x,y]: compute gcd in FractionField(R)[x,y]
myGcd(RawPtr rawlhs,ConstRawPtr rawf,ConstRawPtr rawg)663   void SparsePolyRingBase::myGcd(RawPtr rawlhs, ConstRawPtr rawf, ConstRawPtr rawg) const
664   {
665     CoCoA_ASSERT(myNumIndets() != 0);
666     if ( myIsInvertible(rawf) || myIsInvertible(rawg) ) { myAssign(rawlhs, raw(myOne())); return; }
667     if ( myIsZero(rawf) ) { myAssign(rawlhs, rawg); return; }
668     if ( myIsZero(rawg) ) { myAssign(rawlhs, rawf); return; }
669     const SparsePolyRing P(this);
670     RingElemAlias f(P, rawf);
671     RingElemAlias g(P, rawg);
672     if (IsZZ(myCoeffRing()) || IsQQ(myCoeffRing()))
673     {
674       RingElem ans = GCD_DMPZ(f, g);
675       mySwap(rawlhs, raw(ans));
676       return;
677     }
678     if (!IsField(myCoeffRing()))
679     {
680       if (!IsTrueGCDDomain(myCoeffRing()))
681         CoCoA_THROW_ERROR("NYI gcd of poly with coeffs not in field or TrueGCDDomain", "myGcd");
682       else
683       {
684         FractionField K = NewFractionField(myCoeffRing());
685         SparsePolyRing Kx = NewPolyRing_DMP(K, PPM(P));
686         RingHom phi = PolyRingHom(P, Kx, CoeffEmbeddingHom(Kx)(EmbeddingHom(K)), indets(Kx));
687         RingElem h = gcd(phi(f), phi(g));
688         Kx->myClearDenom(raw(h), raw(h));
689         RingElem ans(P);
690         for (SparsePolyIter it=BeginIter(h) ; !IsEnded(it) ; ++it )
691           ans += monomial(P, num(coeff(it)), PP(it));
692         myDivByCoeff(raw(ans), raw(content(ans)));
693         myMulByCoeff(raw(ans), raw(gcd(content(f), content(g))));
694         P->mySwap(rawlhs, raw(ans));
695         return;
696       }
697     }
698     // From here onwards myCoeffRing is a *FIELD*
699     if (myNumIndets() == 1)
700     {
701       if (IsRingFp(myCoeffRing()))
702       {
703         SmallFpImpl ModP(ConvertTo<long>(characteristic(myCoeffRing())));
704         RingElem ans = ConvertFromDUPFp(myIndets()[0], gcd(ConvertToDUPFp(ModP, f), ConvertToDUPFp(ModP, g)));
705 //        RingElem ans = GCD_DUPFF(f,g);
706         P->mySwap(rawlhs, raw(ans));
707         return;
708       }
709       // Univariate, coeffs in a field but not a small finite field ==> compute G-basis of principal ideal
710       const ideal I = ideal(f, g);
711       const vector<RingElem>& GB = GBasis(I);
712       CoCoA_ASSERT(len(GB) == 1);
713 //      if (len(GB) != 1)
714 //        CoCoA_THROW_ERROR("Unable to compute GCD", "SparsePolyRingBase::gcd");
715       myAssign(rawlhs, raw(GB[0]));
716       return;
717     }
718     // Multivariate polynomial ==> use syzygy method.
719     const vector<ModuleElem> v = gens(SyzOfGens(ideal(f,g)));
720     if ( len(v) != 1 )
721       CoCoA_THROW_ERROR("Unable to compute GCD", "SparsePolyRingBase::gcd");
722     RingElem ans = f/((v[0])[1]);
723 //    if (IsInvertible(ans))  myAssign(rawlhs, raw(myOne()));
724 //    else P->mySwap(rawlhs, raw(ans));  // exception safe
725     ans = monic(ans);
726     P->mySwap(rawlhs, raw(ans));  // exception safe
727   }
728 
729 
myNormalizeFracNoGcd(RawPtr rawnum,RawPtr rawden)730   void SparsePolyRingBase::myNormalizeFracNoGcd(RawPtr rawnum, RawPtr rawden) const
731   {
732     CoCoA_ASSERT(!myIsZero(rawden));
733     // Handle case of 0 specially; later code fails otherwise.
734     if (myIsZero(rawnum)) { myAssign(rawden, 1); return; }
735 
736     const ring& k = myCoeffRing();
737     if (IsTrueGCDDomain(k))
738     {
739       // Coeff ring is a (true) GCD domain
740       if (!IsOrderedDomain(k)) return;
741       if (myLC(rawden) > 0) return;
742       myNegate(rawnum, rawnum);
743       myNegate(rawden, rawden);
744       return;
745     }
746     if (IsFractionFieldOfGCDDomain(k))
747     {
748       // Coeff ring is a FractionField
749       const ring R = BaseRing(k);
750       const RingHom embed = EmbeddingHom(k);
751       RingElemAlias N(ring(this), rawnum);
752       RingElemAlias D(ring(this), rawden);
753 
754       const RingElem ContN = content(N);
755       const RingElem ContD = content(D);
756       const RingElem ContQuot = ContN/ContD;
757       myMulByCoeff(rawnum, raw(embed(num(ContQuot))/ContN));
758       myMulByCoeff(rawden, raw(embed(den(ContQuot))/ContD));
759 
760 //       RingElem scale(k);
761 //       scale = embed(lcm(CommonDenom(N), CommonDenom(D)));
762 //       if (!IsOne(scale))
763 //       {
764 //         myMulByCoeff(rawnum, raw(scale));
765 //         myMulByCoeff(rawden, raw(scale));
766 //       }
767 //       scale = embed(gcd(content(N), content(D)));
768 //       if (!IsOne(scale))
769 //       {
770 //         myDivByCoeff(rawnum, raw(scale));
771 //         myDivByCoeff(rawden, raw(scale));
772 //       }
773       if (!IsOrderedDomain(k)) return;
774       if (myLC(rawden) > 0) return;
775       myNegate(rawnum, rawnum);
776       myNegate(rawden, rawden);
777       return;
778     }
779     // This must come after the case handling FractionFields!
780     if (IsField(k))
781     {
782       if (IsOne(myLC(rawden))) return;
783       myDivByCoeff(rawnum, raw(myLC(rawden))); // exc safe?
784       myDivByCoeff(rawden, raw(myLC(rawden)));
785       return;
786     }
787     if (IsOrderedDomain(k))
788     {
789       if (myLC(rawden) > 0) return;
790       myNegate(rawnum, rawnum);
791       myNegate(rawden, rawden);
792       return;
793     }
794     CoCoA_THROW_ERROR(ERR::NYI, "SparsePolyRing::myNormalizeFracNoGcd");
795   }
796 
797 
myIsInteger(BigInt & N,ConstRawPtr rawx)798   bool SparsePolyRingBase::myIsInteger(BigInt& N, ConstRawPtr rawx) const
799   {
800     if (myIsZero(rawx)) { N = 0; return true; }
801     if (myNumTerms(rawx) > 1) return false;
802     if (!IsOne(myLPP(rawx))) return false;
803     return IsInteger(N, myLC(rawx));
804   }
805 
806 
myIsRational(BigRat & Q,ConstRawPtr rawx)807   bool SparsePolyRingBase::myIsRational(BigRat& Q, ConstRawPtr rawx) const
808   {
809     if (myIsZero(rawx)) { Q = 0; return true; }
810     if (myNumTerms(rawx) > 1) return false;
811     if (!IsOne(myLPP(rawx))) return false;
812     return IsRational(Q, myLC(rawx));
813   }
814 
815 
mySquare(RawPtr rawlhs,ConstRawPtr rawx)816   void SparsePolyRingBase::mySquare(RawPtr rawlhs, ConstRawPtr rawx) const
817   {
818     if (myIsZero(rawx)) { myAssignZero(rawlhs); return; }
819     PowerOverflowCheck(myLPP(rawx),2); // check whether LPP(f)^2 would overflow expv; if not, assume no expv-overflow will occur
820     if (IamCommutative())
821     {
822       const long NT = myNumTerms(rawx);
823       if (NT > 4)
824       {
825         RingElem f(ring(this));
826         RingElem g(ring(this));
827         long count=0;
828         for (SparsePolyIter it=myBeginIter(rawx) ; !IsEnded(it) ; ++it )
829         {
830           if (++count < NT/2) myPushBack(raw(f), raw(coeff(it)), raw(PP(it)));
831           else myPushBack(raw(g), raw(coeff(it)), raw(PP(it)));
832         }
833         RingElem h = power(f, 2) + power(g, 2) + 2*f*g;
834         mySwap(rawlhs, raw(h));
835         return;
836       }
837     }
838     mySequentialPower(rawlhs, rawx, 2); //??? BUG/SLUG myBinaryPower better if univariate or coeffs are finite field
839   }
840 
841 
myPowerSmallExp(RawPtr rawlhs,ConstRawPtr rawx,long n)842   void SparsePolyRingBase::myPowerSmallExp(RawPtr rawlhs, ConstRawPtr rawx, long n) const
843   {
844     // Assert that we have a genuinely non-trivial case.
845     CoCoA_ASSERT(n > 1);
846     CoCoA_ASSERT(!myIsZero(rawx) && !myIsOne(rawx) && !myIsMinusOne(rawx));
847 
848     PowerOverflowCheck(myLPP(rawx),n); // check whether LPP(f)^n would overflow expv; if not, assume no expv-overflow will occur
849     // anna 3 apr 2008: under testing
850     if (myIsMonomial(rawx) && IamCommutative())
851     {
852       RingElem m = monomial(SparsePolyRing(this), power(myLC(rawx), n), power(myLPP(rawx), n));
853       mySwap(rawlhs, raw(m));
854       return;
855     }
856     if (myIsMonomial(rawx)) { myBinaryPower(rawlhs, rawx, n); return; }
857     if (n==2)
858     {
859       mySquare(rawlhs, rawx);
860       return;
861     }
862     mySequentialPower(rawlhs, rawx, n); //??? BUG/SLUG myBinaryPower better if univariate or coeffs are finite field
863   }
864 
865 
866   //---- Special functions on RingElem owned by SparsePolyRing
867 
UnivariateIndetIndex(ConstRefRingElem f)868   long UnivariateIndetIndex(ConstRefRingElem f)  // returns < 0 if not univariate
869   {
870     const SparsePolyRing P = owner(f);
871     const long nvars = NumIndets(P);
872     if (IsZero(f) || StdDeg(f) == 0 || nvars == 0) return 0; // f is constant, or there is only 1 indet.
873     vector<long> expv(nvars);
874     exponents(expv, LPP(f));
875     long ans = 0;
876     while (expv[ans] == 0) ++ans;
877     for (SparsePolyIter it=BeginIter(f); !IsEnded(it); ++it)
878     {
879       exponents(expv, PP(it));
880       for (int i=0; i < nvars; ++i)
881         if (i != ans && expv[i] != 0) return -1;
882     }
883     return ans;
884   }
885 
886 
IndetsProd(ConstRefRingElem f)887   RingElem IndetsProd(ConstRefRingElem f) // (monomial) prod of indets appearing in f
888   {
889     const ring& P = owner(f);
890     if (!IsSparsePolyRing(P)) CoCoA_THROW_ERROR(ERR::NotSparsePolyRing, "IndetsProd");
891     const vector<long> VarIndices = IndetsIn(f);
892     RingElem ans = one(P);
893     for (auto k: VarIndices)
894       ans *= indet(P,k);
895     return ans;
896   }
897 
898 
IndetsProd(const std::vector<RingElem> & L)899   RingElem IndetsProd(const std::vector<RingElem>& L)
900   {
901     if (L.empty()) CoCoA_THROW_ERROR(ERR::Empty, "IndetsProd");
902     const ring& P = owner(L[0]);
903     if (!IsSparsePolyRing(P)) CoCoA_THROW_ERROR(ERR::NotSparsePolyRing, "IndetsProd");
904     const vector<long> VarIndices = IndetsIn(L);
905     RingElem ans = one(P);
906     for (auto k: VarIndices)
907       ans *= indet(P,k);
908     return ans;
909   }
910 
911 
IndetsIn(ConstRefRingElem f)912   std::vector<long> IndetsIn(ConstRefRingElem f)
913   {
914     if (!IsSparsePolyRing(owner(f))) CoCoA_THROW_ERROR(ERR::NotSparsePolyRing, "IndetsIn");
915     const int nvars = NumIndets(owner(f));
916 
917     // This approach is cleaner but about 10% slower :-(
918     // vector<bool> seen(nvars);
919     // const PPMonoid& M = PPM(owner(f));
920     // for (SparsePolyIter it=BeginIter(f); !IsEnded(it); ++it)
921     //   M->myIndetsIn(seen, raw(PP(it)));
922 
923     long NumIndetsSeen = 0;
924     vector<bool> seen(nvars);
925     vector<long> exps(nvars);
926     for (SparsePolyIter it=BeginIter(f); !IsEnded(it); ++it)
927     {
928       exponents(exps, PP(it));  // BUG BUG BUG will throw if exps are too big!!!
929       for (int i=0; i < nvars; ++i)
930       {
931         if (exps[i] == 0 || seen[i]) continue;
932         seen[i] = true;
933         ++NumIndetsSeen;
934       }
935       if (NumIndetsSeen == nvars) break;
936     }
937     // Now convert answer to "list of indices"
938     vector<long> ans;
939     for (int i=0; i < nvars; ++i)
940       if (seen[i]) ans.push_back(i);
941     return ans;
942   }
943 
944   // indices of indets appearing in (non-empty) L
IndetsIn(const std::vector<RingElem> & L)945   std::vector<long> IndetsIn(const std::vector<RingElem>& L)
946   {
947     if (L.empty()) CoCoA_THROW_ERROR(ERR::Empty, "IndetsIn");
948     if (!IsSparsePolyRing(owner(L[0]))) CoCoA_THROW_ERROR(ERR::NotSparsePolyRing, "IndetsIn");
949     const SparsePolyRing& P = owner(L[0]);
950     const int nvars = NumIndets(P);
951     const long n = len(L);
952     for (long i=1; i < n; ++i)
953       if (owner(L[i]) != P) CoCoA_THROW_ERROR(ERR::MixedRings, "IndetsIn");
954     vector<bool> IndetSeen(nvars);
955     int NumSeen = 0;
956     for (long i=0; i < n; ++i)
957     {
958       const vector<long> IndetsInThisPoly = IndetsIn(L[i]);
959       for (long xj: IndetsInThisPoly)
960       {
961         if (IndetSeen[xj]) continue;
962         IndetSeen[xj] = true;
963         if (++NumSeen == nvars) goto double_break;
964       }
965     }
966     double_break:
967     vector<long> ans; ans.reserve(nvars); // potentially wasteful reserve
968     for (int i=0; i < nvars; ++i)
969       if (IndetSeen[i]) ans.push_back(i);
970     return ans;
971   }
972 
973 
974   //----------------------------------------------------------------------
975   //??? the following functions to compute NR will be replaced by GBMill
976 
FindReducerIndex(ConstRefPPMonoidElem pp,const vector<RingElem> & v)977   int FindReducerIndex(ConstRefPPMonoidElem pp, const vector<RingElem>& v)
978   {
979     const long nelems = len(v);
980     for (long i=0; i < nelems; ++i)
981       if (IsDivisible(pp, LPP(v[i])))
982         return i;
983     return -1;
984   }
985 
986 
FindReducerIndex(const ReductionCog & F,const vector<RingElem> & v)987   inline int FindReducerIndex(const ReductionCog& F, const vector<RingElem>& v)
988   {
989     if ( IsActiveZero(F) ) return -1;
990     return FindReducerIndex(ActiveLPP(F), v);
991   }
992 
993 
ReduceActiveLM(ReductionCog & F,const vector<RingElem> & v)994   void ReduceActiveLM(ReductionCog& F, const vector<RingElem>& v)
995   {
996     int i;
997     while ( (i = FindReducerIndex(F, v) ) != -1)
998     {
999       CheckForInterrupt("ReduceActiveLM");
1000       F->myReduce(v[i]);
1001     }
1002   }
1003 
1004 
reduce(ReductionCog & F,const vector<RingElem> & v)1005   void reduce(ReductionCog& F, const vector<RingElem>& v)
1006   {
1007     ReduceActiveLM(F, v);
1008     while ( !IsActiveZero(F) )
1009     {
1010       F->myMoveToNextLM();
1011       ReduceActiveLM(F, v);
1012     }
1013   }
1014   //--------------------------------------------
1015 
NR(ConstRefRingElem f,const vector<RingElem> & v)1016   RingElem NR(ConstRefRingElem f, const vector<RingElem>& v)
1017   {
1018     if ( IsZero(f) ) return f;
1019     if ( v.empty() ) return f;
1020     RingElem ans(f);
1021     ReductionCog F = NewRedCogGeobucketField(owner(ans));
1022     F->myAssignReset(ans);
1023     reduce(F, v);
1024     F->myRelease(ans);
1025     return ans;
1026   }
1027 
1028 
1029   namespace // anonymous for file local defns
1030   {
1031     class ByDecreasingPP
1032     {
1033     public:
operator()1034       bool operator()(const PPMonoidElem& A, const PPMonoidElem& B) const
1035         {
1036           return A > B;
1037         }
1038     };
1039   }
1040 
1041   std::ostream& operator<<(std::ostream& out, const CoeffPP& term)
1042   {
1043     if (!out) return out;  // short-cut for bad ostreams
1044     out << "[coeff:=" << term.myCoeff << ", PP:=" << term.myPP << "]" ;
1045     return out;
1046   }
1047 
1048 
ConstantCoeff(ConstRefRingElem f)1049   RingElem ConstantCoeff(ConstRefRingElem f)
1050   {
1051     // SLUG??? simple impl via iterator; might be able to access last term directly via a pointer???
1052     const ring& P = owner(f);
1053     if (!IsSparsePolyRing(P))
1054       CoCoA_THROW_ERROR(ERR::NotSparsePolyRing, "ConstantCoeff(f)");
1055 //???    if (IsZero(f)) return zero(CoeffRing(P));
1056     for (SparsePolyIter it = BeginIter(f); !IsEnded(it); ++it)
1057     {
1058       if (IsOne(PP(it))) return coeff(it);
1059     }
1060     return zero(CoeffRing(P));
1061   }
1062 
1063 
CoefficientsWRT(ConstRefRingElem f,const std::vector<long> & indets)1064   std::vector<CoeffPP> CoefficientsWRT(ConstRefRingElem f, const std::vector<long>& indets)
1065   {
1066     if (!IsSparsePolyRing(owner(f)))
1067       CoCoA_THROW_ERROR(ERR::NotSparsePolyRing, "CoefficientsWRT(f,indets)");
1068     const SparsePolyRing P = owner(f);
1069     for (long i=0; i < len(indets); ++i)
1070       if (indets[i] < 0 || indets[i] >= NumIndets(P))
1071         CoCoA_THROW_ERROR(ERR::BadIndetIndex, "CoefficientsWRT(f,indets)");
1072 
1073     // Force the sorting criterion in the map
1074     typedef map<PPMonoidElem, RingElem, ByDecreasingPP> CoeffTable_t;
1075     CoeffTable_t CoeffTable;
1076     PPMonoidHom projection = RestrictionHom(PPM(P), indets);
1077     for (SparsePolyIter it = BeginIter(f); !IsEnded(it); ++it)
1078     {
1079       const PPMonoidElem t = projection(PP(it));
1080       CoeffTable_t::iterator pos = CoeffTable.find(t);
1081       if (pos == CoeffTable.end())
1082       {
1083         CoeffTable.insert(make_pair(t, monomial(P, coeff(it), PP(it)/t)));
1084         continue;
1085       }
1086       RingElem m = monomial(P, coeff(it), PP(it)/t);
1087       P->myMoveLMToBack(raw(pos->second), raw(m));
1088 //      pos->second += monomial(P, coeff(it), PP(it)/t);
1089     }
1090     vector<CoeffPP> ans; ans.reserve(len(CoeffTable));
1091     transform(CoeffTable.begin(), CoeffTable.end(), back_inserter(ans), CoeffPPCtor);
1092     // NOTE: CoeffTable will automatically be in decreasing order by PP!
1093     // (see 23.2.4/10 in C++11)
1094     return ans;
1095   }
1096 
1097 
CoefficientsWRT(ConstRefRingElem f,ConstRefRingElem x)1098   std::vector<CoeffPP> CoefficientsWRT(ConstRefRingElem f, ConstRefRingElem x)
1099   {
1100     if (owner(f) != owner(x)) CoCoA_THROW_ERROR(ERR::MixedRings, "CoefficientsWRT(f,x)");
1101     if (!IsSparsePolyRing(owner(f))) CoCoA_THROW_ERROR(ERR::NotSparsePolyRing, "CoefficientsWRT(f,x)");
1102     vector<long> indices(1);
1103     if (!IsIndet(indices[0], x))
1104       CoCoA_THROW_ERROR(ERR::NotIndet, "CoefficientsWRT(f, x)");
1105     return CoefficientsWRT(f, indices);
1106   }
1107 
1108 
CoeffVecWRT(ConstRefRingElem f,ConstRefRingElem x)1109   std::vector<RingElem> CoeffVecWRT(ConstRefRingElem f, ConstRefRingElem x)
1110   {
1111     if (owner(f) != owner(x)) CoCoA_THROW_ERROR(ERR::MixedRings, "CoeffVecWRT(f,x)");
1112     if (!IsSparsePolyRing(owner(f))) CoCoA_THROW_ERROR(ERR::NotSparsePolyRing, "CoeffVecWRT(f,x)");
1113     if (IsZero(f)) return vector<RingElem>();
1114     vector<long> indices(1);
1115     if (!IsIndet(indices[0], x))
1116       CoCoA_THROW_ERROR(ERR::NotIndet, "CoeffVecWRT(f, x)");
1117     vector<CoeffPP> CoeffList = CoefficientsWRT(f, indices);
1118     long Degf = 0;
1119     for (int i=0; i < len(CoeffList); ++i)
1120       Degf = max(Degf, StdDeg(CoeffList[i].myPP));
1121     vector<RingElem> ans(1+Degf, zero(owner(f)));
1122     for (vector<CoeffPP>::iterator it=CoeffList.begin(); it != CoeffList.end(); ++it)
1123       swap(ans[StdDeg(it->myPP)], it->myCoeff); // swap avoids a wasteful copy!
1124     return ans;
1125   }
1126 
1127 
CoeffVecWRTSupport(ConstRefRingElem f,ConstRefRingElem basis)1128   std::vector<RingElem> CoeffVecWRTSupport(ConstRefRingElem f, ConstRefRingElem basis)
1129   {
1130     const char* const FnName = "CoeffVecWRTSupport";
1131     const PolyRing& P = owner(f);
1132     if (P != owner(basis)) CoCoA_THROW_ERROR(ERR::MixedRings, FnName);
1133     const ring& K = CoeffRing(P);
1134 //???    if (!IsField(K)) CoCoA_THROW_ERROR(ERR::NotField, FnName);
1135 
1136     const long n = NumTerms(basis);
1137     vector<RingElem> C(n, zero(K));
1138     if (IsZero(f)) return C;
1139     SparsePolyIter itf = BeginIter(f);
1140     PPMonoidElem PPf = PP(itf);
1141 
1142     int i=n-1;
1143     for (SparsePolyIter it=BeginIter(basis); !IsEnded(it); ++it, --i)
1144     {
1145       if (PPf < PP(it)) continue;
1146       if (PPf != PP(it)) CoCoA_THROW_ERROR("not in span", FnName);
1147       C[i] = coeff(itf);
1148       ++itf;
1149       if (IsEnded(itf)) return C;
1150       PPf = PP(itf);
1151     }
1152     // Get here only if (!IsEnded(itf))
1153     CoCoA_THROW_ERROR("not in span", FnName);
1154     return C; // NEVER EXECUTED; just to keep compiler quiet
1155   }
1156 
1157 
ContentWRT(ConstRefRingElem f,const std::vector<long> & indets)1158   RingElem ContentWRT(ConstRefRingElem f, const std::vector<long>& indets)
1159   {
1160     if (!IsSparsePolyRing(owner(f)))
1161       CoCoA_THROW_ERROR(ERR::NotSparsePolyRing, "ContentWRT(f,indets)");
1162     const SparsePolyRing P = owner(f);
1163     for (long i=0; i < len(indets); ++i)
1164       if (indets[i] < 0 || indets[i] >= NumIndets(P))
1165         CoCoA_THROW_ERROR(ERR::BadIndetIndex, "ContentWRT(f,indets)");
1166 
1167     const vector<CoeffPP> CoeffList = CoefficientsWRT(f, indets);
1168     RingElem ans(P);
1169     const long n = len(CoeffList);
1170     for (long i=0; i < n; ++i)
1171       ans = gcd(ans, CoeffList[i].myCoeff);
1172     return ans;
1173   }
1174 
1175 
ContentWRT(ConstRefRingElem f,ConstRefRingElem x)1176   RingElem ContentWRT(ConstRefRingElem f, ConstRefRingElem x)
1177   {
1178     if (owner(f) != owner(x))
1179       CoCoA_THROW_ERROR(ERR::MixedRings, "ContentWRT(f,x)");
1180     vector<long> indices(1);
1181     if (!IsIndet(indices[0], x))
1182       CoCoA_THROW_ERROR(ERR::NotIndet, "ContentWRT(f,x)");
1183     return ContentWRT(f, indices);
1184   }
1185 
1186 
1187 namespace // anonymous for file local defns
1188 {
1189 
RatReconstruct(BigInt X,BigInt M)1190   BigRat RatReconstruct(BigInt X, BigInt M)
1191   {
1192     RatReconstructByContFrac reconstructor; // default ctor arg
1193     reconstructor.myAddInfo(X, M);
1194     if (!IsConvincing(reconstructor))
1195       CoCoA_THROW_ERROR(ERR::CannotReconstruct, "RatReconstruct");
1196     // std::cout << " rat = " << ReconstructedRat(reconstructor) << std::endl;
1197     return ReconstructedRat(reconstructor);
1198   }
1199 
1200 
AsINT(ConstRefRingElem c)1201   BigInt AsINT(ConstRefRingElem c)
1202   {
1203     BigInt i;
1204     if (!IsInteger(i, c)) CoCoA_THROW_ERROR("not integer", "AsINT");
1205     return i;
1206   }
1207 
1208 
CRT_modulus(BigInt M1,BigInt M2)1209   BigInt CRT_modulus(BigInt M1, BigInt M2)
1210   {
1211     CRTMill CRT;
1212     CRT.myAddInfo(BigInt(0), M1);
1213     CRT.myAddInfo(BigInt(0), M2);
1214     return CombinedModulus(CRT);
1215   }
1216 
CRT4(BigInt R1,BigInt M1,BigInt R2,BigInt M2)1217   BigInt CRT4(BigInt R1, BigInt M1, BigInt R2, BigInt M2)
1218   {
1219     CRTMill CRT;
1220     CRT.myAddInfo(R1, M1);
1221     CRT.myAddInfo(R2, M2);
1222     return CombinedResidue(CRT);
1223   }
1224 
1225 } // end of anonymous namespace
1226 
CRTPoly(RingElem & f,BigInt & M,ConstRefRingElem f1,BigInt M1,ConstRefRingElem f2,BigInt M2)1227   void CRTPoly(RingElem& f, BigInt& M,
1228                ConstRefRingElem f1, BigInt M1,
1229                ConstRefRingElem f2, BigInt M2)
1230   {
1231     const SparsePolyRing P = owner(f1);
1232     RingElem ans(P);
1233     if (!IsQQ(CoeffRing(P)))
1234       CoCoA_THROW_ERROR("reconstruction only into QQ","RatReconstructPoly");
1235     SparsePolyIter it1 = BeginIter(f1);
1236     SparsePolyIter it2 = BeginIter(f2);
1237     const BigInt Zero;
1238     while ( !(IsEnded(it1)||IsEnded(it2)) )
1239       if (PP(it1) == PP(it2))
1240       {
1241         ans += monomial(P, CRT4(AsINT(coeff(it1)),M1, AsINT(coeff(it2)),M2), PP(it1));
1242         ++it1;
1243         ++it2;
1244       }
1245       else
1246       {
1247         if (PP(it1) < PP(it2))
1248         {
1249           ans += monomial(P, CRT4(Zero, M1, AsINT(coeff(it2)), M2), PP(it2));
1250           ++it2;
1251         }
1252         else // LPP(f1) > LPP(f2)
1253         {
1254           ans += monomial(P, CRT4(AsINT(coeff(it1)), M1, Zero, M2), PP(it1));
1255           ++it1;
1256         }
1257       }
1258     for (; !IsEnded(it1); ++it1)
1259       ans += monomial(P, CRT4(AsINT(coeff(it1)), M1, Zero, M2), PP(it1));
1260     for (; !IsEnded(it2); ++it2)
1261       ans += monomial(P, CRT4(Zero, M1, AsINT(coeff(it2)), M2), PP(it2));
1262     swap(f, ans);
1263     //    std::cout << " CRT f = " << f << std::endl;
1264     M = CRT_modulus(M1, M2);   // very likely M1*M2
1265   }
1266 
1267 
RatReconstructPoly(ConstRefRingElem fCRT,BigInt modulus)1268   RingElem RatReconstructPoly(ConstRefRingElem fCRT, BigInt modulus)
1269   {
1270     const SparsePolyRing P(owner(fCRT));
1271     if (!IsQQ(CoeffRing(P)))
1272       CoCoA_THROW_ERROR("reconstruction only into QQ","RatReconstructPoly");
1273     RingElem f(P);
1274     for (SparsePolyIter it = BeginIter(fCRT); !IsEnded(it); ++it)
1275       f += monomial(P, RatReconstruct(AsINT(coeff(it)), modulus), PP(it));
1276     return f;
1277   }
1278 
1279 
1280   //--- move to PolyOps-RingElem.C ???
CoeffHeight(ConstRefRingElem f)1281   RingElem CoeffHeight(ConstRefRingElem f)
1282   {
1283     const char* const FnName = "CoeffHeight";
1284     const ring& P = owner(f);
1285     if (!IsPolyRing(P))
1286       CoCoA_THROW_ERROR(ERR::NotPolyRing, FnName);
1287     if (!IsOrderedDomain(CoeffRing(P)))
1288       CoCoA_THROW_ERROR(ERR::NotOrdDom, FnName);
1289     RingElem ans = zero(CoeffRing(P));
1290     // Loop below makes wasteful copies of the coeffs.
1291     for (SparsePolyIter it = BeginIter(f); !IsEnded(it); ++it)
1292     {
1293       const RingElem c = abs(coeff(it));
1294       if (c > ans)
1295         ans = c;
1296     }
1297     return ans;
1298   }
1299 
1300 
1301   // Just for univariate -- requires non-zero const term
1302   // A bit ugly because I wanted to avoid copying any coeffs.
IsPalindromic(ConstRefRingElem f)1303   bool IsPalindromic(ConstRefRingElem f)
1304   {
1305     const ring& P = owner(f);
1306     if (!IsSparsePolyRing(P))
1307       CoCoA_THROW_ERROR(ERR::NotSparsePolyRing, "IsPalindromic");
1308     if (IsZero(f) || deg(f) == 0) return true;
1309     if (UnivariateIndetIndex(f) < 0)
1310       CoCoA_THROW_ERROR("Expected univariate poly", "IsPalindromic");
1311     const long n = NumTerms(f);
1312     const long degf = deg(f);
1313     vector<RingElemConstRawPtr> C; C.reserve(1+n/2);
1314     vector<long> exp(1+n/2);
1315     long count = 0;
1316     for (SparsePolyIter it = BeginIter(f); !IsEnded(it); ++it)
1317     {
1318       ++count;
1319       if (count <= n/2)
1320       {
1321         C.push_back(raw(coeff(it)));
1322         exp.push_back(deg(PP(it)));
1323       }
1324       else
1325       {
1326         if (IsOdd(n) && count == (n+1)/2) continue;
1327         if (deg(PP(it)) != degf-exp[n-count]) return false;
1328         if (coeff(it) != RingElemAlias(P, C[n-count])) return false;
1329       }
1330     }
1331     return true;
1332   }
1333 
1334   // univariate case
reverse(ConstRefRingElem f)1335   RingElem reverse(ConstRefRingElem f)
1336   {
1337     const ring& P = owner(f);
1338     if (!IsSparsePolyRing(P))
1339       CoCoA_THROW_ERROR(ERR::NotSparsePolyRing, "reverse");
1340     if (IsZero(f)) return f;
1341     if (UnivariateIndetIndex(f) < 0)
1342       CoCoA_THROW_ERROR("Expected univariate poly", "reverse");
1343     return reverse(f, LPP(f));
1344   }
1345 
reverse(ConstRefRingElem f,ConstRefPPMonoidElem t)1346   RingElem reverse(ConstRefRingElem f, ConstRefPPMonoidElem t)
1347   {
1348     const ring& P = owner(f);
1349     if (!IsSparsePolyRing(P))
1350       CoCoA_THROW_ERROR(ERR::NotSparsePolyRing, "reverse");
1351     if (IsZero(f)) return f;
1352     RingElem ans = zero(P);
1353     for (SparsePolyIter it=BeginIter(f); !IsEnded(it); ++it)
1354     {
1355       PushFront(ans, coeff(it), t/PP(it));
1356     }
1357     return ans;
1358   }
1359 
1360 
1361   // Standard graeffe transformation (squares the roots)
graeffe(ConstRefRingElem f)1362   RingElem graeffe(ConstRefRingElem f)
1363   {
1364     const ring& P = owner(f);
1365     if (!IsSparsePolyRing(P))
1366       CoCoA_THROW_ERROR(ERR::NotSparsePolyRing, "graeffe");
1367     if (IsZero(f)) return f;
1368     if (deg(f) == 0) return one(P); //?????
1369     const long IndetIndex = UnivariateIndetIndex(f);
1370     if (IndetIndex < 0)
1371       CoCoA_THROW_ERROR("Expected univariate poly", "graeffe");
1372     PPMonoidElem pp = indet(PPM(P),IndetIndex);
1373     RingElem EvenPart(P);
1374     RingElem OddPart(P);
1375     for (SparsePolyIter it = BeginIter(f); !IsEnded(it); ++it)
1376     {
1377       const int d = deg(PP(it));
1378       if (IsEven(d))
1379         PushBack(EvenPart, coeff(it), power(pp,d/2));
1380       else
1381         PushBack(OddPart, coeff(it), power(pp,d/2)); // integer division!
1382     }
1383     const RingElem& x = indet(P, IndetIndex);
1384     // Fiddle answer so that LC is positive.
1385     RingElem ans = power(EvenPart,2) - x*power(OddPart,2);
1386     if (sign(LC(ans)) < 0) ans *= -1;
1387     return ans;
1388   }
1389 
1390 
1391   // Cubic graeffe transformation  (cubes the roots)
1392   // FORMULA:  f0^3 -3*f0*f1*f2*x +f1^3*x +f2^3*x^2
graeffe3(ConstRefRingElem f)1393   RingElem graeffe3(ConstRefRingElem f)
1394   {
1395     const ring& P = owner(f);
1396     if (!IsSparsePolyRing(P))
1397       CoCoA_THROW_ERROR(ERR::NotSparsePolyRing, "graeffe");
1398     if (IsZero(f)) return f;
1399     const long IndetIndex = UnivariateIndetIndex(f);
1400     if (IndetIndex < 0)
1401       CoCoA_THROW_ERROR("Expected univariate poly", "graeffe");
1402 
1403     PPMonoidElem pp = indet(PPM(P),IndetIndex);
1404     vector<RingElem> part(3, zero(P));
1405     for (SparsePolyIter it = BeginIter(f); !IsEnded(it); ++it)
1406     {
1407       const int d = deg(PP(it));
1408       PushBack(part[d%3], coeff(it), power(pp, d/3)); // integer division!
1409     }
1410     const RingElem& x = indet(P, IndetIndex);
1411     const RingElem part012 = part[0]*part[1]*part[2];
1412     // Fiddle answer so that LC is positive.
1413     RingElem ans = power(part[0],3) + x*(power(part[1],3)-3*part012 + x*power(part[2],3));
1414     if (sign(LC(ans)) < 0) ans *= -1;
1415     return ans;
1416   }
1417 
1418 
1419 } // end of namespace CoCoA
1420 
1421 // RCS header/log in the next few lines
1422 // $Header: /Volumes/Home_1/cocoa/cvs-repository/CoCoALib-0.99/src/AlgebraicCore/SparsePolyOps-RingElem.C,v 1.20 2020/12/04 10:45:07 abbott Exp $
1423 // $Log: SparsePolyOps-RingElem.C,v $
1424 // Revision 1.20  2020/12/04 10:45:07  abbott
1425 // Summary: Made reduction interruptible
1426 //
1427 // Revision 1.19  2020/10/27 10:01:50  abbott
1428 // Summary: Changed comment
1429 //
1430 // Revision 1.18  2020/06/17 15:49:28  abbott
1431 // Summary: Changed CoCoA_ERROR into CoCoA_THROW_ERROR
1432 //
1433 // Revision 1.17  2020/04/19 17:15:07  abbott
1434 // Summary: Added ConstantCoeff
1435 //
1436 // Revision 1.16  2020/03/11 17:00:27  abbott
1437 // Summary: Added new fn HomogCompt; split of fns to do with homog polys into new file.  Cleaned includes.
1438 //
1439 // Revision 1.15  2020/03/04 20:05:40  abbott
1440 // Summary: Changed name CoeffVecWRTBasis to CoeffVecWRTSupport
1441 //
1442 // Revision 1.14  2020/02/27 14:00:28  abbott
1443 // Summary: Added CoeffVecWRTBasis (or CoeffListWRTBasis in CoCoA-5)
1444 //
1445 // Revision 1.13  2020/02/14 12:22:37  abbott
1446 // Summary: Removed old undocumented fn indets(RingElem); merged impl into IndetsIn
1447 //
1448 // Revision 1.12  2020/02/13 13:54:57  abbott
1449 // Summary: Added IndetsProd
1450 //
1451 // Revision 1.11  2020/02/12 15:38:54  bigatti
1452 // -- made new file SparsePolyIter.C for BeginIter and EndIter
1453 //
1454 // Revision 1.10  2020/02/11 16:56:42  abbott
1455 // Summary: Corrected last update (see redmine 969)
1456 //
1457 // Revision 1.9  2020/02/11 16:12:19  abbott
1458 // Summary: Added some checks for bad ostream (even to mem fns myOutput); see redmine 969
1459 //
1460 // Revision 1.8  2020/01/26 14:37:24  abbott
1461 // Summary: Removed useless include (of NumTheory)
1462 //
1463 // Revision 1.7  2019/12/28 17:53:53  abbott
1464 // Summary: Added myIsIndetPosPower (with 3 args)
1465 //
1466 // Revision 1.6  2019/10/29 11:38:03  abbott
1467 // Summary: Added IndetsIn also for vector<RingElem>
1468 //
1469 // Revision 1.5  2019/09/16 17:38:19  abbott
1470 // Summary: Impl myIsEvenPoly, myIsOddPoly
1471 //
1472 // Revision 1.4  2019/03/18 11:23:21  abbott
1473 // Summary: Added new include after splitting NumTheory
1474 //
1475 // Revision 1.3  2018/08/06 13:38:20  bigatti
1476 // -- added functins from SparsePolyOps.C
1477 //
1478 // Revision 1.2  2018/07/26 15:25:38  abbott
1479 // Summary: Added comment about SLUG (not using geobucket in RandomLinearForm)
1480 //
1481 // Revision 1.1  2018/05/18 16:35:52  bigatti
1482 // -- split SparsePolyOps-RingElem from SparsePolyRing
1483 //
1484