1 /**
2     This module provides arrays of prime numbers that are available at compile-time.
3     Arrays are calculated as long as you need, using CTFE.
4 
5     This module uses the Sieve of Eratosthenes to find primes and so is
6     reasonably performant, but care should be taken with memory usage. For
7     example, with dmd without -lowmem, an array of only 26k primes will cost 3
8     GB of memory during compilation.
9 */
10 module ctprimes;
11 
12 import std.traits : isIntegral;
13 
14 pure size_t nth_prime_upper_bound(size_t n)
15 {
16     import std.math : log;
17 
18     if (n > 6)
19         return cast(size_t)(n * log(n) + n * log(log(n)));
20     else
21         return 11;
22 }
23 
24 pure bool[] composites(size_t length)
25 {
26     bool[] sieve; // false = prime. ignore indexes < 2
27     sieve.length = length;
28     foreach (i; 2 .. length)
29     {
30         if (!sieve[i])
31         {
32             size_t j = i + i;
33             while (j < length)
34             {
35                 sieve[j] = true;
36                 j += i;
37             }
38         }
39     }
40     return sieve;
41 }
42 
43 /**
44     Construct an array of prime number using CTFE.
45     The length of the array is `length`, and the type is `T[length]`.
46 
47     Params:
48         length = Length of array
49         T = The type of the elements of the array
50 */
51 public template ctPrimes(size_t length, T = size_t) if (isIntegral!T && 0 < length)
52 {
53     enum T[length] ctPrimes = () {
54         T[] result;
55         result.reserve(length);
56         auto sieve = composites(nth_prime_upper_bound(length));
57         foreach (i; 2 .. sieve.length)
58         {
59             if (!sieve[i])
60                 result ~= cast(T) i;
61         }
62         result.length = length;
63         return result;
64     }();
65 }
66 
67 ///
68 @safe unittest
69 {
70     import std.random : uniform;
71 
72     auto runtimevalue = uniform(0, 5);
73     static assert(!__traits(compiles, {
74             ctPrimes!runtimevalue; // Error: variable runtimevalue cannot be read at compile time
75         }));
76 
77     static assert(__traits(compiles, {
78             auto a = ctPrimes!(5, byte);
79             auto b = ctPrimes!(5, ubyte);
80             auto c = ctPrimes!(5, short);
81             auto d = ctPrimes!(5, int);
82             auto e = ctPrimes!(5, long);
83         }));
84 
85     static assert(!__traits(compiles, {
86             auto a = ctPrimes!(5, bool);
87             auto b = ctPrimes!(5, char);
88             auto c = ctPrimes!(5, double);
89         }));
90 
91     auto primes1 = ctPrimes!(10);
92     static assert(is(typeof(primes1) == size_t[10]));
93     assert(primes1 == [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]);
94 
95     auto primes2 = ctPrimes!(1);
96     assert(primes2 == [2]);
97 
98     auto primes3 = ctPrimes!(10_000);
99     assert(primes3[$ - 1] == 104_729);
100 }
101 
102 /**
103     Construct an array of all prime numbers less than N, using CTFE.
104     The type of the array is `typeof(N)[]`.
105 
106     Params:
107         N = All elements of result are less than this value
108 */
109 public template ctPrimesLessThan(alias N) if (isIntegral!(typeof(N)))
110 {
111     enum typeof(N)[] ctPrimesLessThan = () {
112         typeof(N)[] result;
113         result.reserve(N);
114         auto sieve = composites(N);
115         foreach (i; 2 .. sieve.length)
116         {
117             if (!sieve[i])
118                 result ~= cast(typeof(N)) i;
119         }
120         return result;
121     }();
122 }
123 
124 @safe unittest
125 {
126     import std.random : uniform;
127 
128     auto runtimevalue = uniform(0, 5);
129     static assert(!__traits(compiles, {
130             ctPrimes!runtimevalue; // Error: variable runtimevalue cannot be read at compile time
131         }));
132 
133     static assert(__traits(compiles, {
134             auto a = ctPrimesLessThan!(cast(byte) 5);
135             auto b = ctPrimesLessThan!(cast(ubyte) 5);
136             auto c = ctPrimesLessThan!(cast(short) 5);
137             auto d = ctPrimesLessThan!(cast(int) 5);
138             auto e = ctPrimesLessThan!(cast(long) 5);
139         }));
140 
141     static assert(!__traits(compiles, {
142             auto a = ctPrimesLessThan!(true);
143             auto b = ctPrimesLessThan!('a');
144             auto c = ctPrimesLessThan!(5f);
145         }));
146 
147     auto primes1 = ctPrimesLessThan!(10);
148     static assert(is(typeof(primes1) == typeof([1])));
149     assert(primes1 == [2, 3, 5, 7]);
150 
151     auto primes2 = ctPrimesLessThan!(2);
152     assert(primes2 == []);
153 
154     auto primes3 = ctPrimesLessThan!(0);
155     assert(primes3 == []);
156 
157     auto primes4 = ctPrimesLessThan!(104_730);
158     assert(primes4[$ - 1] == 104_729);
159 }