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

org.nerd4j.utils.math.PrimeSieve Maven / Gradle / Ivy

The newest version!
/*
 * #%L
 * Nerd4j Utils
 * %%
 * Copyright (C) 2011 - 2014 Nerd4j
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 *
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * .
 * #L%
 */
package org.nerd4j.utils.math;

import org.nerd4j.utils.lang.*;

import java.util.Arrays;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveAction;


/**
 * This class permits calculations about prime numbers.
 *
 * 

* This class uses an implementation of the Sieve of Eratosthenes * to get all the prime numbers less or equal than a given number. * *

* This implementation handles numbers up to 3*237-577 * i.e. ({@link Integer#MAX_VALUE} - 2) * {@link Long#SIZE} * 3 -1 * = 412316859839. * *

* This class keeps the bit-array used to compute the Sieve of Eratosthenes so, * if the requested prime number is very big, this class needs and keeps * allocated a lot of memory. To tell if the required N is prime it needs * up to 2*N/3 bits (therefore up to N/12 bytes) of memory. For example, * to tell if 1000000181 is prime (by the way, it is), you need about * 80MB of memory. * *

* The limit of 3*237-577 is based on the fact that the JVM does * not allow to allocate an array with a size greater than {@link Integer#MAX_VALUE} - 2, * but the platform-specific limits can be more restrictive, for example * the VM may not have enough memory. * *

* If the required N is too big, you may run into a {@link java.lang.OutOfMemoryError}. * For example finding all primes less than 3*237-576, besides being * relatively time consuming, it requires {@code 16Gb} of memory. * You can use the method {@link PrimeSieve#getMemoryConsumptionInBytes(long)} to know * the memory occupation given the upper limit and you can use * the method {@link PrimeSieve#getGreatestComputableValue(long)} * to know the greatest value that can be computed using the given amount of memory. * * @author Massimo Coluzzi * @since 2.0.0 */ public class PrimeSieve { /** The size of a data block. */ private static final int BLOCK_SIZE = Long.SIZE; /** * The number of bits representing the block size. * For example if the block size is 64, this value * is 6 = log2 64. */ private static final int BLOCK_SIZE_SHIFT = 6; /** * The limit of blocks that can be allocated is given by * the current limits of array size that is * {@link Integer#MAX_VALUE } - 2 i.e. 231-3. */ private static final int BLOCK_LIMIT = Integer.MAX_VALUE - 2; /** The minimum size for the {@link #primePool}. */ private static final int MIN_POOL_SIZE = 16; /** * Number of bytes of memory occupied by this object * assuming the {@link #primePool} array to be empty. */ private static final int CURRENT_OBJECT_SIZE_IN_BYTES = 68; /** * The greatest value theoretically handled by this class is 3*237-577 * i.e. BLOCK_LIMIT * Long.SIZE * 3 - 1 = (231-3)*64*3-1. * This is an upper limit but the platform-specific limits can be more restrictive. */ public static final long MAX_VALUE = ((long) BLOCK_LIMIT) * ((long) BLOCK_SIZE) * 3L - 1; /** The minimum amount of memory allocated by this object when new created. */ public static final int MIN_MEMORY_CONSUMPTION_IN_BYTES = (MIN_POOL_SIZE << BLOCK_SIZE_SHIFT >> 3) + CURRENT_OBJECT_SIZE_IN_BYTES; /** * Contains data for the Sieve of Eratosthenes. *

* For each bit in this bit-field: *

    *
  • 0 means that the related position is prime,
  • *
  • 1 means that the related position is not prime.
  • *
*/ private long[] primePool; /** The limit of blocks that this instance is allowed to allocate. */ private int maxPoolSize; /** The greatest value that this instance is able to compute. */ private long maxValue; /** * Constructor with parameters. * * @param maxValue the greatest computable value. */ protected PrimeSieve( long maxValue ) { super(); Require.toHold( maxValue >= 0, "The greatest computable value must be positive" ); Require.toHold( maxValue <= MAX_VALUE, () -> "The greatest computable value cannot be greater than " + MAX_VALUE ); this.maxValue = maxValue; double poolSize = maxValue; poolSize = poolSize / BLOCK_SIZE / 3; this.maxPoolSize = (int) Math.ceil( poolSize ); this.init(); } /* ***************** */ /* FACTORY METHODS */ /* ***************** */ /** * Returns a new {@link PrimeSieve} with the default limits: *
    *
  • Greatest computable value: {@code 412316859839}
  • *
  • Memory occupation upper bound: {@code 16GB}
  • *
* * @return a new instance of {@link PrimeSieve} */ public static PrimeSieve get() { return new PrimeSieve( MAX_VALUE ); } /** * Returns a new {@link PrimeSieve} that will compute prime * numbers up to the given value. *

* The provided value cannot be greater than 3*237-577, * otherwise an exception will be thrown. *

* If the method {@link #isPrime(long)} is invoked with a * value greater than this limit an exception will be thrown. * * @param upperLimit the greatest computable value. * @return a new instance of {@link PrimeSieve} * @throws RequirementFailure if the upper limit is greater than 3*237-577 */ public static PrimeSieve withUpperLimit( long upperLimit ) { return new PrimeSieve( upperLimit ); } /** * Returns a new {@link PrimeSieve} that will use up to the * given amount of memory. *

* The minimum amount of memory used by an instance of this class * is {@link #MIN_MEMORY_CONSUMPTION_IN_BYTES}. So, if the given * memory limit is less than {@link #MIN_MEMORY_CONSUMPTION_IN_BYTES}, * it will be ignored. *

* If the method {@link #isPrime(long)} is invoked with a * value that requires, for being computed, more memory than the * given limit, an exception will be thrown. * * @param memoryLimitInBytes amount of memory in bytes that * this instance is allowed to use. * @return a new instance of {@link PrimeSieve} */ public static PrimeSieve withMemoryLimit( long memoryLimitInBytes ) { final long memoryLimit = Require.trueFor( memoryLimitInBytes, memoryLimitInBytes >= MIN_MEMORY_CONSUMPTION_IN_BYTES, "The memory limit must be greater than the minimum amount of required memory of " + MIN_MEMORY_CONSUMPTION_IN_BYTES + " bytes" ); final long upperLimit = getGreatestComputableValue( memoryLimit ); return new PrimeSieve( upperLimit ); } /* **************** */ /* STATIC METHODS */ /* **************** */ /** * Returns the maximum amount of memory (in bytes) needed * to compute all primes less or equal than the given upper limit. *

* In any case the given value must not be greater than 3*237-577. * * @param upperLimit the upper limit of the values to compute. * @return the needed amount of memory expressed in bytes. * @throws RequirementFailure if the upper limit is greater than 3*237-577 */ public static long getMemoryConsumptionInBytes( long upperLimit ) { /* Checks if the limit belongs to the interval [0,412316859839]. */ Require.toHold( upperLimit >= 0 && upperLimit <= MAX_VALUE, () -> upperLimit + " is not in the interval [0,412316859839]." ); /* * We use long words to store the bit-array, * so the memory occupation is a multiple of * 64 bit (8 byte). Each */ long blocks = upperLimit / ( BLOCK_SIZE * 3 ); /* We always use at least 16 blocks of 64 bits. */ blocks = blocks < MIN_POOL_SIZE ? MIN_POOL_SIZE : blocks; /* * The size of an object is 16 bytes regardless * of its content. So we need to count 16 byes * for the current object + 32 bytes for the * array and other 20 for the references in * the current object. * So the memory used is 16 + 32 + 20 + 8 * blocks. */ return ( blocks << 3 ) + CURRENT_OBJECT_SIZE_IN_BYTES; } /** * Returns the the greatest value that can be computed * with the given amount of memory expressed in bytes. * * @param memoryLimitInBytes number of bytes of available memory. * @return the upper limit of the interval computable using the given amount of memory. * @throws RequirementFailure if the amount of available memory is not positive. */ public static long getGreatestComputableValue( long memoryLimitInBytes ) { /* The available memory must be strict positive. */ Require.toHold( memoryLimitInBytes >= MIN_MEMORY_CONSUMPTION_IN_BYTES, "The amount of available memory must be at least " + MIN_MEMORY_CONSUMPTION_IN_BYTES + " bytes" ); /* * First of all we need to remove the amount of memory * used by the object itself. * Than we compute the amount of blocks that we are * able to allocate with the remaining memory. */ final long memoryLimitInBits = (memoryLimitInBytes - CURRENT_OBJECT_SIZE_IN_BYTES) << 3; final long blocks = memoryLimitInBits >> BLOCK_SIZE_SHIFT; /* * The greatest value is given by the amount of values * representable by the number of blocks. * In any case it cannot be greater than MAX_VALUE. */ return Math.min( blocks * BLOCK_SIZE * 3, MAX_VALUE ); } /* **************** */ /* PUBLIC METHODS */ /* **************** */ /** * Tells if the given value is a prime number. *

* The given value must belong to the interval [0,{@link #getMaxValue()}]. *

* The computation for value N takes up to N/12 bytes of memory. *

* If the prime values in the interval {@code [0,value]} have been computed * this method takes {@code O(1)}, otherwise it takes {@code O(N)} * * @param value the value to check. * @return {@code true} if it is a prime number, {@code false} otherwise. * @throws OutOfMemoryError if the required memory exceeds the available one. * @throws RequirementFailure if the value is greater than {@link #getMaxValue()}. */ public boolean isPrime( long value ) { /* Checks if the limit belongs to the interval [0,412316859839]. */ Require.toHold( value >= 0 && value <= maxValue, () -> value + " is not in the interval [0," + maxValue + "]." ); /* * A multiple of 2 or 3 can't be prime. * This check is necessary because the internal * structure is optimized to handle only odd values * that are not multiple of 3. */ if( value < 2 ) return false; if( value == 2 || value == 3 ) return true; if( value % 2 == 0 || value % 3 == 0 ) return false; ensureBounds( value ); return isPrime( this.primePool, value ); } /** * Returns the smallest prime number greater or equal than the provided value. *

* If there are no prime numbers grater or equal than the given threshold * within the internal bounds the value {@code -1} will be returned. *

* If the prime values in the interval {@code [0,value]} have been computed * this method takes {@code O(1)}, otherwise it takes {@code O(N)} * * @param value the threshold value. * @return the prime found if any, {@code -1} otherwise. * @throws OutOfMemoryError if the required memory exceeds the available one. * @throws RequirementFailure if the value is greater than {@link #getMaxValue()}. */ public long getSmallestPrimeGreaterEqual( long value ) { /* Checks if the limit belongs to the interval [0,412316859839]. */ Require.toHold( value >= 0 && value <= maxValue, () -> value + " is not in the interval [0," + maxValue + "]." ); /* * The values represented in the primePool starts from 5 * so we need to handle as special cases the values less than 5. */ if( value <=2 ) return 2; if( value == 3 ) return 3; if( value <= 5 ) return 5; /* * The prime pool represents only odd values that are not multiples of 3. * So, to be able to properly search into the pool, we need to ensure that * the requested value is representable. Since we want to find the smallest * prime greater or equal to the given value we can add some quantity to * fix the requirements. */ final boolean isOdd = value % 2 == 1; final long oddValue = isOdd ? value : value + 1; final long representableValue = oddValue % 3 == 0 ? oddValue + 2 : oddValue; /* We need to ensure the pool capacity for the requested value. */ ensureBounds( representableValue ); /* * If the value to search for is greater than the greatest value represented * in the pool (even after the enlargement) than we are not able to find the * value we are searching for. */ if( representableValue > getGreatestComputedValue() ) return -1; /* We get the index related to the given value. */ final long index = representableValue / 3; /* We get the position of the block containing the given index. */ int blockPos = (int)(index >> BLOCK_SIZE_SHIFT); /* We get the block containing the given index. */ long block = primePool[blockPos]; /* * Now we check if the searched prime is in the current block. * To do so we need to mask all the bits smaller than the * current value. */ long mask = -1L << index; /* If the value is greater than 0 we found the prime. */ long prime = getSmallestPrimeInBlock( blockPos, block | ~mask ); if( prime > 0 ) return prime; /* * If the searched prime is not in the current block * we look in the others. */ while( ++blockPos < maxPoolSize ) { if( blockPos >= primePool.length ) enlargePool( blockPos << BLOCK_SIZE_SHIFT ); block = primePool[blockPos]; if( (prime = getSmallestPrimeInBlock(blockPos, block)) > 0 ) return prime; } return -1; } /** * Returns the greatest prime number less or equal than the provided value. *

* If there are no prime numbers less or equal than the given threshold * within the internal bounds the value {@code -1} will be returned. *

* If the prime values in the interval {@code [0,value]} have been computed * this method takes {@code O(1)}, otherwise it takes {@code O(N)} * * @param value the threshold value. * @return the prime found if any, {@code -1} otherwise. * @throws OutOfMemoryError if the required memory exceeds the available one. * @throws RequirementFailure if the value is greater than {@link #getMaxValue()}. */ public long getGreatestPrimeLessEqual( long value ) { /* Checks if the limit belongs to the interval [0,412316859839]. */ Require.toHold( value >= 0 && value <= maxValue, () -> value + " is not in the interval [0," + maxValue + "]." ); /* * The values represented in the primePool starts from 5 * so we need to handle as special cases the values less than 5. */ if( value < 2 ) return -1; if( value == 2 ) return 2; if( value < 5 ) return 3; // values less than 3 are already been checked. /* * The prime pool represents only odd values that are not multiples of 3. * So, to be able to properly search into the pool, we need to ensure that * the requested value is representable. Since we want to find the greatest * prime less or equal to the given value we can remove some quantity to * fix the requirements. */ final boolean isOdd = value % 2 == 1; final long oddValue = isOdd ? value : value - 1; final long representableValue = oddValue % 3 == 0 ? oddValue - 2 : oddValue; /* We need to ensure the pool capacity for the requested value. */ ensureBounds( representableValue ); /* We get the index related to the given value. */ final long index = representableValue / 3; /* We get the position of the block containing the given index. */ int blockPos = (int)(index >> BLOCK_SIZE_SHIFT); /* We get the block containing the given value. */ long block = primePool[blockPos]; /* * Now we check if the searched prime is in this block. * To do so we need to mask all the bits greater than * the current value. */ long mask = -1L >>> -index-1; /* If the value is greater than 0 we found the prime. */ long prime = getGreatestPrimeInBlock( blockPos, block | ~mask ); if( prime > 0 ) return prime; /* * If the searched prime is not in the current block * we look in the others. */ while( --blockPos >= 0 ) { block = primePool[blockPos]; if( (prime = getGreatestPrimeInBlock(blockPos, block)) > 0 ) return prime; } return -1; } /* ***************** */ /* PRIVATE METHODS */ /* ***************** */ /** * Ensures that the internal {@link #primePool} is big enough * to contain the given value. *

* The given value is granted to be in the interval [0,{@link #maxValue}). * * @param value the value to check. */ private void ensureBounds( long value ) { final long index = value / 3; final long poolSize = primePool.length; final long upperBound = poolSize << BLOCK_SIZE_SHIFT; if( index >= upperBound ) enlargePool( index ); } /** * Returns {@code true} if the bit in the given position * is set to {@code 0}, {@code false} otherwise. * * @param data the {@code int} bit-word containing the bit to evaluate. * @param position the position of the bit in range [0,{@link #BLOCK_SIZE}). * * @return {@code true} if the bit value is {@code 0};
* {@code false} otherwise. */ private boolean booleanAt( long data, int position ) { /* * We assume the bit position to be in range [0,BLOCK_SIZE). * This method return true if the bit in the given * position is 0 and false if the bit is 1. */ return ( (data >> position) & 1L ) == 0; } /** * Returns {@code true} if the given value is prime. * * @param pool pool of primes to check. * @param value value to check. * @return {@code true} if is prime, {@code false} otherwise. */ private boolean isPrime( long[] pool, long value ) { /* * We always ensure the requested value to be odd * and to be not a multiple of 3. * The bits in the bit-array represents only odd * values not multiple of 3, so to get the right * position in the bit-array we need to divide * the value by 3. */ final long index = value / 3; final int block = (int)(index >> BLOCK_SIZE_SHIFT); final int position = (int)(index % BLOCK_SIZE); return booleanAt( pool[block], position ); } /** * Initialize the {@link #primePool} with the * default values. * */ private void init() { /* We start with a pool size of 16. */ final long[] pool = new long[16]; /* * The number 1 is not considered to be prime, * so we start setting 1 as not prime. */ pool[0] = 1L; /* We apply the sieve starting from the first block. */ performSieve( pool, 0 ); this.primePool = pool; } /** * Enlarges the {@link #primePool} size to enclose the given value. *

* This is the only method that modifies the pool. This method is * {@code thread-safe} even without synchronization however, if the * requested values are big, the size of the pool can be remarkable * so we better avoid multiple threads trying to enlarge the pool * at the same time. Therefore this method is synchronized. * * @param index the index to be enclosed by the {@link #primePool}. */ private synchronized void enlargePool( long index ) { final long currentPoolSize = primePool.length; /* * We double the size of the pool until * the requested value belongs to the pool. */ long newPoolSize = primePool.length; while( index >= (newPoolSize << BLOCK_SIZE_SHIFT) ) newPoolSize = newPoolSize << 1; /* poolSize = poolSize * 2 */ if( newPoolSize > maxPoolSize ) newPoolSize = maxPoolSize; /* If another thread had already enlarged the pool nothing needs to be done. */ if( newPoolSize == currentPoolSize ) return; /* * Otherwise we create a new bit-array with the needed size, * we copy the old bit-array into the new one, and apply the * sieve to the new bits. */ final long[] newPool = Arrays.copyOf( this.primePool, (int) newPoolSize ); /* We apply the sieve to the new blocks. */ performSieve( newPool, currentPoolSize ); /* Finally we update the internal primePool. */ this.primePool = newPool; } /** * This method divides the data block into smaller parts to take * advantage of the Java Fork/Join framework. *

* This method can be called with a partially computed bit-array * therefore it requires the index of the block to start with. * * @param data the data to sift. * @param startBlock the block to start with. */ private void performSieve( long[] data, long startBlock ) { final PerformSieve performSieve = new PerformSieve( data, startBlock, data.length ); final ForkJoinPool pool = ForkJoinPool.commonPool(); pool.invoke( performSieve ); } /** * This method actually performs the Sieve of Eratosthenes algorithm. *

* This method can be called with a partially computed bit-array * therefore it requires the index of the block to start with * and the index of the block to end with. * * @param data the data to sift. * @param startBlock the block to start with. * @param endBlock the block to end with. */ private void performSieve( long[] data, long startBlock, long endBlock ) { /* This is the superior limit of the data to be sifted. */ final long endValue = 3L * (endBlock << BLOCK_SIZE_SHIFT); /* The number represented by the first bit of the start block. */ final long startBlockValue = 3L * (startBlock << BLOCK_SIZE_SHIFT); /* * We need only to remove the multiples of the primes * in the interval [5, sqrt(endValue)] because the other * non primes have already been sifted. */ final long lastPrime = Math.round( Math.sqrt(endValue) ); /* * The bit-array doesn't contain multiples of 2 or 3 * so removing them is not necessary, but also they * are not represented so must be skipped. * The boost value is needed to skip multiples of 3 * that are not also multiples of 2 i.e. the odd * multiples of 3. * Starting from the list of all numbers: * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ... * we remove all the even numbers getting: * 1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 ... * n n y y n y y n y y n y y n y y n ... * The numbers represented in the bit-array are all * those marked with 'y'. So in the following loop * we need to skip one position each 2, for this reason * we use the boost. */ int boost = 1; long startValue; for( long i = 5; i <= lastPrime; i += 2 << boost ) { /* * boost is initialized to 1 so during this first * loop boost becomes 0 and the next value of i * is 7. Then boost becomes 1 so 9 will be skipped * and the following value of i will be 11 and so on. */ boost = 1 - boost; if( isPrime(data, i) ) { /* * We don't need to start from the beginning * because all the non-primes in the interval * [3, i*i) have already been sifted. Actually * we can start from the greatest value between * startBlockValue and i * i. */ startValue = i * i; /* * We need the start value to be an odd multiple * of i that is not divisible by 3. If the greatest * value is i * i we already have a valid value, * otherwise we have a power of 2, so we need to * adjust it to get the needed value. */ if( startValue <= startBlockValue ) startValue = adjustValue( startBlockValue, i ); /* The start index is the startValue / 3. */ /* We remove all the multiples of i. */ siftMultiples( data, startValue, endValue, i ); } } } /** * Takes the given value and the given prime and returns * the greatest odd multiple of prime smaller than value. * * @param value value to adjust. * @param prime prime value to adjust for. * @return the adjusted value. */ private long adjustValue( long value, long prime ) { /* * We need the value to be an odd multiple of prime. * As first step we bring it to be a multiple of prime * removing the difference between value and the * greatest multiple of prime smaller then value. * Ex. value = 64, prime = 7 => 64 - (64 % 7) = 64 - 1 = 63; */ value = value - (value % prime); /* * If value is even, we remove prime to get the * greatest odd multiple of prime smaller than * value. The arguments given to this method * ensures that the returned value will never * be smaller then 5; * Ex. 63 is odd, the value remains unchanged. */ value = value % 2 == 0 ? value - prime : value; /* * At this point we need to ensure that the value * is not divisible by 3, otherwise we add 2 * prime * getting the smallest multiple of prime not divisible * by 3 and not sifted yet. * Ex. 63 is a multiple of 3 => 63 + (7 << 1) = 63 + 14 = 77. */ return value % 3 == 0 ? value + (prime << 1) : value; } /** * This method sifts the multiples of the given prime * according to the Sieve of Eratosthenes algorithm. * * @param data the data to sift. * @param startValue the value to start with. * @param endValue the value that represents the end of the interval. * @param prime the prime which multiples have to be sifted out. */ private void siftMultiples( long[] data, long startValue, long endValue, long prime ) { /* * The principle of skipping the odd multiples of 3 exposed in the method * "performSieve" holds also in this method. Here we need to remove all * the multiples of prime that are not divisible by 2 or 3 i.e. we need to * skip all odd values x such that x % 3 = 0. * Assuming prime % 3 = 1 we have: * prime = 3x + 1 => 2 * prime = 6x + 2 => (2 * prime) % 3 = 2. * So we need to skip all the positions p in the form p * i + 2. * Otherwise if prime % 3 = 2 we have: * prime = 3x + 2 => 2 * prime = 6x + 4 => 2 * prime = 3 * (2x + 1) + 1 * => 2 * prime % 3 = 1. * So we need to skip all the positions p in the form p * i + 1. * For that reason we initialize the boost as prime % 3. */ int boost = (int)(prime % 3); /* * The startValue is granted not to be a multiple of 3 * so startValue % 3 can be 1 or 2. In the following loop * we start by startValue and we step forward by 2 * prime * or 4 * prime depending on the boost. So we need to * adjust the boost so that we not catch a multiple of 3. * To do that we need to properly combine the values of * startValue % 3 and prime % 3. * The following operation is given by the table: * * startValue % 3 prime % 3 result * 1 1 2 * 1 2 1 * 2 1 1 * 2 2 2 * * But considering the fact that the boost will be * inverted during the loop we need to invert the * operation to get the right starting value. */ boost = startValue % 3 == 2 ? 3 - boost : boost; /* * NOTES REGARDING THE FOLLOWING BINARY OPERATIONS: * * Setting the bit-word to 1 means that all the * bits in the bit-word are 0 rather than the last one. * 1 = 00000000-00000000-00000000-00000001 (32 bit-word) * * This operation moves all the bits to the left by the given * number of positions and fills the rest of the word with 0. * For example if N = 20 we obtain the following mask: * mask = 00000000-00010000-00000000-00000000 * The positions are 0-based so shifting position 20 means * that the bit 1 is in the 21th position. * * This operation is modular that means that shifting 33 positions * is the same as shifting 1 position. We need a mask with all 0 and * only one 1 in the requested position. */ int block; long mask, index; for( long value = startValue; value < endValue; value += prime << boost ) { /* * For the same reason exposed in the method "performSieve" * the boost value must be alternated between 1 and 2. * We ensure boost to be 1 or 2, and with this operation * we set boost = 2 if it was 1 and vice versa. */ boost = 3 - boost; /* The bit-array index is given by the value / 3. */ index = value / 3; /* We get the current block. */ block = (int)( index >> BLOCK_SIZE_SHIFT ); /* We create the bit-mask for the given value. */ mask = 1L << index; /* We use the bit mask to set the bit to 1. */ data[ block ] |= mask; } } /** * Returns the value of the smallest prime in the given block if any. * * @param blockPos the position of the block * @param block the block to analyze. * @return the smallest prime if any, {@code -1} otherwise. */ private long getSmallestPrimeInBlock( long blockPos, long block ) { /* * For this operation we need to invert the logic * considering 1 to be prime and 0 to be non prime. * Therefore we ne to complement the bits in the block. */ final long complement = ~block; /* Returns the number of zero-bit preceding the first 1-bit. */ int zeros = Long.numberOfTrailingZeros( complement ); /* * If the number of zeros equals the block size it means that * there is no 1-bits in the complemented block, otherwise we * use the zeros to retrieve the position of the searched prime. */ if( zeros == BLOCK_SIZE ) return -1; final long index = (blockPos << BLOCK_SIZE_SHIFT) + zeros; final long value = index * 3; final long boost = (value % 2) + 1; final long prime = value + boost; return prime; } /** * Returns the value of the greatest prime in the given block if any. * * @param blockPos the position of the block * @param block the block to analyze. * @return the greatest prime if any, {@code -1} otherwise. */ private long getGreatestPrimeInBlock( long blockPos, long block ) { /* * For this operation we need to invert the logic * considering 1 to be prime and 0 to be non prime. * Therefore we ne to complement the bits in the block. */ final long complement = ~block; /* Returns the number of zero-bit following the last 1-bit. */ int zeros = Long.numberOfLeadingZeros( complement ); /* * If the number of zeros equals the block size it means that * there is no 1-bits in the complemented block, otherwise we * use the zeros to retrieve the position of the searched prime. */ if( zeros == BLOCK_SIZE ) return -1; final long index = (++blockPos << BLOCK_SIZE_SHIFT) - ++zeros; final long value = index * 3; final long boost = (value % 2) + 1; final long prime = value + boost; return prime; } /* *************** */ /* INNER CLASSES */ /* *************** */ /** * Implementation of the {@link ForkJoinTask} * that divides the {@link #primePool} into * blocks and performs the Sieve of Eratosthenes * algorithm in parallel on each block. * *

* This class follows the Fork/Join best practices * to take the most advantage on the divide and conquer * paradigm. */ private class PerformSieve extends RecursiveAction { /** * If the number of blocks to compute * is smaller than this threshold it * is more performing to execute the * sieve sequentially. */ private static final long THRESHOLD = 1 << 16; /** The bit array to sieve. */ private final long[] data; /** The block to start with. */ private final long start; /** The block to start with. */ private final long end; /** * Constructor with parameters. * * @param data The bit array to sieve. * @param start The block to start with. * @param end The block to end with. */ public PerformSieve(long[] data, long start, long end ) { super(); this.data = Require.nonEmpty( data, "The bit array to sieve cannot be empty" ); this.start = Require.trueFor( start, start >= 0, "The start block cannot be negative" ); this.end = Require.trueFor( end, end > start, "The end block must be greater than the start block" ); } /** * {@inheritDoc} */ @Override protected void compute() { if( end - start > THRESHOLD ) { final long middle = (start + end) >> 1; final PerformSieve left_half = new PerformSieve( data, start, middle ); final PerformSieve right_half = new PerformSieve( data, middle, end ); ForkJoinTask.invokeAll( left_half, right_half ); } else performSieve( data, start, end ); } } /* *********************** */ /* CONFIGURATION METHODS */ /* *********************** */ /** * Returns the greatest value computable by this instance. * * @return the greatest value computable by this instance. */ public long getMaxValue() { return maxValue; } /** * Returns the greatest value computed so far. * * @return the greatest value computed so far. */ public long getGreatestComputedValue() { final long poolSize = primePool.length; return ( poolSize << BLOCK_SIZE_SHIFT ) * 3L; } /** * Returns the amount of memory, in bytes, currently used by this object. * * @return the amount of memory, in bytes, currently used by this object. */ public long getCurrentMemoryConsumptionInBytes() { return getMemoryConsumptionInBytes( getGreatestComputedValue() ); } /* ***************** */ /* OBJECT OVERRIDES */ /* ***************** */ /** * {@inheritDoc} */ @Override public int hashCode() { return Hashcode.of( maxValue ); } /** * {@inheritDoc} */ @Override public boolean equals( Object other ) { return Equals.ifSameClass( this, other, o -> o.maxValue ); } /** * {@inheritDoc} */ @Override public String toString() { return ToString.of( this ).print( maxValue ).using( "( [0,", "", ") )" ); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy