GNU Octave 10.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 
Loading...
Searching...
No Matches
__isprimelarge__.cc
Go to the documentation of this file.
1////////////////////////////////////////////////////////////////////////
2//
3// Copyright (C) 2021-2025 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.
39uint64_t
40safemultiply (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.
64uint64_t
65safepower (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.
80bool
81millerrabin (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.
98bool
99isprimescalar (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
148DEFUN (__isprimelarge__, args, ,
149 doc: /* -*- texinfo -*-
150@deftypefn {} {@var{x} =} __isprimelarge__ (@var{n})
151Use the Miller-Rabin test to find out whether the elements of N are prime or
152composite. The input N is required to be a vector or array of 64-bit integers.
153You 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.
203inline
204uint64_t
205localgcd (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.
214uint64_t
215pollardrho (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 return g;
245 }
246}
247
248DEFUN (__pollardrho__, args, ,
249 doc: /* -*- texinfo -*-
250@deftypefn {} {@var{x} =} __pollardrho__ (@var{n})
251Private function. Use the Pollard Rho test to find a factor of @var{n}.
252The input @var{n} is required to be a composite 64-bit integer.
253Do not pass it a prime input! You should call factor(@var{n}) instead
254of directly calling this function.
255
256@seealso{isprime, factor}
257@end deftypefn */)
258{
259 if (args.length () != 1)
260 print_usage ();
261
262 octave_uint64 inp = args(0).xuint64_scalar_value
263 ("__pollardrho__: unable to convert input. Call factor() instead.");
264
265 uint64_t n = inp;
266 octave_uint64 retval = pollardrho (n);
267
268 return ovl (retval);
269}
270
271/*
272%!assert (__pollardrho__ (uint64 (78567695338254293)), uint64 (443363))
273%!assert (__pollardrho__ (1084978968791), uint64 (832957))
274
275%!error <unable to convert input> (__pollardrho__ ({'foo'; 'bar'}))
276*/
277
278OCTAVE_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)
const dim_vector & dims() const
Return a const-reference so that dims ()(i) works efficiently.
Definition Array.h:507
octave_idx_type numel() const
Number of elements in the array.
Definition Array.h:418
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
void print_usage()
Definition defun-int.h:72
#define DEFUN(name, args_name, nargout_name, doc)
Macro to define a builtin function.
Definition defun.h:56
F77_RET_T const F77_DBLE const F77_DBLE F77_DBLE * d
F77_RET_T const F77_DBLE * x
octave_value_list ovl(const OV_Args &... args)
Construct an octave_value_list with less typing.
Definition ovl.h:217