All Downloads are FREE. Search and download functionalities are using the official Maven repository.

spire.math.prime.SieveSegment.scala Maven / Gradle / Ivy

package spire.math.prime

import scala.annotation.tailrec
import scala.collection.mutable.{ArrayBuffer}
import System.arraycopy

import spire.math.{SafeLong, min}
import spire.syntax.cfor._

import SieveUtil._

/**
 * 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.
 */
object SieveSegment {
  val wheel30: Array[Int] = {
    var b: Long = 0L
    b |= (1 << 1)
    b |= (1 << 7)
    b |= (1 << 11)
    b |= (1 << 13)
    b |= (1 << 17)
    b |= (1 << 19)
    b |= (1 << 23)
    b |= (1 << 29)
    val n: Long = b | (b << 30L)
    val arr = new Array[Int](15)
    cfor(0)(_ < 15, _ + 1) { i =>
      arr(i) = ((n >>> (i * 2)) & 0xffffffffL).toInt
    }
    arr
  }

}

case class SieveSegment(start: SafeLong, primes: BitSet, cutoff: SafeLong) {
  def isPrime(n: SafeLong): Boolean = primes((n - start).toInt)
  def isComposite(n: SafeLong): Boolean = !primes((n - start).toInt)
  def set(n: SafeLong): Unit = primes += (n - start).toInt
  def unset(n: SafeLong): Unit = primes -= (n - start).toInt

  def nextAfter(n: SafeLong): SafeLong = {
    var i = (n - start + 2).toInt
    val len = primes.length
    while (i < len) {
      if (primes(i)) return start + i
      i += 2
    }
    SafeLong(-1L) // fail
  }

  def init(fastq: FastFactors, slowq: FactorHeap): Unit = {
    initMod30()
    if (start == 0) {
      initFirst(fastq, slowq)
    } else {
      val limit = min(cutoff ** 2, start + primes.length)
      initFromArray(fastq)
      initFromQueue(limit, slowq)
      initRest(slowq)
    }
  }

  def initMod30(): Unit = {
    val arr = primes.array
    assert(arr.length % 15 == 0)
    val limit = arr.length
    val wheel = SieveSegment.wheel30
    cfor(0)(_ < limit, _ + 15)(i => arraycopy(wheel, 0, arr, i, 15))
    if (start == 0L) {
      primes -= 1
      primes += 2
      primes += 3
      primes += 5
    }
  }

  private def initFromArray(fastq: FastFactors): Unit = {
    val arr = fastq.arr
    var i = 0

    val len: Long = if (start + primes.length < cutoff)
      (cutoff - start).toLong
    else
      primes.length

    while (i < arr.length) {
      val factor = arr(i)
      var j = (factor.m - start).toInt
      val k = factor.p
      val kk = k + k
      val lim = len - kk
      primes -= j
      while (j < lim) {
        j += kk
        primes -= j
      }
      factor.m = start + j + kk
      i += 1
    }
  }

  @tailrec private def initFromQueue(limit: SafeLong, q: FactorHeap): Unit = {
    if (q.isEmpty) return ()

    val factor = q.dequeue
    val m = factor.next
    if (m < limit) {
      val p = factor.p
      val len = primes.length
      var i = (m - start).toInt
      val m2 = if (p < len) {
        val k = p.toInt
        val kk = k + k
        while (i < len) { primes -= i; i += kk }
        start + i
      } else {
        primes -= i
        m + p
      }
      factor.next = m2
      q += factor
      initFromQueue(limit, q)
    } else {
      q += factor
    }
  }

  def initFirst(fastq: FastFactors, slowq: FactorHeap): Unit = {
    var p: Int = 1
    val len = primes.length
    val buf = ArrayBuffer.empty[FastFactor]
    while (p < len) {
      if (primes(p)) {
        var m = p.toLong * p.toLong
        if (m < len) {
          val pp = p + p
          var k = m.toInt
          primes -= k
          val lim = len - pp
          while (k < lim) { k += pp; primes -= k }
          m = k.toLong + pp
        }
        if (p < 7) {
        } else if (m - primes.length < primes.length) {
          buf += FastFactor(p, SafeLong(m))
        } else if (cutoff > p) {
          slowq += Factor(SafeLong(p), SafeLong(m))
        }
      }
      p += 2
    }
    fastq.arr = buf.toArray
  }

  def initRest(slowq: FactorHeap): Unit = {
    if (start >= cutoff) return ()

    val len: Long = if (start + primes.length >= cutoff)
      (cutoff - start).toLong
    else
      primes.length

    var i = 1
    while (i < len) {
      if (primes(i)) {
        val p: SafeLong = start + i
        slowq += Factor(p, p ** 2)
      }
      i += 2
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy