Skip to content

Commit b17dce4

Browse files
authored
Merge pull request #532 from StephenWampler/fastprime
Support large integer primes, fix typos in matrix_util.icn
2 parents 4724eb9 + aecb669 commit b17dce4

File tree

2 files changed

+107
-13
lines changed

2 files changed

+107
-13
lines changed

uni/lib/fastprime.icn

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
#<p>
2+
# Efficient support for large primes.
3+
#</p>
4+
5+
link factors # Need to get <i>prime</i> generator
6+
7+
#<p>
8+
# Generate all primes >= <i>n</i>.
9+
# <[param n start looking for primes >= <i>n</> (defaults to 2)]>
10+
#</p>
11+
procedure genprimes(n)
12+
/n := 2
13+
if n = 2 then suspend 2
14+
if n%2 = 0 then n +:= 1
15+
suspend is_prime(seq(n,2))
16+
end
17+
18+
#<p>
19+
# Is <i>n</i> probably prime at least to some large probability?
20+
# Fully accurate for <i>n</i> < 341550071728321. Accuracy above that
21+
# value depends on value of <i>precision</i>. The default value for
22+
# <i>precision</i> is generally sufficient.
23+
# Google the <b>Miller-Rabin primality test</b> for details on the
24+
# algorithm.
25+
# <[param n integer value to test for primality.]>
26+
# <[param precision degree of confidence to use (defaults to 16).]>
27+
#</p>
28+
procedure is_prime(n, precision)
29+
static known_primes, k_primes_set
30+
initial {
31+
/precision := 16
32+
every put(known_primes := [], prime()\17)
33+
k_primes_set := set(known_primes)
34+
}
35+
/precision := 16
36+
37+
if n <= 1 then fail
38+
# Fast check for small n
39+
if member(k_primes_set, n) then return n
40+
if (n % known_primes[1 to precision]) = 0 then fail
41+
42+
# d and s are needed for primetest()
43+
(d := n-1, s := 0)
44+
while d%2 = 0 do (d /:= 2, s +:= 1)
45+
# Returns exact according to http://primes.utm.edu/prove/prove2_3.html
46+
if n < 1373653 then return primetest(n, known_primes[1:2], d, s)
47+
if n < 25326001 then return primetest(n, known_primes[1:3], d, s)
48+
if n < 118670087467 then
49+
if n ~= 3215031751 then return primetest(n, known_primes[1:4], d, s)
50+
else fail
51+
if n < 2152302898747 then return primetest(n, known_primes[1:5], d, s)
52+
if n < 3474749660383 then return primetest(n, known_primes[1:6], d, s)
53+
if n < 341550071728321 then return primetest(n, known_primes[1:7], d, s)
54+
# otherwise
55+
return primetest(n, known_primes[1:precision], d, s)
56+
end
57+
58+
#<p>
59+
# Efficient implementation of <i>(b^e)%m</i>.
60+
# <[param b base]>
61+
# <[param e exponent]>
62+
# <[param m modulus]>
63+
#</p>
64+
procedure pow(b,e,m)
65+
r := 1
66+
while e > 0 do
67+
if e%2 = 0 then (e /:= 2, b := (b*b)%m)
68+
else (e -:= 1, r := (r*b)%m, e /:= 2, b := (b*b)%m)
69+
return r
70+
end
71+
72+
#<p>
73+
# Is <i>n</i> probably prime, based on the known primes in <i>A</i>?
74+
# (Used to simplify the code in <b>is_prime</b> as it just wraps
75+
# and inverts <b>try_composite</b>.)
76+
# <b>Internal use only unless you know how to compute <i>d</i> and <i>s</i>.</b>
77+
#</p>
78+
procedure primetest(n,A,d,s)
79+
if try_composite(n,!A,d,s) then fail
80+
return n
81+
end
82+
83+
#<p>
84+
# Succeed if <i>n</i> might be a composite, based solely on the value
85+
# of <i>a</i>.
86+
# <b>Internal use only unless you know how to compute <i>d</i> and <i>s</i>.</b>
87+
#</p>
88+
procedure try_composite(n, a, d, s)
89+
if pow(a,d,n) = 1 then fail
90+
every i := 0 to (s-1) do if pow(a,(2^i)*d,n) = n-1 then fail
91+
return n
92+
end

uni/lib/matrix_util.icn

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,10 @@ end
2828
#<p>
2929
# <[param m number of rows]>
3030
# <[param n number of columns]>
31-
# <[param x initial value of every element]>
31+
# <[param x initial value of every element (defaults to 0)]>
3232
# <[returns a <tt>m</tt> x <tt>n<tt> matrix with <tt>x</tt> everywhere]>
3333
#</p>
34-
procedure m_constant(m,n,x:0.0)
34+
procedure m_constant(m,n,x:0)
3535
local A
3636
/n := m
3737

