1 /**
2 Precise and detailed description of the expected behaviour of
3 `checkedint.safeOp` 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.safeop;
15 import checkedint.tests.contract.internal;
16 
17 import checkedint.flags;
18 
19 void all()()
20 {
21     writeln();
22     write("Testing safeOp... ");
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         enum sc = "safeOp.cmp!\"" ~ op ~ "\"(n, m)";
60 
61         static assert(real.mant_dig >= max(precision!N, precision!M));
62         auto control(const real n, const real m)
63         {
64             auto wret = stdm.cmp(n, m);
65             return mixin("wret " ~ op ~ " 0");
66         }
67 
68         static if (mostNegative!N <= cast(M)0 && mostNegative!M <= cast(N)0)
69             fuzz!(sc, Unqual, OutIs!bool, control, N, M)();
70         else
71             forbid!(sc, N, M)();
72     }
73 }
74 alias cmp(N, M = void) = cmp!(null, N, M);
75 
76 void abs(N = void)()
77 {
78     static if (is(N == void))
79     {
80         foreach (N1; AliasSeq!(IntegralTypes, CharTypes))
81             abs!N1();
82     }
83     else
84     {
85         static assert(isFixedPoint!N);
86 
87         enum sc = "safeOp.abs(n)";
88 
89         static assert(real.mant_dig >= (8 * N.sizeof));
90         auto control(const real n, Unused m = null)
91         {
92             return stdm.abs(n);
93         }
94         alias R = CallType!(stdm.abs, N);
95 
96         static if (isIntegral!R)
97             fuzz!(sc, Unqual, OutIs!R, control, N)();
98         else
99             forbid!(sc, N)();
100     }
101 }
102 
103 void ilogb(N = void)()
104 {
105     static if (is(N == void))
106     {
107         foreach (N1; AliasSeq!(IntegralTypes, CharTypes))
108             ilogb!N1();
109     }
110     else
111     {
112         static assert(isFixedPoint!N);
113 
114         enum sc = "safeOp.ilogb(n)";
115 
116         static assert(real.mant_dig >= (8 * N.sizeof));
117         auto control(const real n, Unused m = null)
118         {
119             return n != 0? stdm.ilogb(n) : real.nan;
120         }
121         alias R = CallType!(stdm.ilogb, IntFromChar!N);
122 
123         fuzz!(sc, Unqual, OutIs!R, control, N)();
124     }
125 }
126 
127 void unary(string op = null, N = void)()
128 {
129     static if (op == null)
130     {
131         foreach (op1; AliasSeq!("+", "-", "~", "++", "--"))
132             unary!(op1, N)();
133     }
134     else static if (is(N == void))
135     {
136         foreach (N1; AliasSeq!(IntegralTypes, CharTypes))
137             unary!(op, N1)();
138     }
139     else
140     {
141         static assert(isFixedPoint!N);
142 
143         enum sc = "safeOp.unary!\"" ~ op ~ "\"(n)";
144 
145         static assert(real.mant_dig >= precision!N);
146         auto control(Select!(op == "~", N, real) n, Unused m = null)
147         {
148             return mixin(op ~ "n");
149         }
150         alias R = OpType!(op, N);
151 
152         static if ((op != "-" || isSigned!N) && isIntegral!R)
153             fuzz!(sc, Unqual, OutIs!R, control, N)();
154         else
155             forbid!(sc, N)();
156     }
157 }
158 alias unary(N) = unary!(null, N);
159 
160 void binary(string op = null, N = void, M = void)()
161 {
162     static if (op == null)
163     {
164         foreach (op1; AliasSeq!("+", "-", "*", "/", "%", "<<", ">>", ">>>", "&", "|", "^"))
165             binary!(op1, N, M)();
166     }
167     else static if (is(N == void))
168     {
169         foreach (N1; AliasSeq!(IntegralTypes, CharTypes))
170             binary!(op, N1, M)();
171     }
172     else static if (is(M == void))
173     {
174         foreach (M1; AliasSeq!(IntegralTypes, CharTypes))
175             binary!(op, N, M1)();
176     }
177     else
178     {
179         static assert(isFixedPoint!N && isFixedPoint!M);
180 
181         static void cover(bool assign)()
182         {
183             enum sc = "safeOp.binary!\"" ~ op ~ (assign? "=" : "") ~ "\"(n, m)";
184 
185             alias P = OpType!(N, op, M);
186             alias R = Select!(assign, N, P);
187 
188             static assert(real.mant_dig >= max(precision!N, precision!M));
189             enum bc = "n " ~ op ~ " m";
190             static if (op.among!("<<", ">>", ">>>"))
191             {
192                 real control(const N n, const M m)
193                 {
194                     if (m < 0 || m >= (8 * cast(int)Promoted!(N).sizeof))
195                         return real.nan;
196 
197                     const wret = mixin(bc);
198                     return assign? cast(N)wret : wret;
199                 }
200             }
201             else static if (op.among!("&", "|", "^"))
202             {
203                 auto control(const N n, const M m)
204                 {
205                     const wret = mixin(bc);
206                     static if (assign)
207                         return cast(N)wret;
208                     else
209                         return wret;
210                 }
211             }
212             else
213             {
214                 real control(const real n , const real m)
215                 {
216                     const wret = mixin(bc);
217                     static if (op == "/")
218                         return stdm.trunc(wret);
219                     else
220                     static if (op == "%")
221                         return ((R.min < 0) && (n == R.min) && (m == -1))? real.nan : wret;
222                     else
223                         return wret;
224                 }
225             }
226 
227             enum matchedSigns = (!isSigned!N && !isSigned!M) ||
228                 (isSigned!P && (isSigned!R || (op == "%" && !isSigned!N)));
229             static if ((matchedSigns || op.among!("<<", ">>", ">>>", "&", "|", "^")) && isIntegral!R)
230                 fuzz!(sc, Unqual, OutIs!R, control, N, M)();
231             else
232                 forbid!(sc, N, M)();
233         }
234         cover!false();
235         cover!true();
236     }
237 }
238 alias binary(N, M = void) = binary!(null, N, M);
239 
240 void byPow2(string op = null, N = void, M = void)()
241 {
242     static if (op == null)
243     {
244         foreach (op1; AliasSeq!("*", "/", "%"))
245             byPow2!(op1, N, M)();
246     }
247     else static if (is(N == void))
248     {
249         foreach (N1; AliasSeq!(IntegralTypes, CharTypes))
250             byPow2!(op, N1, M)();
251     }
252     else static if (is(M == void))
253     {
254         foreach (M1; AliasSeq!(IntegralTypes, CharTypes))
255             byPow2!(op, N, M1)();
256     }
257     else
258     {
259         static assert(isScalarType!N && isScalarType!M);
260 
261         enum sc = "safeOp." ~ (op == "*"? "mul" : (op == "/"? "div" : "mod")) ~ "Pow2(n, m)";
262 
263         static assert(real.mant_dig >= max(precision!N, precision!M));
264         real control(const real n, const real m)
265         {
266             if (n == 0 && stdm.isFinite(m))
267                 return 0;
268             else
269             {
270                 const p2 = stdm.exp2(m);
271 
272                 static if (op.among!("*", "/"))
273                 {
274                     const wret = mixin("n " ~ op ~ " p2");
275                     static if (isFloatingPoint!N || isFloatingPoint!M)
276                         return wret;
277                     else
278                         return stdm.trunc(wret);
279                 }
280                 else
281                 {
282                     if (!stdm.isFinite(p2))
283                         return (p2 > 0)? n : (p2 < 0)? 0 : real.nan;
284                     else
285                         return n % (p2 == 0? real.min_normal : p2);
286                 }
287             }
288         }
289         enum isVO(N, M, PR) = (PR.sizeof >= N.sizeof) &&
290             ((isFloatingPoint!N || isFloatingPoint!M)?
291                 isFloatingPoint!PR :
292                 isIntegral!PR && (isSigned!PR || !isSigned!N));
293 
294         fuzz!(sc, Unqual, isVO, control, N, M)();
295     }
296 }
297 alias byPow2(N, M = void) = byPow2!(null, N, M);
298 
299 void pow(N = void, M = void)()
300 {
301     static if (is(N == void))
302     {
303         foreach (N1; IntegralTypes)
304             pow!(N1, M)();
305     }
306     else static if (is(M == void))
307     {
308         foreach (M1; IntegralTypes)
309             pow!(N, M1)();
310     }
311     else
312     {
313         static assert(isFixedPoint!N && isFixedPoint!M);
314         forbid!("safeOp.binary!\"^^\"(n, m)", N, M)();
315         forbid!("safeOp.binary!\"^^=\"(n, m)", N, M)();
316 
317         enum sc = "safeOp.pow(n, m)";
318 
319         static assert(real.mant_dig >= max(precision!N, precision!M));
320         real control(const real n, const real m)
321         {
322             return (m < 0)?
323                 real.nan :
324                 stdm.pow(n, m);
325         }
326         alias R = CallType!(stdm.pow, N, M);
327 
328         static if ((!isSigned!N || isSigned!R) && isIntegral!R)
329             fuzz!(sc, Unqual, OutIs!R, control, N, M)();
330         else
331             forbid!(sc, N, M)();
332     }
333 }