1 /**
2 Generate numeric test values hitting all the likely corner cases for simple
3 integer math algorithms. Floating-point is also well-covered, with the
4 exception of subnormal values.
5 
6 Copyright: Copyright Thomas Stuart Bockman 2015
7 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
8 Authors: Thomas Stuart Bockman
9 **/
10 module checkedint.tests.values;
11 
12 import std.math, future.traits0;
13 import std.meta : AliasSeq;
14 
15 pure: nothrow: @nogc: @safe:
16 
17 alias
18     IntegralTypes = AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong),
19     FloatingTypes = AliasSeq!(float, double ,real),
20     NumericTypes = AliasSeq!(IntegralTypes, FloatingTypes),
21     CharTypes = AliasSeq!(char, wchar, dchar),
22     FixedTypes = AliasSeq!(bool, CharTypes, IntegralTypes),
23     ScalarTypes = AliasSeq!(FixedTypes, FloatingTypes);
24 
25 struct TestValues(N)
26     if (is(N == typeof(null)))
27 {
28     bool empty = false;
29     typeof(null) front() const
30     {
31         return null;
32     }
33     void popFront()
34     {
35         empty = true;
36     }
37 }
38 
39 struct TestValues(N)
40     if (is(N == bool))
41 {
42 private:
43     int idx = 0;
44 
45 public:
46     @property bool empty() const
47     {
48         return idx <= 1;
49     }
50     @property bool front() const
51     {
52         return idx != 0;
53     }
54     @property void popFront()
55     {
56         ++idx;
57     }
58 }
59 
60 struct TestValues(N)
61     if ((isIntegral!N || isSomeChar!N) && isUnqual!N)
62 {
63 private:
64     static if (is(N == ulong))
65     {
66         static immutable N[] naturals = function()
67         {
68             ulong[34 + 3*(64 - 5) - 2] nats;
69 
70             size_t n = 0;
71             while (n <= 33)
72             {
73                 nats[n] = n;
74                 ++n;
75             }
76 
77             int sh = 6;
78             while (sh < 64)
79             {
80                 nats[n++] = (1uL << sh) -1;
81                 nats[n++] = (1uL << sh);
82                 nats[n++] = (1uL << sh) + 1;
83                 ++sh;
84             }
85             nats[n] = ulong.max;
86 
87             return nats;
88         }();
89 
90         enum ptrdiff_t maxIdx = naturals.length - 1;
91     }
92     else
93     {
94         alias naturals = TestValues!(ulong).naturals;
95 
96         enum ptrdiff_t maxIdx = function()
97         {
98             // Test dchar values greater than dchar.max, also:
99             enum ulong trueNmax = isSomeChar!N? ~0uL : N.max;
100 
101             auto x = cast(ptrdiff_t)(naturals.length - 1);
102             while (naturals[x] > trueNmax)
103                 --x;
104             return x;
105         }();
106     }
107     enum ptrdiff_t minIdx = isSigned!N? -(maxIdx + 1) : 0;
108 
109     ptrdiff_t index = minIdx;
110 
111 public:
112     @property bool empty() const
113     {
114         return index > maxIdx;
115     }
116     @property N front() const
117     {
118         static if (isSigned!N)
119         {
120             if (index < 0)
121                 return cast(N)-cast(Promoted!N)naturals[-index];
122         }
123 
124         return cast(N)naturals[index];
125     }
126     @property void popFront()
127     {
128         ++index;
129     }
130 }
131 
132 private enum real ctPow2(int exponent) = mixin("0x1.0p" ~ exponent.stringof ~ "L");
133 
134 struct TestValues(N)
135     if (isFloatingPoint!N && isUnqual!N)
136 {
137 private:
138     static if (is(N == real))
139     {
140         enum real[] normal_exps_small = [
141             ctPow2!(real.min_exp - 1),
142             ctPow2!(real.min_exp)
143         ];
144         enum real[] normal_exps_core = [
145             ctPow2!(double.min_exp - 1),
146             ctPow2!(double.min_exp),
147 
148             ctPow2!(-722),
149 
150             ctPow2!(float.min_exp - 1),
151             ctPow2!(float.min_exp),
152 
153             ctPow2!(-13),
154             ctPow2!(-12),
155 
156             ctPow2!(-4),
157             ctPow2!(-3),
158             ctPow2!(-2),
159             ctPow2!(-1),
160 
161             1uL << 0,
162             1uL << 1,
163             1uL << 2,
164             1uL << 3,
165             1uL << 4,
166 
167             // byte
168             (byte.max >> 1) + 1,
169             // ubyte
170             (ubyte.max >> 1) + 1,
171             1uL << 8,
172             1uL << 10,
173             1uL << 11,
174             1uL << 12,
175             1uL << 13,
176             // short
177             (short.max >> 1) + 1,
178             // ushort
179             (ushort.max >> 1) + 1,
180             1uL << 16,
181             1uL << 23,
182             1uL << 24,
183             1uL << 25,
184             // int
185             (int.max >> 1) + 1,
186             // uint
187             (uint.max >> 1) + 1,
188             1uL << 32,
189             1uL << 52,
190             1uL << 53,
191             1uL << 54,
192             1uL << 61,
193             // long
194             (long.max >> 1) + 1,
195             // ulong
196             (ulong.max >> 1) + 1,
197             ctPow2!(64),
198 
199             ctPow2!(104),
200             ctPow2!(105),
201             ctPow2!(106),
202             ctPow2!(111),
203             ctPow2!(112),
204             ctPow2!(113),
205             ctPow2!(125),
206 
207             // cent
208             ctPow2!(float.max_exp - 2),
209             // ucent
210             ctPow2!(float.max_exp - 1),
211             ctPow2!(float.max_exp),
212             ctPow2!(437),
213             ctPow2!(double.max_exp - 2),
214             ctPow2!(double.max_exp - 1)
215         ];
216         enum real[] normal_exps_large = [
217             ctPow2!(double.max_exp),
218             ctPow2!(real.max_exp - 2),
219             ctPow2!(real.max_exp - 1)
220         ];
221 
222         static if (is(real == double))
223             static immutable real[] normal_exps = normal_exps_core;
224         else
225             static immutable real[] normal_exps = normal_exps_small ~ normal_exps_core ~ normal_exps_large;
226     }
227     else
228     {
229         static immutable N[] normal_exps = function()
230         {
231             const normal_exps = TestValues!(real).normal_exps;
232 
233             ptrdiff_t minExpIdx = 0;
234             while (!(normal_exps[minExpIdx] >= N.min_normal))
235                 ++minExpIdx;
236 
237             ptrdiff_t maxExpIdx = normal_exps.length - 1;
238             while (!(normal_exps[maxExpIdx] <= N.max))
239                 --maxExpIdx;
240 
241             auto ret = new N[(maxExpIdx - minExpIdx) + 1];
242             foreach(x; minExpIdx .. maxExpIdx + 1)
243                 ret[x - minExpIdx] = cast(N)normal_exps[x];
244             return ret.idup;
245         }();
246     }
247 
248     static immutable N[] normal_mants = [
249         1.0L,
250         1.0L + N.epsilon,
251         LN2 * 2.0L,
252         E * 0.5L,
253         SQRT2,
254         PI * 0.5L,
255         2.0L - (N.epsilon * 2.0L),
256         2.0L - N.epsilon
257     ];
258     enum ptrdiff_t maxIdx = (normal_exps.length * normal_mants.length) + 2;
259 
260     auto index = -maxIdx;
261     N _front = -N.infinity;
262 
263 public:
264     @property bool empty() const
265     {
266         return index > maxIdx;
267     }
268     @property N front() const
269     {
270         return _front;
271     }
272     @property void popFront()
273     {
274         ++index;
275 
276         const negIdx = index < 0;
277         const absIdx = negIdx? -index : index;
278 
279         if (absIdx <= 1)
280             _front = (absIdx == 0)? N.nan : 0;
281         else if (absIdx < maxIdx)
282         {
283             const mant = normal_mants[(absIdx - 2) % normal_mants.length];
284             const expC = normal_exps[(absIdx - 2) / normal_mants.length];
285             _front = mant * expC;
286         }
287         else
288             _front = N.infinity;
289 
290         if (negIdx)
291             _front = -_front;
292     }
293 }