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 }