1 /**
2 Precise and detailed description of the expected behaviour of
3 `checkedint.smartOp` against which it can be automatically tested.
4 80+ bit floating-point is used to compute the expected value for each
5 operation with many different combinations of inputs.
6 
7 $(RED Note:) These tests currently will not work on systems where
8 `is(real == double)`, because `Precision!double < Precision!ulong`.
9 
10 Copyright: Copyright Thomas Stuart Bockman 2015
11 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
12 Authors: Thomas Stuart Bockman
13 **/
14 module checkedint.tests.contract.smartop;
15 import checkedint.tests.contract.internal;
16 
17 import checkedint.flags;
18 
19 void all()()
20 {
21     writeln();
22     write("Testing smartOp... ");
23     stdout.flush();
24 
25     cmp();
26     abs();
27     ilogb();
28     unary();
29     binary();
30     byPow2();
31     pow();
32 
33     writeln("DONE");
34 }
35 
36 @safe:
37 
38 void cmp(string op = null, N = void, M = void)()
39 {
40     static if (op == null)
41     {
42         foreach (op1; AliasSeq!("==", "!=", "<", "<=", ">", ">="))
43             cmp!(op1, N, M)();
44     }
45     else static if (is(N == void))
46     {
47         foreach (N1; AliasSeq!(IntegralTypes, CharTypes))
48             cmp!(op, N1, M)();
49     }
50     else static if (is(M == void))
51     {
52         foreach (M1; AliasSeq!(IntegralTypes, CharTypes))
53             cmp!(op, N, M1)();
54     }
55     else
56     {
57         static assert(isScalarType!N && isScalarType!M);
58 
59         static void cover(bool direct)()
60         {
61             static if (direct)
62                 enum sc = "smartOp.cmp!\"" ~ op ~ "\"(n, m)";
63             else
64                 enum sc = "smartOp.cmp(n, m) " ~ op ~ " 0";
65 
66             static assert(real.mant_dig >= max(precision!N, precision!M));
67             auto control(const real n, const real m)
68             {
69                 auto wret = stdm.cmp(n, m);
70                 return mixin("wret " ~ op ~ " 0");
71             }
72 
73             fuzz!(sc, Unqual, OutIs!bool, control, N, M)();
74         }
75         cover!true();
76         cover!false();
77     }
78 }
79 alias cmp(N, M = void) = cmp!(null, N, M);
80 
81 void abs(N = void)()
82 {
83     static if (is(N == void))
84     {
85         foreach (N1; AliasSeq!(IntegralTypes, CharTypes))
86             abs!N1();
87     }
88     else
89     {
90         static assert(isFixedPoint!N);
91 
92         enum sc = "smartOp.abs(n)";
93 
94         static assert(real.mant_dig >= precision!N);
95         auto control(const real n, Unused m = null)
96         {
97             return stdm.abs(n);
98         }
99         alias R = Unsigned!(IntFromChar!N);
100 
101         fuzz!(sc, Unqual, OutIs!R, control, N)();
102     }
103 }
104 
105 void ilogb(N = void)()
106 {
107     static if (is(N == void))
108     {
109         foreach (N1; AliasSeq!(IntegralTypes, CharTypes))
110             ilogb!N1();
111     }
112     else
113     {
114         static assert(isFixedPoint!N);
115 
116         enum sc = "smartOp.ilogb(n)";
117 
118         static assert(real.mant_dig >= (8 * N.sizeof));
119         auto control(const real n, Unused m = null)
120         {
121             return n != 0? stdm.ilogb(n) : real.nan;
122         }
123 
124         fuzz!(sc, Unqual, OutIs!ubyte, control, N)();
125     }
126 }
127 
128 void unary(string op = null, N = void)()
129 {
130     static if (op == null)
131     {
132         foreach (op1; AliasSeq!("~", "+", "-", "++", "--"))
133             unary!(op1, N)();
134     }
135     else static if (is(N == void))
136     {
137         foreach (N1; AliasSeq!(IntegralTypes, CharTypes))
138             unary!(op, N1)();
139     }
140     else
141     {
142         static assert(isFixedPoint!N);
143 
144         enum sc = "smartOp.unary!\"" ~ op ~ "\"(n)";
145 
146         static assert(real.mant_dig >= precision!N);
147         static if (op == "~")
148         {
149             auto control(const N n, Unused m = null)
150             {
151                 return cast(N)~cast(Promoted!N)n;
152             }
153         }
154         else
155         {
156             auto control(real n, Unused m = null)
157             {
158                 return mixin(op ~ "n");
159             }
160         }
161         enum isVO(N, M, PR) = (PR.sizeof >= N.sizeof) && (isSigned!PR || !op.among!("-", "+"));
162 
163         static if (isIntegral!N || !op.among!("++", "--"))
164             fuzz!(sc, Unqual, isVO, control, N)();
165         else
166             forbid!(sc, N)();
167     }
168 }
169 alias unary(N) = unary!(null, N);
170 
171 void binary(string op = null, N = void, M = void)()
172 {
173     static if (op == null)
174     {
175         foreach (op1; AliasSeq!("+", "-", "*", "/", "%", "<<", ">>", ">>>", "&", "|", "^"))
176             binary!(op1, N, M)();
177     }
178     else static if (is(N == void))
179     {
180         foreach (N1; AliasSeq!(IntegralTypes, CharTypes))
181             binary!(op, N1, M)();
182     }
183     else static if (is(M == void))
184     {
185         foreach (M1; AliasSeq!(IntegralTypes, CharTypes))
186             binary!(op, N, M1)();
187     }
188     else
189     {
190         static assert(isFixedPoint!N && isFixedPoint!M);
191 
192         static if (isIntegral!N)
193             alias UN = Unsigned!N;
194         else
195             alias UN = N;
196 
197         static if (isIntegral!M)
198             alias UM = Unsigned!M;
199         else
200             alias UM = M;
201 
202         static void cover(bool assign)()
203         {
204             enum sc = "smartOp.binary!\"" ~ op ~ (assign? "=" : "") ~ "\"(n, m)";
205 
206             static assert(real.mant_dig >= max(precision!N, precision!M));
207             static if (op.among!("<<", ">>", ">>>"))
208             {
209                 static N control(const N n, const M m)
210                 {
211                     const shL = (op == "<<") ^ (m < 0);
212                     enum int maxSh = (8 * N.sizeof) - 1;
213                     const um = cast(UM)stdm.abs(cast(Promoted!M)m);
214 
215                     static if (op == ">>>")
216                         auto wret = cast(UN)n;
217                     else
218                         N wret = n;
219                     bool again = um > maxSh;
220                     const im = again? maxSh : cast(int)um;
221                 Lagain:
222                     wret = cast(typeof(wret))(shL?
223                         wret << im :
224                         wret >> im);
225                     if (again)
226                     {
227                         again = false;
228                         goto Lagain;
229                     }
230 
231                     return cast(N)wret;
232                 }
233                 enum isVO(N, M, PR) = isIntegral!PR && (PR.sizeof == N.sizeof) && (isSigned!PR == isSigned!N);
234             }
235             else static if (op.among!("&", "|", "^"))
236             {
237                 static auto control(const N n, const M m)
238                 {
239                     static if (assign)
240                         alias R = N;
241                     else
242                     {
243                         alias P = Select!(N.sizeof >= M.sizeof, N, M);
244                         static if (isIntegral!P)
245                             alias UP = Unsigned!P;
246                         else
247                             alias UP = P;
248 
249                         alias R = Select!(isSigned!N && isSigned!M, P, UP);
250                     }
251 
252                     return cast(R)mixin("n " ~ op ~ " m");
253                 }
254                 enum isVO(N, M, PR) = isIntegral!PR && (PR.sizeof == max(N.sizeof, M.sizeof)) &&
255                     (isSigned!PR == (isSigned!N && isSigned!M));
256             }
257             else
258             {
259                 static auto control(const real n, const real m)
260                 {
261                     static if (op == "/")
262                         return stdm.trunc(n / m);
263                     else
264                         return mixin("n " ~ op ~ " m");
265                 }
266                 enum isVO(N, M, PR) = isIntegral!PR &&
267                     ((isSigned!PR == (isSigned!N || (isSigned!M && op != "%"))) || op == "-");
268             }
269 
270             static if (isIntegral!N || !assign)
271                 fuzz!(sc, Unqual, Select!(assign, OutIs!N, isVO), control, N, M)();
272             else
273                 forbid!(sc, N, M)();
274         }
275         cover!false();
276         cover!true();
277     }
278 }
279 alias binary(N, M = void) = binary!(null, N, M);
280 
281 void byPow2(string op = null, N = void, M = void)()
282 {
283     static if (op == null)
284     {
285         foreach (op1; AliasSeq!("*", "/", "%"))
286             byPow2!(op1, N, M)();
287     }
288     else static if (is(N == void))
289     {
290         foreach (N1; AliasSeq!(IntegralTypes, CharTypes))
291             byPow2!(op, N1, M)();
292     }
293     else static if (is(M == void))
294     {
295         foreach (M1; AliasSeq!(IntegralTypes, CharTypes))
296             byPow2!(op, N, M1)();
297     }
298     else
299     {
300         static assert(isScalarType!N && isScalarType!M);
301 
302         enum sc = "smartOp." ~ (op == "*"? "mul" : (op == "/"? "div" : "mod")) ~ "Pow2(n, m)";
303 
304         static assert(real.mant_dig >= max(precision!N, precision!M));
305         real control(const real n, const real m)
306         {
307             if (n == 0 && stdm.isFinite(m))
308                 return 0;
309             else
310             {
311                 const p2 = stdm.exp2(m);
312 
313                 static if (op.among!("*", "/"))
314                 {
315                     const wret = mixin("n " ~ op ~ " p2");
316                     static if (isFloatingPoint!N || isFloatingPoint!M)
317                         return wret;
318                     else
319                         return stdm.trunc(wret);
320                 }
321                 else
322                 {
323                     if (!stdm.isFinite(p2))
324                         return (p2 > 0)? n : (p2 < 0)? 0 : real.nan;
325                     else
326                         return n % (p2 == 0? real.min_normal : p2);
327                 }
328             }
329         }
330         enum isVO(N, M, PR) = (PR.sizeof >= N.sizeof) &&
331             ((isFloatingPoint!N || isFloatingPoint!M)?
332                 isFloatingPoint!PR :
333                 isIntegral!PR && (isSigned!PR || !isSigned!N));
334 
335         fuzz!(sc, Unqual, isVO, control, N, M)();
336     }
337 }
338 alias byPow2(N, M = void) = byPow2!(null, N, M);
339 
340 void pow(N = void, M = void)()
341 {
342     static if (is(N == void))
343     {
344         foreach (N1; AliasSeq!(IntegralTypes, CharTypes))
345             pow!(N1, M)();
346     }
347     else static if (is(M == void))
348     {
349         foreach (M1; AliasSeq!(IntegralTypes, CharTypes))
350             pow!(N, M1)();
351     }
352     else
353     {
354         static assert(isFixedPoint!N && isFixedPoint!M);
355         forbid!("smartOp.binary!\"^^\"(n, m)", N, M)();
356         forbid!("smartOp.binary!\"^^=\"(n, m)", N, M)();
357 
358         enum sc = "smartOp.pow(n, m)";
359 
360         static assert(real.mant_dig >= max(precision!N, precision!M));
361         real control(const real n, const real m)
362         {
363             return stdm.trunc(stdm.pow(n, m));
364         }
365         enum isVO(N, M, PR) = isIntegral!PR && (PR.sizeof >= N.sizeof) && (isSigned!PR == isSigned!N);
366 
367         fuzz!(sc, Unqual, isVO, control, N, M)();
368     }
369 }