1 // Written in the D programming language.
2 
3 /**
4  * Implements compile-time checks for different features of random
5  * number generating code.
6  *
7  * Copyright: © 2008-2011 Andrei Alexandrescu,
8  *              2013-2014 Joseph Rushton Wakeling
9  *
10  * License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
11  *
12  * Authors: $(WEB erdani.org, Andrei Alexandrescu),
13  *          $(WEB braingam.es, Joseph Rushton Wakeling)
14  *
15  * Source: $(HAPSRC hap/random/_traits.d)
16  */
17 module hap.random.traits;
18 
19 import std.range, std.traits;
20 
21 /**
22  * Test if $(D Range) is a uniform random number generator. The overload
23  * taking an $(D ElementType) also makes sure that the RNG generates
24  * values of that type.
25  *
26  * A uniform random number generator has at least the following features:
27  * $(UL
28  *   $(LI it is an InputRange)
29  *   $(LI it has a $(D bool isUniformRandom) field readable in CTFE)
30  *   $(LI its element type is a uniform integral type)
31  *   $(LI it has $(D min) and $(D max) fields whose type is the same
32  *        as the element type)
33  * )
34  *
35  * This quite strict definition follows that in the C++11 standard.
36  * Note that the template is unable to enforce some required features,
37  * such as the requirement that the RNG's values must be drawn from the
38  * $(I closed) interval $(D [min, max]).
39  */
40 template isUniformRNG(Range, ElementType)
41 {
42     enum bool isUniformRNG = isInputRange!Range &&
43         is(typeof(Range.front) == ElementType) &&
44         is(typeof(Range.min) == ElementType) &&
45         is(typeof(Range.max) == ElementType) &&
46         isIntegral!ElementType &&
47         isUnsigned!ElementType &&
48         is(typeof(
49         {
50             static assert(Range.isUniformRandom); //tag
51         }));
52 }
53 
54 /// ditto
55 template isUniformRNG(Range)
56 {
57     enum bool isUniformRNG =
58         is(typeof(
59         {
60             static assert(isUniformRNG!(Range, typeof(Range.front)));
61         }));
62 }
63 
64 /**
65  * Test if $(D UniformRNG) is a seedable uniform random number generator.
66  * The overload taking a $(D SeedType) also makes sure that the generator
67  * can be seeded with $(D SeedType).
68  *
69  * A seedable random-number generator has the following additional features:
70  * $(UL
71  *   $(LI it has a $(D seed(ElementType)) function)
72  * )
73  */
74 template isSeedable(UniformRNG, SeedType)
75 {
76     enum bool isSeedable = isUniformRNG!UniformRNG &&
77         is(typeof(
78         {
79             UniformRNG r = void;    // can define a Rng object
80             r.seed(SeedType.init);  // can seed a Rng
81         }));
82 }
83 
84 ///ditto
85 template isSeedable(UniformRNG)
86 {
87     enum bool isSeedable = isUniformRNG!UniformRNG &&
88         is(typeof(
89         {
90             UniformRNG r = void;           // can define a Rng object
91             r.seed(typeof(r.front).init);  // can seed a Rng
92         }));
93 }
94 
95 unittest
96 {
97     /* Not an RNG because it lacks isUniformRandom,
98      * min and max
99      */
100     struct NoRng
101     {
102         @property uint front() {return 0;}
103         @property bool empty() {return false;}
104         void popFront() {}
105     }
106     static assert(!isUniformRNG!(NoRng, uint));
107     static assert(!isUniformRNG!(NoRng));
108     static assert(!isSeedable!(NoRng, uint));
109     static assert(!isSeedable!(NoRng));
110     NoRng noRng;
111     noRng.popFront();
112     assert(!noRng.empty);
113     assert(!noRng.front);
114 
115     /* Not an RNG because isUniformRandom is false,
116      * and it lacks min and max
117      */
118     struct NoRng2
119     {
120         @property uint front() {return 0;}
121         @property bool empty() {return false;}
122         void popFront() {}
123 
124         enum isUniformRandom = false;
125     }
126     static assert(!isUniformRNG!(NoRng2, uint));
127     static assert(!isUniformRNG!(NoRng2));
128     static assert(!isSeedable!(NoRng2, uint));
129     static assert(!isSeedable!(NoRng2));
130     NoRng2 noRng2;
131     noRng2.popFront();
132     assert(!noRng2.empty);
133     assert(!noRng2.front);
134 
135     /* Not an RNG because it lacks front, min
136      * and max
137      */
138     struct NoRng3
139     {
140         @property bool empty() {return false;}
141         void popFront() {}
142 
143         enum isUniformRandom = true;
144     }
145     static assert(!isUniformRNG!(NoRng3, uint));
146     static assert(!isUniformRNG!(NoRng3));
147     static assert(!isSeedable!(NoRng3, uint));
148     static assert(!isSeedable!(NoRng3));
149     NoRng3 noRng3;
150     noRng3.popFront();
151     assert(!noRng3.empty);
152 
153     // Not an RNG because it lacks min and max
154     struct NoRng4
155     {
156         @property uint front() {return 0;}
157         @property bool empty() {return false;}
158         void popFront() {}
159 
160         enum isUniformRandom = true;
161     }
162     static assert(!isUniformRNG!(NoRng4, uint));
163     static assert(!isUniformRNG!(NoRng4));
164     static assert(!isSeedable!(NoRng4, uint));
165     static assert(!isSeedable!(NoRng4));
166     NoRng4 noRng4;
167     noRng4.popFront();
168     assert(!noRng4.empty);
169     assert(!noRng4.front);
170 
171     /* Not an RNG because max is different type
172      * to front and min
173      */
174     struct NoRng5
175     {
176         enum uint min = 0;
177         enum ulong max = ulong.max;
178         @property uint front() {return 0;}
179         @property bool empty() {return false;}
180         void popFront() {}
181 
182         enum isUniformRandom = true;
183     }
184     static assert(!isUniformRNG!(NoRng5, uint));
185     static assert(!isUniformRNG!(NoRng5));
186     static assert(!isSeedable!(NoRng5, uint));
187     static assert(!isSeedable!(NoRng5));
188     NoRng5 noRng5;
189     noRng5.popFront();
190     assert(!noRng5.empty);
191     assert(!noRng5.front);
192 
193     /* Not an RNG because its element type is
194      * not an unsigned integer
195      */
196     struct NoRng6
197     {
198         enum double min = 0;
199         enum double max = 23.5;
200         @property double front() {return 0;}
201         @property bool empty() {return false;}
202         void popFront() {}
203 
204         enum isUniformRandom = true;
205     }
206     static assert(!isUniformRNG!(NoRng6, double));
207     static assert(!isUniformRNG!(NoRng6));
208     static assert(!isSeedable!(NoRng6, double));
209     static assert(!isSeedable!(NoRng6));
210     NoRng6 noRng6;
211     noRng6.popFront();
212     assert(!noRng6.empty);
213     assert(!noRng6.front);
214 
215     // Not an RNG because it lacks isUniformRandom
216     struct NoRng7
217     {
218         enum uint min = 0;
219         enum uint max = uint.max;
220         @property uint front() {return 0;}
221         @property bool empty() {return false;}
222         void popFront() {}
223     }
224     static assert(!isUniformRNG!(NoRng7, uint));
225     static assert(!isUniformRNG!(NoRng7));
226     static assert(!isSeedable!(NoRng7, uint));
227     static assert(!isSeedable!(NoRng7));
228     NoRng7 noRng7;
229     noRng7.popFront();
230     assert(!noRng7.empty);
231     assert(!noRng7.front);
232 
233     // Not an RNG because isUniformRandom is false
234     struct NoRng8
235     {
236         enum uint min = 0;
237         enum uint max = uint.max;
238         @property uint front() {return 0;}
239         @property bool empty() {return false;}
240         void popFront() {}
241 
242         enum isUniformRandom = false;
243     }
244     static assert(!isUniformRNG!(NoRng8, uint));
245     static assert(!isUniformRNG!(NoRng8));
246     static assert(!isSeedable!(NoRng8, uint));
247     static assert(!isSeedable!(NoRng8));
248     NoRng8 noRng8;
249     noRng8.popFront();
250     assert(!noRng8.empty);
251     assert(!noRng8.front);
252 
253     // Valid RNG, but not seedable
254     struct ValidRng
255     {
256         enum uint min = 0;
257         enum uint max = uint.max;
258         @property uint front() {return 0;}
259         @property bool empty() {return false;}
260         void popFront() {}
261 
262         enum isUniformRandom = true;
263     }
264     static assert(isUniformRNG!(ValidRng, uint));
265     static assert(isUniformRNG!(ValidRng));
266     static assert(!isSeedable!(ValidRng, uint));
267     static assert(!isSeedable!(ValidRng));
268     ValidRng validRng;
269     validRng.popFront();
270     assert(!validRng.empty);
271     assert(!validRng.front);
272 
273     // Valid and seedable RNG
274     struct SeedRng
275     {
276         enum ulong min = 0;
277         enum ulong max = ulong.max;
278         @property ulong front() {return 0;}
279         @property bool empty() {return false;}
280         void popFront() {}
281         void seed(ulong val){}
282         enum isUniformRandom = true;
283     }
284     static assert(isUniformRNG!(SeedRng, ulong));
285     static assert(isUniformRNG!(SeedRng));
286     static assert(isSeedable!(SeedRng, ulong));
287     static assert(isSeedable!(SeedRng));
288     SeedRng seedRng;
289     seedRng.seed(123456789uL);
290     seedRng.popFront();
291     assert(!seedRng.empty);
292     assert(!seedRng.front);
293 }
294 
295 
296 /**
297  * Test if $(D RandomDist) is a random distribution. The overload
298  * taking an $(D ElementType) also makes sure that the distribution
299  * generates values of that type.
300  *
301  * A random distribution has at least the following features:
302  * $(UL
303  *   $(LI it's an InputRange)
304  *   $(LI it has a $(D bool isRandomDistribution) field readable
305  *        in CTFE)
306  * )
307  */
308 template isRandomDistribution(RandomDist, ElementType)
309 {
310     enum bool isRandomDistribution = isInputRange!RandomDist &&
311         is(typeof(RandomDist.front) == ElementType) &&
312         is(typeof(
313         {
314             static assert(RandomDist.isRandomDistribution); //tag
315         }));
316 }
317 
318 /// ditto
319 template isRandomDistribution(RandomDist)
320 {
321     enum bool isRandomDistribution = isInputRange!RandomDist &&
322         is(typeof(
323         {
324             static assert(RandomDist.isRandomDistribution); //tag
325         }));
326 }
327 
328 unittest
329 {
330     struct NoDist
331     {
332         @property uint front() {return 0;}
333         @property bool empty() {return false;}
334         void popFront() {}
335     }
336     static assert(!isRandomDistribution!(NoDist, uint));
337     static assert(!isRandomDistribution!(NoDist));
338     NoDist noDist;
339     noDist.popFront();
340     assert(!noDist.empty);
341     assert(!noDist.front);
342 
343     struct NoDist2
344     {
345         @property uint front() {return 0;}
346         @property bool empty() {return false;}
347         void popFront() {}
348 
349         enum isRandomDistribution = false;
350     }
351     static assert(!isRandomDistribution!(NoDist2, uint));
352     static assert(!isRandomDistribution!(NoDist2));
353     NoDist2 noDist2;
354     noDist2.popFront();
355     assert(!noDist2.empty);
356     assert(!noDist2.front);
357 
358     struct NoDist3
359     {
360         @property bool empty() {return false;}
361         void popFront() {}
362 
363         enum isRandomDistribution = true;
364     }
365     static assert(!isRandomDistribution!(NoDist3, uint));
366     static assert(!isRandomDistribution!(NoDist3));
367     NoDist3 noDist3;
368     noDist3.popFront();
369     assert(!noDist3.empty);
370 
371     struct ValidDist
372     {
373         @property uint front() {return 0;}
374         @property bool empty() {return false;}
375         void popFront() {}
376 
377         enum isRandomDistribution = true;
378     }
379     static assert(isRandomDistribution!(ValidDist, uint));
380     static assert(isRandomDistribution!(ValidDist));
381     ValidDist validDist;
382     validDist.popFront();
383     assert(!validDist.empty);
384     assert(!validDist.front);
385 }