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