### 9 releases

0.3.3 | Jun 7, 2024 |
---|---|

0.3.2 | Jan 3, 2023 |

0.3.1 | Aug 15, 2022 |

0.3.0 | Jun 18, 2020 |

0.1.14 | May 16, 2015 |

#**547** in Math

**16,591** downloads per month

Used in **65** crates
(34 directly)

**MIT/Apache**

580KB

8K
SLoC

# primal

puts raw power into prime numbers.`primal`

This crates includes

- optimised prime sieves
- checking for primality
- enumerating primes
- factorising numbers
- estimating upper and lower bounds for π(
*n*) (the number of primes below*n*) and*p*(the_{k}*k*th prime)

This uses a state-of-the-art cache-friendly Sieve of Eratosthenes to enumerate the primes up to some fixed bound (in a memory efficient manner), and then allows this cached information to be used for things like enumerating and counting primes.

takes around 2.8 seconds and less than 3MB of RAM to
count the exact number of primes below 10`primal`^{10} (455052511)
on the author's laptop (i7-3517U).

###
`lib.rs`

:

puts raw power into prime numbers.`primal`

This crates includes

- optimised prime sieves
- checking for primality
- enumerating primes
- factorising numbers
- estimating upper and lower bounds for π(
*n*) (the number of primes below*n*) and*p*(the_{k}*k*th prime)

This uses a state-of-the-art cache-friendly Sieve of Eratosthenes to enumerate the primes up to some fixed bound (in a memory efficient manner), and then allows this cached information to be used for things like enumerating and counting primes.

takes around 2.8 seconds and less than 3MB of RAM to
count the exact number of primes below 10`primal`^{10} (455052511)
on my laptop (i7-3517U).

# Using this library

Just add the following to your

:`Cargo.toml`

`[``dependencies``]`
`primal ``=` `"`0.2`"`

# Examples

## "Indexing" Primes

Let's find the 10001st prime. The easiest way is to enumerate the primes, and find the 10001st:

`//` (.nth is zero indexed.)
`let` p `=` `primal``::``Primes``::`all`(``)``.``nth``(``10001` `-` `1``)``.``unwrap``(``)``;`
`println!``(``"`The 10001st prime is `{}``"``,` p`)``;` `//` 104743

This takes around 400 microseconds on my computer, which seems
nice and quick, but,

is flexible at the cost of
performance: we can make it faster. The `Primes`

type
offers a specialised `StreamingSieve`

function:`nth_prime`

`let` p `=` `primal``::``StreamingSieve``::`nth_prime`(``10001``)``;`
`println!``(``"`The 10001st prime is `{}``"``,` p`)``;` `//` 104743

This runs in only 10 microseconds!

is extremely
efficient and uses very little memory. It is the best way to solve
this task with `StreamingSieve`

.`primal`

Since that was so easy, let's now make the problem bigger and harder: find the sum of the 100,000th, 200,000th, 300,000th, ..., 10,000,000th primes (100 in total).

We could call

repeatedly:`StreamingSieve ::`nth_prime

`//` the primes we want to find
`let` ns `=` `(``1``..``100` `+` `1``)``.``map``(``|``x``|` `x ``*` `100_000``)``.``collect``::``<``Vec``<``_``>``>``(``)``;`
`//` search and sum them up
`let` sum `=` ns`.``iter``(``)`
`.``map``(``|``n``|` `primal``::``StreamingSieve``::`nth_prime`(``*`n`)``)`
`.``fold``(``0``,` `|``a``,` `b``|` `a ``+` b`)``;`
`println!``(``"`the sum is `{}``"``,` sum`)``;`

This takes around 1.6s seconds to print

;
not so speedy. Each call to `the sum is 8795091674`

`nth_prime`

is individually fast (400
microseconds for 100,000 to 40 milliseconds for 10,000,000) but
they add up to something bad. Every one is starting from the start
and redoing work that previous calls have done... wouldn't it be
nice if we could just do the computation for 10,000,000 and reuse
that for the smaller ones?The

type is a wrapper around `Sieve`

that
caches information, allowing repeated queries to be answered
efficiently.`StreamingSieve`

There's one hitch:

requires a limit to know how far to
sieve: we need some way to find an upper bound to be guaranteed to
be at least as large as all our primes. We could guess that, say,
10`Sieve`^{10} will be large enough and use that, but that's a
huge overestimate (spoilers: the 10,000,000th prime is around
2×10^{8}). We could also try filtering with
exponentially larger upper bounds until we find one that works
(e.g. doubling each time), or, we could just take a shortcut and
use deeper mathematics via

.`estimate_nth_prime`

`//` the primes we want to find
`let` ns `=` `(``1``..``100` `+` `1``)``.``map``(``|``x``|` `x ``*` `100_000``)``.``collect``::``<``Vec``<``_``>``>``(``)``;`
`//` find our upper bound
`let` `(`_lo`,` hi`)` `=` `primal``::`estimate_nth_prime`(``10_000_000``)``;`
`//` find the primes up to this upper bound
`let` sieve `=` `primal``::``Sieve``::`new`(`hi `as` `usize``)``;`
`//` now we can efficiently sum them up
`let` sum `=` ns`.``iter``(``)`
`.``map``(``|``n``|` `sieve``.``nth_prime``(``*`n`)``)`
`.``fold``(``0``u64``,` `|``a``,` `b``|` `a ``+` b `as` `u64``)``;`
`println!``(``"`the sum is `{}``"``,` sum`)``;`

