1 /** 2 Provide a 2^N-bit integer type. 3 Guaranteed to never allocate and expected binary layout 4 Recursive implementation with very slow division. 5 6 <b>Supports all operations that builtin integers support.</b> 7 8 Bugs: it's not sure if the unsigned operand would take precedence in a comparison/division. 9 TODO: add literals. 10 */ 11 module gfm.math.wideint; 12 13 import std.traits, 14 std.ascii; 15 16 static if( __VERSION__ < 2066 ) private enum nogc = 1; 17 18 /// Wide signed integer. 19 /// Params: 20 /// bits = number of bits, must be a power of 2. 21 template wideint(int bits) 22 { 23 alias integer!(true, bits) wideint; 24 } 25 26 /// Wide unsigned integer. 27 /// Params: 28 /// bits = number of bits, must be a power of 2. 29 template uwideint(int bits) 30 { 31 alias integer!(false, bits) uwideint; 32 } 33 34 // Some predefined integers (any power of 2 greater than 128 would work) 35 36 alias wideint!128 int128; // cent and ucent! 37 alias uwideint!128 uint128; 38 39 alias wideint!256 int256; 40 alias uwideint!256 uint256; 41 42 /// Use this template to get an arbitrary sized integer type. 43 private template integer(bool signed, int bits) 44 { 45 static assert((bits & (bits - 1)) == 0); // bits must be a power of 2 46 47 // forward to native type for lower numbers of bits 48 static if (bits == 8) 49 { 50 static if (signed) 51 alias byte integer; 52 else 53 alias ubyte integer; 54 } 55 else static if (bits == 16) 56 { 57 static if (signed) 58 alias short integer; 59 else 60 alias ushort integer; 61 } 62 else static if (bits == 32) 63 { 64 static if (signed) 65 alias int integer; 66 else 67 alias uint integer; 68 } 69 else static if (bits == 64) 70 { 71 static if (signed) 72 alias long integer; 73 else 74 alias ulong integer; 75 } 76 else 77 { 78 alias wideIntImpl!(signed, bits) integer; 79 } 80 } 81 82 /// Recursive 2^n integer implementation. 83 align(1) struct wideIntImpl(bool signed, int bits) 84 { 85 align(1): 86 static assert(bits >= 128); 87 private 88 { 89 alias wideIntImpl self; 90 91 template isSelf(T) 92 { 93 enum bool isSelf = is(Unqual!T == self); 94 } 95 96 alias integer!(true, bits/2) sub_int_t; // signed bits/2 integer 97 alias integer!(false, bits/2) sub_uint_t; // unsigned bits/2 integer 98 99 alias integer!(true, bits/4) sub_sub_int_t; // signed bits/4 integer 100 alias integer!(false, bits/4) sub_sub_uint_t; // unsigned bits/4 integer 101 102 static if(signed) 103 alias sub_int_t hi_t; // hi_t has same signedness as the whole struct 104 else 105 alias sub_uint_t hi_t; 106 107 alias sub_uint_t low_t; // low_t is always unsigned 108 109 enum _isWideIntImpl = true, 110 _bits = bits, 111 _signed = signed; 112 } 113 114 /// Construct from a value. 115 @nogc this(T)(T x) pure nothrow 116 { 117 opAssign!T(x); 118 } 119 120 /// Assign with a smaller unsigned type. 121 @nogc ref self opAssign(T)(T n) pure nothrow if (isIntegral!T && isUnsigned!T) 122 { 123 hi = 0; 124 lo = n; 125 return this; 126 } 127 128 /// Assign with a smaller signed type (sign is extended). 129 @nogc ref self opAssign(T)(T n) pure nothrow if (isIntegral!T && isSigned!T) 130 { 131 // shorter int always gets sign-extended, 132 // regardless of the larger int being signed or not 133 hi = (n < 0) ? cast(hi_t)(-1) : cast(hi_t)0; 134 135 // will also sign extend as well if needed 136 lo = cast(sub_int_t)n; 137 return this; 138 } 139 140 /// Assign with a wide integer of the same size (sign is lost). 141 @nogc ref self opAssign(T)(T n) pure nothrow if (is(typeof(T._isWideIntImpl)) && T._bits == bits) 142 { 143 hi = n.hi; 144 lo = n.lo; 145 return this; 146 } 147 148 /// Assign with a smaller wide integer (sign is extended accordingly). 149 @nogc ref self opAssign(T)(T n) pure nothrow if (is(typeof(T._isWideIntImpl)) && T._bits < bits) 150 { 151 static if (T._signed) 152 { 153 // shorter int always gets sign-extended, 154 // regardless of the larger int being signed or not 155 hi = cast(hi_t)((n < 0) ? -1 : 0); 156 157 // will also sign extend as well if needed 158 lo = cast(sub_int_t)n; 159 return this; 160 } 161 else 162 { 163 hi = 0; 164 lo = n; 165 return this; 166 } 167 } 168 169 /// Cast to a smaller integer type (truncation). 170 @nogc T opCast(T)() pure const nothrow if (isIntegral!T) 171 { 172 return cast(T)lo; 173 } 174 175 /// Cast to bool. 176 @nogc T opCast(T)() pure const nothrow if (is(T == bool)) 177 { 178 return this != 0; 179 } 180 181 /// Cast to wide integer of any size. 182 @nogc T opCast(T)() pure const nothrow if (is(typeof(T._isWideIntImpl))) 183 { 184 static if (T._bits < bits) 185 return cast(T)lo; 186 else 187 return T(this); 188 } 189 190 /// Converts to a hexadecimal string. 191 string toString() pure const nothrow 192 { 193 string outbuff = "0x"; 194 enum hexdigits = bits / 8; 195 196 for (int i = 0; i < hexdigits; ++i) 197 { 198 outbuff ~= hexDigits[cast(int)((hi >> ((15 - i) * 4)) & 15)]; 199 } 200 for (int i = 0; i < hexdigits; ++i) 201 { 202 outbuff ~= hexDigits[cast(int)((lo >> ((15 - i) * 4)) & 15)]; 203 } 204 return outbuff; 205 } 206 207 @nogc self opBinary(string op, T)(T o) pure const nothrow if (!isSelf!T) 208 { 209 self r = this; 210 self y = o; 211 return r.opOpAssign!(op)(y); 212 } 213 214 @nogc self opBinary(string op, T)(T y) pure const nothrow if (isSelf!T) 215 { 216 self r = this; // copy 217 self o = y; 218 return r.opOpAssign!(op)(o); 219 } 220 221 @nogc ref self opOpAssign(string op, T)(T y) pure nothrow if (!isSelf!T) 222 { 223 const(self) o = y; 224 return opOpAssign!(op)(o); 225 } 226 227 @nogc ref self opOpAssign(string op, T)(T y) pure nothrow if (isSelf!T) 228 { 229 static if (op == "+") 230 { 231 hi += y.hi; 232 if (lo + y.lo < lo) // deal with overflow 233 ++hi; 234 lo += y.lo; 235 } 236 else static if (op == "-") 237 { 238 opOpAssign!"+"(-y); 239 } 240 else static if (op == "<<") 241 { 242 if (y >= bits) 243 { 244 hi = 0; 245 lo = 0; 246 } 247 else if (y >= bits / 2) 248 { 249 hi = lo << (y.lo - bits / 2); 250 lo = 0; 251 } 252 else if (y > 0) 253 { 254 hi = (lo >>> (-y.lo + bits / 2)) | (hi << y.lo); 255 lo = lo << y.lo; 256 } 257 } 258 else static if (op == ">>" || op == ">>>") 259 { 260 assert(y >= 0); 261 static if (!signed || op == ">>>") 262 immutable(sub_int_t) signFill = 0; 263 else 264 immutable(sub_int_t) signFill = cast(sub_int_t)(isNegative() ? -1 : 0); 265 266 if (y >= bits) 267 { 268 hi = signFill; 269 lo = signFill; 270 } 271 else if (y >= bits/2) 272 { 273 lo = hi >> (y.lo - bits/2); 274 hi = signFill; 275 } 276 else if (y > 0) 277 { 278 lo = (hi << (-y.lo + bits/2)) | (lo >> y.lo); 279 hi = hi >> y.lo; 280 } 281 } 282 else static if (op == "*") 283 { 284 sub_sub_uint_t[4] a = toParts(); 285 sub_sub_uint_t[4] b = y.toParts(); 286 287 this = 0; 288 for(int i = 0; i < 4; ++i) 289 for(int j = 0; j < 4 - i; ++j) 290 this += self(cast(sub_uint_t)(a[i]) * b[j]) << ((bits/4) * (i + j)); 291 } 292 else static if (op == "&") 293 { 294 hi &= y.hi; 295 lo &= y.lo; 296 } 297 else static if (op == "|") 298 { 299 hi |= y.hi; 300 lo |= y.lo; 301 } 302 else static if (op == "^") 303 { 304 hi ^= y.hi; 305 lo ^= y.lo; 306 } 307 else static if (op == "/" || op == "%") 308 { 309 self q = void, r = void; 310 static if(signed) 311 Internals!bits.signedDivide(this, y, q, r); 312 else 313 Internals!bits.unsignedDivide(this, y, q, r); 314 static if (op == "/") 315 this = q; 316 else 317 this = r; 318 } 319 else 320 { 321 static assert(false, "unsupported operation '" ~ op ~ "'"); 322 } 323 return this; 324 } 325 326 // const unary operations 327 @nogc self opUnary(string op)() pure const nothrow if (op == "+" || op == "-" || op == "~") 328 { 329 static if (op == "-") 330 { 331 self r = this; 332 r.not(); 333 r.increment(); 334 return r; 335 } 336 else static if (op == "+") 337 return this; 338 else static if (op == "~") 339 { 340 self r = this; 341 r.not(); 342 return r; 343 } 344 } 345 346 // non-const unary operations 347 @nogc self opUnary(string op)() pure nothrow if (op == "++" || op == "--") 348 { 349 static if (op == "++") 350 increment(); 351 else static if (op == "--") 352 decrement(); 353 return this; 354 } 355 356 @nogc bool opEquals(T)(T y) pure const if (!isSelf!T) 357 { 358 return this == self(y); 359 } 360 361 @nogc bool opEquals(T)(T y) pure const if (isSelf!T) 362 { 363 return lo == y.lo && y.hi == hi; 364 } 365 366 @nogc int opCmp(T)(T y) pure const if (!isSelf!T) 367 { 368 return opCmp(self(y)); 369 } 370 371 @nogc int opCmp(T)(T y) pure const if (isSelf!T) 372 { 373 if (hi < y.hi) return -1; 374 if (hi > y.hi) return 1; 375 if (lo < y.lo) return -1; 376 if (lo > y.lo) return 1; 377 return 0; 378 } 379 380 // binary layout should be what is expected on this platform 381 version (LittleEndian) 382 { 383 low_t lo; 384 hi_t hi; 385 } 386 else 387 { 388 hi_t hi; 389 low_t lo; 390 } 391 392 private 393 { 394 static if (signed) 395 { 396 @nogc bool isNegative() pure nothrow const 397 { 398 return signBit(); 399 } 400 } 401 else 402 { 403 @nogc bool isNegative() pure nothrow const 404 { 405 return false; 406 } 407 } 408 409 @nogc void not() pure nothrow 410 { 411 hi = ~hi; 412 lo = ~lo; 413 } 414 415 @nogc void increment() pure nothrow 416 { 417 ++lo; 418 if (lo == 0) ++hi; 419 } 420 421 @nogc void decrement() pure nothrow 422 { 423 if (lo == 0) --hi; 424 --lo; 425 } 426 427 @nogc bool signBit() pure const nothrow 428 { 429 enum SIGN_SHIFT = bits / 2 - 1; 430 return ((hi >> SIGN_SHIFT) & 1) != 0; 431 } 432 433 @nogc sub_sub_uint_t[4] toParts() pure const nothrow 434 { 435 sub_sub_uint_t[4] p = void; 436 enum SHIFT = bits / 4; 437 immutable lomask = cast(sub_uint_t)(cast(sub_sub_int_t)(-1)); 438 p[3] = cast(sub_sub_uint_t)(hi >> SHIFT); 439 p[2] = cast(sub_sub_uint_t)(hi & lomask); 440 p[1] = cast(sub_sub_uint_t)(lo >> SHIFT); 441 p[0] = cast(sub_sub_uint_t)(lo & lomask); 442 return p; 443 } 444 } 445 } 446 447 @nogc public wideIntImpl!(signed, bits) abs(bool signed, int bits)(wideIntImpl!(signed, bits) x) pure nothrow 448 { 449 if(x >= 0) 450 return x; 451 else 452 return -x; 453 } 454 455 private struct Internals(int bits) 456 { 457 alias wideIntImpl!(true, bits) wint_t; 458 alias wideIntImpl!(false, bits) uwint_t; 459 460 @nogc static void unsignedDivide(uwint_t dividend, uwint_t divisor, 461 out uwint_t quotient, out uwint_t remainder) pure nothrow 462 { 463 assert(divisor != 0); 464 465 uwint_t rQuotient = 0; 466 uwint_t cDividend = dividend; 467 468 while (divisor <= cDividend) 469 { 470 // find N so that (divisor << N) <= cDividend && cDividend < (divisor << (N + 1) ) 471 472 uwint_t N = 0; 473 uwint_t cDivisor = divisor; 474 while (cDividend > cDivisor) 475 { 476 if (cDivisor.signBit()) 477 break; 478 479 if (cDividend < (cDivisor << 1)) 480 break; 481 482 cDivisor <<= 1; 483 ++N; 484 } 485 cDividend = cDividend - cDivisor; 486 rQuotient += (uwint_t(1) << N); 487 } 488 489 quotient = rQuotient; 490 remainder = cDividend; 491 } 492 493 @nogc static void signedDivide(wint_t dividend, wint_t divisor, 494 out wint_t quotient, out wint_t remainder) pure nothrow 495 { 496 uwint_t q, r; 497 unsignedDivide(uwint_t(abs(dividend)), uwint_t(abs(divisor)), q, r); 498 499 // remainder has same sign as the dividend 500 if (dividend < 0) 501 r = -r; 502 503 // negate the quotient if opposite signs 504 if ((dividend >= 0) != (divisor >= 0)) 505 q = -q; 506 507 quotient = q; 508 remainder = r; 509 510 assert(remainder == 0 || ((remainder < 0) == (dividend < 0))); 511 } 512 } 513 514 unittest 515 { 516 long step = 164703072086692425; 517 for (long si = long.min; si <= long.max - step; si += step) 518 { 519 for (long sj = long.min; sj <= long.max - step; sj += step) 520 { 521 ulong ui = cast(ulong)si; 522 ulong uj = cast(ulong)sj; 523 int128 csi = si; 524 uint128 cui = si; 525 int128 csj = sj; 526 uint128 cuj = sj; 527 assert(csi == csi); 528 assert(~~csi == csi); 529 assert(-(-csi) == csi); 530 assert(++csi == si + 1); 531 assert(--csi == si); 532 533 string testSigned(string op) 534 { 535 return "assert(cast(ulong)(si" ~ op ~ "sj) == cast(ulong)(csi" ~ op ~ "csj));"; 536 } 537 538 string testMixed(string op) 539 { 540 return "assert(cast(ulong)(ui" ~ op ~ "sj) == cast(ulong)(cui" ~ op ~ "csj));" 541 ~ "assert(cast(ulong)(si" ~ op ~ "uj) == cast(ulong)(csi" ~ op ~ "cuj));"; 542 } 543 544 string testUnsigned(string op) 545 { 546 return "assert(cast(ulong)(ui" ~ op ~ "uj) == cast(ulong)(cui" ~ op ~ "cuj));"; 547 } 548 549 string testAll(string op) 550 { 551 return testSigned(op) ~ testMixed(op) ~ testUnsigned(op); 552 } 553 554 mixin(testAll("+")); 555 mixin(testAll("-")); 556 mixin(testAll("*")); 557 mixin(testAll("|")); 558 mixin(testAll("&")); 559 mixin(testAll("^")); 560 if (sj != 0) 561 { 562 mixin(testSigned("/")); 563 mixin(testSigned("%")); 564 if (si >= 0 && sj >= 0) 565 { 566 // those operations are not supposed to be the same at 567 // higher bitdepth: a sign-extended negative may yield higher dividend 568 testMixed("/"); 569 testUnsigned("/"); 570 testMixed("%"); 571 testUnsigned("%"); 572 } 573 } 574 } 575 } 576 }