Skip to content

Commit 5b60e55

Browse files
committed
Added the Sieve
1 parent e7e52ae commit 5b60e55

File tree

2 files changed

+86
-2
lines changed

2 files changed

+86
-2
lines changed

src/lib.rs

+73-1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ case, but slower in the long term as they do not use any caching of primes.
6363
#![doc(html_root_url = "https://wackywendell.github.io/primes/")]
6464

6565
use std::cmp::Ordering::{Equal, Greater, Less};
66+
use std::collections::hash_map::Entry;
67+
use std::collections::HashMap;
6668
use std::ops::Index;
6769
use std::slice;
6870

@@ -85,6 +87,25 @@ pub struct TrialDivision {
8587
lst: Vec<u64>,
8688
}
8789

90+
/**
91+
A prime generator, using the Sieve of Eratosthenes method. This is asymptotically more efficient
92+
than the Trial Division method, but slower earlier on.
93+
94+
Create with `let mut pset = Sieve::new()`, and then use `pset.iter()` to iterate over all primes.
95+
**/
96+
#[derive(Default)]
97+
pub struct Sieve {
98+
primes: Vec<u64>,
99+
100+
// Keys are composites, values are prime factors.
101+
//
102+
// Every prime is in here once.
103+
//
104+
// Each entry corresponds to the last composite "crossed off" by the given prime,
105+
// not including any composite less than the values in 'primes'.
106+
sieve: HashMap<u64, u64>,
107+
}
108+
88109
/// An iterator over generated primes. Created by `PrimeSet::iter` or
89110
/// `PrimeSet::generator`
90111
pub struct PrimeSetIter<'a, P: PrimeSet> {
@@ -103,7 +124,7 @@ impl TrialDivision {
103124
impl PrimeSetBasics for TrialDivision {
104125
/// Finds one more prime, and adds it to the list
105126
fn expand(&mut self) {
106-
let mut l: u64 = self.lst[self.lst.len() - 1] + 2;
127+
let mut l: u64 = self.lst.last().unwrap() + 2;
107128
let mut remainder = 0;
108129
loop {
109130
for &n in &self.lst {
@@ -128,6 +149,57 @@ impl PrimeSetBasics for TrialDivision {
128149
}
129150
}
130151

152+
impl Sieve {
153+
/// A new prime generator, primed with 2 and 3
154+
pub fn new() -> Sieve {
155+
let primes = vec![2, 3];
156+
let mut sieve = HashMap::new();
157+
sieve.insert(9, 3);
158+
159+
Sieve { primes, sieve }
160+
}
161+
162+
// insert a prime and its composite. If the composite is already occupied, we'll increase
163+
// the composite by prime and put it there, repeating as necessary.
164+
fn insert(&mut self, prime: u64, composite: u64) {
165+
for n in 0.. {
166+
// We can skip composite + prime, because its even, so go straight to
167+
// composite + 2*prime. If that's already "crossed off" (in self.sieve),
168+
// go on to composite + 4*prime and so on.
169+
let value = composite + prime * 2 * n;
170+
if let Entry::Vacant(v) = self.sieve.entry(value) {
171+
v.insert(prime);
172+
return;
173+
}
174+
}
175+
}
176+
}
177+
178+
impl PrimeSetBasics for Sieve {
179+
/// Finds one more prime, and adds it to the list
180+
fn expand(&mut self) {
181+
let mut nextp = *self.primes.last().unwrap();
182+
loop {
183+
nextp += 2;
184+
let factor = match self.sieve.entry(nextp) {
185+
Entry::Vacant(_) => {
186+
self.sieve.insert(nextp * nextp, nextp);
187+
self.primes.push(nextp);
188+
return;
189+
}
190+
Entry::Occupied(o) => *o.get(),
191+
};
192+
193+
self.insert(factor, nextp + 2 * factor);
194+
}
195+
}
196+
197+
/// Return all primes found so far as a slice
198+
fn list(&self) -> &[u64] {
199+
&self.primes[..]
200+
}
201+
}
202+
131203
pub trait PrimeSet: PrimeSetBasics + Sized {
132204
/// Number of primes found so far
133205
fn len(&self) -> usize {

tests/basictests.rs

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use primes::{factors, factors_uniq, is_prime, PrimeSet, PrimeSetBasics, TrialDivision};
1+
use primes::{factors, factors_uniq, is_prime, PrimeSet, PrimeSetBasics, Sieve, TrialDivision};
22

33
#[test]
44
fn test_primesetbasics() {
@@ -133,3 +133,15 @@ fn test_factors() {
133133
pset = TrialDivision::new();
134134
assert_eq!(pset.prime_factors(12), vec!(2, 2, 3));
135135
}
136+
137+
// Test that the Sieve method works the same as the TrialDivision method
138+
#[test]
139+
fn test_sieve() {
140+
let mut sieve = Sieve::new();
141+
let mut td = TrialDivision::new();
142+
143+
let sieved: Vec<u64> = sieve.iter().take(1000).collect();
144+
let trialled: Vec<u64> = td.iter().take(1000).collect();
145+
146+
assert_eq!(sieved, trialled);
147+
}

0 commit comments

Comments
 (0)