1 /** Data sampling, typically randomly. 2 * 3 * Test: dmd -version=show -preview=dip1000 -preview=in -vcolumns -d -I.. -i -debug -g -checkaction=context -allinst -unittest -main -run sampling.d 4 */ 5 module nxt.sampling; 6 7 alias Length = size_t; 8 9 struct Span { 10 Length min = 0; 11 Length max = 1; 12 invariant (min <= max); 13 } 14 15 /++ Format (flags) of sampling. +/ 16 struct Format { 17 /// Array length span. 18 Span arrayLength = Span(0,3); 19 /// Field recursion depth span for recursive aggregate type with unbounded depth like `std.json.JSONValue`. 20 Span fieldDepth = Span(Length.min, 3); 21 } 22 23 import std.random : Random; 24 25 /++ Returns: A random sample of the type `T`. 26 TODO: Use direct field setting for T only when __traits(isPOD, T) is true 27 otherwise use __traits(getOverloads, T, "__ctor"). 28 Test on `std.json.JSONValue`. 29 +/ 30 auto sample(T)(ref Random rnd, in Format fmt = Format.init) { 31 static if (is(T == U[], U)) { // isArray 32 import std.random : uniform; 33 T t; 34 t.length = uniform(fmt.arrayLength.min, fmt.arrayLength.max+1, rnd); 35 foreach (ref e; t) 36 e = rnd.sample!(U); 37 return t; 38 } else static if (is(T == struct)) { 39 import std.traits : FieldNameTuple; 40 T t; /+ TODO: = void +/ 41 foreach (mn; FieldNameTuple!T) 42 __traits(getMember, t, mn) = rnd.sample!(typeof(__traits(getMember, t, mn)))(fmt); 43 return t; 44 } else { 45 import std.random : uniform; 46 return uniform!(T); 47 } 48 } 49 50 /// scalar 51 @safe unittest { 52 auto rnd = Random(42); 53 auto s = rnd.sample!ubyte; 54 } 55 56 /// char[] 57 @safe unittest { 58 auto rnd = Random(42); 59 foreach (_; 0 .. 100) { 60 auto s = rnd.sample!(char[])(Format(Span(0,10))); 61 // dbg(s); 62 } 63 } 64 65 /// struct 66 @safe unittest { 67 auto rnd = Random(42); 68 struct S { byte x, y;} 69 struct T { short a, b; ushort c, d; S s; } 70 struct U { int a, b; uint c, d; T t; int[] ia; } 71 auto s = rnd.sample!U; 72 // dbg(s); 73 } 74 75 version (unittest) { 76 import nxt.debugio; 77 }