This takes around 40 milliseconds, and gives the same output: much better!

(By the way, the version using 10^{10} as the bound
instead of the more accurate estimate still only takes ~3
seconds.)

## Counting Primes

Another problem: count the number of primes below 1 million. This
is evaluating the prime-counting function
π,
i.e. π(10^{6}).

As above, there's a few ways to attack this: the iterator, and the sieves.

`const` `LIMIT``:` `usize` `=` `1_000_000``;`
`//` iterator
`let` count `=` `primal``::``Primes``::`all`(``)``.``take_while``(``|``p``|` `*`p `<` `LIMIT``)``.``count``(``)``;`
`println!``(``"`there are `{}` primes below 1 million`"``,` count`)``;` `//` 78498
`//` sieves
`let` sieve `=` `primal``::``Sieve``::`new`(``LIMIT``)``;`
`let` count `=` sieve`.``prime_pi``(``LIMIT``)``;`
`println!``(``"`there are `{}` primes below 1 million`"``,` count`)``;`
`let` count `=` `primal``::``StreamingSieve``::`prime_pi`(``LIMIT``)``;`
`println!``(``"`there are `{}` primes below 1 million`"``,` count`)``;`

is fastest (380 microseconds) followed by `StreamingSieve`

(400) with `Sieve`

bringing up the rear at 1300 microseconds. Of
course, repeated queries will be faster with `Primes`

than with
`Sieve`

, but that flexibility comes at the cost of extra
memory use.`StreamingSieve`

If an approximation is all that is required,

provides close upper and lower bounds:`estimate_prime_pi`

`let` `(`lo`,` hi`)` `=` `primal``::`estimate_prime_pi`(``1_000_000``)``;`
`println!``(``"`there are between `{}` and `{}` primes below 1 million`"``,` lo`,` hi`)``;`
`//` 78380, 78573

## Searching Primes

Now for something where

might be useful: find the first
prime where the binary expansion (not including trailing zeros)
ends like `Primes`

with at least 27 zeros. This condition is
checked by:`00``..``001`

`fn` `check``(``p``:` `usize``)`` ``->` `bool` `{`
p `>` `1` `&&` `(`p `/` `2``)``.``trailing_zeros``(``)` `>=` `27`
`}`

I have no idea how large the prime might be: I know it's
guaranteed to be at *least* 2^{27 + 1} + 1, but not an
upper limit.

The

iterator works perfectly for this:`Primes`

`let` p `=` `primal``::``Primes``::`all`(``)``.``find``(``|``p``|` `check``(``*`p`)``)``.``unwrap``(``)``;`
`println!``(``"`the prime is `{}``"``,` p`)``;`

It takes about 3.1 seconds for my computer to spit out 3,221,225,473.

Using a sieve is a little trickier: one approach is to start with
some estimated upper bound (like double the absolute lower bound),
look for a valid prime. If one isn't found, double the upper bound
and start again. The

method allows for saving a
little bit of work: we can start iterating from an arbitrary point
in the sequence, such as the lower bound.`primes_from`

`let` p`;`
`let` `mut` lower_bound `=` `1` `<``<` `(``27` `+` `1``)``;`
`loop` `{`
`//` our upper bound is double the lower bound
`let` sieve `=` `primal``::``Sieve``::`new`(`lower_bound `*` `2``)``;`
`if` `let` `Some``(`p_`)` `=` sieve`.``primes_from``(`lower_bound`)``.``find``(``|``p``|` `check``(``*`p`)``)` `{`
p `=` p_`;`
`break`
`}`
lower_bound `*=` `2``;`
`}`
`println!``(``"`the prime is `{}``"``,` p`)``;`

This takes around 3.5 seconds to print the same number. Slower than the iterator!

I was just using this silly condition as an example of something
that doesn't have an obvious upper bound, rather than a problem
that is hard to do fast. There's a much faster way to tackle it,
by inverting the problem: construct numbers that satisfy

,
and check the primality of those.`check`

The numbers that satisfy

are `check`

for
`k * (1 << (27 + 1)) + 1`

`k ``>=` `1`

, so the only hard bit is testing primality. Fortunately,
`primal`

offers the `is_prime`

function which is an efficient way
to do primality tests, even of very large numbers.`let` `mut` p `=` `0``;`
`for` k `in` `1``..` `{`
p `=` k `*` `(``1` `<``<` `(``27` `+` `1``)``)` `+` `1``;`
`if` `primal``::`is_prime`(`p`)` `{` `break` `}`
`}`
`println!``(``"`the prime is `{}``"``,` p`)``;`

This takes 6 *micro*seconds: more than 500,000×
faster than the iterator!

#### Dependencies

~225–315KB