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 }