1 module unit_threaded.randomized.random;
2 
3 import unit_threaded.randomized.gen;
4 import std.random : Random;
5 
6 
7 /** This type will generate a $(D Gen!T) for all passed $(D T...).
8     Every call to $(D genValues) will call $(D gen) of all $(D Gen) structs
9     present in $(D values). The member $(D values) can be passed to every
10     function accepting $(D T...).
11 */
12 struct RndValueGen(T...)
13 {
14     import std.meta : staticMap;
15 
16     /* $(D Values) is a collection of $(D Gen) types created through
17        $(D ParameterToGen) of passed $(T ...).
18     */
19     static if(is(typeof(T[0]) == string[])) {
20         alias generators = T[1 .. $];
21         string[] parameterNames = T[0];
22     } else {
23         alias generators = T;
24         string[T.length] parameterNames;
25     }
26 
27     alias Values = staticMap!(ParameterToGen, generators);
28 
29     /// Ditto
30     Values values;
31 
32     /* The constructor accepting the required random number generator.
33        Params:
34        rnd = The required random number generator.
35     */
36     this(Random* rnd) @safe
37     {
38         this.rnd = rnd;
39     }
40 
41     this(ref Random rnd) {
42         this.rnd = &rnd;
43     }
44 
45     /* The random number generator used to generate new value for all
46        $(D values).
47     */
48     Random* rnd;
49 
50     /** A call to this member function will call $(D gen) on all items in
51         $(D values) passing $(D the provided) random number generator
52     */
53     void genValues()
54     {
55         assert(rnd !is null);
56         foreach (ref it; this.values)
57         {
58             it.gen(*this.rnd);
59         }
60     }
61 
62     void toString(scope void delegate(const(char)[]) sink)
63     {
64         import std.format : formattedWrite;
65 
66         foreach (idx, ref it; values)
67         {
68             formattedWrite(sink, "'%s' = %s ", parameterNames[idx], it);
69         }
70     }
71 }
72 
73 ///
74 unittest
75 {
76     auto rnd = Random(1337);
77     auto generator = (&rnd).RndValueGen!(["i", "f"],
78                                          Gen!(int, 0, 10),
79                                          Gen!(float, 0.0, 10.0));
80     generator.genValues();
81 
82     static fun(int i, float f)
83     {
84         import std.conv: to;
85         assert(i >= 0 && i <= 10, i.to!string);
86         assert(f >= 0.0 && f <= 10.0, f.to!string);
87     }
88 
89     fun(generator.values);
90 }
91 
92 @("RndValueGen can be used without parameter names")
93 unittest
94 {
95     auto rnd = Random(1337);
96     auto generator = rnd.RndValueGen!(Gen!(int, 0, 10),
97                                       Gen!(float, 0.0, 10.0));
98     generator.genValues();
99 
100     static fun(int i, float f)
101     {
102         import std.conv: to;
103         assert(i >= 0 && i <= 10, i.to!string);
104         assert(f >= 0.0 && f <= 10.0, f.to!string);
105     }
106 
107     fun(generator.values);
108 }
109 
110 
111 unittest
112 {
113     static fun(int i, float f)
114     {
115         assert(i >= 0 && i <= 10);
116         assert(f >= 0.0 && i <= 10.0);
117     }
118 
119     auto rnd = Random(1337);
120     auto generator = (&rnd).RndValueGen!(["i", "f"],
121                                          Gen!(int, 0, 10),
122                                          Gen!(float, 0.0, 10.0));
123 
124     generator.genValues();
125     foreach (i; 0 .. 1000)
126     {
127         fun(generator.values);
128     }
129 }
130 
131 @("RndValueGen with int[]")
132 unittest {
133     void fun(int[] i) {
134 
135     }
136     auto rnd = Random(1337);
137     auto gen = rnd.RndValueGen!(Gen!(int[]));
138     gen.genValues;
139     fun(gen.values);
140 }
141 
142 /** A template that turns a $(D T) into a $(D Gen!T) unless $(D T) is
143     already a $(D Gen) or no $(D Gen) for given $(D T) is available.
144 */
145 template ParameterToGen(T)
146 {
147     import std.traits : isIntegral, isFloatingPoint, isSomeString;
148     static if (isGen!T)
149         alias ParameterToGen = T;
150     else static if (is(T : GenASCIIString!(S), S...))
151         alias ParameterToGen = T;
152     else {
153         static assert(__traits(compiles, Gen!T),
154                       "ParameterToGen does not handle " ~ T.stringof);
155         alias ParameterToGen = Gen!T;
156     }
157 }
158 
159 ///
160 unittest
161 {
162     alias GenInt = ParameterToGen!int;
163 
164     static fun(int i)
165     {
166         assert(i == 1337);
167     }
168 
169     GenInt a;
170     a.value = 1337;
171     fun(a);
172 }
173 
174 unittest
175 {
176     import std.meta : AliasSeq, staticMap;
177 
178     foreach (T; AliasSeq!(byte, ubyte, ushort, short, uint, int, ulong, long,
179                           float, double, real,
180                           string, wstring, dstring))
181     {
182         alias TP = staticMap!(ParameterToGen, T);
183         static assert(isGen!TP);
184     }
185 }