1 // Copyright (c) 1999,2009-2011,2019 John Abbott 2 3 // This file is part of the source of CoCoALib, the CoCoA Library. 4 5 // CoCoALib is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 10 // CoCoALib is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU General Public License for more details. 14 15 // You should have received a copy of the GNU General Public License 16 // along with CoCoALib. If not, see <http://www.gnu.org/licenses/>. 17 18 19 #include "CoCoA/NumTheory-gcd.H" 20 #include "CoCoA/NumTheory-factor.H" 21 22 #include "CoCoA/BigIntOps.H" 23 #include "CoCoA/assert.H" 24 #include "CoCoA/bool3.H" 25 #include "CoCoA/config.H" 26 #include "CoCoA/convert.H" 27 #include "CoCoA/error.H" 28 #include "CoCoA/utils.H" 29 30 #include <algorithm> 31 using std::min; 32 // using std::max; 33 // using std::swap; 34 #include <cmath> 35 // myThreshold uses floor,pow,sqrt 36 // #include <cstdlib> 37 // using std::ldiv; 38 #include <limits> 39 using std::numeric_limits; 40 #include <vector> 41 using std::vector; 42 43 namespace CoCoA 44 { 45 46 //------------------------------------------------------------------ 47 radical(const MachineInt & n)48 long radical(const MachineInt& n) 49 { 50 if (IsZero(n)) return 0; 51 const factorization<long> FacInfo = factor(n); 52 const vector<long>& facs = FacInfo.myFactors(); 53 long ans = 1; 54 const int nfacs = len(facs); 55 for (int i=0; i < nfacs; ++i) 56 ans *= facs[i]; 57 return ans; 58 } 59 radical(const BigInt & N)60 BigInt radical(const BigInt& N) 61 { 62 if (IsZero(N)) return N; 63 if (IsProbPrime(abs(N))) return abs(N); 64 const factorization<BigInt> FacInfo = factor(N); 65 const vector<BigInt>& facs = FacInfo.myFactors(); 66 BigInt ans(1); 67 const int nfacs = len(facs); 68 for (int i=0; i < nfacs; ++i) 69 ans *= facs[i]; 70 return ans; 71 } 72 73 SmoothFactor(const MachineInt & n,const MachineInt & TrialLimit)74 factorization<long> SmoothFactor(const MachineInt& n, const MachineInt& TrialLimit) 75 { 76 if (IsZero(n)) 77 CoCoA_ERROR(ERR::BadArg, "SmoothFactor(n,TrialLimit): n must be non-zero"); 78 if (!IsSignedLong(TrialLimit) || AsSignedLong(TrialLimit) < 1) 79 CoCoA_ERROR(ERR::BadArg, "SmoothFactor(n,TrialLimit): TrialLimit must be at least 1 and fit into a machine long"); 80 if (!IsSignedLong(n)) 81 CoCoA_ERROR(ERR::ArgTooBig, "SmoothFactor(n,TrialLimit): number to be factorized must fit into a machine long"); 82 // Below Pmax is unsigned long so that the code will work even if input TrialLimit is numeric_limits<long>::max() 83 const unsigned long Pmax = AsUnsignedLong(TrialLimit); 84 unsigned long RemainingFactor = uabs(n); 85 86 87 // Main loop: we simply do trial divisions by primes up to 31, & by numbers not divisible by 2,3,5,...,31. 88 vector<long> factors; 89 vector<long> exponents; 90 91 // Use "mostly primes"; faster than using primes 92 FastMostlyPrimeSeq TrialFactorSeq; 93 94 unsigned long stop = Pmax; // highest prime we shall consider is "stop" 95 if (RemainingFactor/stop < stop) stop = ConvertTo<long>(FloorSqrt(RemainingFactor)); 96 for (unsigned long p = *TrialFactorSeq; p <= stop; p = *++TrialFactorSeq) 97 { 98 unsigned long rem = RemainingFactor % p; 99 if (rem != 0) continue; 100 int exp = 0; 101 while (rem == 0) 102 { 103 ++exp; 104 RemainingFactor /= p; 105 rem = RemainingFactor % p; 106 } 107 CoCoA_ASSERT(exp > 0); 108 factors.push_back(p); 109 exponents.push_back(exp); 110 if (RemainingFactor/stop < stop) stop = ConvertTo<long>(FloorSqrt(RemainingFactor)); 111 } 112 // if RemainingFactor is non-triv & below limit, add it to the list of factors found. 113 if (RemainingFactor > 1 && RemainingFactor <= Pmax) 114 { 115 factors.push_back(RemainingFactor); 116 exponents.push_back(1); 117 RemainingFactor = 1; 118 } 119 if (IsNegative(n)) 120 return factorization<long>(factors, exponents, -static_cast<long>(RemainingFactor)); 121 return factorization<long>(factors, exponents, RemainingFactor); 122 } 123 124 125 // This is very similar to the function above -- but I don't see how to share code. SmoothFactor(const BigInt & N,const MachineInt & TrialLimit)126 factorization<BigInt> SmoothFactor(const BigInt& N, const MachineInt& TrialLimit) 127 { 128 if (IsZero(N)) 129 CoCoA_ERROR(ERR::BadArg, "SmoothFactor(N,TrialLimit): N must be non-zero"); 130 if (!IsSignedLong(TrialLimit) || AsSignedLong(TrialLimit) < 1) 131 CoCoA_ERROR(ERR::BadArg, "SmoothFactor(N,TrialLimit): TrialLimit must be at least 1 and fit into a machine long"); 132 // Below Pmax is unsigned long so that the code will work even if input TrialLimit is numeric_limits<long>::max() 133 const unsigned long Pmax = AsUnsignedLong(TrialLimit); 134 BigInt RemainingFactor = abs(N); 135 136 // Main loop: we simply do trial divisions by primes up to 31 & then by numbers not divisible by 2,3,5,...,31. 137 vector<BigInt> factors; 138 vector<long> exponents; 139 140 // Use "mostly primes"; faster than using primes 141 FastMostlyPrimeSeq TrialFactorSeq; 142 143 unsigned long stop = Pmax; // highest prime we shall consider is "stop" 144 if (RemainingFactor/stop < stop) stop = ConvertTo<long>(FloorSqrt(RemainingFactor)); 145 long LogRemainingFactor = FloorLog2(RemainingFactor); 146 long CountDivTests = 0; 147 for (unsigned long p = *TrialFactorSeq; p <= stop; p = *++TrialFactorSeq) 148 { 149 ++CountDivTests; 150 // If several div tests have found no factors, check whether RemainingFactor is prime... 151 if (p > 256 && CountDivTests == LogRemainingFactor && LogRemainingFactor < 64) 152 { 153 if (IsPrime(RemainingFactor)) break; 154 } 155 156 if (mpz_fdiv_ui(mpzref(RemainingFactor),p) != 0) continue; 157 158 // p does divide RemainingFactor, so divide out highest power. 159 int exp = 0; 160 BigInt quo,rem; 161 quorem(quo,rem, RemainingFactor, p); 162 while (rem == 0) 163 { 164 ++exp; 165 RemainingFactor = quo; 166 quorem(quo,rem, RemainingFactor, p); 167 } 168 169 CoCoA_ASSERT(exp > 0); 170 factors.push_back(BigInt(p)); 171 exponents.push_back(exp); 172 if (quo <= p+1) break; // quo was set by last "failed" call to quorem 173 LogRemainingFactor = FloorLog2(RemainingFactor); 174 if (LogRemainingFactor < 64 && RemainingFactor/stop < stop) 175 stop = ConvertTo<long>(FloorSqrt(RemainingFactor)); 176 CountDivTests = 0; 177 } 178 179 // if RemainingFactor is non-triv & below limit, add it to the list of factors found. 180 if (RemainingFactor > 1 && RemainingFactor <= Pmax) 181 { 182 factors.push_back(RemainingFactor); 183 exponents.push_back(1); 184 RemainingFactor = 1; 185 } 186 if (N < 0) 187 return factorization<BigInt>(factors, exponents, -RemainingFactor); 188 return factorization<BigInt>(factors, exponents, RemainingFactor); 189 } 190 SmoothFactor(const BigInt & N,const BigInt & TrialLimit)191 factorization<BigInt> SmoothFactor(const BigInt& N, const BigInt& TrialLimit) 192 { 193 if (IsZero(N)) 194 CoCoA_ERROR(ERR::BadArg, "SmoothFactor(N,TrialLimit): N must be non-zero"); 195 if (TrialLimit < 1) 196 CoCoA_ERROR(ERR::BadArg, "SmoothFactor(N,TrialLimit): TrialLimit must be at least 1"); 197 198 // Not implemented for large TrialLimit because it would be hideously slow... 199 // A naive implementation could simply copy code from SmoothFactor(N,pmax) above. 200 201 long pmax; 202 if (!IsConvertible(pmax, TrialLimit)) 203 CoCoA_ERROR(ERR::NYI, "SmoothFactor(N,TrialLimit) with TrialLimit greater than largest signed long"); 204 return SmoothFactor(N, pmax); 205 } 206 207 factor(const MachineInt & n)208 factorization<long> factor(const MachineInt& n) 209 { 210 if (IsZero(n)) 211 CoCoA_ERROR(ERR::BadArg, "factor(n): n must be non-zero"); 212 if (!IsSignedLong(n)) 213 CoCoA_ERROR(ERR::ArgTooBig, "factor(n): n must fit into a signed long"); 214 // Simple rather than efficient. 215 if (uabs(n) < 2) return SmoothFactor(n,1); 216 return SmoothFactor(n,uabs(n)); 217 } 218 factor(const BigInt & N)219 factorization<BigInt> factor(const BigInt& N) 220 { 221 if (IsZero(N)) 222 CoCoA_ERROR(ERR::BadArg, "factor(N): N must be non-zero"); 223 const long PrimeLimit = FactorBigIntTrialLimit; // defined in config.H 224 factorization<BigInt> ans = SmoothFactor(N, PrimeLimit); 225 const BigInt& R = ans.myRemainingFactor(); 226 if (abs(R) == 1) return ans; 227 if (abs(R) < power(PrimeLimit,2) || IsPrime(abs(R))) // ??? IsPrime or IsProbPrime ??? 228 { 229 ans.myAppend(R,1); 230 ans.myNewRemainingFactor(BigInt(sign(R))); 231 return ans; 232 } 233 234 // Could check for abs(R) being a perfect power... 235 CoCoA_ERROR(ERR::NYI, "factor(N) unimplemented in this case -- too many large factors"); 236 return ans; 237 } 238 239 240 //------------------------------------------------------------------ 241 PollardRhoSeq(const BigInt & N,long StartVal,long incr)242 PollardRhoSeq::PollardRhoSeq(const BigInt& N, long StartVal, long incr): 243 myNumIters(1), 244 myAnchorIterNum(1), 245 myAnchorVal(StartVal), 246 myN(abs(N)), 247 myCurrVal(StartVal), 248 myGCD(1), 249 myStartVal(StartVal), 250 myIncr(incr), 251 myBlockSize(50) // heuristic: 50-100 works well on my machine 252 { 253 if (myN < 2) CoCoA_ERROR(ERR::BadArg, "PollardRhoSeq ctor N must be >= 2"); 254 if (incr == 0) CoCoA_ERROR(ERR::BadArg, "PollardRhoSeq ctor incr must be non-zero"); 255 } 256 257 myAdvance(long k)258 void PollardRhoSeq::myAdvance(long k) 259 { 260 CoCoA_ASSERT(k > 0); 261 const long stop = k + myNumIters; // BUG overflow??? 262 while (myNumIters < stop) 263 { 264 if (!IsOne(myGCD)) return; // don't advance if myGCD != 1 265 BigInt prod(1); 266 for (int j=0; j < myBlockSize; ++j) 267 { 268 myCurrVal = (myCurrVal*myCurrVal+myIncr)%myN; 269 prod = (prod*(myCurrVal-myAnchorVal))%myN; 270 ++myNumIters; 271 if (myNumIters == 2*myAnchorIterNum) 272 { 273 myAnchorIterNum = myNumIters; 274 myAnchorVal = myCurrVal; 275 } 276 } 277 myGCD = gcd(myN, prod); 278 } 279 } 280 281 IsEnded(const PollardRhoSeq & PRS)282 bool IsEnded(const PollardRhoSeq& PRS) 283 { 284 return !IsOne(PRS.myGCD); 285 } 286 287 GetFactor(const PollardRhoSeq & PRS)288 const BigInt& GetFactor(const PollardRhoSeq& PRS) 289 { 290 // if (!IsEnded(PRS)) CoCoA_ERROR("",""); 291 return PRS.myGCD; 292 } 293 294 GetNumIters(const PollardRhoSeq & PRS)295 long GetNumIters(const PollardRhoSeq& PRS) 296 { 297 return PRS.myNumIters; 298 } 299 300 301 std::ostream& operator<<(std::ostream& out, const PollardRhoSeq& PRS) 302 { 303 if (!out) return out; // short-cut for bad ostreams 304 if (out) 305 { 306 out << "PollardRhoSeq(N=" << PRS.myN 307 << ", start=" << PRS.myStartVal 308 << ", incr=" << PRS.myIncr 309 << ", NumIters=" << PRS.myNumIters 310 << ", gcd=" << PRS.myGCD << ")"; 311 } 312 return out; 313 } 314 315 316 //------------------------------------------------------------------ SumOfFactors(const MachineInt & n,long k)317 BigInt SumOfFactors(const MachineInt& n, long k) 318 { 319 if (IsNegative(n) || IsZero(n)) CoCoA_ERROR(ERR::NotPositive, "SumOfFactors"); 320 if (IsNegative(k)) CoCoA_ERROR(ERR::NotNonNegative, "SumOfFactors"); 321 const factorization<long> FacInfo = factor(n); 322 const vector<long>& mult = FacInfo.myMultiplicities(); 323 const int s = len(mult); 324 if (k == 0) 325 { 326 long ans = 1; 327 for (int i=0; i < s; ++i) 328 ans *= 1+mult[i]; 329 return BigInt(ans); 330 } 331 // Here k > 0 332 BigInt ans(1); 333 const vector<long>& fac = FacInfo.myFactors(); 334 for (int i=0; i < s; ++i) 335 ans *= (power(fac[i], k*(mult[i]+1)) - 1)/(power(fac[i],k) - 1); 336 return ans; 337 } 338 339 SmallestNonDivisor(const MachineInt & N)340 long SmallestNonDivisor(const MachineInt& N) 341 { 342 if (IsZero(N)) CoCoA_ERROR(ERR::NotNonZero, "SmallestNonDivisor"); 343 unsigned long n = uabs(N); 344 if (IsOdd(n)) return 2; 345 FastFinitePrimeSeq TrialDivisorList; 346 while (n%(*TrialDivisorList) == 0) 347 ++TrialDivisorList; 348 return *TrialDivisorList; 349 } 350 SmallestNonDivisor(const BigInt & N)351 long SmallestNonDivisor(const BigInt& N) 352 { 353 if (IsZero(N)) CoCoA_ERROR(ERR::NotNonZero, "SmallestNonDivisor"); 354 if (IsOdd(N)) return 2; 355 // SLUG! simple rather than quick 356 FastMostlyPrimeSeq TrialDivisorList; 357 while (N%(*TrialDivisorList) == 0) 358 ++TrialDivisorList; 359 return *TrialDivisorList; 360 } 361 362 IsSqFree(const MachineInt & N)363 bool IsSqFree(const MachineInt& N) 364 { 365 if (IsZero(N)) 366 CoCoA_ERROR(ERR::BadArg, "IsSqFree(N): N must be non-zero"); 367 if (!IsSignedLong(N)) 368 CoCoA_ERROR(ERR::BadArg, "IsSqFree(N): N must be non-zero"); 369 long n = uabs(N); // implicit cast to long is safe 370 if (n < 4) return true; 371 372 // Main loop: we simply do trial divisions by the first few primes, then divisors from NoSmallFactorSeq 373 const long Pmax = min(FactorBigIntTrialLimit, MaxSquarableInteger<long>()); 374 long counter = 0; 375 const long LogN = FloorLog2(N); 376 const long TestIsPrime = (LogN>1000)?1000000:LogN*LogN; 377 378 // Use "mostly primes"; much faster than using primes. 379 FastMostlyPrimeSeq TrialFactorSeq; 380 long p = *TrialFactorSeq; 381 382 while (p <= Pmax) 383 { 384 ldiv_t qr = ldiv(n, p); 385 if (p > qr.quot) return true; // test equiv to p^2 > N 386 if (qr.rem == 0) 387 { 388 n = qr.quot; // equiv. to N /= p; 389 if (n%p == 0) return false; 390 } 391 else 392 { 393 ++counter; 394 if (counter == TestIsPrime && IsProbPrime(n)) 395 return true; 396 } 397 398 ++TrialFactorSeq; 399 p = *TrialFactorSeq; 400 } 401 402 if (counter < TestIsPrime && IsProbPrime(n)) return true; 403 if (IsPower(n)) return false; 404 return true; 405 } 406 407 IsSqFree(BigInt N)408 bool3 IsSqFree(BigInt N) 409 { 410 if (IsZero(N)) 411 CoCoA_ERROR(ERR::BadArg, "IsSqFree(N): N must be non-zero"); 412 N = abs(N); 413 if (N < 4) return true3; 414 415 // Main loop: we simply do trial divisions by the first few primes, then divisors from NoSmallFactorSeq 416 const long Pmax = FactorBigIntTrialLimit; 417 long counter = 0; 418 const long LogN = FloorLog2(N); 419 const long TestIsPrime = (LogN>1000)?1000000:LogN*LogN; 420 421 // Use "mostly primes"; much faster than using primes. 422 FastMostlyPrimeSeq TrialFactorSeq; 423 long p = *TrialFactorSeq; 424 425 BigInt quot, rem; 426 while (p <= Pmax) 427 { 428 quorem(quot, rem, N, p); 429 if (p > quot) return true3; // test equiv to p^2 > N 430 if (IsZero(rem)) 431 { 432 swap(N, quot); // equiv. to N /= p; 433 if (N%p == 0) return false3; 434 } 435 else 436 { 437 ++counter; 438 if (counter == TestIsPrime && IsProbPrime(N)) 439 return true3; 440 } 441 442 ++TrialFactorSeq; 443 p = *TrialFactorSeq; 444 } 445 446 if (IsPower(N)) return false3; 447 if (counter < TestIsPrime && IsProbPrime(N)) return true3; 448 return uncertain3; 449 } 450 451 FactorMultiplicity(const MachineInt & b,const MachineInt & n)452 long FactorMultiplicity(const MachineInt& b, const MachineInt& n) 453 { 454 if (IsZero(n)) CoCoA_ERROR(ERR::NotNonZero, "FactorMultiplicity"); 455 if (IsNegative(b)) CoCoA_ERROR(ERR::BadArg, "FactorMultiplicity"); 456 const unsigned long base = AsUnsignedLong(b); 457 if (base == 0 || base == 1) CoCoA_ERROR(ERR::BadArg, "FactorMultiplicity"); 458 unsigned long m = uabs(n); 459 long mult = 0; 460 while (m%base == 0) 461 { 462 m /= base; 463 ++mult; 464 } 465 return mult; 466 } 467 468 FactorMultiplicity(const MachineInt & b,BigInt N)469 long FactorMultiplicity(const MachineInt& b, BigInt N) 470 { 471 if (IsZero(N)) CoCoA_ERROR(ERR::NotNonZero, "FactorMultiplicity"); 472 if (IsNegative(b)) CoCoA_ERROR(ERR::BadArg, "FactorMultiplicity"); 473 const unsigned long base = AsUnsignedLong(b); 474 if (base == 0 || base == 1) CoCoA_ERROR(ERR::BadArg, "FactorMultiplicity"); 475 476 long mult = 0; 477 if (FloorLog2(N) > 2*numeric_limits<unsigned long>::digits) 478 { 479 // Case: N is fairly large 480 // Repeatedly divide by largest power of prime which fits in ulong: 481 unsigned long pwr = base; 482 int exp = 1; 483 while (numeric_limits<unsigned long>::max()/base >= pwr) 484 { 485 pwr *= base; 486 ++exp; 487 } 488 BigInt r; 489 while (true) 490 { 491 quorem(N, r, N, pwr); 492 if (!IsZero(r)) break; 493 mult += exp; 494 } 495 N = r; // any remaining powers of base are now in r 496 } 497 498 // Case: N is fairly small 499 while (N%base == 0) 500 { 501 N /= base; 502 ++mult; // BUG??? could conceivably overflow if N is a high power of 2??? 503 } 504 return mult; 505 } 506 FactorMultiplicity(const BigInt & B,BigInt N)507 long FactorMultiplicity(const BigInt& B, BigInt N) 508 { 509 if (IsZero(N)) CoCoA_ERROR(ERR::NotNonZero, "FactorMultiplicity"); 510 long b; 511 if (IsConvertible(b, B)) return FactorMultiplicity(b,N); 512 if (B < 2) CoCoA_ERROR(ERR::BadArg, "FactorMultiplicity"); 513 long mult = 0; 514 BigInt r; 515 while (true) 516 { 517 quorem(N, r, N, B); 518 if (!IsZero(r)) break; 519 ++mult; 520 } 521 return mult; 522 } 523 524 525 } // end of namespace CoCoA 526 527 528 // RCS header/log in the next few lines 529 // $Header: /Volumes/Home_1/cocoa/cvs-repository/CoCoALib-0.99/src/AlgebraicCore/NumTheory-factor.C,v 1.4 2020/02/11 16:12:18 abbott Exp $ 530 // $Log: NumTheory-factor.C,v $ 531 // Revision 1.4 2020/02/11 16:12:18 abbott 532 // Summary: Added some checks for bad ostream (even to mem fns myOutput); see redmine 969 533 // 534 // Revision 1.3 2020/01/26 14:41:59 abbott 535 // Summary: Revised includes after splitting NumTheory (redmine 1161) 536 // 537 // Revision 1.2 2019/09/16 17:31:07 abbott 538 // Summary: SmoothFactor now allows TrialLimit==1; added PollardRhoSeq 539 // 540 // Revision 1.1 2019/03/18 11:24:20 abbott 541 // Summary: Split NumTheory into several smaller files 542 // 543 // 544