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         auto control(Select!(op == "~", N, real) n, Unused m = null)
148         {
149             return mixin(op ~ "n");
150         }
151         enum isVO(N, M, PR) = (PR.sizeof >= N.sizeof) && (isSigned!PR || !op.among!("-", "+"));
152 
153         static if (isIntegral!N || !op.among!("++", "--"))
154             fuzz!(sc, Unqual, isVO, control, N)();
155         else
156             forbid!(sc, N)();
157     }
158 }
159 alias unary(N) = unary!(null, N);
160 
161 void binary(string op = null, N = void, M = void)()
162 {
163     static if (op == null)
164     {
165         foreach (op1; AliasSeq!("+", "-", "*", "/", "%", "<<", ">>", ">>>", "&", "|", "^"))
166             binary!(op1, N, M)();
167     }
168     else static if (is(N == void))
169     {
170         foreach (N1; AliasSeq!(IntegralTypes, CharTypes))
171             binary!(op, N1, M)();
172     }
173     else static if (is(M == void))
174     {
175         foreach (M1; AliasSeq!(IntegralTypes, CharTypes))
176             binary!(op, N, M1)();
177     }
178     else
179     {
180         static assert(isFixedPoint!N && isFixedPoint!M);
181 
182         static if (isIntegral!N)
183             alias UN = Unsigned!N;
184         else
185             alias UN = N;
186 
187         static if (isIntegral!M)
188             alias UM = Unsigned!M;
189         else
190             alias UM = M;
191 
192         static void cover(bool assign)()
193         {
194             enum sc = "smartOp.binary!\"" ~ op ~ (assign? "=" : "") ~ "\"(n, m)";
195 
196             static assert(real.mant_dig >= max(precision!N, precision!M));
197             static if (op.among!("<<", ">>", ">>>"))
198             {
199                 static N control(const N n, const M m)
200                 {
201                     const shL = (op == "<<") ^ (m < 0);
202                     enum int maxSh = (8 * N.sizeof) - 1;
203                     const um = cast(UM)stdm.abs(m);
204 
205                     static if (op == ">>>")
206                         auto wret = cast(UN)n;
207                     else
208                         N wret = n;
209                     bool again = um > maxSh;
210                     const im = again? maxSh : cast(int)um;
211                 Lagain:
212                     wret = cast(typeof(wret))(shL?
213                         wret << im :
214                         wret >> im);
215                     if (again)
216                     {
217                         again = false;
218                         goto Lagain;
219                     }
220 
221                     return cast(N)wret;
222                 }
223                 enum isVO(N, M, PR) = isIntegral!PR && (PR.sizeof == N.sizeof) && (isSigned!PR == isSigned!N);
224             }
225             else static if (op.among!("&", "|", "^"))
226             {
227                 static auto control(const N n, const M m)
228                 {
229                     static if (assign)
230                         alias R = N;
231                     else
232                     {
233                         alias P = Select!(N.sizeof >= M.sizeof, N, M);
234                         static if (isIntegral!P)
235                             alias UP = Unsigned!P;
236                         else
237                             alias UP = P;
238 
239                         alias R = Select!(isSigned!N && isSigned!M, P, UP);
240                     }
241 
242                     return cast(R)mixin("n " ~ op ~ " m");
243                 }
244                 enum isVO(N, M, PR) = isIntegral!PR && (PR.sizeof == max(N.sizeof, M.sizeof)) &&
245                     (isSigned!PR == (isSigned!N && isSigned!M));
246             }
247             else
248             {
249                 static auto control(const real n, const real m)
250                 {
251                     static if (op == "/")
252                         return stdm.trunc(n / m);
253                     else
254                         return mixin("n " ~ op ~ " m");
255                 }
256                 enum isVO(N, M, PR) = isIntegral!PR &&
257                     ((isSigned!PR == (isSigned!N || (isSigned!M && op != "%"))) || op == "-");
258             }
259 
260             static if (isIntegral!N || !assign)
261                 fuzz!(sc, Unqual, Select!(assign, OutIs!N, isVO), control, N, M)();
262             else
263                 forbid!(sc, N, M)();
264         }
265         cover!false();
266         cover!true();
267     }
268 }
269 alias binary(N, M = void) = binary!(null, N, M);
270 
271 void byPow2(string op = null, N = void, M = void)()
272 {
273     static if (op == null)
274     {
275         foreach (op1; AliasSeq!("*", "/", "%"))
276             byPow2!(op1, N, M)();
277     }
278     else static if (is(N == void))
279     {
280         foreach (N1; AliasSeq!(IntegralTypes, CharTypes))
281             byPow2!(op, N1, M)();
282     }
283     else static if (is(M == void))
284     {
285         foreach (M1; AliasSeq!(IntegralTypes, CharTypes))
286             byPow2!(op, N, M1)();
287     }
288     else
289     {
290         static assert(isScalarType!N && isScalarType!M);
291 
292         enum sc = "smartOp." ~ (op == "*"? "mul" : (op == "/"? "div" : "mod")) ~ "Pow2(n, m)";
293 
294         static assert(real.mant_dig >= max(precision!N, precision!M));
295         real control(const real n, const real m)
296         {
297             if (n == 0 && stdm.isFinite(m))
298                 return 0;
299             else
300             {
301                 const p2 = stdm.exp2(m);
302 
303                 static if (op.among!("*", "/"))
304                 {
305                     const wret = mixin("n " ~ op ~ " p2");
306                     static if (isFloatingPoint!N || isFloatingPoint!M)
307                         return wret;
308                     else
309                         return stdm.trunc(wret);
310                 }
311                 else
312                 {
313                     if (!stdm.isFinite(p2))
314                         return (p2 > 0)? n : (p2 < 0)? 0 : real.nan;
315                     else
316                         return n % (p2 == 0? real.min_normal : p2);
317                 }
318             }
319         }
320         enum isVO(N, M, PR) = (PR.sizeof >= N.sizeof) &&
321             ((isFloatingPoint!N || isFloatingPoint!M)?
322                 isFloatingPoint!PR :
323                 isIntegral!PR && (isSigned!PR || !isSigned!N));
324 
325         fuzz!(sc, Unqual, isVO, control, N, M)();
326     }
327 }
328 alias byPow2(N, M = void) = byPow2!(null, N, M);
329 
330 void pow(N = void, M = void)()
331 {
332     static if (is(N == void))
333     {
334         foreach (N1; AliasSeq!(IntegralTypes, CharTypes))
335             pow!(N1, M)();
336     }
337     else static if (is(M == void))
338     {
339         foreach (M1; AliasSeq!(IntegralTypes, CharTypes))
340             pow!(N, M1)();
341     }
342     else
343     {
344         static assert(isFixedPoint!N && isFixedPoint!M);
345         forbid!("smartOp.binary!\"^^\"(n, m)", N, M)();
346         forbid!("smartOp.binary!\"^^=\"(n, m)", N, M)();
347 
348         enum sc = "smartOp.pow(n, m)";
349 
350         static assert(real.mant_dig >= max(precision!N, precision!M));
351         real control(const real n, const real m)
352         {
353             static if (__VERSION__ >= 2070)
354             {
355                 version(GNU) { static assert(false); }
356                 return stdm.trunc(stdm.pow(n, m));
357             }
358             else
359             {
360                 // DMD issue #14786
361                 if (n == -1 && stdm.fabs(m) <= ulong.max)
362                     return (cast(ulong)m & 0x1)? -1 : 1;
363                 else
364                     return stdm.trunc(stdm.pow(n, m));
365             }
366         }
367         enum isVO(N, M, PR) = isIntegral!PR && (PR.sizeof >= N.sizeof) && (isSigned!PR == isSigned!N);
368 
369         fuzz!(sc, Unqual, isVO, control, N, M)();
370     }
371 }