xref: /inferno-os/libsec/port/probably_prime.c (revision 7de2b42d50e3c05cc143e7b51284009b5e185581)
1 #include "os.h"
2 #include <mp.h>
3 #include <libsec.h>
4 
5 /* Miller-Rabin probabilistic primality testing */
6 /*	Knuth (1981) Seminumerical Algorithms, p.379 */
7 /*	Menezes et al () Handbook, p.39 */
8 /* 0 if composite; 1 if almost surely prime, Pr(err)<1/4**nrep */
9 int
10 probably_prime(mpint *n, int nrep)
11 {
12 	int j, k, rep, nbits, isprime;
13 	mpint *nm1, *q, *x, *y, *r;
14 
15 	if(n->sign < 0)
16 		sysfatal("negative prime candidate");
17 
18 	if(nrep <= 0)
19 		nrep = 18;
20 
21 	k = mptoi(n);
22 	if(k == 2)		/* 2 is prime */
23 		return 1;
24 	if(k < 2)		/* 1 is not prime */
25 		return 0;
26 	if((n->p[0] & 1) == 0)	/* even is not prime */
27 		return 0;
28 
29 	/* test against small prime numbers */
30 	if(smallprimetest(n) < 0)
31 		return 0;
32 
33 	/* fermat test, 2^n mod n == 2 if p is prime */
34 	x = uitomp(2, nil);
35 	y = mpnew(0);
36 	mpexp(x, n, n, y);
37 	k = mptoi(y);
38 	if(k != 2){
39 		mpfree(x);
40 		mpfree(y);
41 		return 0;
42 	}
43 
44 	nbits = mpsignif(n);
45 	nm1 = mpnew(nbits);
46 	mpsub(n, mpone, nm1);	/* nm1 = n - 1 */
47 	k = mplowbits0(nm1);
48 	q = mpnew(0);
49 	mpright(nm1, k, q);	/* q = (n-1)/2**k */
50 
51 	for(rep = 0; rep < nrep; rep++){
52 		for(;;){
53 			/* find x = random in [2, n-2] */
54 			r = mprand(nbits, prng, nil);
55 			mpmod(r, nm1, x);
56 			mpfree(r);
57 			if(mpcmp(x, mpone) > 0)
58 				break;
59 		}
60 
61 		/* y = x**q mod n */
62 		mpexp(x, q, n, y);
63 
64 		if(mpcmp(y, mpone) == 0 || mpcmp(y, nm1) == 0)
65 			continue;
66 
67 		for(j = 1;; j++){
68 			if(j >= k) {
69 				isprime = 0;
70 				goto done;
71 			}
72 			mpmul(y, y, x);
73 			mpmod(x, n, y);	/* y = y*y mod n */
74 			if(mpcmp(y, nm1) == 0)
75 				break;
76 			if(mpcmp(y, mpone) == 0){
77 				isprime = 0;
78 				goto done;
79 			}
80 		}
81 	}
82 	isprime = 1;
83 done:
84 	mpfree(y);
85 	mpfree(x);
86 	mpfree(q);
87 	mpfree(nm1);
88 	return isprime;
89 }
90