Skip to content

Commit 1836b8d

Browse files
committed
Add an example of generating large primes
1 parent ccd54a1 commit 1836b8d

File tree

1 file changed

+149
-0
lines changed

1 file changed

+149
-0
lines changed

examples/primes.rs

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
//! Example generating prime numbers in a few large bit sizes.
2+
3+
#![cfg_attr(not(feature = "rand"), allow(unused))]
4+
5+
extern crate num_bigint;
6+
extern crate num_integer;
7+
extern crate num_traits;
8+
extern crate rand;
9+
10+
use num_bigint::{BigUint, RandBigInt};
11+
use num_integer::Integer;
12+
use num_traits::{One, ToPrimitive, Zero};
13+
use rand::{Rng, SeedableRng, XorShiftRng};
14+
use std::env;
15+
use std::u64;
16+
17+
#[cfg(not(feature = "rand"))]
18+
fn main() {
19+
println!("This example requires `--features rand`");
20+
}
21+
22+
#[cfg(feature = "rand")]
23+
fn main() {
24+
// Use a seeded Rng for benchmarking purposes.
25+
let seeded = env::var_os("SEEDED_PRIMES").is_some();
26+
let (mut thread_rng, mut seeded_rng);
27+
let mut rng: &mut Rng = if seeded {
28+
seeded_rng = XorShiftRng::from_seed([4, 3, 2, 1]);
29+
&mut seeded_rng
30+
} else {
31+
thread_rng = rand::thread_rng();
32+
&mut thread_rng
33+
};
34+
35+
for &bits in &[256, 512, 1024, 2048] {
36+
let min = BigUint::one() << (bits - 1);
37+
for i in 1usize.. {
38+
let mut n = &min + rng.gen_biguint_below(&min);
39+
if n.is_even() {
40+
n += 1u32;
41+
}
42+
if is_prime(&n) {
43+
println!("Found a {}-bit prime in {} attempts:", bits, i);
44+
println!("{}", n);
45+
println!("");
46+
break;
47+
}
48+
}
49+
}
50+
}
51+
52+
/// Attempt to determine whether n is prime. This algorithm is exact for
53+
/// numbers less than 2^64, and highly probabilistic for other numbers.
54+
#[cfg(feature = "rand")]
55+
fn is_prime(n: &BigUint) -> bool {
56+
// guarantees of primality given by Sloane's [A014233](https://oeis.org/A014233)
57+
const A014233: [(u8, u64); 12] = [
58+
(2, 2_047),
59+
(3, 1_373_653),
60+
(5, 25_326_001),
61+
(7, 3_215_031_751),
62+
(11, 2_152_302_898_747),
63+
(13, 3_474_749_660_383),
64+
(17, 341_550_071_728_321),
65+
(19, 341_550_071_728_321),
66+
(23, 3_825_123_056_546_413_051),
67+
(29, 3_825_123_056_546_413_051),
68+
(31, 3_825_123_056_546_413_051),
69+
(37, u64::MAX),
70+
];
71+
72+
let n64 = n.to_u64();
73+
if n.is_even() {
74+
return n64 == Some(2);
75+
} else if n64 == Some(1) {
76+
return false;
77+
}
78+
79+
// try some simple divisors
80+
for &(p, _) in &A014233[1..] {
81+
if n64 == Some(u64::from(p)) {
82+
return true;
83+
}
84+
if (n % p).is_zero() {
85+
return false;
86+
}
87+
}
88+
89+
// try series of primes as witnesses
90+
for &(p, u) in &A014233 {
91+
if witness(&BigUint::from(p), n) {
92+
return false;
93+
}
94+
if let Some(n) = n64 {
95+
if n < u || u == u64::MAX {
96+
return true;
97+
}
98+
}
99+
}
100+
101+
// Now we're into the "probably prime" arena...
102+
// Generate a few witnesses in the range `[2, n - 2]`
103+
let mut rng = rand::thread_rng();
104+
let max = n - 3u32;
105+
for _ in 0..10 {
106+
let w = rng.gen_biguint_below(&max) + 2u32;
107+
if witness(&w, n) {
108+
return false;
109+
}
110+
}
111+
true
112+
}
113+
114+
/// Test a possible witness to the compositeness of n.
115+
///
116+
/// Computes a**(n-1) (mod n), and checks if the result gives evidence that
117+
/// n is composite. Returning false means that n is likely prime, but not
118+
/// necessarily.
119+
#[cfg(feature = "rand")]
120+
fn witness(a: &BigUint, n: &BigUint) -> bool {
121+
// let n-1 = (2**t)*u, where t ≥ 1 and u is odd
122+
// TODO `trailing_zeros` would make this trivial
123+
let n_m1 = n - 1u32;
124+
let (t, u) = if n_m1.is_even() {
125+
let mut t = 1usize;
126+
let mut u = n_m1.clone() >> 1;
127+
while u.is_even() {
128+
t += 1;
129+
u >>= 1;
130+
}
131+
(t, u)
132+
} else {
133+
(0, n_m1.clone())
134+
};
135+
136+
let mut x = a.modpow(&u, n);
137+
if x.is_one() || x == n_m1 {
138+
return false;
139+
}
140+
141+
for _ in 1..t {
142+
x = &x * &x % n;
143+
if x == n_m1 {
144+
return false;
145+
}
146+
}
147+
148+
true
149+
}

0 commit comments

Comments
 (0)