GNU Octave  8.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
__isprimelarge__.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2021-2023 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING. If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 #if defined (HAVE_CONFIG_H)
27 # include "config.h"
28 #endif
29 
30 #include "defun.h"
31 #include "error.h"
32 #include "ovl.h"
33 
35 
36 // This function implements the Schrage technique for modular multiplication.
37 // The returned value is equivalent to "mod (a*b, modulus)"
38 // but calculated without overflow.
39 uint64_t
40 safemultiply (uint64_t a, uint64_t b, uint64_t modulus)
41 {
42  if (! a || ! b)
43  return 0;
44  else if (b == 1)
45  return a;
46  else if (a == 1)
47  return b;
48  else if (a > b)
49  {
50  uint64_t tmp = a;
51  a = b;
52  b = tmp;
53  }
54 
55  uint64_t q = modulus / a;
56  uint64_t r = modulus - q * a;
57  uint64_t term1 = a * (b % q);
58  uint64_t term2 = (r < q) ? r * (b / q) : safemultiply (r, b / q, modulus);
59  return (term1 > term2) ? (term1 - term2) : (term1 + modulus - term2);
60 }
61 
62 // This function returns "mod (a^b, modulus)"
63 // but calculated without overflow.
64 uint64_t
65 safepower (uint64_t a, uint64_t b, uint64_t modulus)
66 {
67  uint64_t retval = 1;
68  while (b > 0)
69  {
70  if (b & 1)
71  retval = safemultiply (retval, a, modulus);
72  b >>= 1;
73  a = safemultiply (a, a, modulus);
74  }
75  return retval;
76 }
77 
78 // This function implements a single round of Miller-Rabin primality testing.
79 // Returns false if composite, true if pseudoprime for this divisor.
80 bool
81 millerrabin (uint64_t div, uint64_t d, uint64_t r, uint64_t n)
82 {
83  uint64_t x = safepower (div, d, n);
84  if (x == 1 || x == n-1)
85  return true;
86 
87  for (uint64_t j = 1; j < r; j++)
88  {
89  x = safemultiply (x, x, n);
90  if (x == n-1)
91  return true;
92  }
93  return false;
94 }
95 
96 // This function uses the Miller-Rabin test to find out whether the input is
97 // prime or composite. The input is required to be a scalar 64-bit integer.
98 bool
99 isprimescalar (uint64_t n)
100 {
101  // Fast return for even numbers.
102  // n==2 is excluded by the time this function is called.
103  if (! (n & 1))
104  return false;
105 
106  // n is now odd. Rewrite n as d * 2^r + 1, where d is odd.
107  uint64_t d = n-1;
108  uint64_t r = 0;
109  while (! (d & 1))
110  {
111  d >>= 1;
112  r++;
113  }
114 
115  // Miller-Rabin test with the first 12 primes.
116  // If the number passes all 12 tests, then it is prime.
117  // If it fails any, then it is composite.
118  // The first 12 primes suffice to test all 64-bit integers.
119  return millerrabin ( 2, d, r, n) && millerrabin ( 3, d, r, n)
120  && millerrabin ( 5, d, r, n) && millerrabin ( 7, d, r, n)
121  && millerrabin (11, d, r, n) && millerrabin (13, d, r, n)
122  && millerrabin (17, d, r, n) && millerrabin (19, d, r, n)
123  && millerrabin (23, d, r, n) && millerrabin (29, d, r, n)
124  && millerrabin (31, d, r, n) && millerrabin (37, d, r, n);
125 
126  /*
127  Mathematical references for the curious as to why we need only
128  the 12 smallest primes for testing all 64-bit numbers:
129  (1) https://oeis.org/A014233
130  Comment: a(12) > 2^64. Hence the primality of numbers < 2^64 can be
131  determined by asserting strong pseudoprimality to all prime bases <= 37
132  (=prime(12)). Testing to prime bases <=31 does not suffice,
133  as a(11) < 2^64 and a(11) is a strong pseudoprime
134  to all prime bases <= 31 (=prime(11)). - Joerg Arndt, Jul 04 2012
135  (2) https://arxiv.org/abs/1509.00864
136  Strong Pseudoprimes to Twelve Prime Bases
137  Jonathan P. Sorenson, Jonathan Webster
138 
139  In addition, a source listed here: https://miller-rabin.appspot.com/
140  reports that all 64-bit numbers can be covered with only 7 divisors,
141  namely 2, 325, 9375, 28178, 450775, 9780504, and 1795265022.
142  There was no peer-reviewed article to back it up though,
143  so this code uses the 12 primes <= 37.
144  */
145 
146 }
147 
148 DEFUN (__isprimelarge__, args, ,
149  doc: /* -*- texinfo -*-
150 @deftypefn {} {@var{x} =} __isprimelarge__ (@var{n})
151 Use the Miller-Rabin test to find out whether the elements of N are prime or
152 composite. The input N is required to be a vector or array of 64-bit integers.
153 You should call isprime(N) instead of directly calling this function.
154 
155 @seealso{isprime, factor}
156 @end deftypefn */)
157 {
158  if (args.length () != 1)
159  print_usage ();
160 
161  // This function is intended for internal use by isprime.m,
162  // so the following error handling should not be necessary. But it is
163  // probably good practice for any curious users calling it directly.
164  uint64NDArray vec = args(0).xuint64_array_value
165  ("__isprimelarge__: unable to convert input. Call isprime() instead.");
166 
167  boolNDArray retval (vec.dims(), false);
168 
169  for (octave_idx_type i = vec.numel() - 1; i >= 0; i--)
170  retval(i) = isprimescalar (vec(i));
171  // Note: If vec(i) <= 37, this function could go into an infinite loop.
172  // That situation does not arise when calling this from isprime.m
173  // but it could arise if the user calls this function directly with low input
174  // or negative input.
175  // But it turns out that adding this validation:
176  // "if (vec(i) <= 37) then raise an error else call isprimescalar (vec(i))"
177  // slows this function down by over 20% for some inputs,
178  // so it is better to leave all the input validation in isprime.m
179  // and not add it here. The function DOCSTRING now explicitly says:
180  // "You should call isprime(N) instead of directly calling this function."
181 
182  return ovl (retval);
183 }
184 
185 /*
186 %!assert (__isprimelarge__ (41:50), logical ([1 0 1 0 0 0 1 0 0 0]))
187 %!assert (__isprimelarge__ (uint64 (12345)), false)
188 %!assert (__isprimelarge__ (uint64 (2147483647)), true)
189 %!assert (__isprimelarge__ (uint64 (2305843009213693951)), true)
190 %!assert (__isprimelarge__ (uint64 (18446744073709551557)), true)
191 
192 %!assert (__isprimelarge__ ([uint64(12345), uint64(2147483647), ...
193 %! uint64(2305843009213693951), ...
194 %! uint64(18446744073709551557)]),
195 %! logical ([0 1 1 1]))
196 
197 %!error <unable to convert input> (__isprimelarge__ ({'foo'; 'bar'}))
198 */
199 
200 
201 // This function implements a fast, private GCD function
202 // optimized for uint64_t. No input validation by design.
203 inline
204 uint64_t
205 localgcd (uint64_t a, uint64_t b)
206 {
207  return (a <= b) ? ( (b % a == 0) ? a : localgcd (a, b % a) )
208  : ( (a % b == 0) ? b : localgcd (a % b, b) );
209 }
210 
211 // This function implements a textbook version of the Pollard Rho
212 // factorization algorithm with Brent update.
213 // The code is short and simple, but the math behind it is complicated.
214 uint64_t
215 pollardrho (uint64_t n, uint64_t c = 1)
216 {
217  uint64_t i = 1, j = 2; // cycle index values
218  uint64_t x = (c+1) % n; // can also be rand () % n
219  uint64_t y = x; // other value in the chain
220  uint64_t g = 0; // GCD
221 
222  while (true)
223  {
224  i++;
225 
226  // Calculate x = mod (x^2 + c, n) without overflow.
227  x = safemultiply (x, x, n) + c;
228  if (x >= n)
229  x -= n;
230 
231  // Calculate GCD (abs (x-y), n).
232  g = (x > y) ? localgcd (x - y, n) : (x < y) ? localgcd (y - x, n) : 0;
233 
234  if (i == j) // cycle detected ==> double j
235  {
236  y = x;
237  j <<= 1;
238  }
239 
240  if (g == n || i > 1000000) // cut losses, restart with a different c
241  return pollardrho (n, c + 2);
242 
243  if (g > 1) // found GCD ==> exit loop properly
244  {
245  error_unless (n % g == 0); // theoretical possibility of GCD error
246  return g;
247  }
248  }
249 }
250 
251 DEFUN (__pollardrho__, args, ,
252  doc: /* -*- texinfo -*-
253 @deftypefn {} {@var{x} =} __pollardrho__ (@var{n})
254 Private function. Use the Pollard Rho test to find a factor of @var{n}.
255 The input @var{n} is required to be a composite 64-bit integer.
256 Do not pass it a prime input! You should call factor(@var{n}) instead
257 of directly calling this function.
258 
259 @seealso{isprime, factor}
260 @end deftypefn */)
261 {
262  if (args.length () != 1)
263  print_usage ();
264 
265  octave_uint64 inp = args(0).xuint64_scalar_value
266  ("__pollardrho__: unable to convert input. Call factor() instead.");
267 
268  uint64_t n = inp;
269  octave_uint64 retval = pollardrho (n);
270 
271  return ovl (retval);
272 }
273 
274 /*
275 %!assert (__pollardrho__ (uint64 (78567695338254293)), uint64 (443363))
276 %!assert (__pollardrho__ (1084978968791), uint64 (832957))
277 
278 %!error <unable to convert input> (__pollardrho__ ({'foo'; 'bar'}))
279 */
280 
OCTAVE_END_NAMESPACE(octave)
bool isprimescalar(uint64_t n)
bool millerrabin(uint64_t div, uint64_t d, uint64_t r, uint64_t n)
uint64_t safemultiply(uint64_t a, uint64_t b, uint64_t modulus)
uint64_t localgcd(uint64_t a, uint64_t b)
uint64_t pollardrho(uint64_t n, uint64_t c=1)
uint64_t safepower(uint64_t a, uint64_t b, uint64_t modulus)
OCTARRAY_OVERRIDABLE_FUNC_API octave_idx_type numel(void) const
Number of elements in the array.
Definition: Array.h:414
OCTARRAY_OVERRIDABLE_FUNC_API const dim_vector & dims(void) const
Return a const-reference so that dims ()(i) works efficiently.
Definition: Array.h:503
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
OCTINTERP_API void print_usage(void)
Definition: defun-int.h:72
#define DEFUN(name, args_name, nargout_name, doc)
Macro to define a builtin function.
Definition: defun.h:56
void error_unless(bool cond)
Definition: error.h:549
F77_RET_T const F77_DBLE const F77_DBLE F77_DBLE * d
F77_RET_T const F77_DBLE * x
octave_idx_type n
Definition: mx-inlines.cc:753
T * r
Definition: mx-inlines.cc:773
octave_value_list ovl(const OV_Args &... args)
Construct an octave_value_list with less typing.
Definition: ovl.h:211