diff --git a/Cargo.toml b/Cargo.toml index 0dd1697b..31a791e2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ prime). primal-check = { path = "primal-check", version = "0.2" } primal-estimate = { path = "primal-estimate", version = "0.2" } primal-sieve = { path = "primal-sieve", version = "0.2" } +primal-count = { path = "primal-count", version = "0.1" } [dev-dependencies] primal-slowsieve = { path = "primal-slowsieve", version = "0.2" } @@ -42,5 +43,6 @@ members = [ "primal-estimate", "primal-sieve", "primal-slowsieve", + "primal-count", "generators", ] diff --git a/benches/bench.rs b/benches/bench.rs index 23acda6e..07a132fd 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -1,10 +1,31 @@ #[macro_use] extern crate criterion; extern crate primal; -use criterion::{Criterion, Fun}; +use criterion::{Criterion, Fun, ParameterizedBenchmark}; const N: usize = 1_000_000; const STEP: usize = 101; +const SIZES: [usize; 8] = [100, 10_000, 100_000, 1_000_000, 10_000_000, 100_000_000, 1_000_000_000, 10_000_000_000]; + +macro_rules! create_benchmarks { + ($( + fn $group_id: ident($input: expr) { + $first_name: expr => $first_func: expr, + $($rest_name: expr => $rest_func: expr,)* + } + )*) => { + $( + fn $group_id(c: &mut Criterion) { + let input = $input; + + let bench = ParameterizedBenchmark::new( + $first_name, $first_func, input.into_iter().cloned()) + $( .with_function($rest_name, $rest_func) )*; + c.bench(stringify!($group_id), bench); + } + )* + } +} fn is_prime(c: &mut Criterion) { let miller_rabin = Fun::new( @@ -35,6 +56,38 @@ fn is_prime(c: &mut Criterion) { "is_prime", vec![miller_rabin, sieve, sieve_with_init], ()); } -criterion_group!(benches, is_prime); -criterion_main!(benches); +create_benchmarks!{ + fn prime_pi(SIZES) { + "PrimeCounter" => |b, upto: &usize| { + let mut s = primal::PrimeCounter::new(*upto + 1); + b.iter(|| s.prime_pi(*upto)); + }, + "Sieve" => |b, upto: &usize| { + let s = primal::Sieve::new(*upto + 1); + b.iter(|| s.prime_pi(*upto)); + }, + + "PrimeCounter with init" => |b, upto: &usize| { + b.iter(|| { + let mut s = primal::PrimeCounter::new(*upto + 1); + s.prime_pi(*upto) + }); + }, + "Sieve with init" => |b, upto: &usize| { + b.iter(|| { + let s = primal::Sieve::new(*upto + 1); + s.prime_pi(*upto) + }); + }, + "StreamingSieve" => |b, upto: &usize| { + b.iter(|| primal::StreamingSieve::prime_pi(*upto)) + }, + "Primes" => |b, upto: &usize| { + b.iter(|| primal::Primes::all().take_while(|x| *x <= *upto).count()) + }, + } +} + +criterion_group!(benches, is_prime, prime_pi); +criterion_main!(benches); diff --git a/primal-count/Cargo.toml b/primal-count/Cargo.toml new file mode 100644 index 00000000..0e53f6b5 --- /dev/null +++ b/primal-count/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "primal-count" +version = "0.1.0" +authors = ["Kaimyn Chapman "] + +homepage = "https://github.com/huonw/primal" +repository = "https://github.com/huonw/primal" +documentation = "http://docs.rs/primal-sieve/" +license = "MIT/Apache-2.0" +keywords = ["math", "mathematics", "primes", "number-theory"] + +description = """ +A combinatorial prime counting library +""" + +[dependencies] +primal-sieve = { path = "../primal-sieve", version = "0.2.9" } + +[dev-dependencies] +primal = { path = "..", version = "0.2" } +bencher = "0.1.5" +criterion = "0.2" + +[[bench]] +name = "bench" +harness = false diff --git a/primal-count/benches/bench.rs b/primal-count/benches/bench.rs new file mode 100644 index 00000000..598299b1 --- /dev/null +++ b/primal-count/benches/bench.rs @@ -0,0 +1,58 @@ +#[macro_use] +extern crate criterion; +extern crate primal_count; +use criterion::{Criterion, ParameterizedBenchmark}; +use primal_count::PrimeCounter; + +const SIZES: [usize; 7] = [ + 100, + 10_000, + 100_000, + 1_000_000, + 10_000_000, + 10_000_000_000, + 100_000_000_000, +]; + +macro_rules! create_benchmarks { + ($( + fn $group_id: ident($input: expr) { + $first_name: expr => $first_func: expr, + $($rest_name: expr => $rest_func: expr,)* + } + )*) => { + $( + fn $group_id(c: &mut Criterion) { + let input = $input; + + let bench = ParameterizedBenchmark::new( + $first_name, $first_func, input.into_iter().cloned()) + $( .with_function($rest_name, $rest_func) )*; + c.bench(stringify!($group_id), bench); + } + )* + } +} + +create_benchmarks! { + fn new(SIZES) { + "PrimeCounter" => |b, upto: &usize| b.iter(|| PrimeCounter::new(*upto)), + } + + fn prime_pi(SIZES) { + "PrimeCounter" => |b, upto: &usize| { + let mut s = PrimeCounter::new(*upto + 1); + b.iter(|| s.prime_pi(*upto)); + }, + + "PrimeCounter with init" => |b, upto: &usize| { + b.iter(|| { + let mut s = PrimeCounter::new(*upto + 1); + s.prime_pi(*upto) + }); + }, + } +} + +criterion_group!(benches, new, prime_pi); +criterion_main!(benches); diff --git a/primal-count/src/lib.rs b/primal-count/src/lib.rs new file mode 100644 index 00000000..817c341b --- /dev/null +++ b/primal-count/src/lib.rs @@ -0,0 +1,4 @@ +mod prime_count; +mod util; +pub use prime_count::PrimeCounter; +pub use util::{int_cubic_root, int_quartic_root, int_square_root}; diff --git a/primal-count/src/prime_count.rs b/primal-count/src/prime_count.rs new file mode 100644 index 00000000..b811d282 --- /dev/null +++ b/primal-count/src/prime_count.rs @@ -0,0 +1,253 @@ +extern crate primal_sieve; + +use super::util; +use std::cmp; +use std::collections::HashMap; + +const MEISSEL_LOOKUP_SIZE: usize = 8; // Number of primes we do the reduce trick for +const SMALL_PRIME_PRODUCTS: [usize; MEISSEL_LOOKUP_SIZE + 1] = + [1, 2, 6, 30, 210, 2310, 30030, 510510, 9699690]; +const SMALL_TOTIENT_VALUES: [usize; MEISSEL_LOOKUP_SIZE + 1] = + [1, 1, 2, 8, 48, 480, 5760, 92160, 1658880]; + +/// Generate a vec of primes from 2 up to and including limit +/// Leverages the fast sieve in primal to do so +fn generate_primes(limit: usize) -> Vec { + let sieve = primal_sieve::Sieve::new(limit); + let sieve_iter = sieve.primes_from(2).take_while(|x| *x <= limit); + // Note that we put the primes into a vec here because later we want to have both + // 1) Very quick access to the nth prime + // 2) Quick counting of number of primes below a value, achieved with a binary search + // Experiments replacing 1) or 2) with the methods in sieve seem to significantly + // slow things down for larger numbers + sieve_iter.collect() +} + +/// Memoized combinatorial prime counting function +/// Basic idea here: https://en.wikipedia.org/wiki/Meissel%E2%80%93Lehmer_algorithm +/// The "Meissel Function" here is phi on that Wikipedia page +pub struct PrimeCounter { + limit: usize, + primes: Vec, + pi_cache: HashMap, + meissel_cache: HashMap<(usize, usize), usize>, +} + +impl PrimeCounter { + /// Create a new PrimeCounter instance, which generates all the primes up to sqrt(limit) + pub fn new(limit: usize) -> PrimeCounter { + let mut pi_cache = HashMap::new(); + let primes = generate_primes(util::int_cubic_root(limit * limit)); + + // Insert primes <= 10 - this is mainly to deal with underflow issues later + for n in 0..=10 { + let nprimes = match n { + 2 => 1, + 3..=4 => 2, + 5..=6 => 3, + 7..=10 => 4, + 0..=1 | _ => 0, // N.B. _ never hit + }; + pi_cache.insert(n, nprimes); + } + // let meissel_cache = meissel::generate_meissel_lookup(6); + let meissel_cache = HashMap::new(); + + PrimeCounter { + limit, + primes, + pi_cache, + meissel_cache, + } + } + + /// Updates the limit - to be used when you want to make the prime cache larger + /// May be slow for large values of limit - it's recommended that you don't call + /// this and instead ensure that your first call to construct the PrimeCounter + /// object is large enough. + pub fn update_limit(&mut self, limit: usize) { + self.limit = limit; + self.primes = generate_primes(util::int_square_root(limit)); + } + + /// The number of primes that are at least `bound` + /// + /// # Panics + /// + /// If the limit in the constructor is smaller than the input of prime_pi + /// (Unless it's later been updated with update_limit) + /// + /// # Examples + /// + /// ```rust + /// # extern crate primal; + /// let mut pc = primal::PrimeCounter::new(10_000); + /// assert_eq!(pc.prime_pi(100), 25); + /// assert_eq!(pc.prime_pi(8166), 1024); + /// ``` + pub fn prime_pi(&mut self, bound: usize) -> usize { + self.primes_less_than(bound) + } + + /// The number of numbers less than `m` that are coprime to the first `n` prime numbers + /// + /// # Panics + /// + /// If the `n`th prime is larger than `limit` + /// + /// # Examples + /// + /// ```rust + /// # extern crate primal; + /// let mut pc = primal::PrimeCounter::new(10_000); + /// assert_eq!(pc.meissel_fn(100, 10), 16); + /// assert_eq!(pc.meissel_fn(1234, 5), 256); + /// ``` + pub fn meissel_fn(&mut self, m: usize, n: usize) -> usize { + self.meissel_fn_large(m, n) + } + + /// The number of numbers less than m that are coprime to the first n prime numbers + /// Get recurrence m_fn(m, n) = m_fn(m, n - 1) - m_fn(m/p_n, n-1) by thinking about p_n, the nth prime + fn meissel_fn_small(&mut self, m: usize, n: usize) -> usize { + if n == 0 || m < 2 { + return m; + } + if self.primes[n - 1] >= m { + return 1; + } + match self.meissel_cache.get(&(m, n)).map(|entry| entry.clone()) { + Some(result) => result, + None => { + // For small values of n, we can decrease the size of m by noting that + // the meissel function is almost periodic with period p_1 * .. * p_n + let mut value = 0; + let mut m_shrunk = m; + if n <= MEISSEL_LOOKUP_SIZE { + value = (m / SMALL_PRIME_PRODUCTS[n]) * SMALL_TOTIENT_VALUES[n]; + m_shrunk = m_shrunk % SMALL_PRIME_PRODUCTS[n]; + } + + // After shrinkage, just apply the recursion + value += self.meissel_fn_small(m_shrunk, n - 1) + - self.meissel_fn_small(m_shrunk / self.primes[n - 1], n - 1); + + self.meissel_cache.insert((m, n), value); + value + } + } + } + + // Here we make use of the fact that m_fn(m, n) can be evaluated with fewer calls by + // taking advantage of n being large + fn meissel_fn_large(&mut self, m: usize, n: usize) -> usize { + if n <= MEISSEL_LOOKUP_SIZE { + return self.meissel_fn_small(m, n); + } + if self.primes[n - 1] >= m { + return 1; + } + match self.meissel_cache.get(&(m, n)).map(|entry| entry.clone()) { + Some(result) => result, + None => { + let mut result = self.meissel_fn_small(m, MEISSEL_LOOKUP_SIZE); + let sqrt_m = util::int_square_root(m); + let bound = self.primes_less_than(sqrt_m); + let largest_prime_index = cmp::min(cmp::max(bound, MEISSEL_LOOKUP_SIZE), n); + result = result + largest_prime_index - n; + + for idx in MEISSEL_LOOKUP_SIZE..largest_prime_index { + result -= self.meissel_fn_large(m / self.primes[idx], idx); + } + + self.meissel_cache.insert((m, n), result); + result + } + } + } + + /// Find the number of primes less than bound using the Meissel-Lehmer method + /// Leverages caching to speed up the recursive calls + fn primes_less_than(&mut self, bound: usize) -> usize { + // First check if it's in the cache already + match self.pi_cache.get(&bound).map(|entry| entry.clone()) { + Some(value) => value, + None => { + // The meat of the function + if bound < 2 { + return 0; + } else if bound <= self.primes[self.primes.len() - 1] { + let result = match self.primes.binary_search(&bound) { + Ok(idx) => idx + 1, + Err(idx) => idx, + }; + self.pi_cache.insert(bound, result); + return result; + } + + let sqrt_bound = util::int_square_root(bound); + let quartic_bound = util::int_quartic_root(bound); + + let nprimes_below_4thr = self.primes_less_than(quartic_bound); + let nprimes_below_3rdr = self.primes_less_than(util::int_cubic_root(bound)); + let nprimes_below_2ndr = self.primes_less_than(sqrt_bound); + + // Issues with underflow here if nprimes_below_2ndr + nprimes_below_4thr < 2 + // Dealt with by populating the offending (small) values in the cache at the top level + let mut result = ((nprimes_below_2ndr + nprimes_below_4thr - 2) + * (nprimes_below_2ndr - nprimes_below_4thr + 1)) + / 2; + result += self.meissel_fn_large(bound, nprimes_below_4thr); + + for i in nprimes_below_4thr..nprimes_below_2ndr { + let ith_prime = self.primes[i]; + // Need to make this faster + result -= self.primes_less_than(bound / ith_prime); + if i < nprimes_below_3rdr { + let bi = self.primes_less_than(util::int_square_root(bound / ith_prime)); + for j in i..bi { + let jth_prime = self.primes[j]; + // p_i, p_j > bound^(1/4), so bound / (p_i * p_j) < bound ^ (1/2) + // Hence, this is a cheap lookup, and thus why we don't bother + // optimising this further... + result -= self.primes_less_than(bound / ith_prime / jth_prime) - j; + } + } + } + + // Caching + self.pi_cache.insert(bound, result); + result + } + } + } +} + +#[cfg(test)] +mod tests { + #[test] + fn test_meissel_fn() { + use crate::prime_count::PrimeCounter; + let mut pc = PrimeCounter::new(10_000_000); + assert_eq!(pc.meissel_fn(30, 8), 3); + assert_eq!(pc.meissel_fn(100, 1), 50); + assert_eq!(pc.meissel_fn(100, 25), 1); + assert_eq!(pc.meissel_fn(10_000_000, 50), 1025747); + } + + #[test] + fn test_prime_pi() { + use crate::prime_count::PrimeCounter; + let mut pc = PrimeCounter::new(10_000); + assert_eq!(pc.prime_pi(7), 4); + assert_eq!(pc.prime_pi(100), 25); + assert_eq!(pc.prime_pi(2143), 324); + + pc.update_limit(1_000_000_000); + assert_eq!(pc.prime_pi(1_000_000), 78_498); + assert_eq!(pc.prime_pi(1_000_000_000), 50_847_534); + // pc.update_limit(1_000_000_000_000); + // assert_eq!(pc.prime_pi(1_000_000_000_000), 37_607_912_018); + // assert_eq!(pc.prime_pi(1_000_000_000_000_000), 29_844_570_422_669); + } +} diff --git a/primal-count/src/util.rs b/primal-count/src/util.rs new file mode 100644 index 00000000..96443bc7 --- /dev/null +++ b/primal-count/src/util.rs @@ -0,0 +1,64 @@ +/// Returns the largest integer at least sqrt(n) using Newton's method +pub fn int_square_root(value: usize) -> usize { + let mut x = value; + let mut y = (x + 1) >> 1; + while y < x { + x = y; + y = (x + value / x) >> 1; + } + return x; +} + +/// Returns the largest integer at least cbrt(n) using Newton's method +pub fn int_cubic_root(value: usize) -> usize { + let mut x = value; + let mut y = (2 * value + 1) / 3; // Plus 1 only to protect against division by 0 later + while y < x { + x = y; + y = (2 * x + value / x / x) / 3; // Divide split to protect against overflow + } + return x; +} + +// Returns the largest integer at least n^(1/4) using our fast integer sqrt +// N.b. it's faster to use two sqrts than naively apply Newton here +pub fn int_quartic_root(value: usize) -> usize { + return int_square_root(int_square_root(value)); +} + +#[cfg(test)] +mod tests { + #[test] + fn test_int_sqrt() { + use util::int_square_root; + assert_eq!(int_square_root(0), 0); + assert_eq!(int_square_root(1), 1); + assert_eq!(int_square_root(16), 4); + assert_eq!(int_square_root(17), 4); + assert_eq!(int_square_root(24), 4); + assert_eq!(int_square_root(587 * 587 - 1), 586); + } + + #[test] + fn test_int_cbrt() { + use util::int_cubic_root; + assert_eq!(int_cubic_root(0), 0); + assert_eq!(int_cubic_root(1), 1); + assert_eq!(int_cubic_root(26), 2); + assert_eq!(int_cubic_root(27), 3); + assert_eq!(int_cubic_root(28), 3); + assert_eq!(int_cubic_root(587 * 587 * 587 - 1), 586); + } + + #[test] + fn test_int_quartic_root() { + use util::int_quartic_root; + assert_eq!(int_quartic_root(0), 0); + assert_eq!(int_quartic_root(1), 1); + assert_eq!(int_quartic_root(15), 1); + assert_eq!(int_quartic_root(16), 2); + assert_eq!(int_quartic_root(17), 2); + assert_eq!(int_quartic_root(587 * 587 * 587 * 587 - 1), 586); + assert_eq!(int_quartic_root(587 * 587 * 587 * 587 + 1), 587); + } +} diff --git a/primal-sieve/src/sieve.rs b/primal-sieve/src/sieve.rs index 8be885ef..39fb24f3 100644 --- a/primal-sieve/src/sieve.rs +++ b/primal-sieve/src/sieve.rs @@ -183,11 +183,11 @@ impl Sieve { pub fn prime_pi(&self, n: usize) -> usize { assert!(n <= self.upper_bound()); match n { - 0...1 => 0, + 0..=1 => 0, 2 => 1, - 3...4 => 2, - 5...6 => 3, - 7...10 => 4, + 3..=4 => 2, + 5..=6 => 3, + 7..=10 => 4, _ => { let (includes, base, tweak) = self.index_for(n); let mut count = match wheel::BYTE_MODULO { @@ -354,9 +354,9 @@ impl Sieve { pub fn primes_from<'a>(&'a self, n: usize) -> SievePrimes<'a> { assert!(n <= self.upper_bound()); let early = match n { - 0...2 => Early::Two, + 0..=2 => Early::Two, 3 => Early::Three, - 4...5 => Early::Five, + 4..=5 => Early::Five, _ => Early::Done }; let (_, base, tweak) = self.index_for(n); diff --git a/primal-sieve/src/streaming/mod.rs b/primal-sieve/src/streaming/mod.rs index 25bd0d31..ae3dd0f7 100644 --- a/primal-sieve/src/streaming/mod.rs +++ b/primal-sieve/src/streaming/mod.rs @@ -108,11 +108,11 @@ impl StreamingSieve { /// ``` pub fn prime_pi(n: usize) -> usize { match n { - 0...1 => 0, + 0..=1 => 0, 2 => 1, - 3...4 => 2, - 5...6 => 3, - 7...10 => 4, + 3..=4 => 2, + 5..=6 => 3, + 7..=10 => 4, _ => { let mut sieve = StreamingSieve::new(n); let (includes, base, tweak) = sieve.index_for(n); diff --git a/primal-sieve/src/wheel/wheel210.rs b/primal-sieve/src/wheel/wheel210.rs index a07a1f63..d68499d9 100755 --- a/primal-sieve/src/wheel/wheel210.rs +++ b/primal-sieve/src/wheel/wheel210.rs @@ -689,7 +689,7 @@ pub unsafe fn hardcoded_sieve(bytes: &mut [u8], si_: &mut usize, wi_: &mut usize 'outer: loop { match wi { - 0...47 => { // 30 * x + 1 + 0..=47 => { // 30 * x + 1 loop { 'label47: loop { 'label46: loop { @@ -1180,7 +1180,7 @@ pub unsafe fn hardcoded_sieve(bytes: &mut [u8], si_: &mut usize, wi_: &mut usize wi = 0 } } - 48...95 => { // 30 * x + 11 + 48..=95 => { // 30 * x + 11 loop { 'label95: loop { 'label94: loop { @@ -1671,7 +1671,7 @@ pub unsafe fn hardcoded_sieve(bytes: &mut [u8], si_: &mut usize, wi_: &mut usize wi = 48 } } - 96...143 => { // 30 * x + 13 + 96..=143 => { // 30 * x + 13 loop { 'label143: loop { 'label142: loop { @@ -2162,7 +2162,7 @@ pub unsafe fn hardcoded_sieve(bytes: &mut [u8], si_: &mut usize, wi_: &mut usize wi = 96 } } - 144...191 => { // 30 * x + 17 + 144..=191 => { // 30 * x + 17 loop { 'label191: loop { 'label190: loop { @@ -2653,7 +2653,7 @@ pub unsafe fn hardcoded_sieve(bytes: &mut [u8], si_: &mut usize, wi_: &mut usize wi = 144 } } - 192...239 => { // 30 * x + 19 + 192..=239 => { // 30 * x + 19 loop { 'label239: loop { 'label238: loop { @@ -3144,7 +3144,7 @@ pub unsafe fn hardcoded_sieve(bytes: &mut [u8], si_: &mut usize, wi_: &mut usize wi = 192 } } - 240...287 => { // 30 * x + 23 + 240..=287 => { // 30 * x + 23 loop { 'label287: loop { 'label286: loop { @@ -3635,7 +3635,7 @@ pub unsafe fn hardcoded_sieve(bytes: &mut [u8], si_: &mut usize, wi_: &mut usize wi = 240 } } - 288...335 => { // 30 * x + 29 + 288..=335 => { // 30 * x + 29 loop { 'label335: loop { 'label334: loop { @@ -4126,7 +4126,7 @@ pub unsafe fn hardcoded_sieve(bytes: &mut [u8], si_: &mut usize, wi_: &mut usize wi = 288 } } - 336...383 => { // 30 * x + 31 + 336..=383 => { // 30 * x + 31 loop { 'label383: loop { 'label382: loop { diff --git a/primal-sieve/src/wheel/wheel30.rs b/primal-sieve/src/wheel/wheel30.rs index bd85c76a..f544f838 100755 --- a/primal-sieve/src/wheel/wheel30.rs +++ b/primal-sieve/src/wheel/wheel30.rs @@ -195,7 +195,7 @@ pub unsafe fn hardcoded_sieve(bytes: &mut [u8], si_: &mut usize, wi_: &mut usize 'outer: loop { match wi { - 0...7 => { // 30 * x + 1 + 0..=7 => { // 30 * x + 1 loop { 'label7: loop { 'label6: loop { @@ -286,7 +286,7 @@ pub unsafe fn hardcoded_sieve(bytes: &mut [u8], si_: &mut usize, wi_: &mut usize wi = 0 } } - 8...15 => { // 30 * x + 7 + 8..=15 => { // 30 * x + 7 loop { 'label15: loop { 'label14: loop { @@ -377,7 +377,7 @@ pub unsafe fn hardcoded_sieve(bytes: &mut [u8], si_: &mut usize, wi_: &mut usize wi = 8 } } - 16...23 => { // 30 * x + 11 + 16..=23 => { // 30 * x + 11 loop { 'label23: loop { 'label22: loop { @@ -468,7 +468,7 @@ pub unsafe fn hardcoded_sieve(bytes: &mut [u8], si_: &mut usize, wi_: &mut usize wi = 16 } } - 24...31 => { // 30 * x + 13 + 24..=31 => { // 30 * x + 13 loop { 'label31: loop { 'label30: loop { @@ -559,7 +559,7 @@ pub unsafe fn hardcoded_sieve(bytes: &mut [u8], si_: &mut usize, wi_: &mut usize wi = 24 } } - 32...39 => { // 30 * x + 17 + 32..=39 => { // 30 * x + 17 loop { 'label39: loop { 'label38: loop { @@ -650,7 +650,7 @@ pub unsafe fn hardcoded_sieve(bytes: &mut [u8], si_: &mut usize, wi_: &mut usize wi = 32 } } - 40...47 => { // 30 * x + 19 + 40..=47 => { // 30 * x + 19 loop { 'label47: loop { 'label46: loop { @@ -741,7 +741,7 @@ pub unsafe fn hardcoded_sieve(bytes: &mut [u8], si_: &mut usize, wi_: &mut usize wi = 40 } } - 48...55 => { // 30 * x + 23 + 48..=55 => { // 30 * x + 23 loop { 'label55: loop { 'label54: loop { @@ -832,7 +832,7 @@ pub unsafe fn hardcoded_sieve(bytes: &mut [u8], si_: &mut usize, wi_: &mut usize wi = 48 } } - 56...63 => { // 30 * x + 29 + 56..=63 => { // 30 * x + 29 loop { 'label63: loop { 'label62: loop { diff --git a/src/lib.rs b/src/lib.rs index c902837b..5e654f14 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -254,6 +254,7 @@ extern crate primal_estimate; extern crate primal_check; extern crate primal_sieve; +extern crate primal_count; pub use primal_estimate::prime_pi as estimate_prime_pi; pub use primal_estimate::nth_prime as estimate_nth_prime; @@ -261,3 +262,4 @@ pub use primal_check::miller_rabin as is_prime; pub use primal_check::{as_perfect_power, as_prime_power}; pub use primal_sieve::{StreamingSieve, Sieve, SievePrimes, Primes}; +pub use primal_count::PrimeCounter;