is_prime_number 0.1.0
is_prime_number: ^0.1.0 copied to clipboard
Prime numbers for Dart: a primality test that stays exact at 64 bits, a sieve, factorisation, and a lazy stream of primes. Pure Dart, no dependencies.
is_prime_number
Prime numbers for Dart — exact across the whole int range.
A primality test, a sieve, factorisation, and a lazy stream of primes.
import 'package:is_prime_number/is_prime_number.dart';
isPrime(97); // true
isPrime(2147483647); // true — 2³¹ − 1
primesUpTo(30); // [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
primeFactors(360); // [2, 2, 2, 3, 3, 5]
primes().take(5).toList(); // [2, 3, 5, 7, 11]
Pure Dart, no dependencies. Runs anywhere Dart does — Flutter, server, web, CLI.
dependencies:
is_prime_number: ^0.1.0
Why not just write the loop #
The four-line prime check everyone writes has two bugs in it, and both are quiet.
i <= sqrt(n) drops a divisor. sqrt returns a double. When a double lands a
hair under a perfect square, toInt() truncates to one less than it should, the
last divisor is never tried, and a composite is reported as prime. This package
bounds with i * i <= n and stays in integers.
Trial division stops being viable long before int does. Testing a number
near 2⁶³ by division is billions of operations. Above a threshold this switches
to Miller–Rabin with a witness set that is proven complete for this range — so
it is exact, not probabilistic. There is no "probably prime" here.
Testing one number #
isPrime(n) |
Whether n is prime. Negatives, 0 and 1 are not. |
nextPrime(n) |
The smallest prime greater than n. |
previousPrime(n) |
The largest prime below n, or null if there is none. |
nthPrime(n) |
The n-th prime, counting from 1. nthPrime(1) is 2. |
nextPrime(11); // 13 — strictly greater
previousPrime(2); // null — nothing prime lies below 2
nthPrime(1000); // 7919
previousPrime returns null rather than throwing: "there is no prime below 2"
is an ordinary fact about the number line, not a mistake the caller made.
nthPrime(0) does throw — asking for the zeroth prime is a bug.
Testing a range #
primesUpTo(30); // [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
primeCountUpTo(1000); // 168
primesUpTo sieves rather than looping over isPrime, which makes the work per
number close to constant instead of proportional to its square root. It holds one
byte per odd number, so ten million costs about 5 MB — past that, walk primes()
and keep only what you need.
Every prime, lazily #
primes().take(5).toList(); // [2, 3, 5, 7, 11]
primes().takeWhile((p) => p < 100).length; // 25
primes().firstWhere((p) => p > 10000); // 10007
Infinite and lazy, so taking ten primes costs ten primes — not a sieve sized for a guess at how many you might end up wanting.
Taking a number apart #
primeFactors(n) |
Prime factors in order, with repeats. |
factorise(n) |
The same as prime → exponent. |
divisors(n) |
Every divisor including 1 and n, ascending. |
primeFactors(360); // [2, 2, 2, 3, 3, 5]
factorise(360); // {2: 3, 3: 2, 5: 1}
divisors(28); // [1, 2, 4, 7, 14, 28]
Odds and ends #
isPerfect(28); // true — 1 + 2 + 4 + 7 + 14 == 28
areTwinPrimes(11, 13) // true
isCoprime(9, 28); // true — neither is prime, but they share no factor
gcd(12, 18); // 6 — sign is ignored
Exactness #
Every result is exact for any value a Dart int can hold. Small numbers go
through trial division by 6k±1; larger ones through Miller–Rabin with the bases
2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, which are proven deterministic for
everything below 3.3 × 10²⁴ — comfortably past 2⁶³.
The Miller–Rabin arithmetic runs on BigInt. The squaring step multiplies two
values that are each already near the limit, and on a 64-bit int that silently
wraps — which does not throw, it just returns the wrong answer for large primes.
On the web a Dart int is a JavaScript double, so integers above 2⁵³ cannot
be represented exactly to begin with. Results are exact up to that point.
License #
MIT © Samandar Ahadjonov