1 // Copyright (c) 2012-2018 John Abbott and Anna Maria Bigatti
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 #include "CoCoA/BigIntOps.H"
19
20 #include "CoCoA/convert.H"
21 #include "CoCoA/error.H"
22 #include "CoCoA/utils.H"
23
24 #include <cmath>
25 //using std::log;
26 //using std::atan;
27 #include <limits>
28 using std::numeric_limits;
29
30
31
32 namespace CoCoA
33 {
34
35 namespace //anonymous
36 {
37 // Compute round(n/d) rounding halves up.
38 // NB cannot simply do (n+d/2)/d because of possible overflow!
uround_half_up(unsigned long n,unsigned long d)39 inline unsigned long uround_half_up(unsigned long n, unsigned long d)
40 {
41 CoCoA_ASSERT(d != 0);
42 const unsigned long q = n/d;
43 if (n-q*d <= (d-1)/2) return q;
44 return q+1; // cannot overflow
45 }
46
47 // Compute round(n/d) rounding halves down.
48 // NB cannot simply do (n+(d-1)/2)/d because of possible overflow!
uround_half_down(unsigned long n,unsigned long d)49 inline unsigned long uround_half_down(unsigned long n, unsigned long d)
50 {
51 CoCoA_ASSERT(d != 0);
52 const unsigned long q = n/d;
53 if (n-q*d <= d/2) return q;
54 return q+1; // cannot overflow
55 }
56
57 // These two defns are used inside cmp(MachineInt, MachineInt)
cmp(long a,long b)58 inline int cmp(long a, long b)
59 {
60 if (a < b) return -1;
61 if (b < a) return 1;
62 return 0;
63 }
64
cmp(unsigned long a,unsigned long b)65 inline int cmp(unsigned long a, unsigned long b)
66 {
67 if (a < b) return -1;
68 if (b < a) return 1;
69 return 0;
70 }
71
72 } // end of anonymous namespace
73
74
abs(const BigInt & N)75 const BigInt abs(const BigInt& N)
76 {
77 BigInt ans;
78 mpz_abs(mpzref(ans), mpzref(N));
79 return ans;
80 }
81
82
83 const BigInt operator-(const BigInt& N)
84 {
85 BigInt ans(N);
86 negate(ans);
87 return ans;
88 }
89
90
91 const BigInt operator+(const BigInt& N1, const BigInt& N2)
92 {
93 BigInt ans;
94 mpz_add(mpzref(ans), mpzref(N1), mpzref(N2));
95 return ans;
96 }
97
98 const BigInt operator+(const BigInt& N1, const MachineInt& n2)
99 {
100 BigInt ans;
101 if (IsNegative(n2))
102 mpz_sub_ui(mpzref(ans), mpzref(N1), negate(n2));
103 else
104 mpz_add_ui(mpzref(ans), mpzref(N1), AsUnsignedLong(n2));
105 return ans;
106 }
107
108 const BigInt operator+(const MachineInt& n1, const BigInt& N2)
109 {
110 BigInt ans;
111 if (IsNegative(n1))
112 mpz_sub_ui(mpzref(ans), mpzref(N2), negate(n1));
113 else
114 mpz_add_ui(mpzref(ans), mpzref(N2), AsUnsignedLong(n1));
115 return ans;
116 }
117
118
119 const BigInt operator-(const BigInt& N1, const BigInt& N2)
120 {
121 BigInt ans;
122 mpz_sub(mpzref(ans), mpzref(N1), mpzref(N2));
123 return ans;
124 }
125
126 const BigInt operator-(const BigInt& N1, const MachineInt& n2)
127 {
128 BigInt ans;
129 if (IsNegative(n2))
130 mpz_add_ui(mpzref(ans), mpzref(N1), negate(n2));
131 else
132 mpz_sub_ui(mpzref(ans), mpzref(N1), AsUnsignedLong(n2));
133 return ans;
134 }
135
136 const BigInt operator-(const MachineInt& n1, const BigInt& N2)
137 {
138 BigInt ans;
139 if (IsNegative(n1))
140 mpz_add_ui(mpzref(ans), mpzref(N2), negate(n1));
141 else
142 mpz_sub_ui(mpzref(ans), mpzref(N2), AsUnsignedLong(n1));
143 negate(ans);
144 return ans;
145 }
146
147
148 const BigInt operator*(const BigInt& N1, const BigInt& N2)
149 {
150 BigInt ans;
151 mpz_mul(mpzref(ans), mpzref(N1), mpzref(N2));
152 return ans;
153 }
154
155 const BigInt operator*(const BigInt& N1, const MachineInt& n2)
156 {
157 BigInt ans;
158 if (IsNegative(n2))
159 mpz_mul_si(mpzref(ans), mpzref(N1), AsSignedLong(n2));
160 else
161 mpz_mul_ui(mpzref(ans), mpzref(N1), AsUnsignedLong(n2));
162 return ans;
163 }
164
165 const BigInt operator*(const MachineInt& n1, const BigInt& N2)
166 {
167 BigInt ans;
168 if (IsNegative(n1))
169 mpz_mul_si(mpzref(ans), mpzref(N2), AsSignedLong(n1));
170 else
171 mpz_mul_ui(mpzref(ans), mpzref(N2), AsUnsignedLong(n1));
172 return ans;
173 }
174
175
176 const BigInt operator/(const BigInt& N1, const BigInt& N2)
177 {
178 if (IsZero(N2))
179 CoCoA_THROW_ERROR(ERR::DivByZero, "BigInt / BigInt");
180 BigInt ans;
181 mpz_tdiv_q(mpzref(ans), mpzref(N1), mpzref(N2));
182 return ans;
183 }
184
185 const BigInt operator/(const BigInt& N1, const MachineInt& n2)
186 {
187 if (IsZero(n2))
188 CoCoA_THROW_ERROR(ERR::DivByZero, "BigInt / MachineInt");
189 BigInt ans;
190 mpz_tdiv_q_ui(mpzref(ans), mpzref(N1), uabs(n2));
191 if (IsNegative(n2))
192 negate(ans);
193
194 return ans;
195 }
196
197 // Impl is simple rather than fast.
198 const BigInt operator/(const MachineInt& n1, const BigInt& N2)
199 {
200 if (IsZero(N2))
201 CoCoA_THROW_ERROR(ERR::DivByZero, "MachineInt / BigInt");
202 BigInt ans(n1);
203 ans /= N2;
204 return ans;
205 }
206
207
208 const BigInt operator%(const BigInt& N1, const BigInt& N2)
209 {
210 if (IsZero(N2))
211 CoCoA_THROW_ERROR(ERR::ZeroModulus, "BigInt % BigInt");
212 BigInt ans;
213 mpz_tdiv_r(mpzref(ans), mpzref(N1), mpzref(N2));
214 return ans;
215 }
216
217 // This impl is valid for any second arg, but would then
218 // have to return an unsigned long. I have decided to forbid
219 // 2nd args which are too big to fit into signed long; this
220 // guarantees that the result will fit into signed long.
221 // An alternative would be simply to NumericCast the result to
222 // long (which will throw if the result doesn't fit).
223 long operator%(const BigInt& N1, const MachineInt& n2)
224 {
225 if (IsZero(n2))
226 CoCoA_THROW_ERROR(ERR::DivByZero, "BigInt % MachineInt");
227 if (!IsSignedLong(n2))
228 CoCoA_THROW_ERROR(ERR::ArgTooBig, "BigInt % MachineInt");
229 const long ans = mpz_tdiv_ui(mpzref(N1), uabs(n2)); // abs value of remainder
230 if (N1 < 0) return -ans; // CAREFUL: read GMP doc for mpz_tdiv_ui
231 return ans;
232 }
233
234 // Impl is simple rather than fast.
235 const BigInt operator%(const MachineInt& n1, const BigInt& N2)
236 {
237 if (IsZero(N2))
238 CoCoA_THROW_ERROR(ERR::DivByZero, "MachineInt % BigInt");
239 return BigInt(n1)%N2;
240 // BigInt ans(n1);
241 // ans %= N2;
242 // return ans;
243 }
244
245
LeastNNegRemainder(const BigInt & N1,const BigInt & N2)246 const BigInt LeastNNegRemainder(const BigInt& N1, const BigInt& N2)
247 {
248 if (IsZero(N2))
249 CoCoA_THROW_ERROR(ERR::ZeroModulus, "LeastNNegRemainder(BigInt, BigInt)");
250 BigInt ans;
251 mpz_mod(mpzref(ans), mpzref(N1), mpzref(N2));
252 return ans;
253 }
254
LeastNNegRemainder(const BigInt & N1,const MachineInt & n2)255 long LeastNNegRemainder(const BigInt& N1, const MachineInt& n2)
256 {
257 if (IsZero(n2) || !IsSignedLong(n2)) CoCoA_THROW_ERROR(ERR::BadModulus, "LeastNNegRemainder");
258 return mpz_fdiv_ui(mpzref(N1), uabs(n2)); // harmless silent conversion to long
259 }
260
LeastNNegRemainder(const MachineInt & n1,const BigInt & N2)261 const BigInt LeastNNegRemainder(const MachineInt& n1, const BigInt& N2)
262 {
263 // Simple rather than fast;
264 return LeastNNegRemainder(BigInt(n1), N2);
265 }
266
LeastNNegRemainder(const MachineInt & n1,const MachineInt & n2)267 long LeastNNegRemainder(const MachineInt& n1, const MachineInt& n2)
268 {
269 if (IsZero(n2) || !IsSignedLong(n2)) CoCoA_THROW_ERROR(ERR::BadModulus, "LeastNNegRemainder");
270 const unsigned long N2 = uabs(n2);
271 const unsigned long ans = uabs(n1)%N2;
272 if (ans != 0 && IsNegative(n1)) return N2-ans; // harmless silent conversion to long
273 return ans; // harmless silent conversion to long
274 }
275
276
SymmRemainder(const BigInt & N1,const BigInt & N2)277 const BigInt SymmRemainder(const BigInt& N1, const BigInt& N2)
278 {
279 if (IsZero(N2))
280 CoCoA_THROW_ERROR(ERR::ZeroModulus, "SymmRemainder(BigInt, BigInt)");
281 BigInt ans;
282 mpz_tdiv_r(mpzref(ans), mpzref(N1), mpzref(N2));
283 BigInt HalfN2; mpz_tdiv_q_2exp(mpzref(HalfN2), mpzref(N2), 1); mpz_abs(mpzref(HalfN2), mpzref(HalfN2));
284 if (CmpAbs(ans, HalfN2) < 0) return ans;
285 if (ans == HalfN2) return ans;
286 if (sign(ans) == sign(N2)) ans -= N2;
287 else ans += N2;
288 return ans;
289 }
290
SymmRemainder(const BigInt & N1,const MachineInt & n2)291 long SymmRemainder(const BigInt& N1, const MachineInt& n2)
292 {
293 if (IsZero(n2) || !IsSignedLong(n2)) CoCoA_THROW_ERROR(ERR::BadModulus, "SymmRemainder");
294 const unsigned long N2 = uabs(n2);
295 unsigned long ans = mpz_fdiv_ui(mpzref(N1), N2);
296 if (ans > N2/2) return -NumericCast<long>(N2-ans); // cannot overflow (on a binary computer)
297 return ans;
298 }
299
SymmRemainder(const MachineInt & n1,const BigInt & N2)300 const BigInt SymmRemainder(const MachineInt& n1, const BigInt& N2)
301 {
302 // Simple rather than fast.
303 return SymmRemainder(BigInt(n1), N2);
304 }
305
306
SymmRemainder(const MachineInt & n1,const MachineInt & n2)307 long SymmRemainder(const MachineInt& n1, const MachineInt& n2)
308 {
309 if (IsZero(n2) || !IsSignedLong(n2)) CoCoA_THROW_ERROR(ERR::BadModulus, "SymmRemainder");
310 const unsigned long N2 = uabs(n2);
311 unsigned long ans = uabs(n1)%N2;
312 if (IsNegative(n1)) ans = N2-ans;
313 if (ans > N2/2) return -NumericCast<long>(N2-ans); // cannot overflow (on a binary computer)
314 return ans;
315 }
316
317
power(const BigInt & base,const BigInt & exponent)318 const BigInt power(const BigInt& base, const BigInt& exponent)
319 {
320 if (exponent < 0)
321 CoCoA_THROW_ERROR(ERR::NegExp, "power(BigInt, BigInt)");
322 // BUG??? Below could allow high powers of 0 and +/-1. Is it worth it?
323 unsigned long exp;
324 if (!IsConvertible(exp, exponent))
325 CoCoA_THROW_ERROR(ERR::ExpTooBig, "power(BigInt, BigInt)");
326 BigInt ans;
327 mpz_pow_ui(mpzref(ans), mpzref(base), exp);
328 return ans;
329 }
330
power(const BigInt & base,const MachineInt & exponent)331 const BigInt power(const BigInt& base, const MachineInt& exponent)
332 {
333 if (IsNegative(exponent))
334 CoCoA_THROW_ERROR(ERR::NegExp, "power(BigInt, MachineInt)");
335 BigInt ans;
336 mpz_pow_ui(mpzref(ans), mpzref(base), AsUnsignedLong(exponent));
337 return ans;
338 }
339
power(const MachineInt & base,const BigInt & exponent)340 const BigInt power(const MachineInt& base, const BigInt& exponent)
341 {
342 const char* const FnName = "power(MachineInt, BigInt)";
343 if (exponent < 0)
344 CoCoA_THROW_ERROR(ERR::NegExp, FnName);
345 // BUG??? Below could allow high powers of 0 and +/-1. Is it worth it?
346 unsigned long exp;
347 if (!IsConvertible(exp, exponent))
348 CoCoA_THROW_ERROR(ERR::ExpTooBig, FnName);
349 const bool NegateResult = IsNegative(base) & IsOdd(exp);
350 const unsigned long B = uabs(base);
351 BigInt ans;
352 mpz_ui_pow_ui(mpzref(ans), B, exp);
353 if (NegateResult)
354 negate(ans);
355 return ans;
356 }
357
power(const MachineInt & base,const MachineInt & exponent)358 const BigInt power(const MachineInt& base, const MachineInt& exponent)
359 {
360 const char* const FnName = "power(MachineInt, MachineInt)";
361 if (IsNegative(exponent))
362 CoCoA_THROW_ERROR(ERR::NegExp, FnName);
363
364 const bool NegateResult = IsNegative(base) & IsOdd(AsUnsignedLong(exponent));
365 const unsigned long B = uabs(base);
366 BigInt ans;
367 mpz_ui_pow_ui(mpzref(ans), B, AsUnsignedLong(exponent));
368 if (NegateResult)
369 negate(ans);
370 return ans;
371 }
372
373
SmallPower(const MachineInt & base,const MachineInt & exponent)374 long SmallPower(const MachineInt& base, const MachineInt& exponent)
375 {
376 if (IsNegative(exponent)) CoCoA_THROW_ERROR(ERR::NegExp, "SmallPower");
377 if (!IsSignedLong(base)) CoCoA_THROW_ERROR(ERR::BadArg, "SmallPower: base is too big to fit into a long");
378 if (IsZero(exponent)) return 1; // Order is important for 0^0
379 if (IsZero(base)) return 0; // ...
380 long b = AsSignedLong(base);
381 if (b == 1) return 1;
382 unsigned long e = AsUnsignedLong(exponent);
383 if (b == -1) return (IsEven(e)?1:-1);
384 const bool negative = (e&1) && IsNegative(base);
385 long ans = 1;
386 while (e != 1)
387 {
388 if (e&1) ans *= b;
389 e >>= 1;
390 b *= b;
391 }
392 ans *= b;
393 if (negative) return -ans;
394 return ans;
395 }
396
397
add(BigInt & lhs,const BigInt & N1,const BigInt & N2)398 void add(BigInt& lhs, const BigInt& N1, const BigInt& N2)
399 {
400 mpz_add(mpzref(lhs), mpzref(N1), mpzref(N2));
401 }
402
add(BigInt & lhs,const BigInt & N1,const MachineInt & n2)403 void add(BigInt& lhs, const BigInt& N1, const MachineInt& n2)
404 {
405 if (IsNegative(n2))
406 mpz_sub_ui(mpzref(lhs), mpzref(N1), negate(n2));
407 else
408 mpz_add_ui(mpzref(lhs), mpzref(N1), AsUnsignedLong(n2));
409 }
410
add(BigInt & lhs,const MachineInt & n1,const BigInt & N2)411 void add(BigInt& lhs, const MachineInt& n1, const BigInt& N2)
412 {
413 add(lhs, N2, n1);
414 }
415
416
sub(BigInt & lhs,const BigInt & N1,const BigInt & N2)417 void sub(BigInt& lhs, const BigInt& N1, const BigInt& N2)
418 {
419 mpz_sub(mpzref(lhs), mpzref(N1), mpzref(N2));
420 }
421
sub(BigInt & lhs,const BigInt & N1,const MachineInt & n2)422 void sub(BigInt& lhs, const BigInt& N1, const MachineInt& n2)
423 {
424 if (IsNegative(n2))
425 mpz_add_ui(mpzref(lhs), mpzref(N1), negate(n2));
426 else
427 mpz_sub_ui(mpzref(lhs), mpzref(N1), AsUnsignedLong(n2));
428 }
429
sub(BigInt & lhs,const MachineInt & n1,const BigInt & N2)430 void sub(BigInt& lhs, const MachineInt& n1, const BigInt& N2)
431 {
432 sub(lhs, N2, n1);
433 negate(lhs);
434 }
435
436
mul(BigInt & lhs,const BigInt & N1,const BigInt & N2)437 void mul(BigInt& lhs, const BigInt& N1, const BigInt& N2)
438 {
439 mpz_mul(mpzref(lhs), mpzref(N1), mpzref(N2));
440 }
441
mul(BigInt & lhs,const BigInt & N1,const MachineInt & n2)442 void mul(BigInt& lhs, const BigInt& N1, const MachineInt& n2)
443 {
444 if (IsNegative(n2))
445 mpz_mul_si(mpzref(lhs), mpzref(N1), AsSignedLong(n2));
446 else
447 mpz_mul_ui(mpzref(lhs), mpzref(N1), AsUnsignedLong(n2));
448 }
449
mul(BigInt & lhs,const MachineInt & n1,const BigInt & N2)450 void mul(BigInt& lhs, const MachineInt& n1, const BigInt& N2)
451 {
452 mul(lhs, N2, n1);
453 }
454
455
div(BigInt & lhs,const BigInt & N1,const BigInt & N2)456 void div(BigInt& lhs, const BigInt& N1, const BigInt& N2)
457 {
458 if (IsZero(N2))
459 CoCoA_THROW_ERROR(ERR::DivByZero, "div(BigInt, BigInt, BigInt)");
460 mpz_tdiv_q(mpzref(lhs), mpzref(N1), mpzref(N2));
461 }
462
div(BigInt & lhs,const BigInt & N1,const MachineInt & n2)463 void div(BigInt& lhs, const BigInt& N1, const MachineInt& n2)
464 {
465 if (IsZero(n2))
466 CoCoA_THROW_ERROR(ERR::DivByZero, "div(BigInt, BigInt, MachineInt)");
467 mpz_tdiv_q_ui(mpzref(lhs), mpzref(N1), uabs(n2));
468 if (IsNegative(n2))
469 negate(lhs);
470 }
471
472 // Impl is simple rather than fast.
div(BigInt & lhs,const MachineInt & n1,const BigInt & N2)473 void div(BigInt& lhs, const MachineInt& n1, const BigInt& N2)
474 {
475 if (N2 == 0)
476 CoCoA_THROW_ERROR(ERR::DivByZero, "div(BigInt, MachineInt, BigInt)");
477 div(lhs, BigInt(n1), N2);
478 }
479
480
mod(BigInt & lhs,const BigInt & N1,const BigInt & N2)481 void mod(BigInt& lhs, const BigInt& N1, const BigInt& N2)
482 {
483 if (IsZero(N2))
484 CoCoA_THROW_ERROR(ERR::ZeroModulus, "mod(BigInt, BigInt, BigInt)");
485 mpz_tdiv_r(mpzref(lhs), mpzref(N1), mpzref(N2));
486 }
487
mod(BigInt & lhs,const BigInt & N1,const MachineInt & n2)488 void mod(BigInt& lhs, const BigInt& N1, const MachineInt& n2)
489 {
490 if (IsZero(n2))
491 CoCoA_THROW_ERROR(ERR::ZeroModulus, "mod(BigInt, BigInt, MachineInt)");
492 mpz_tdiv_r_ui(mpzref(lhs), mpzref(N1), uabs(n2));
493 }
494
495 // Impl is simple rather than fast.
mod(BigInt & lhs,const MachineInt & n1,const BigInt & N2)496 void mod(BigInt& lhs, const MachineInt& n1, const BigInt& N2)
497 {
498 if (IsZero(N2))
499 CoCoA_THROW_ERROR(ERR::ZeroModulus, "mod(BigInt, MachineInt, BigInt)");
500 mod(lhs, BigInt(n1), N2);
501 }
502
503
quorem(BigInt & quo,BigInt & rem,const BigInt & num,const BigInt & den)504 void quorem(BigInt& quo, BigInt& rem, const BigInt& num, const BigInt& den)
505 {
506 if (IsZero(den))
507 CoCoA_THROW_ERROR(ERR::DivByZero, "quorem(BigInt, BigInt, BigInt, BigInt)");
508 mpz_tdiv_qr(mpzref(quo), mpzref(rem), mpzref(num), mpzref(den));
509 }
510
quorem(BigInt & quo,BigInt & rem,const BigInt & num,const MachineInt & den)511 void quorem(BigInt& quo, BigInt& rem, const BigInt& num, const MachineInt& den)
512 {
513 if (IsZero(den))
514 CoCoA_THROW_ERROR(ERR::DivByZero, "quorem(BigInt, BigInt, BigInt, MachineInt)");
515 if (IsNegative(den))
516 {
517 mpz_tdiv_qr_ui(mpzref(quo), mpzref(rem), mpzref(num), negate(den));
518 negate(quo);
519 }
520 else
521 mpz_tdiv_qr_ui(mpzref(quo), mpzref(rem), mpzref(num), AsUnsignedLong(den));
522 }
523
524 // Impl is simple rather than fast.
quorem(BigInt & quo,BigInt & rem,const MachineInt & num,const BigInt & den)525 void quorem(BigInt& quo, BigInt& rem, const MachineInt& num, const BigInt& den)
526 {
527 if (IsZero(den))
528 CoCoA_THROW_ERROR(ERR::DivByZero, "quorem(BigInt, BigInt, MachineInt, BigInt)");
529 quorem(quo, rem, BigInt(num), den);
530 }
531
532
divexact(BigInt & lhs,const BigInt & N1,const BigInt & N2)533 void divexact(BigInt& lhs, const BigInt& N1, const BigInt& N2)
534 {
535 CoCoA_ASSERT(N1%N2 == 0);
536 mpz_divexact(mpzref(lhs), mpzref(N1), mpzref(N2));
537 }
538
539 //??? void divexact(BigInt& lhs, const MachineInt& N1, const BigInt& N2);
540 //??? void divexact(BigInt& lhs, const BigInt& N1, const MachineInt& N2);
541
542
IsZero(const BigInt & N)543 bool IsZero(const BigInt& N)
544 {
545 return mpz_sgn(mpzref(N))==0;
546 }
547
IsOne(const BigInt & N)548 bool IsOne(const BigInt& N)
549 {
550 return mpz_cmp_ui(mpzref(N), 1)==0;
551 }
552
IsMinusOne(const BigInt & N)553 bool IsMinusOne(const BigInt& N)
554 {
555 return mpz_cmp_si(mpzref(N),-1)==0;
556 }
557
558
IsPowerOf2(const BigInt & N)559 bool IsPowerOf2(const BigInt& N)
560 {
561 if (N <= 0) return false;
562 const unsigned long LogN = mpz_sizeinbase(mpzref(N), 2)-1;
563 const unsigned long Last1Bit = mpz_scan1(mpzref(N), 0);
564 return (Last1Bit == LogN);
565 }
566
IsPowerOf2(const MachineInt & n)567 bool IsPowerOf2(const MachineInt& n)
568 {
569 if (IsZero(n) || IsNegative(n)) return false;
570 const unsigned long N = AsUnsignedLong(n);
571 return !(N & (N-1));
572 }
573
574
IsDivisible(const MachineInt & N,const MachineInt & D)575 bool IsDivisible(const MachineInt& N, const MachineInt& D) // is N divisibile by D?
576 {
577 if (IsZero(D)) CoCoA_THROW_ERROR(ERR::DivByZero, "IsDivisible");
578 return uabs(N)%uabs(D) == 0;
579 }
580
IsDivisible(const MachineInt & N,const BigInt & D)581 bool IsDivisible(const MachineInt& N, const BigInt& D) // is N divisibile by D?
582 {
583 if (IsZero(D)) CoCoA_THROW_ERROR(ERR::DivByZero, "IsDivisible");
584 // Simple rather than fast...
585 return IsDivisible(BigInt(N), D);
586 }
587
IsDivisible(const BigInt & N,const MachineInt & D)588 bool IsDivisible(const BigInt& N, const MachineInt& D) // is N divisibile by D?
589 {
590 if (IsZero(D)) CoCoA_THROW_ERROR(ERR::DivByZero, "IsDivisible");
591 return mpz_divisible_ui_p(mpzref(N), uabs(D));
592 }
593
IsDivisible(const BigInt & N,const BigInt & D)594 bool IsDivisible(const BigInt& N, const BigInt& D) // is N divisibile by D?
595 {
596 if (IsZero(D)) CoCoA_THROW_ERROR(ERR::DivByZero, "IsDivisible");
597 return mpz_divisible_p(mpzref(N), mpzref(D));
598 }
599
600
cmp(const BigInt & N1,const BigInt & N2)601 int cmp(const BigInt& N1, const BigInt& N2)
602 {
603 return sign(mpz_cmp(mpzref(N1), mpzref(N2)));
604 }
605
cmp(const BigInt & N1,const MachineInt & n2)606 int cmp(const BigInt& N1, const MachineInt& n2)
607 {
608 if (IsNegative(n2))
609 return sign(mpz_cmp_si(mpzref(N1), AsSignedLong(n2)));
610 else
611 return sign(mpz_cmp_ui(mpzref(N1), AsUnsignedLong(n2)));
612 }
613
cmp(const MachineInt & n1,const BigInt & N2)614 int cmp(const MachineInt& n1, const BigInt& N2)
615 {
616 return -cmp(N2, n1);
617 }
618
cmp(const MachineInt & n1,const MachineInt & n2)619 int cmp(const MachineInt& n1, const MachineInt& n2)
620 {
621 if (IsNegative(n1))
622 {
623 if (IsNegative(n2))
624 return cmp(AsSignedLong(n1), AsSignedLong(n2));
625 else
626 return -1;
627 }
628 // n1 is non-negative
629 if (IsNegative(n2)) return 1;
630 // Both n1 and n2 are non-negative
631 return cmp(AsUnsignedLong(n1), AsUnsignedLong(n2));
632 }
633
634
CmpAbs(const BigInt & N1,const BigInt & N2)635 int CmpAbs(const BigInt& N1, const BigInt& N2)
636 {
637 return sign(mpz_cmpabs(mpzref(N1), mpzref(N2)));
638 }
639
CmpAbs(const BigInt & N1,const MachineInt & n2)640 int CmpAbs(const BigInt& N1, const MachineInt& n2)
641 {
642 return sign(mpz_cmpabs_ui(mpzref(N1), uabs(n2)));
643 }
644
CmpAbs(const MachineInt & n1,const BigInt & N2)645 int CmpAbs(const MachineInt& n1, const BigInt& N2)
646 {
647 return -sign(mpz_cmpabs_ui(mpzref(N2), uabs(n1)));
648 }
649
CmpAbs(const MachineInt & n1,const MachineInt & n2)650 int CmpAbs(const MachineInt& n1, const MachineInt& n2)
651 {
652 const unsigned long a1 = uabs(n1);
653 const unsigned long a2 = uabs(n2);
654 if (a1 > a2) return 1;
655 if (a1 < a2) return -1;
656 return 0;
657 }
658
659
660
661 bool operator==(const BigInt& N1, const BigInt& N2)
662 {
663 return cmp(N1, N2) == 0;
664 }
665
666 bool operator==(const BigInt& N1, const MachineInt& n2)
667 {
668 return cmp(N1, n2) == 0;
669 }
670
671 bool operator==(const MachineInt& n1, const BigInt& N2)
672 {
673 return cmp(n1, N2) == 0;
674 }
675
676
677 bool operator!=(const BigInt& N1, const BigInt& N2)
678 {
679 return cmp(N1, N2) != 0;
680 }
681
682 bool operator!=(const BigInt& N1, const MachineInt& n2)
683 {
684 return cmp(N1, n2) != 0;
685 }
686
687 bool operator!=(const MachineInt& n1, const BigInt& N2)
688 {
689 return cmp(n1, N2) != 0;
690 }
691
692
693 bool operator> (const BigInt& N1, const BigInt& N2)
694 {
695 return cmp(N1, N2) > 0;
696 }
697
698 bool operator> (const BigInt& N1, const MachineInt& n2)
699 {
700 return cmp(N1, n2) > 0;
701 }
702
703 bool operator> (const MachineInt& n1, const BigInt& N2)
704 {
705 return cmp(n1, N2) > 0;
706 }
707
708
709 bool operator>=(const BigInt& N1, const BigInt& N2)
710 {
711 return cmp(N1, N2) >= 0;
712 }
713
714 bool operator>=(const BigInt& N1, const MachineInt& n2)
715 {
716 return cmp(N1, n2) >= 0;
717 }
718
719 bool operator>=(const MachineInt& n1, const BigInt& N2)
720 {
721 return cmp(n1, N2) >= 0;
722 }
723
724
725 bool operator< (const BigInt& N1, const BigInt& N2)
726 {
727 return cmp(N1, N2) < 0;
728 }
729
730 bool operator< (const BigInt& N1, const MachineInt& n2)
731 {
732 return cmp(N1, n2) < 0;
733 }
734
735 bool operator< (const MachineInt& n1, const BigInt& N2)
736 {
737 return cmp(n1, N2) < 0;
738 }
739
740
741 bool operator<=(const BigInt& N1, const BigInt& N2)
742 {
743 return cmp(N1, N2) <= 0;
744 }
745
746 bool operator<=(const BigInt& N1, const MachineInt& n2)
747 {
748 return cmp(N1, n2) <= 0;
749 }
750
751 bool operator<=(const MachineInt& n1, const BigInt& N2)
752 {
753 return cmp(n1, N2) <= 0;
754 }
755
756
mantissa(const BigInt & N)757 double mantissa(const BigInt& N)
758 {
759 long DiscardExponent;
760 return mpz_get_d_2exp(&DiscardExponent, mpzref(N));
761 }
762
763
exponent(const BigInt & N)764 long exponent(const BigInt& N)
765 {
766 return NumericCast<long>(mpz_sizeinbase(mpzref(N), 2));
767 }
768
769
MultiplicityOf2(const BigInt & N)770 long MultiplicityOf2(const BigInt& N)
771 {
772 if (IsZero(N)) CoCoA_THROW_ERROR(ERR::NotNonZero, "MultiplicityOf2");
773 return mpz_scan1(mpzref(N), 0);
774 }
775
MultiplicityOf2(const MachineInt & n)776 long MultiplicityOf2(const MachineInt& n)
777 {
778 if (IsZero(n)) CoCoA_THROW_ERROR(ERR::NotNonZero, "MultiplicityOf2");
779 long val = AsSignedLong(n);
780 int i=0;
781 while ((val&1) == 0) { val /= 2; ++i; }
782 return i;
783 }
784
785
SizeInBase(const BigInt & N,long base)786 long SizeInBase(const BigInt& N, long base)
787 {
788 if (base < 2 || base > 36)
789 CoCoA_THROW_ERROR(ERR::BadNumBase, "SizeInBase(BigInt,base)");
790 return NumericCast<long>(mpz_sizeinbase(mpzref(N), base));
791 }
792
793
log(const BigInt & N)794 double log(const BigInt& N)
795 {
796 if (IsZero(N))
797 CoCoA_THROW_ERROR(ERR::LogZero, "log(BigInt)");
798 if (exponent(N) < numeric_limits<double>::max_exponent)
799 return std::log(std::abs(ConvertTo<double>(N)));
800 long exp;
801 const double mantissa = mpz_get_d_2exp(&exp, mpzref(N));
802 const static double log2 = std::log(2.0);
803 return std::log(std::abs(mantissa)) + exp*log2;
804 }
805
806
FloorLog2(const MachineInt & n)807 long FloorLog2(const MachineInt& n) { return FloorLogBase(n,2); }
FloorLog2(const BigInt & N)808 long FloorLog2(const BigInt& N) { return FloorLogBase(N,2); }
FloorLog10(const MachineInt & n)809 long FloorLog10(const MachineInt& n) { return FloorLogBase(n,10); }
FloorLog10(const BigInt & N)810 long FloorLog10(const BigInt& N) { return FloorLogBase(N,10); }
811
812
FloorLogBase(const MachineInt & n,const MachineInt & base)813 long FloorLogBase(const MachineInt& n, const MachineInt& base)
814 {
815 return FloorLogBase(BigInt(n), BigInt(base));
816 }
817
FloorLogBase(const MachineInt & n,const BigInt & base)818 long FloorLogBase(const MachineInt& n, const BigInt& base)
819 {
820 return FloorLogBase(BigInt(n), base);
821 }
822
FloorLogBase(const BigInt & N,const MachineInt & base)823 long FloorLogBase(const BigInt& N, const MachineInt& base)
824 {
825 return FloorLogBase(N, BigInt(base));
826 }
827
FloorLogBase(const BigInt & N,const BigInt & base)828 long FloorLogBase(const BigInt& N, const BigInt& base)
829 {
830 if (abs(base) < 2) CoCoA_THROW_ERROR(ERR::BadArg, "FloorLogBase: base must be at least 2");
831 if (IsZero(N)) CoCoA_THROW_ERROR(ERR::LogZero, "FloorLogBase");
832 const double ApproxLog = log(N)/log(base);
833 // Check whether ApproxLog is far from integer.
834 const double delta = 5* ApproxLog * numeric_limits<double>::epsilon();
835 const long candidate = static_cast<long>(floor(ApproxLog+delta)); // probably right but could be too big by 1
836 if (std::abs(ApproxLog - candidate) > delta)
837 return candidate;
838 // ApproxLog was close to integer, so we make a slow but sure check.
839 const BigInt pwr = power(base, candidate);
840 const int test = cmp(abs(N), pwr);
841 if (test == 0) return candidate;
842 if (test < 0) return candidate-1;
843 if (abs(N) >= base*pwr) return candidate+1;
844 return candidate;
845 }
846
847
848 // This fn comes out a long horrible mess because I cannot return a
849 // machine int as a BigInt. There must be a design error somewhere.
RangeFactorial(const MachineInt & lo,const MachineInt & hi)850 BigInt RangeFactorial(const MachineInt& lo, const MachineInt& hi)
851 {
852 BigInt ans;
853 // If zero is in the range, result is zero
854 if ((IsZero(lo) || IsNegative(lo)) && !IsNegative(hi)) return ans;
855 ans = 1;
856 // First some checks for empty products.
857 if (IsNegative(hi) && !IsNegative(lo)) return ans;
858 // We now know that hi and lo have the same sign.
859 // Two more checks for empty products.
860 if (IsNegative(lo) && AsSignedLong(lo) > AsSignedLong(hi)) return ans;
861 if (!IsNegative(lo) && AsUnsignedLong(lo) > AsUnsignedLong(hi)) return ans;
862 // We now know that the product is not empty & does not span 0.
863 unsigned long l = uabs(lo);
864 unsigned long h = uabs(hi);
865 if (IsNegative(hi))
866 {
867 std::swap(l, h);
868 if (((h^l)&1) == 0) negate(ans);
869 }
870
871 if (h-l > 15)
872 {
873 const unsigned long mid = l + (h-l)/2; // equiv to (l+h)/2 but avoids overflow
874 return RangeFactorial(l,mid) * RangeFactorial(1+mid,h);
875 }
876 // From here on 0 < l < h.
877 for (; l <= h; ++l)
878 {
879 ans *= l;
880 // mpz_mul_ui(mpzref(ans), mpzref(ans), l);
881 }
882 return ans;
883 }
884
factorial(const BigInt & N)885 const BigInt factorial(const BigInt& N)
886 {
887 if (N < 0)
888 CoCoA_THROW_ERROR(ERR::NotNonNegative, "factorial(BigInt)");
889 unsigned long n;
890 if (!IsConvertible(n, N))
891 CoCoA_THROW_ERROR(ERR::ArgTooBig, "factorial(BigInt)");
892 // return factorial(n);
893 BigInt ans;
894 mpz_fac_ui(mpzref(ans), n);
895 return ans;
896 }
897
898
factorial(const MachineInt & n)899 const BigInt factorial(const MachineInt& n)
900 {
901 if (IsNegative(n))
902 CoCoA_THROW_ERROR(ERR::NotNonNegative, "factorial(MachineInt)");
903 BigInt ans;
904 mpz_fac_ui(mpzref(ans), AsUnsignedLong(n));
905 return ans;
906 }
907
908
909 // From Wikipedia; apparently due to Ramanujan
910 // Experimentally max error is 4.6*10^(-8) at n=24.
LogFactorial(const MachineInt & N)911 double LogFactorial(const MachineInt& N)
912 {
913 using std::log;
914 using std::atan;
915 if (IsNegative(N)) CoCoA_THROW_ERROR(ERR::NotNonNegative, "LogFactorial");
916 const long n = AsSignedLong(N);
917 const static double LogSqrtPi = log(4*atan(1.0))/2;
918 if (n > 23)
919 return n*log(double(n))-n + log(n*(1+n*(4.0+8*n)))/6 + LogSqrtPi;
920 double fact = 1.0;
921 for (long k=2; k <= n; ++k)
922 fact *= k;
923 return log(fact);
924 }
925
LogFactorial(const BigInt & N)926 double LogFactorial(const BigInt& N)
927 {
928 if (N < 0) CoCoA_THROW_ERROR(ERR::NotNonNegative, "LogFactorial");
929 double n;
930 if (!IsConvertible(n, N)) CoCoA_THROW_ERROR(ERR::ArgTooBig, "LogFactorial");
931 {
932 long SmallN;
933 if (IsConvertible(SmallN, N)) return LogFactorial(SmallN);
934 }
935 using std::log;
936 using std::atan;
937 const static double LogSqrtPi = log(4*atan(1.0))/2;
938 return n*log(n)-n + log(n*(1+n*(4.0+8*n)))/6 + LogSqrtPi;
939 }
940
941
942
primorial(const BigInt & N)943 const BigInt primorial(const BigInt& N)
944 {
945 if (N < 0)
946 CoCoA_THROW_ERROR(ERR::NotNonNegative, "primorial(BigInt)");
947 unsigned long n;
948 if (!IsConvertible(n, N))
949 CoCoA_THROW_ERROR(ERR::ArgTooBig, "primorial(BigInt)");
950 // return factorial(n);
951 BigInt ans;
952 mpz_primorial_ui(mpzref(ans), n);
953 return ans;
954 }
955
956
primorial(const MachineInt & n)957 const BigInt primorial(const MachineInt& n)
958 {
959 if (IsNegative(n))
960 CoCoA_THROW_ERROR(ERR::NotNonNegative, "factorial(MachineInt)");
961 BigInt ans;
962 mpz_primorial_ui(mpzref(ans), AsUnsignedLong(n));
963 return ans;
964 }
965
966
binomial(const BigInt & N,const BigInt & R)967 const BigInt binomial(const BigInt& N, const BigInt& R)
968 {
969 // if (R < 0) return BigInt(0);
970 if (R < 0)
971 CoCoA_THROW_ERROR(ERR::BadArg, "binomial(BigInt,BigInt): 2nd arg must be non-neg");
972 if (N < 0)
973 {
974 if (IsEven(R)) return binomial(R-N-1,R);
975 else return -binomial(R-N-1,R);
976 }
977 if (R > N)
978 return BigInt(0);
979
980 // General case: N >= R >= 0
981 unsigned long r;
982 const bool RIsSmall = (2*R < N);
983 if ((RIsSmall && !IsConvertible(r, R)) ||
984 (!RIsSmall && !IsConvertible(r, N-R)))
985 CoCoA_THROW_ERROR(ERR::ArgTooBig, "binomial(BigInt, BigInt)");
986
987 BigInt ans;
988 mpz_bin_ui(mpzref(ans), mpzref(N), r); // we know that r >= 0.
989 return ans;
990 }
991
992
binomial(const MachineInt & n,const BigInt & R)993 const BigInt binomial(const MachineInt& n, const BigInt& R)
994 {
995 if (R < 0)
996 CoCoA_THROW_ERROR(ERR::BadArg, "binomial(MachineInt,BigInt): 2nd arg must be non-neg");
997 return binomial(BigInt(n), R);
998 }
999
1000
binomial(const BigInt & N,const MachineInt & r)1001 const BigInt binomial(const BigInt& N, const MachineInt& r)
1002 {
1003 // if (IsNegative(r)) return BigInt(0);
1004 if (IsNegative(r))
1005 CoCoA_THROW_ERROR(ERR::BadArg, "binomial(BigInt,MachineInt): 2nd arg must be non-neg");
1006 BigInt ans;
1007 mpz_bin_ui(mpzref(ans), mpzref(N), AsUnsignedLong(r));
1008 return ans;
1009 }
1010
1011
binomial(const MachineInt & n,const MachineInt & r)1012 const BigInt binomial(const MachineInt& n, const MachineInt& r)
1013 {
1014 // if (IsNegative(r)) return BigInt(0);
1015 if (IsNegative(r))
1016 CoCoA_THROW_ERROR(ERR::BadArg, "binomial(MachineInt,MachineInt): 2nd arg must be non-neg");
1017
1018 BigInt ans;
1019 if (IsNegative(n))
1020 mpz_bin_ui(mpzref(ans), mpzref(BigInt(n)), AsUnsignedLong(r));
1021 else
1022 mpz_bin_uiui(mpzref(ans), AsUnsignedLong(n), AsUnsignedLong(r));
1023 return ans;
1024 }
1025
1026
fibonacci(const BigInt & N)1027 const BigInt fibonacci(const BigInt& N)
1028 {
1029 long n;
1030 if (!IsConvertible(n, N))
1031 CoCoA_THROW_ERROR(ERR::ArgTooBig, "fibonacci(BigInt)");
1032 return fibonacci(n);
1033 }
1034
1035
fibonacci(const MachineInt & n)1036 const BigInt fibonacci(const MachineInt& n)
1037 {
1038 const bool EvenNegative = IsNegative(n) && IsEven(n);
1039 BigInt ans;
1040 unsigned long arg = uabs(n);
1041 mpz_fib_ui(mpzref(ans), arg);
1042 if (EvenNegative)
1043 negate(ans);
1044 return ans;
1045 }
1046
1047
1048 // NB halves are rounded away from zero.
RoundDiv(const BigInt & num,const BigInt & den)1049 const BigInt RoundDiv(const BigInt& num, const BigInt& den)
1050 {
1051 if (IsZero(den))
1052 CoCoA_THROW_ERROR(ERR::DivByZero, "RoundDiv(BigInt,BigInt)");
1053 BigInt q,r;
1054 quorem(q, r, num, den);
1055 if (CmpAbs(2*r,den) >= 0) // ROUND AWAY FROM ZERO
1056 // if (CmpAbs(2*r,den) > 0) // ROUND TOWARDS ZERO
1057 q += sign(r)*sign(den);
1058 return q;
1059 }
1060
RoundDiv(const BigInt & num,const MachineInt & den)1061 const BigInt RoundDiv(const BigInt& num, const MachineInt& den)
1062 {
1063 if (IsZero(den))
1064 CoCoA_THROW_ERROR(ERR::DivByZero, "RoundDiv(BigInt,MachInt)");
1065 return RoundDiv(num, BigInt(den));
1066 }
1067
RoundDiv(const MachineInt & num,const BigInt & den)1068 const BigInt RoundDiv(const MachineInt& num, const BigInt& den)
1069 {
1070 if (IsZero(den))
1071 CoCoA_THROW_ERROR(ERR::DivByZero, "RoundDiv(MachInt,BigInt)");
1072 return RoundDiv(BigInt(num), den);
1073 }
1074
1075 // NB Halves are rounded away from zero.
RoundDiv(const MachineInt & n,const MachineInt & d)1076 long RoundDiv(const MachineInt& n, const MachineInt& d)
1077 {
1078 if (IsZero(d))
1079 CoCoA_THROW_ERROR(ERR::DivByZero, "RoundDiv(n,d)");
1080 if (IsZero(n)) return 0;
1081 // Henceforth n and d are both non-zero.
1082 const long s = sign(n)*sign(d);
1083 const unsigned long q = uround_half_up(uabs(n), uabs(d)); // ROUND AWAY FROM ZERO
1084 // const unsigned long q = uround_half_down(uabs(n), uabs(d)); // ROUND TOWARDS ZERO
1085 if (s > 0) return NumericCast<long>(q);
1086 static const long MinLong = numeric_limits<long>::min();
1087 if (q + MinLong == 0) return MinLong; // to avoid overflow if result is MinLong.
1088 return -NumericCast<long>(q);
1089
1090 }
1091
1092
FloorRoot(const MachineInt & n,const MachineInt & r)1093 const BigInt FloorRoot(const MachineInt& n, const MachineInt& r)
1094 {
1095 return FloorRoot(BigInt(n), r);
1096 }
1097
FloorRoot(const MachineInt & n,const BigInt & R)1098 const BigInt FloorRoot(const MachineInt& n, const BigInt& R)
1099 {
1100 if (IsNegative(n))
1101 CoCoA_THROW_ERROR(ERR::NotNonNegative, "FloorRoot: 1st arg must be non-negative");
1102 if (R < 1)
1103 CoCoA_THROW_ERROR(ERR::BadArg, "FloorRoot: 2nd arg must be positive");
1104 unsigned long r;
1105 if (!IsConvertible(r, R))
1106 CoCoA_THROW_ERROR(ERR::ArgTooBig, "FloorRoot: 2nd arg is too big"); // could just return 1???
1107 return FloorRoot(BigInt(n), r);
1108 }
1109
FloorRoot(const BigInt & N,const MachineInt & r)1110 const BigInt FloorRoot(const BigInt& N, const MachineInt& r)
1111 {
1112 if (N < 0)
1113 CoCoA_THROW_ERROR(ERR::NotNonNegative, "FloorRoot: 1st arg must be non-negative");
1114 if (IsNegative(r) || IsZero(r))
1115 CoCoA_THROW_ERROR(ERR::BadArg, "FloorRoot: 2nd arg must be at least 1");
1116 BigInt ans;
1117 mpz_root(mpzref(ans), mpzref(N), AsUnsignedLong(r));
1118 return ans;
1119 }
1120
FloorRoot(const BigInt & N,const BigInt & R)1121 const BigInt FloorRoot(const BigInt& N, const BigInt& R)
1122 {
1123 if (N < 0)
1124 CoCoA_THROW_ERROR(ERR::NotNonNegative, "FloorRoot: 1st arg must be non-negative");
1125 if (R < 1)
1126 CoCoA_THROW_ERROR(ERR::BadArg, "FloorRoot: 2nd arg must be at least 1");
1127 unsigned long r;
1128 if (!IsConvertible(r, R))
1129 CoCoA_THROW_ERROR(ERR::ArgTooBig, "FloorRoot: 2nd arg is too big"); // could just return 1???
1130 return FloorRoot(N, r);
1131 }
1132
1133
IsExactFloorRoot(long & ans,const MachineInt & n,const MachineInt & r)1134 bool IsExactFloorRoot(long& ans, const MachineInt& n, const MachineInt& r)
1135 {
1136 BigInt root;
1137 const bool IsExact = IsExactFloorRoot(root, BigInt(n), r);
1138 if (!IsConvertible(ans, root))
1139 CoCoA_THROW_ERROR(ERR::ArgTooBig, "IsExactFloorRoot");
1140 return IsExact;
1141 }
1142
IsExactFloorRoot(BigInt & ans,const MachineInt & n,const MachineInt & r)1143 bool IsExactFloorRoot(BigInt& ans, const MachineInt& n, const MachineInt& r)
1144 {
1145 return IsExactFloorRoot(ans, BigInt(n), r);
1146 }
1147
IsExactFloorRoot(long & ans,const MachineInt & n,const BigInt & R)1148 bool IsExactFloorRoot(long& ans, const MachineInt& n, const BigInt& R)
1149 {
1150 // if (IsNegative(n))
1151 // CoCoA_THROW_ERROR(ERR::NotNonNegative, "IsExactFloorRoot: 1st arg must be non-negative");
1152 unsigned long r;
1153 if (!IsConvertible(r, R))
1154 CoCoA_THROW_ERROR(ERR::ArgTooBig, "IsExactFloorRoot: root index must be >=1 & fit into a ulong");
1155 BigInt root;
1156 const bool IsExact = IsExactFloorRoot(root, BigInt(n), r);
1157 if (!IsConvertible(ans, root))
1158 CoCoA_THROW_ERROR(ERR::ArgTooBig, "IsExactFloorRoot");
1159 return IsExact;
1160 }
1161
IsExactFloorRoot(BigInt & ans,const MachineInt & n,const BigInt & R)1162 bool IsExactFloorRoot(BigInt& ans, const MachineInt& n, const BigInt& R)
1163 {
1164 unsigned long r;
1165 if (!IsConvertible(r, R))
1166 CoCoA_THROW_ERROR(ERR::ArgTooBig, "IsExactFloorRoot: root index must be >=1 & fit into a ulong");
1167 return IsExactFloorRoot(ans, BigInt(n), r);
1168 }
1169
IsExactFloorRoot(BigInt & ans,const BigInt & N,const MachineInt & r)1170 bool IsExactFloorRoot(BigInt& ans, const BigInt& N, const MachineInt& r)
1171 {
1172 if (N < 0)
1173 CoCoA_THROW_ERROR(ERR::NotNonNegative, "IsExactFloorRoot: 1st arg must be non-negative");
1174 if (IsNegative(r) || IsZero(r))
1175 CoCoA_THROW_ERROR(ERR::BadArg, "IsExactFloorRoot: root index must be at least 1");
1176 // if (N < 0 && IsEven(AsUnsignedLong(r)))
1177 // CoCoA_THROW_ERROR(ERR::BadArg, "IsExactFloorRoot: even root of negative number");
1178 const bool IsExact = mpz_root(mpzref(ans), mpzref(N), AsUnsignedLong(r));
1179 return IsExact;
1180 }
1181
IsExactFloorRoot(BigInt & ans,const BigInt & N,const BigInt & R)1182 bool IsExactFloorRoot(BigInt& ans, const BigInt& N, const BigInt& R)
1183 {
1184 unsigned long r;
1185 if (!IsConvertible(r, R))
1186 CoCoA_THROW_ERROR(ERR::ArgTooBig, "IsExactFloorRoot: root index must be >=1 & fit into a ulong");
1187 return IsExactFloorRoot(ans, N, r);
1188 }
1189
1190
FloorSqrt(const MachineInt & n)1191 long FloorSqrt(const MachineInt& n)
1192 {
1193 if (IsNegative(n)) CoCoA_THROW_ERROR(ERR::NotNonNegative, "FloorSqrt(n)");
1194 return ConvertTo<long>(FloorSqrt(BigInt(n))); // conversion will succeed.
1195 // long ans;
1196 // IsConvertible(ans, FloorSqrt(BigInt(n))); // conversion will succeed.
1197 // return ans;
1198 }
1199
FloorSqrt(const BigInt & N)1200 const BigInt FloorSqrt(const BigInt& N)
1201 {
1202 if (N < 0) CoCoA_THROW_ERROR(ERR::NotNonNegative, "FloorSqrt(N)");
1203 BigInt ans;
1204 mpz_sqrt(mpzref(ans), mpzref(N));
1205 return ans;
1206 }
1207
1208
IsSquare(const MachineInt & n)1209 bool IsSquare(const MachineInt& n)
1210 {
1211 return IsSquare(BigInt(n));
1212 }
1213
IsSquare(const BigInt & N)1214 bool IsSquare(const BigInt& N)
1215 {
1216 return mpz_perfect_square_p(mpzref(N));
1217 }
1218
1219
IsPower(const MachineInt & n)1220 bool IsPower(const MachineInt& n)
1221 {
1222 return IsPower(BigInt(n));
1223 }
1224
IsPower(const BigInt & N)1225 bool IsPower(const BigInt& N)
1226 {
1227 return mpz_perfect_power_p(mpzref(N));
1228 }
1229
1230
1231 } // end of namespace CoCoA
1232
1233
1234 // RCS header/log in the next few lines
1235 // $Header: /Volumes/Home_1/cocoa/cvs-repository/CoCoALib-0.99/src/AlgebraicCore/BigIntOps.C,v 1.4 2020/10/06 19:14:30 abbott Exp $
1236 // $Log: BigIntOps.C,v $
1237 // Revision 1.4 2020/10/06 19:14:30 abbott
1238 // Summary: SmallPower now handles some simple case specially (incl power of -1)
1239 //
1240 // Revision 1.3 2020/06/17 15:49:21 abbott
1241 // Summary: Changed CoCoA_ERROR into CoCoA_THROW_ERROR
1242 //
1243 // Revision 1.2 2019/09/16 14:30:34 abbott
1244 // Summary: Changed iroot -> FloorRoot, IsEactIroot -> IsExactFloorRoot; added primorial
1245 //
1246 // Revision 1.1 2018/05/18 12:09:25 bigatti
1247 // -- renamed IntOperations --> BigIntOps
1248 //
1249 // Revision 1.20 2017/11/29 20:30:17 abbott
1250 // Summary: Added MultiplicityOf2C
1251 //
1252 // Revision 1.19 2017/10/16 14:48:05 abbott
1253 // Summary: Corrected internal conversion to long (was unsigned long) in fibonacci
1254 //
1255 // Revision 1.18 2017/02/08 17:01:26 abbott
1256 // Summary: Made uround_half_up and uround-half-down inline
1257 //
1258 // Revision 1.17 2016/08/02 12:49:34 abbott
1259 // Summary: Renamed NumDigits to SizeInBase; updated doc.
1260 //
1261 // Revision 1.16 2015/11/24 12:47:35 abbott
1262 // Summary: Renamed "isqrt" --> "FloorSqrt"
1263 //
1264 // Revision 1.15 2015/11/23 18:21:52 abbott
1265 // Summary: Renamed ILogBase -> FloorLogBase; added FloorLog2, FloorLog10
1266 //
1267 // Revision 1.14 2015/11/05 14:20:06 abbott
1268 // Summary: Changed rtn type of LeastNonNegRemainder to long when modulus is MachineInt
1269 //
1270 // Revision 1.13 2015/10/09 18:26:36 abbott
1271 // Summary: Corrected redmine reference
1272 //
1273 // Revision 1.12 2015/10/09 18:18:27 abbott
1274 // Summary: Renamed "abs" to "uabs" for MachineInt; new fn "negate"; see redmine 783
1275 //
1276 // Revision 1.11 2014/05/16 14:15:29 abbott
1277 // Summary: Rewrote DoundDiv for MachineInts to handle rounding away from zero correctly when result is MinLong
1278 // Author: JAA
1279 //
1280 // Revision 1.10 2014/05/16 12:26:11 abbott
1281 // Summary: Changed rounding (in RoundDiv): it is now "away from zero"
1282 // Author: JAA
1283 //
1284 // Revision 1.9 2014/05/05 18:21:00 abbott
1285 // Removed another M_PI (replaced by 4*atan(1.0))
1286 //
1287 // Revision 1.8 2014/04/30 16:08:42 abbott
1288 // Summary: Removed dependency on nonstd M_PI; now uses atan; cast arg of log to double
1289 // Author: JAA
1290 //
1291 // Revision 1.7 2014/04/10 15:30:33 abbott
1292 // Summary: Corrected impl of IsPowerOf2
1293 // Author: JAA
1294 //
1295 // Revision 1.6 2014/04/08 13:04:13 abbott
1296 // Summary: Added new fn IsPowerOf2
1297 // Author: JAA
1298 //
1299 // Revision 1.5 2014/03/06 15:49:30 abbott
1300 // Summary: Zero to power zero now gives 1
1301 // Author: JAA
1302 //
1303 // Revision 1.4 2013/05/20 15:48:13 abbott
1304 // Added new fn LogFactorial (placed in IntOperations).
1305 //
1306 // Revision 1.3 2013/05/14 14:23:00 abbott
1307 // Minor improvement to log(BigInt)
1308 //
1309 // Revision 1.2 2012/12/04 09:55:47 abbott
1310 // Added new fns LeastNNegRemainder and SymmRemainder (with doc & new test).
1311 // Some minor corrections to the doc (for operator/ and operator%).
1312 //
1313 // Revision 1.1 2012/05/28 09:18:21 abbott
1314 // Created IntOperations which gathers together all operations on
1315 // integers (both big and small). Many consequential changes.
1316 //
1317 //
1318