diff --git a/src/main/java/com/autonomouslogic/primes/Primorials.java b/src/main/java/com/autonomouslogic/primes/Primorials.java new file mode 100644 index 0000000..5f858ff --- /dev/null +++ b/src/main/java/com/autonomouslogic/primes/Primorials.java @@ -0,0 +1,188 @@ +package com.autonomouslogic.primes; + +import java.util.Arrays; +import java.util.stream.IntStream; +import java.util.stream.LongStream; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import lombok.Value; + +/** + *
+ * Functions for working with primorials.
+ * Each order n represents the primorial p_n# = c#, which is the sum of all primes between 2 and c.
+ * Each order consists of the order n, the last prime c, the product,
+ * and all the coprime offsets from product.
+ *
+ * See Primorial + * and Primality test (Simple methods). + *
+ * + *+ * The main function in this class summarises the orders with the output: + *
+ * Order 0: c=1, product=1, 1 coprime offsets, 100.0% space + * Order 1: c=2, product=2, 1 coprime offsets, 50.0% space + * Order 2: c=3, product=6, 2 coprime offsets, 33.3% space + * Order 3: c=5, product=30, 8 coprime offsets, 26.7% space + * Order 4: c=7, product=210, 48 coprime offsets, 22.9% space + * Order 5: c=11, product=2310, 480 coprime offsets, 20.8% space + * Order 6: c=13, product=30030, 5760 coprime offsets, 19.2% space + * Order 7: c=17, product=510510, 92160 coprime offsets, 18.1% space + * Order 8: c=19, product=9699690, 1658880 coprime offsets, 17.1% space + * Order 9: c=23, product=223092870, 36495360 coprime offsets, 16.4% space + *+ * + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class Primorials { + /** + * The maximum order that will be cached and used in {@link #allPossiblePrimes(long)}. + */ + private static final int MAX_ORDER = 3; + + private static final Order[] ORDER_CACHE = new Order[MAX_ORDER + 1]; + + @Value + @AllArgsConstructor(access = AccessLevel.PROTECTED) + public static class Order { + /** + * The order of the primorial. + */ + int n; + + /** + * The last prime of the product. + */ + int c; + + /** + * The product of the primorial. + */ + long product; + + /** + * The coprime offsets which when added to multiples of the product are not divisible by any of the primes + * less than or equal to
c.
+ */
+ long[] coprimeOffsets;
+
+ /**
+ * Returns all possible primes associated with this primorial order.
+ * Each number returned is in the form product * k + i, where k is an incrementing
+ * positive integer and i is each possible coprime offset.
+ * For instance, order 1 will return all odd numbers.
+ * Order 2 will first return 3 and 5, then 7 and 9, and so on.
+ * Order 3 will first return 31, 37, 41, 43, 47, 49, 53, 59, then 61, 67, 71, 73, 77, 79, 83, 89, and so on.
+ * Each order will return a higher start number, but the stream will contain fewer and fewer numbers.
+ * This can be used when searching for prime numbers.
+ *
+ * @return
+ */
+ public LongStream possiblePrimes() {
+ return LongStream.iterate(1, i -> i + 1)
+ .flatMap(k -> Arrays.stream(coprimeOffsets).map(i -> k * this.product + i));
+ }
+ }
+
+ /**
+ * Returns the primorial p_n#
+ * @param n
+ * @return
+ */
+ public static long ofOrder(int n) {
+ return Arrays.stream(PrimeList.PRIMES).limit(n).asLongStream().reduce(1, (left, right) -> left * right);
+ }
+
+ /**
+ * Returns a primorial order with all the coprime offsets calculated.
+ * @param n
+ * @return
+ */
+ public static Primorials.Order ofOrderWithCoprimes(int n) {
+ if (n <= MAX_ORDER && ORDER_CACHE[n] != null) {
+ return ORDER_CACHE[n];
+ }
+
+ var primes = Arrays.stream(PrimeList.PRIMES).limit(n).toArray();
+ long product = 1;
+ for (int prime : primes) {
+ product *= prime;
+ }
+ final var finalProduct = product;
+ var offsets = LongStream.range(product, 2 * product)
+ .filter(num -> {
+ for (int prime : primes) {
+ if ((num % prime) == 0) {
+ return false;
+ }
+ }
+ return true;
+ })
+ .map(num -> num - finalProduct)
+ .toArray();
+ var c = primes.length == 0 ? 1 : primes[primes.length - 1];
+ var order = new Primorials.Order(n, c, product, offsets);
+ if (n <= MAX_ORDER) {
+ ORDER_CACHE[n] = order;
+ }
+ return order;
+ }
+
+ /**
+ * @see #allPossiblePrimes(long)
+ * @return
+ */
+ public static LongStream allPossiblePrimes() {
+ return allPossiblePrimes(2);
+ }
+
+ /**
+ * Returns all the possible primes from each primorial order in sequence, starting from the supplied number.
+ * The sequence will gradually contain fewer and fewer numbers as the orders are able to advance.
+ * @param from the number to start from, must be at least 2
+ * @return
+ */
+ public static LongStream allPossiblePrimes(long from) {
+ if (from < 2) {
+ throw new IllegalArgumentException("from must be at least 2");
+ }
+ var mainStream = IntStream.rangeClosed(1, MAX_ORDER)
+ .mapToObj(Primorials::ofOrderWithCoprimes)
+ .filter(o -> ofOrder(o.getN() + 1) >= from)
+ .flatMapToLong(order -> {
+ System.out.printf("Order: %s%n", order);
+ var stream = order.possiblePrimes();
+ if (order.getN() < MAX_ORDER) {
+ System.out.println("not MAX_ORDER");
+ var nextK = ofOrder(order.getN() + 1);
+ System.out.printf("nextK: %s%n", nextK);
+ stream = order.possiblePrimes().takeWhile(num -> num < nextK);
+ }
+ if (order.getProduct() < from) {
+ stream = stream.filter(n -> n >= from);
+ }
+ System.out.println("return stream");
+ return stream;
+ });
+ if (from == 2) {
+ return LongStream.concat(LongStream.of(2), mainStream);
+ } else {
+ return mainStream;
+ }
+ }
+
+ public static void main(String[] args) {
+ for (int i = 0; i <= 9; i++) {
+ var order = Primorials.ofOrderWithCoprimes(i);
+ var offsetsLen = order.getCoprimeOffsets().length;
+ var space = 100.0 * offsetsLen / order.getProduct();
+ System.out.printf(
+ "Order %d: c=%d, product=%d, %d coprime offsets, %.1f%% space%n",
+ order.getN(), order.getC(), order.getProduct(), offsetsLen, space);
+ }
+ }
+}
diff --git a/src/main/java/com/autonomouslogic/primes/TrialDivision.java b/src/main/java/com/autonomouslogic/primes/TrialDivision.java
index d00b3e3..3869c6e 100644
--- a/src/main/java/com/autonomouslogic/primes/TrialDivision.java
+++ b/src/main/java/com/autonomouslogic/primes/TrialDivision.java
@@ -8,15 +8,34 @@ public boolean isPrime(long number) {
if (number == 2) {
return true;
}
+ var maxCheck = PrimeUtils.maxRequiredCheck(number);
+
+ // This is so much faster than using the stream of possible primes below. It can probably be optimised.
if (number % 2 == 0) {
return false;
}
- var max = PrimeUtils.maxRequiredCheck(number);
- for (int i = 3; i <= max; i += 2) {
- if (number % i == 0) {
+ // for (int n = 3; n <= maxCheck; n += 2) {
+ // if (number % n == 0) {
+ // return false;
+ // }
+ // }
+
+ var iterator = Primorials.allPossiblePrimes().iterator();
+ while (iterator.hasNext()) {
+ var n = iterator.next();
+ System.out.println("n=" + n);
+ if (n > maxCheck) {
+ break;
+ }
+ if (number % n == 0) {
return false;
}
}
+
return true;
+
+ // return Primorials.allPossiblePrimes()
+ // .takeWhile(n -> n <= maxCheck)
+ // .noneMatch(n -> number % n == 0);
}
}
diff --git a/src/test/java/com/autonomouslogic/primes/PrimorialsTest.java b/src/test/java/com/autonomouslogic/primes/PrimorialsTest.java
new file mode 100644
index 0000000..e144765
--- /dev/null
+++ b/src/test/java/com/autonomouslogic/primes/PrimorialsTest.java
@@ -0,0 +1,111 @@
+package com.autonomouslogic.primes;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.LongStream;
+import java.util.stream.Stream;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+public class PrimorialsTest {
+ @ParameterizedTest
+ @MethodSource("orderTests")
+ void shouldCreateOrdersWithCoprimeOffsets(int order, Primorials.Order expected) {
+ assertEquals(expected, Primorials.ofOrderWithCoprimes(order));
+ }
+
+ public static Stream