File: isprime.m

package info (click to toggle)
octave 9.4.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 144,300 kB
  • sloc: cpp: 332,784; ansic: 77,239; fortran: 20,963; objc: 9,396; sh: 8,213; yacc: 4,925; lex: 4,389; perl: 1,544; java: 1,366; awk: 1,259; makefile: 648; xml: 189
file content (222 lines) | stat: -rw-r--r-- 7,621 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
########################################################################
##
## Copyright (C) 2000-2024 The Octave Project Developers
##
## See the file COPYRIGHT.md in the top-level directory of this
## distribution or <https://octave.org/copyright/>.
##
## This file is part of Octave.
##
## Octave is free software: you can redistribute it and/or modify it
## under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## Octave is distributed in the hope that it will be useful, but
## WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Octave; see the file COPYING.  If not, see
## <https://www.gnu.org/licenses/>.
##
########################################################################

## -*- texinfo -*-
## @deftypefn {} {@var{tf} =} isprime (@var{x})
## Return a logical array which is true where the elements of @var{x} are prime
## numbers and false where they are not.
##
## A prime number is conventionally defined as a positive integer greater than
## 1 (e.g., 2, 3, @dots{}) which is divisible only by itself and 1.  Octave
## extends this definition to include both negative integers and complex
## values.  A negative integer is prime if its positive counterpart is prime.
## This is equivalent to @code{isprime (abs (x))}.
##
## If @code{class (@var{x})} is complex, then primality is tested in the domain
## of Gaussian integers (@url{https://en.wikipedia.org/wiki/Gaussian_integer}).
## Some non-complex integers are prime in the ordinary sense, but not in the
## domain of Gaussian integers.  For example, @math{5 = (1+2i)*(1-2i)} shows
## that 5 is not prime because it has a factor other than itself and 1.
## Exercise caution when testing complex and real values together in the same
## matrix.
##
## Examples:
##
## @example
## @group
## isprime (1:6)
##   @result{}  0  1  1  0  1  0
## @end group
## @end example
##
## @example
## @group
## isprime ([i, 2, 3, 5])
##   @result{}  0  0  1  0
## @end group
## @end example
##
## Programming Note: @code{isprime} is suitable for all @var{x}
## in the range abs(@var{x})
## @tex
## $ < 2^{64}$.
## @end tex
## @ifnottex
##  < 2^64.
## @end ifnottex
## Cast inputs larger than @code{flintmax} to @code{uint64}.
##
## For larger inputs, use ‘sym’ if you have the Symbolic package installed
## and loaded:
##
## @example
## @group
## isprime (sym ('58745389709258902525390450') + (0:4))
##    @result{}  0  1  0  0  0
## @end group
## @end example
##
## Compatibility Note: @sc{matlab} does not extend the definition of prime
## numbers and will produce an error if given negative or complex inputs.
## @seealso{primes, factor, gcd, lcm}
## @end deftypefn

function tf = isprime (x)

  if (nargin < 1)
    print_usage ();
  elseif (any (fix (x) != x))
    error ("isprime: X contains non-integer entries");
  endif

  if (isempty (x))
    tf = x;
    return;
  endif

  if (iscomplex (x))
    tf = isgaussianprime (x);
    return;
  endif

  ## Code strategy is to quickly compare entries in x with small primes
  ## using lookup(), then do direct division on larger numbers up to
  ## a threshold, then call Miller-Rabin for numbers over the threshold.

  x = abs (x);  # handle negative entries

  ## Generate prime table of suitable length up to maxp.
  ## The value of maxp needs to be at least 37,
  ## because of the method used by __isprimelarge__ below.
  maxp = 37;
  pr = [2 3 5 7 11 13 17 19 23 29 31 37];
  tf = lookup (pr, x, "b");  # quick search for table matches.

  THRESHOLD = 195e8;
  ## FIXME: THRESHOLD is the input value at which Miller-Rabin
  ## becomes more efficient than direct division. For smaller numbers,
  ## use direct division. For larger numbers, use Miller-Rabin.
  ##
  ## From numerical experiments in Jun 2022, this was observed:
  ##    THRESHOLD       Division       Miller-Rabin
  ##         29e9       29.8196s       26.2484s       (previous value)
  ##         20e9       26.7445s       26.0161s
  ##         10e9       20.9330s       25.3247s
  ##         19e9       26.5397s       26.8987s
  ##        195e8       26.5735s       26.4749s
  ## which is close enough, so new threshold = 195e8.
  ##
  ## The test code was this:
  ##   n = THRESHOLD - (1:1e7); tic; isprime(n); toc
  ##   n = THRESHOLD + (1:1e7); tic; isprime(n); toc
  ##
  ## Two notes for future programmers:
  ##
  ## 1. Test and tune THRESHOLD periodically. Miller-Rabin is only CPU-limited,
  ##    while factorization by division is very memory-intensive. This is
  ##    plainly noticeable from the loudness of the computer fans when running
  ##    the division technique! CPU speed and RAM speed scale differently over
  ##    time, so test and tune THRESHOLD periodically.
  ##
  ## 2. If you make improvements elsewhere in the code that favor one over
  ##    the other (not symmetric), you should also retune THRESHOLD afterwards.
  ##    If the Miller-Rabin part is sped up, the optimum THRESHOLD will
  ##    decrease, and if factorization is sped up, it will increase.
  ##

  ## Process large entries that are still suitable for direct division
  m = x (x > maxp & x <= THRESHOLD);
  if ( ! isempty (m))
    ## Start by dividing through by the small primes until the remaining list
    ## of entries is small (and most likely prime themselves).
    pr2 = primes (sqrt (max (m)));
    tf |= lookup (pr2, x, "b");
    for p = pr2
      m = m(rem (m, p) != 0);
      if (numel (m) < numel (pr) / 10)
        break;
      endif
    endfor

    ## Check the remaining list of possible primes against the remaining
    ## prime factors which were not tested in the for loop.
    ## This is just an optimization to use arrayfun over for loop.
    pr2 = pr2 (pr2 > p);
    mm = arrayfun (@(x) all (rem (x, pr2)), m);
    m = m(mm);

    ## Add any remaining entries, which are truly prime, to the results.
    if ( ! isempty (m))
      tf |= lookup (sort (m), x, "b");
    endif
  endif

  ## Process remaining entries (everything above THRESHOLD) with Miller-Rabin
  ii = (x(:)' > THRESHOLD);
  tf(ii) = __isprimelarge__ (x(ii));

endfunction

function tf = isgaussianprime (z)

  ## Assume prime unless proven otherwise
  tf = true (size (z));

  x = real (z);
  y = imag (z);

  ## If purely real or purely imaginary, ordinary prime test for
  ## that complex part if that part is 3 mod 4.
  xidx = y==0 & mod (x, 4) == 3;
  yidx = x==0 & mod (y, 4) == 3;

  tf(xidx) &= isprime (x(xidx));
  tf(yidx) &= isprime (y(yidx));

  ## Otherwise, prime if x^2 + y^2 is prime
  zidx = ! (xidx | yidx);          # Skip entries that were already evaluated
  zabs = x(zidx).^2 + y(zidx).^2;
  tf(zidx) &= isprime (zabs);

endfunction


%!assert (isprime (3), true)
%!assert (isprime (4), false)
%!assert (isprime (uint64 (18446744073709551557)), true)
%!assert (isprime (5i), false)
%!assert (isprime (7i), true)
%!assert (isprime ([1+2i, (2+3i)*(-1+2i)]), [true, false])
%!assert (isprime (-2), true)
%!assert (isprime (complex (-2)), false)
%!assert (isprime (2i), false)
%!assert (isprime ([i, 2, 3, 5]), [false, false, true, false])
%!assert (isprime (0), false)
%!assert (isprime (magic (3)), logical ([0, 0, 0; 1, 1, 1; 0, 0, 1]))

## Test input validation
%!error <Invalid call> isprime ()
%!error <X contains non-integer entries> isprime (0.5i)
%!error <X contains non-integer entries> isprime (0.5)