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