1 #!/usr/bin/env rdmd-dev-module
2 
3 /** Generate Randomized Instances.
4 
5     Copyright: Per Nordlöw 2014-.
6     License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
7     Authors: $(WEB Per Nordlöw)
8 
9     See also: http://forum.dlang.org/thread/byonwfghdqgcirdjyboh@forum.dlang.org
10 
11     TODO Can these be tagged with @nogc? Currently std.random.uniform may allocate.
12     TODO Tags as nothrow when std.random gets there.
13     TODO How to handle possibly null reference (class, dynamic types) types?
14     Answer relates to how to randomize empty/null variable length structures
15     (arrays, strings, etc).
16     - Maybe some kind of length randomization?
17  */
18 module random_ex;
19 
20 import std.traits: isIntegral, isFloatingPoint, isNumeric, isIterable, isStaticArray, isArray, hasIndirections, isSomeString, isScalarType;
21 import std.range: isInputRange, ElementType, hasAssignableElements, isBoolean;
22 import std.random: uniform;
23 
24 version(unittest) private enum testLength = 64;
25 
26 /** Randomize Contents of $(D x). */
27 auto ref randInPlace(E)(ref E x) @trusted
28     if (isBoolean!E)
29 {
30     return x = cast(bool)uniform(0, 2);
31 }
32 
33 /** Randomize Contents of $(D x), optionally in range [$(D low), $(D high)]. */
34 auto ref randInPlace(E)(ref E x,
35                         E low = E.min,
36                         E high = E.max) @trusted
37     if (isIntegral!E)
38 {
39     return x = uniform(low, high);    // BUG: Never assigns the value E.max
40 }
41 
42 /** Randomize Contents of $(D x), optional in range [$(D low), $(D high)]. */
43 auto ref randInPlace(E)(ref E x,
44                         E low = 0 /* E.min_normal */,
45                         E high = 1 /* E.max */) @trusted
46     if (isFloatingPoint!E)
47 {
48     return x = uniform(low, high);
49 }
50 
51 version(unittest)
52 {
53     import rational: Rational, rational;
54 }
55 
56 /** Randomize Contents of $(D x). */
57 auto ref randInPlace(Rational, E)(ref Rational!E x) @trusted
58     if (isIntegral!E)
59 {
60     return x = rational(uniform(E.min, E.max),
61                         uniform(1, E.max));
62 }
63 
64 unittest
65 {
66     Rational!int x;
67     x.randInPlace;
68 }
69 
70 /** Generate Random Contents of $(D x).
71     See also: http://forum.dlang.org/thread/emlgflxpgecxsqweauhc@forum.dlang.org
72  */
73 auto ref randInPlace(ref dchar x) @trusted
74 {
75     auto ui = uniform(0,
76                       0xD800 +
77                       (0x110000 - 0xE000) - 2 // minus two for U+FFFE and U+FFFF
78         );
79     if (ui < 0xD800)
80     {
81         return x = ui;
82     }
83     else
84     {
85         ui -= 0xD800;
86         ui += 0xE000;
87 
88         // skip undefined
89         if (ui < 0xFFFE)
90             return x = ui;
91         else
92             ui += 2;
93 
94         assert(ui < 0x110000);
95         return x = ui;
96     }
97 }
98 
99 unittest
100 {
101     auto x = randomized!dchar;
102     dstring d = "alphaalphaalphaalphaalphaalphaalphaalphaalphaalpha";
103     auto r = d.randomize; // TODO Use Phobos function to check if string is legally coded.
104 }
105 
106 /** Randomize Contents of $(D x). */
107 auto ref randInPlace(dstring x) @trusted
108 {
109     dstring y;
110     foreach (ix; 0 .. x.length)
111         y ~= randomized!dchar; // TODO How to do this in a better way?
112     x = y;
113     return y;
114 }
115 
116 /** Randomize Contents of $(D x).
117  */
118 auto ref randInPlace(R)(R x)
119     if (hasAssignableElements!R)
120 {
121     foreach (ref e; x)
122     {
123         e.randInPlace;
124     }
125     return x;
126 }
127 
128 unittest
129 {
130     void testDynamic(T)()
131     {
132         auto x = new T[testLength];
133         auto y = x.dup;
134         x.randInPlace;
135         y.randInPlace;
136         assert(y != x);
137     }
138     testDynamic!bool;
139     testDynamic!int;
140     testDynamic!float;
141 }
142 
143 /** Randomize Contents of $(D x).
144  */
145 auto ref randInPlace(T)(ref T x) @trusted
146     if (isStaticArray!T)
147 {
148     foreach (ref e; x)
149     {
150         e.randInPlace;
151     }
152     return x;
153 }
154 
155 unittest
156 {
157     void testStatic(T)()
158     {
159         T[testLength] x;
160         auto y = x;
161         x.randInPlace;
162         y.randInPlace;
163         assert(y != x);
164     }
165     testStatic!bool;
166     testStatic!int;
167     testStatic!real;
168     enum E { a, b, c, d, e, f, g, h,
169              i, j, k, l, m, n, o, p }
170     testStatic!E;
171 }
172 
173 /** Blockwise Randomize Contents of $(D x) of Array Type $(D A).
174     Randomizes in array blocks of type $(D B).
175  */
176 auto ref randInPlaceBlockwise(B = size_t, A)(ref A x) @trusted
177     if (isArray!A &&
178         isIntegral!(ElementType!A))
179 {
180     alias E = ElementType!A;
181     static assert(E.sizeof < B.sizeof);
182     enum mult = B.sizeof / E.sizeof; // block multiplicity
183 
184     immutable n = x.length;
185 
186     // beginning unaligned bytes
187     auto p = cast(size_t)x.ptr;
188     immutable size_t mask = B.sizeof - 1;
189     immutable r = (p & mask) / E.sizeof; // element-offset from B-aligned address before x
190     size_t k = 0; // E-index to first B-block
191     if (r)
192     {
193         import std.algorithm: min;
194         k = min(n, mult - r); // at first aligned B-block
195         foreach (i, ref e; x[0 .. k])
196         {
197             e.randInPlace;
198         }
199     }
200 
201     // mid blocks of type B
202     auto bp = cast(B*)(x.ptr + k); // block pointer
203     immutable nB = (n - k) / mult; // number of B-blocks
204     foreach (ref b; 0 .. nB) // for each block index
205     {
206         bp[b].randInPlace;
207     }
208 
209     // ending unaligned bytes
210     foreach (i, ref e; x[k + nB*mult .. $])
211     {
212         e.randInPlace;
213     }
214 }
215 
216 unittest
217 {
218     static void test(B = size_t, T)()
219     {
220         enum n = 1024;
221 
222         // dynamic array
223         for (size_t i = 0; i < n; i++)
224         {
225             T[] da = new T[i];
226             da.randInPlaceBlockwise!B;
227             size_t j = randomInstanceOf!(typeof(i))(0, n/2);
228             da.randInPlaceBlockwise!B;
229         }
230 
231         // static array
232         T[n] sa;
233         auto sa2 = sa[1 .. $];
234         sa2.randInPlaceBlockwise!B;
235     }
236 
237     import std.meta : AliasSeq;
238     foreach (T; AliasSeq!(byte, short, int, ubyte, ushort, uint))
239     {
240         test!(size_t, T);
241     }
242 }
243 
244 /** Randomize Contents of members of $(D x).
245  */
246 auto ref randInPlace(T)(ref T x)
247     if (is(T == struct))
248 {
249     foreach (ref e; x.tupleof)
250     {
251         e.randInPlace;
252     }
253     return x;
254 }
255 
256 unittest
257 {
258     struct T { ubyte a, b, c, d; }
259     T[testLength] x;
260     auto y = x;
261     x.randInPlace;
262     y.randInPlace;
263     assert(y != x);
264 }
265 
266 /** Randomize Contents of members of $(D x).
267  */
268 auto ref randInPlace(T)(T x)
269     if (is(T == class))
270 {
271     foreach (ref e; x.tupleof)
272     {
273         e.randInPlace;
274     }
275     return x;
276 }
277 
278 unittest
279 {
280     void testClass(E)()
281     {
282         class T { E a, b; }
283         auto x = new T;
284         auto y = new T;
285         x.randInPlace;
286         y.randInPlace;
287         assert(y != x);
288     }
289     testClass!bool;
290     testClass!int;
291     testClass!float;
292 }
293 
294 /** Get New Randomized Instance of Type $(D T).
295  */
296 T randomInstanceOf(T)()
297 {
298     /* TODO recursively only void-initialize parts of T that are POD, not
299      reference types */
300     static if (hasIndirections!T)
301         T x;
302     else
303         /* don't init - randInPlace below fills in everything safely */
304         T x = void;
305     return x.randInPlace;
306 }
307 
308 /** Get New Randomized Instance of Type $(D T).
309  */
310 T randomInstanceOf(T)(T low = T.min,
311                       T high = T.max)
312     if (isNumeric!T)
313 {
314     /* TODO recursively only void-initialize parts of T that are POD, not
315        reference types */
316     static if (hasIndirections!T)
317         T x;
318     else
319         /* don't init - randInPlace below fills in everything safely */
320         T x = void;
321     return x.randInPlace(low, high);
322 }
323 
324 alias randomize = randInPlace;
325 alias randomized = randomInstanceOf;