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 }