spire.math

prime

package prime

Basic tools for prime factorization.

This package is intended to provide tools for factoring numbers, checking primality, generating prime numbers, etc. For now, its main contributions are a method for factoring integers (spire.math.prime.factor) and a type for representing prime factors and their exponents (spire.math.prime.Factors).

The factorization currently happens via an implementation of Pollard-Rho with Brent's optimization. This technique works very well for composites with small prime factors (up to 10 decimal digits or so) and can support semiprimes (products of two similarly-sized primes) of 20-40 digits.

The implementation does cheat, using BigInteger.isProbablePrime(40) to test basic primality. This has a roughly 1-in-1,000,000,000,000 chance of being wrong.

Since Pollard-Rho uses random primes, its performance is somewhat non-deterministic. On this machine, factoring 20-digit semiprimes seem to average about 1.5s and factoring 30-digit semiprimes seem to average about 20s. Much larger numbers can be factored provided they are either prime or composites with smallish factors.

Linear Supertypes
AnyRef, Any
Ordering
  1. Alphabetic
  2. By inheritance
Inherited
  1. prime
  2. AnyRef
  3. Any
  1. Hide All
  2. Show all
Learn more about member selection
Visibility
  1. Public
  2. All

Type Members

  1. case class BitSet(length: Int, array: Array[Int]) extends Product with Serializable

  2. class FactorHeap extends AnyRef

    Simple heap implementation for storing Factors.

    Simple heap implementation for storing Factors.

    The heap can hold at most ~2B items, which means we can't store more than this many prime factors.

    Note that "fast factors" don't end up in this heap, so the number of primes we can sieve is actaully the max heap size + the number of fast factors.

    The sieve implementation itself uses a cutoff, so to test primality of numbers <= K, we need to be able to store prime factors up to sqrt(K) in our heap. Since our heap can hold ~2B prime factors, this means the theoretical upper bound on our segmented sieve is (~2Bth prime)^2.

    In practice the sieve will slow down to the point of not being useful far before we could reach this limit.

  3. case class Factors(factors: Map[SafeLong, Int], sign: Sign) extends Iterable[(SafeLong, Int)] with Ordered[Factors] with Product with Serializable

  4. case class SieveSegment(start: SafeLong, primes: BitSet, cutoff: SafeLong) extends Product with Serializable

  5. case class Siever(chunkSize: Int, cutoff: SafeLong) extends Product with Serializable

    The Siever manages the segmented sieve process.

    The Siever manages the segmented sieve process.

    At any given time, it holds onto a single sieve segment. Thus, the siever should be used for a single lookup or traversal.

    Sievers are built using 'chunkSize' and 'cutoff' parameters. These are passed along to any sieve segments they create. When possible, it's probably better to use methods on the companion object, which will instantiate a Siever for you with reasonable parameters.

Value Members

  1. object BitSet extends Serializable

    Fast BitSet implementation.

    Fast BitSet implementation.

    This bitset is just intended to be a little bit faster than Scala's, and to support accessing its internals, which we do in some cases.

    The max length (~2B) is a current limit to how big individual sieve segments can get. Until our sieving is more efficient, we don't want segments that big anyway, so this is OK.

  2. object Factors extends Serializable

  3. object SieveSegment extends Serializable

    This respresents a single sieve segment.

    This respresents a single sieve segment.

    The 'start' field says what this segment's first number is. 'primes' is a bitset of possible primes in this segment. 'cutoff' specifies the largest prime factor we're interested in. This means that cutoff**2-1 is the largest number we could reliably identify as prime.

    We are using a mod30 wheel, which means that we don't need to manually factor using 2, 3, or 5 (30 is the lcm of 2, 3, and 5). Since each wheel turn is 30-bits, and our bitset groups elements into 32-bit groups (integers), we have a 480-bit (15 integer) period between the wheel and the bitset. This requires our segment length to be divisible by 480.

    When building a sieve, we will first initialize using the mod30 wheel. Then, if we are on the first segment, we'll do a traditional sieve. We'll save any primes greater than 5 we find as factors, either fast factors (if they will show up frequently in each segment) or slow factors otherwise. If a factor is larger than cutoff we don't save it. After that we'll be done with the first segment.

    For later segments, we will use our fast and slow factors to block out composites as we find them. Like in the first segment, we'll save factors we find (although any new factors we find now will always be slow). And of course we won't save any factors above our cutoff.

    Once the sieve is initialized it doesn't do anything else interesting, besides report prime numbers. Currently its internals are made available to the Siever.

  4. object SieveUtil

  5. def factor(n: SafeLong): Factors

    Factor the given integer with the default factorization method.

  6. def factorPollardRho(n0: SafeLong): Factors

  7. def factorTrialDivision(n0: SafeLong): Factors

    Factor the given integer using trial division.

    Factor the given integer using trial division.

    This is the slowest method, but is still reasonable for numbers up to about 14 decimal digits or so.

  8. def factorWheelDivision(n0: SafeLong): Factors

    Factor the given integer using trial division with a wheel.

    Factor the given integer using trial division with a wheel.

    This is slightly faster than basic trial divison (about 30% or so). It's still mostly appropriate for small-ish numbers.

  9. def fill(start: Int, limit: Int): Array[SafeLong]

  10. def fill(n: Int): Array[SafeLong]

  11. def isPrime(n: SafeLong): Boolean

    Determine if the given integer is prime.

    Determine if the given integer is prime.

    Currently this is using a strong pseudo-primality test (so there is a 1-in-1,000,000,000,000 chance of being wrong).

  12. def nth(n: Long): SafeLong

  13. def sieverUpToNth(n: Long): Siever

  14. def stream(chunkSize: Int, cutoff: SafeLong): Stream[SafeLong]

  15. def stream: Stream[SafeLong]

Inherited from AnyRef

Inherited from Any

Ungrouped