1 /++ 2 This module contains utilities relating to prime numbers. 3 4 Right now this module mainly has an infinite range of primes backed by a 5 Sieve of Eratosthenes. 6 7 HELP_WELCOMED: 8 The potential number of "prime number utilities" is much, much larger than 9 the relatively small set here, that I've just wanted for some minor 10 benchmarks, but please feel free to submit additional utilities so the 11 namespace can become more useful. 12 +/ 13 module math.primes; 14 15 import std.traits : isIntegral; 16 17 /++ 18 Calculates the upper bound of the integer range containing the nth prime. 19 20 Useful for knowing how large of a sieve to prepare if you want to collect a 21 certain number of prime numbers, for example. 22 +/ 23 size_t nthPrimeUpperBound(size_t n) @nogc @safe pure nothrow { 24 import std.math : log; 25 26 if (n > 6) 27 return cast(size_t)(n * log(n) + n * log(log(n))); 28 else 29 return 11; 30 } 31 32 /// 33 unittest { 34 assert(nthPrimeUpperBound(4) == 11); 35 assert(nthPrimeUpperBound(7) == 18); 36 assert(nthPrimeUpperBound(50) == 263); 37 } 38 39 /++ 40 The Sieve of Eratosthenes 41 42 Backed by a bool[]; the template argument is only used for integral type 43 yielded by the range. 44 +/ 45 struct Sieve(T = size_t) if (isIntegral!T) { 46 private bool[] sieve; /// false = prime. ignore indexes < 2 47 48 /// Constructs a sieve with the given size. 49 this(size_t length) @safe nothrow { 50 sieve.length = length; 51 52 // code duplication with sift, but avoids unnecessary division 53 foreach (i; 2 .. length / 2) { 54 if (!sieve[i]) { 55 size_t j = i + i; 56 while (j < length) { 57 sieve[j] = true; 58 j += i; 59 } 60 } 61 } 62 } 63 64 /++ 65 Loop over the entire sieve, but only start eliminating composite 66 numbers from after the 'from' index on. Useful after resizing the 67 sieve. 68 +/ 69 private void sift(size_t from) @nogc @safe nothrow { 70 immutable size_t length = sieve.length; 71 foreach (i; 2 .. length / 2) { 72 if (!sieve[i]) { 73 size_t j = i >= from ? i + i : from - from % i + i; 74 while (j < length) { 75 sieve[j] = true; 76 j += i; 77 } 78 } 79 } 80 } 81 82 private size_t i = 2; 83 84 bool empty = false; 85 86 T front() @nogc @safe nothrow { 87 return cast(T) i; 88 } 89 90 void popFront() @safe nothrow { 91 while (1) { 92 while (++i < sieve.length) { 93 if (!sieve[i]) 94 return; 95 } 96 --i; 97 // ran out of sieve! 98 sieve.length += 5000; 99 sift(i - 1); 100 } 101 } 102 103 /// Return a finite slice of the sieve. 104 auto fixed() @nogc @safe nothrow { 105 struct FixedSieve { 106 private bool[] sieve; 107 private size_t i = 2; 108 109 bool empty() { 110 return i >= sieve.length; 111 } 112 113 T front() { 114 return cast(T) i; 115 } 116 117 void popFront() { 118 while (++i < sieve.length) { 119 if (!sieve[i]) 120 break; 121 } 122 } 123 } 124 125 return FixedSieve(sieve); 126 } 127 } 128 129 /// 130 unittest { 131 import std.algorithm : sum, until; 132 import std.range : take, array; 133 134 assert(Sieve!int(50).until!(n => n >= 50).sum == 328); 135 assert(Sieve!int(nthPrimeUpperBound(50)).take(50).sum == 5117); 136 assert(Sieve!int(30).fixed.array == [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]); 137 assert(Sieve!int(0).fixed.array == []); 138 139 // this causes compilation to take about 9s and 2 GB of memory. 140 // static assert(Sieve!ulong(2_000_000).fixed.sum = 142913828922); 141 142 auto ps = Sieve!int(); 143 assert(ps.front == 2); 144 ps.popFront(); 145 assert(ps.front == 3); 146 }