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 }