@@ -44,11 +44,11 @@ end
4444
# Creates a m x n matrix with ones on the diagonal and zeros everywhere else
4545
# <[param m number of rows]>
4646
# <[param n number of columns]>
47-
# <[param zero (<i>optional</i>) can be used in place of <tt>0.0</tt>]>
48-
# <[param one (<i>optional</i>) can be used in place of <tt>1.0</tt>]>
47+
# <[param zero (<i>optional</i>) can be used in place of <tt>0</tt>]>
48+
# <[param one (<i>optional</i>) can be used in place of <tt>1</tt>]>
4949
# <[returns <tt>m</tt>X<tt>n</tt> identify matrix]>
5050
#</p>
51-
procedure m_identity(m,n, zero:0.0,one:1.0)
51+
procedure m_identity(m,n, zero:0,one:1)
5252
local A, i
5353
/n := m
5454

@@ -106,17 +106,18 @@ end
106106
# <[param M1 first matrix]>
107107
# <[param M2 second matrix]>
108108
# <[param op binary operation to invoke]>
109+
# <[param zero (<i>optional</i>) can be used in place of <tt>0</tt>]>
109110
# <[returns <tt>M1 op M2</tt>, with <tt>op</tt> defaulting to
110111
# <tt>proc("+",2)</tt>]>
111112
#</p>
112-
procedure m_binop(M1, M1, op)
113+
procedure m_binop(M1, M1, op, zero:0)
113114
local R, i, j
114115
/op := ::proc("+", 2)
115116

116117
if (*M1 ~= *M2) | (*M1[1] ~= *M2[1]) then
117118
::runerr(500, "nonequal matrix dimensions to m_binop")
118119

119-
R = m_constant(*M1, *M1[1])
120+
R = m_constant(*M1, *M1[1],zero)
120121
every (i := 1 to *M1, j := 1 to *M1[1]) do
121122
R[i,j] := invokeFcn(op,M1[i,j],M2[i,j])
122123
return R
@@ -148,14 +149,15 @@ end
148149
# Generic unary operation across a matrix.
149150
# <[param M matrix]>
150151
# <[param op unary operation to invoke]>
152+
# <[param zero (<i>optional</i>) can be used in place of <tt>0</tt>]>
151153
# <[returns <tt>op M</tt>, with <tt>op</tt> defaulting to
152154
# <tt>proc("-",1)</tt>]>
153155
#<p>
154-
procedure m_unaryop(M, op)
156+
procedure m_unaryop(M, op, zero:0)
155157
local R, i, j
156158
/op := ::proc("-", 1)
157159

158-
R = m_constant(*M, *M[1])
160+
R = m_constant(*M, *M[1], zero)
159161
every (i := 1 to *M, j := 1 to *M[1]) do
160162
R[i,j] := invokeFcn(op, M[i,j])
161163
return R
@@ -185,7 +187,7 @@ procedure m_multiply(M1, M2, addident:0, addfcn, mulfcn)
185187
if (*M1[1] ~= *M2) then
186188
::runerr(500, "incompatible matrix dimensions to m_multiply")
187189

188-
R := m_constant(*M1, *M2[1])
190+
R := m_constant(*M1, *M2[1], addident)
189191
every (i := 1 to *R, j := 1 to *R[1]) do {
190192
R[i,j] := addident
191193
every k := 1 to *M2 do
@@ -204,7 +206,7 @@ end
204206
# by overriding various functions and constants used internally.
205207
#</p>
206208
procedure m_lupDecomposition(M, subfcn, mulfcn, divfcn,
207-
invertMetric, addident:0.0, mulident:1.0)
209+
invertMetric, addident:0, mulident:1)
208210
local n, i, b, k, k2, p, j, L, U
209211
/subfcn := ::proc("-", 2)
210212
/mulfcn := ::proc("*", 2)
@@ -252,7 +254,7 @@ end
252254
# Additional optional arguments allow you to customize the operation
253255
# by overriding various functions and constants used internally.
254256
#</p>
255-
procedure m_lupSolve(L,U,p,b,addfun,subfun,mulfun,divfun,addident:0.0)
257+
procedure m_lupSolve(L,U,p,b,addfun,subfun,mulfun,divfun,addident:0)
256258
local n, i, j, y, x
257259
/addfun := ::proc("+", 2)
258260
/subfun := ::proc("-", 2)
@@ -346,7 +348,7 @@ procedure m_write(args[]) # Matrices and output files
346348
case ::type(arg) of {
347349
"file": outfile := arg
348350
"list": { # Assume it's a matrix!
349-
every i := !M do {
351+
every i := !arg do {
350352
every ::writes(outfile, !i," ")
351353
::write(outfile)
352354
}

0 commit comments

Comments
 (0)