XACC
number_theory.h
Go to the documentation of this file.
1 /*
2  * This file is part of Quantum++.
3  *
4  * MIT License
5  *
6  * Copyright (c) 2013 - 2020 Vlad Gheorghiu.
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining a copy
9  * of this software and associated documentation files (the "Software"), to deal
10  * in the Software without restriction, including without limitation the rights
11  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12  * copies of the Software, and to permit persons to whom the Software is
13  * furnished to do so, subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be included in
16  * all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24  * SOFTWARE.
25  */
26 
32 #ifndef NUMBER_THEORY_H_
33 #define NUMBER_THEORY_H_
34 
35 namespace qpp {
47 inline std::vector<int> x2contfrac(double x, idx N, idx cut = 1e5) {
48  // EXCEPTION CHECKS
49 
50  if (N == 0)
51  throw exception::OutOfRange("qpp::x2contfrac()");
52  // END EXCEPTION CHECKS
53 
54  std::vector<int> result;
55 
56  for (idx i = 0; i < N; ++i) {
57  if (x > 0) {
58  result.emplace_back(static_cast<int>(std::llround(std::floor(x))));
59  x = 1 / (x - std::floor(x));
60  } else // x < 0
61  {
62  result.emplace_back(static_cast<int>(std::llround(std::ceil(x))));
63  x = 1 / (x - std::ceil(x));
64  }
65  if (!std::isfinite(x) || std::abs(x) > cut)
66  return result;
67  }
68 
69  return result;
70 }
71 
84 inline double contfrac2x(const std::vector<int>& cf, idx N = idx(-1)) {
85  // EXCEPTION CHECKS
86  if (cf.empty())
87  throw exception::ZeroSize("qpp::contfrac2x()");
88 
89  if (N == 0)
90  throw exception::OutOfRange("qpp::contfrac2x()");
91  // END EXCEPTION CHECKS
92 
93  if (N > cf.size())
94  N = cf.size();
95 
96  if (N == 1) // degenerate case, integer
97  return cf[0];
98 
99  double tmp = 1. / cf[N - 1];
100  for (idx i = N - 2; i != 0; --i) {
101  tmp = 1. / (tmp + cf[i]);
102  }
103 
104  return cf[0] + tmp;
105 }
106 
115 inline bigint gcd(bigint a, bigint b) {
116  // EXCEPTION CHECKS
117 
118  if (a == 0 && b == 0)
119  throw exception::OutOfRange("qpp::gcd()");
120  // END EXCEPTION CHECKS
121 
122  if (a == 0 || b == 0)
123  return (std::max(std::abs(a), std::abs(b)));
124 
125  bigint result = 1;
126  while (b) {
127  result = b;
128  b = a % result;
129  a = result;
130  }
131 
132  return (result > 0) ? result : -result;
133 }
134 
142 inline bigint gcd(const std::vector<bigint>& as) {
143  // EXCEPTION CHECKS
144 
145  if (as.empty())
146  throw exception::ZeroSize("qpp::gcd()");
147  // END EXCEPTION CHECKS
148 
149  bigint result = as[0]; // convention: gcd({a}) = a
150  for (idx i = 1; i < as.size(); ++i) {
151  result = gcd(result, as[i]);
152  }
153 
154  return (result > 0) ? result : -result;
155 }
156 
165 inline bigint lcm(bigint a, bigint b) {
166  // EXCEPTION CHECKS
167 
168  if (a == 0 && b == 0)
169  throw exception::OutOfRange("qpp::lcm()");
170  // END EXCEPTION CHECKS
171 
172  bigint result = a * b / gcd(a, b);
173 
174  return (result > 0) ? result : -result;
175 }
176 
184 inline bigint lcm(const std::vector<bigint>& as) {
185  // EXCEPTION CHECKS
186 
187  if (as.empty())
188  throw exception::ZeroSize("qpp::lcm()");
189 
190  if (as.size() == 1) // convention: lcm({a}) = a
191  return as[0];
192 
193  if (std::find(std::begin(as), std::end(as), 0) != std::end(as))
194  throw exception::OutOfRange("qpp::lcm()");
195  // END EXCEPTION CHECKS
196 
197  bigint result = as[0]; // convention: lcm({n}) = a
198 
199  for (idx i = 1; i < as.size(); ++i) {
200  result = lcm(result, as[i]);
201  }
202 
203  return (result > 0) ? result : -result;
204 }
205 
212 inline std::vector<idx> invperm(const std::vector<idx>& perm) {
213  // EXCEPTION CHECKS
214 
215  if (!internal::check_perm(perm))
216  throw exception::PermInvalid("qpp::invperm()");
217  // END EXCEPTION CHECKS
218 
219  // construct the inverse
220  std::vector<idx> result(perm.size());
221  for (idx i = 0; i < perm.size(); ++i)
222  result[perm[i]] = i;
223 
224  return result;
225 }
226 
235 inline std::vector<idx> compperm(const std::vector<idx>& perm,
236  const std::vector<idx>& sigma) {
237  // EXCEPTION CHECKS
238 
239  if (!internal::check_perm(perm))
240  throw exception::PermInvalid("qpp::compperm()");
241  if (!internal::check_perm(sigma))
242  throw exception::PermInvalid("qpp::compperm()");
243  if (perm.size() != sigma.size())
244  throw exception::PermInvalid("qpp::compperm()");
245  // END EXCEPTION CHECKS
246 
247  // construct the composition perm(sigma)
248  std::vector<idx> result(perm.size());
249  for (idx i = 0; i < perm.size(); ++i)
250  result[i] = perm[sigma[i]];
251 
252  return result;
253 }
254 
263 inline std::vector<bigint> factors(bigint a) {
264  // flip the sign if necessary
265  a = std::abs(a);
266 
267  // EXCEPTION CHECKS
268 
269  if (a == 0 || a == 1)
270  throw exception::OutOfRange("qpp::factors()");
271  // END EXCEPTION CHECKS
272 
273  std::vector<bigint> result;
274  bigint d = 2;
275 
276  while (a > 1) {
277  while (a % d == 0) {
278  result.emplace_back(d);
279  a /= d;
280  }
281  ++d;
282  if (d * d > a) // changes the running time from O(a) to O(sqrt(a))
283  {
284  if (a > 1) {
285  result.emplace_back(a);
286  }
287  break;
288  }
289  }
290 
291  return result;
292 }
293 
304 inline bigint modmul(bigint a, bigint b, bigint p) {
305  using ubigint = unsigned long long int;
306 
307  // EXCEPTION CHECKS
308 
309  if (p < 1)
310  throw exception::OutOfRange("qpp::modmul()");
311  // END EXCEPTION CHECKS
312 
313  if (a == 0 || b == 0)
314  return 0;
315 
316  ubigint ua, ub, up;
317 
318  bool is_positive = true;
319  if (a < 0) {
320  ua = -a;
321  is_positive = false;
322  } else
323  ua = a;
324  if (b < 0) {
325  ub = -b;
326  is_positive = false;
327  } else
328  ub = b;
329 
330  if (a < 0 && b < 0)
331  is_positive = true;
332 
333  up = static_cast<ubigint>(p);
334  ua %= up;
335  ub %= up;
336 
337  // the code below is taken from
338  // http://stackoverflow.com/a/18680280/3093378
339  ubigint res = 0;
340  ubigint temp_b;
341 
342  if (ub > ua)
343  std::swap(ua, ub);
344 
345  /* only needed if un may be >= up */
346  if (ub >= up) {
347  if (up > std::numeric_limits<ubigint>::max() / 2u)
348  ub -= up;
349  else
350  ub %= up;
351  }
352 
353  while (ua != 0) {
354  if (ua & 1) {
355  /* add un to res, modulo p, without overflow */
356  /* equiv to if (res + ub >= p), without overflow */
357  if (ub >= up - res)
358  res -= up;
359  res += ub;
360  }
361  ua >>= 1;
362 
363  /* double b, modulo a */
364  temp_b = ub;
365  if (ub >= up - ub) /* equiv to if (2 * ub >= p), without overflow */
366  temp_b -= up;
367  ub += temp_b;
368  }
369 
370  return is_positive ? static_cast<bigint>(res)
371  : static_cast<bigint>(p - res);
372 }
373 
387 inline bigint modpow(bigint a, bigint n, bigint p) {
388  // EXCEPTION CHECKS
389 
390  if (a < 0 || n < 0 || p < 1)
391  throw exception::OutOfRange("qpp::modpow()");
392 
393  if (a == 0 && n == 0)
394  throw exception::OutOfRange("qpp::modpow()");
395  // END EXCEPTION CHECKS
396 
397  if (a == 0 && n > 0)
398  return 0;
399 
400  if (n == 0 && p == 1)
401  return 0;
402 
403  bigint result = 1;
404 
405  for (; n > 0; n /= 2) {
406  if (n % 2)
407  result = modmul(result, a, p) % p; // MULTIPLY
408  a = modmul(a, a, p) % p; // SQUARE
409  }
410 
411  return result;
412 }
413 
424 inline std::tuple<bigint, bigint, bigint> egcd(bigint a, bigint b) {
425  // EXCEPTION CHECKS
426 
427  if (a == 0 && b == 0)
428  throw exception::OutOfRange("qpp::egcd()");
429  // END EXCEPTION CHECKS
430 
431  bigint m, n, c, q, r;
432  bigint m1 = 0, m2 = 1, n1 = 1, n2 = 0;
433 
434  while (b) {
435  q = a / b, r = a - q * b;
436  m = m2 - q * m1, n = n2 - q * n1;
437  a = b, b = r;
438  m2 = m1, m1 = m, n2 = n1, n1 = n;
439  }
440  c = a, m = m2, n = n2;
441 
442  // correct the signs
443  if (c < 0) {
444  m = -m;
445  n = -n;
446  c = -c;
447  }
448 
449  return std::make_tuple(m, n, c);
450 }
451 
462 inline bigint modinv(bigint a, bigint p) {
463  // EXCEPTION CHECKS
464 
465  if (a <= 0 || p <= 0)
466  throw exception::OutOfRange("qpp::modinv()");
467 
468  bigint x, y;
469  bigint gcd_ap;
470  std::tie(x, y, gcd_ap) = egcd(p, a);
471 
472  if (gcd_ap != 1)
473  throw exception::OutOfRange("qpp::modinv()");
474  // END EXCEPTION CHECKS
475 
476  return (y > 0) ? y : y + p;
477 }
478 
487 inline bool isprime(bigint p, idx k = 80) {
488  p = std::abs(p);
489 
490  // EXCEPTION CHECKS
491 
492  if (p < 2)
493  throw exception::OutOfRange("qpp::isprime()");
494  // END EXCEPTION CHECKS
495 
496  if (p == 2 || p == 3)
497  return true;
498 
499  // // perform a Fermat primality test
500  bigint x = rand(2, p - 1);
501  if (modpow(x, p - 1, p) != 1)
502  return false;
503 
504  // compute u and r
505  bigint u = 0, r = 1;
506 
507  // write n - 1 as 2^u * r
508  for (bigint i = p - 1; i % 2 == 0; ++u, i /= 2)
509  ;
510  r = (p - 1) / static_cast<bigint>(std::llround(std::pow(2, u)));
511 
512  // repeat k times
513  for (idx i = 0; i < k; ++i) {
514  // pick a random integer a in the range [2, p - 2]
515  bigint a = rand(2, p - 2);
516 
517  // set z = a^r mod p
518  bigint z = modpow(a, r, p);
519 
520  if (z == 1 || z == p - 1)
521  continue;
522 
523  // repeat u - 1 times
524  bool jump = false;
525  for (idx j = 0; j < static_cast<idx>(u); ++j) {
526  z = (modmul(z, z, p)) % p;
527  if (z == 1) {
528  // composite
529  return false;
530  }
531  if (z == p - 1) {
532  jump = true;
533  break;
534  }
535  }
536  if (jump)
537  continue;
538 
539  return false;
540  }
541 
542  return true;
543 }
544 
554 // A std::optional<bigint> return type would have been awesome here!
555 inline bigint randprime(bigint a, bigint b, idx N = 1000) {
556  // EXCEPTION CHECKS
557 
558  if (a > b)
559  throw exception::OutOfRange("qpp::randprime()");
560  // END EXCEPTION CHECKS
561 
562  idx i = 0;
563  for (; i < N; ++i) {
564  // select a candidate
565  bigint candidate = rand(a, b);
566  if (std::abs(candidate) < 2)
567  continue;
568  if (std::abs(candidate) == 2)
569  return candidate;
570 
571  // perform a Fermat test
572  bigint x = rand(2, candidate - 1);
573  if (modpow(x, candidate - 1, candidate) != 1)
574  continue; // candidate fails
575 
576  // passed the Fermat test, perform a Miller-Rabin test
577  if (isprime(candidate))
578  return candidate;
579  }
580 
581  if (i == N)
582  throw exception::CustomException("qpp::randprime()",
583  "Prime not found!");
584 
585  return 0; // so we don't get a warning
586 }
587 
588 // see http://mathworld.wolfram.com/Convergent.html
597 inline std::vector<std::pair<int, int>>
598 convergents(const std::vector<int>& cf) {
599 
600  idx N = cf.size();
601  // EXCEPTIONS CHECKS
602 
603  if (N == 0)
604  throw exception::OutOfRange("qpp::convergents()");
605 
606  std::vector<std::pair<int, int>> result(N);
607  int a_minus_one = 1;
608  int b_minus_one = 0;
609  int a_0 = cf[0];
610  int b_0 = 1;
611 
612  // END EXCEPTIONS CHECKS
613 
614  result[0] = std::make_pair(a_0, b_0);
615  if (N == 1)
616  return result;
617 
618  result[1] = std::make_pair(cf[1] * std::get<0>(result[0]) + a_minus_one,
619  cf[1] * std::get<1>(result[0]) + b_minus_one);
620  for (idx i = 2; i < N; ++i) {
621  result[i].first =
622  cf[i] * std::get<0>(result[i - 1]) + std::get<0>(result[i - 2]);
623  result[i].second =
624  cf[i] * std::get<1>(result[i - 1]) + std::get<1>(result[i - 2]);
625  }
626 
627  return result;
628 }
629 
630 // see http://mathworld.wolfram.com/Convergent.html
644 inline std::vector<std::pair<int, int>> convergents(double x, idx N) {
645 
646  // EXCEPTIONS CHECKS
647 
648  if (N == 0)
649  throw exception::OutOfRange("qpp::convergents()");
650 
651  auto cf = x2contfrac(x, N);
652  if (cf.size() < N)
653  N = cf.size();
654 
655  return convergents(x2contfrac(x, N));
656 }
657 
658 } /* namespace qpp */
659 
660 #endif /* NUMBER_THEORY_H_ */
Quantum++ main namespace.
Definition: circuits.h:35