1 /++ 2 Note: 3 The module doesn't provide full arithmetic API for now. 4 +/ 5 module mir.bignum.fixed; 6 7 import std.traits; 8 import mir.bitop; 9 import mir.utility; 10 11 /++ 12 Fixed-length unsigned integer. 13 14 Params: 15 size = size in bits 16 +/ 17 struct UInt(size_t size) 18 if (size % (size_t.sizeof * 8) == 0 && size >= size_t.sizeof * 8) 19 { 20 import mir.bignum.fixed: UInt; 21 /++ 22 Payload. The data is located in the target endianness. 23 +/ 24 size_t[size / (size_t.sizeof * 8)] data; 25 26 /// 27 this(size_t N)(auto ref const size_t[N] data) 28 if (N && N <= this.data.length) 29 { 30 version(LittleEndian) 31 this.data[0 .. N] = data; 32 else 33 this.data[$ - N .. $] = data; 34 } 35 36 /// 37 this(size_t argSize)(auto ref const UInt!argSize arg) 38 if (argSize <= size) 39 { 40 this(arg.data); 41 } 42 43 static if (size_t.sizeof == uint.sizeof && data.length % 2 == 0) 44 /// 45 this()(auto ref const ulong[data.length / 2] data) 46 { 47 if (!__ctfe) 48 { 49 this.data = cast(typeof(this.data)) data; 50 } 51 else 52 { 53 version(LittleEndian) 54 { 55 static foreach (i; 0 .. data.length) 56 { 57 this.data[i * 2 + 0] = cast(uint) data[i]; 58 this.data[i * 2 + 1] = cast(uint) (data[i] >> 32); 59 } 60 } 61 else 62 { 63 static foreach (i; 0 .. data.length) 64 { 65 this.data[i * 2 + 1] = cast(uint) data[i]; 66 this.data[i * 2 + 0] = cast(uint) (data[i] >> 32); 67 } 68 } 69 } 70 } 71 72 static if (size >= 64) 73 /// 74 this(ulong data) 75 { 76 static if (size_t.sizeof == ulong.sizeof) 77 { 78 this.data[0] = data; 79 } 80 else 81 { 82 this.data[0] = cast(uint) data; 83 this.data[1] = cast(uint) (data >> 32); 84 } 85 } 86 87 static if (size < 64) 88 /// 89 this(uint data) 90 { 91 this.data[0] = data; 92 } 93 94 /// 95 this(C)(scope const(C)[] str) @safe pure @nogc 96 if (isSomeChar!C) 97 { 98 if (fromStringImpl(str)) 99 return; 100 static if (__traits(compiles, () @nogc { throw new Exception("Can't parse UInt."); })) 101 { 102 import mir.exception: MirException; 103 throw new MirException("Can't parse UInt!" ~ size.stringof ~ " from string `", str , "`."); 104 } 105 else 106 { 107 static immutable exception = new Exception("Can't parse UInt!" ~ size.stringof ~ "."); 108 { import mir.exception : toMutable; throw exception.toMutable; } 109 } 110 } 111 112 static if (size == 128) 113 /// 114 version(mir_bignum_test) @safe pure @nogc unittest 115 { 116 import mir.math.constant: PI; 117 UInt!256 integer = "34010447314490204552169750449563978034784726557588085989975288830070948234680"; // constructor 118 assert(integer == UInt!256.fromHexString("4b313b23aa560e1b0985f89cbe6df5460860e39a64ba92b4abdd3ee77e4e05b8")); 119 } 120 121 /++ 122 Returns: false in case of overflow or incorrect string. 123 Precondition: non-empty coefficients. 124 +/ 125 bool fromStringImpl(C)(scope const(C)[] str) 126 scope @trusted pure @nogc nothrow 127 if (isSomeChar!C) 128 { 129 import mir.bignum.low_level_view: BigUIntView; 130 return BigUIntView!size_t(data[]).fromStringImpl(str); 131 } 132 133 /// 134 immutable(C)[] toString(C = char)() scope const @safe pure nothrow 135 if(isSomeChar!C && isMutable!C) 136 { 137 UInt!size copy = this; 138 auto work = copy.view.normalized; 139 import mir.bignum.low_level_view: ceilLog10Exp2; 140 C[ceilLog10Exp2(data.length * (size_t.sizeof * 8))] buffer = void; 141 return buffer[$ - work.toStringImpl(buffer) .. $].idup; 142 } 143 144 static if (size == 128) 145 /// 146 version(mir_bignum_test) @safe pure unittest 147 { 148 auto str = "34010447314490204552169750449563978034784726557588085989975288830070948234680"; 149 auto integer = UInt!256(str); 150 assert(integer.toString == str); 151 152 integer = UInt!256.init; 153 assert(integer.toString == "0"); 154 } 155 156 /// 157 void toString(C = char, W)(ref scope W w) scope const 158 if(isSomeChar!C && isMutable!C) 159 { 160 UInt!size copy = this; 161 auto work = copy.view.normalized; 162 import mir.bignum.low_level_view: ceilLog10Exp2; 163 C[ceilLog10Exp2(data.length * (size_t.sizeof * 8))] buffer = void; 164 w.put(buffer[$ - work.toStringImpl(buffer) .. $]); 165 } 166 167 static if (size == 128) 168 /// Check @nogc toString impl 169 version(mir_bignum_test) @safe pure @nogc unittest 170 { 171 import mir.format: stringBuf; 172 auto str = "34010447314490204552169750449563978034784726557588085989975288830070948234680"; 173 auto integer = UInt!256(str); 174 auto buffer = stringBuf; 175 buffer << integer; 176 assert(buffer.data == str); 177 } 178 179 /// 180 enum UInt!size max = ((){UInt!size ret; ret.data = size_t.max; return ret;})(); 181 182 /// 183 enum UInt!size min = UInt!size.init; 184 185 import mir.bignum.low_level_view: BigUIntView; 186 187 /// 188 BigUIntView!size_t view() @property pure nothrow @nogc scope return @safe 189 { 190 return BigUIntView!size_t(data); 191 } 192 193 /// 194 BigUIntView!(const size_t) view() const @property pure nothrow @nogc scope return @safe 195 { 196 return BigUIntView!(const size_t)(data); 197 } 198 199 /// 200 static UInt!size fromHexString(bool allowUnderscores = false)(scope const(char)[] str) 201 { 202 typeof(return) ret; 203 if (ret.fromHexStringImpl!(char, allowUnderscores)(str)) 204 return ret; 205 version(D_Exceptions) 206 { 207 import mir.bignum.low_level_view: hexStringException; 208 { import mir.exception : toMutable; throw hexStringException.toMutable; } 209 } 210 else 211 { 212 import mir.bignum.low_level_view: hexStringErrorMsg; 213 assert(0, hexStringErrorMsg); 214 } 215 } 216 217 /++ 218 +/ 219 bool fromHexStringImpl(C, bool allowUnderscores = false)(scope const(C)[] str) 220 @safe pure @nogc nothrow 221 if (isSomeChar!C) 222 { 223 return view.fromHexStringImpl!(C, allowUnderscores)(str); 224 } 225 226 /// 227 static UInt!size fromBinaryString(bool allowUnderscores = false)(scope const(char)[] str) 228 { 229 typeof(return) ret; 230 if (ret.fromBinaryStringImpl!(char, allowUnderscores)(str)) 231 return ret; 232 version(D_Exceptions) 233 { 234 import mir.bignum.low_level_view: binaryStringException; 235 { import mir.exception : toMutable; throw binaryStringException.toMutable; } 236 } 237 else 238 { 239 import mir.bignum.low_level_view: binaryStringErrorMsg; 240 assert(0, binaryStringErrorMsg); 241 } 242 } 243 244 /++ 245 +/ 246 bool fromBinaryStringImpl(C, bool allowUnderscores = false)(scope const(C)[] str) 247 @safe pure @nogc nothrow 248 if (isSomeChar!C) 249 { 250 return view.fromBinaryStringImpl!(C, allowUnderscores)(str); 251 } 252 253 /++ 254 +/ 255 auto opEquals(size_t rhsSize)(auto ref const UInt!rhsSize rhs) const 256 { 257 static if (rhsSize == size) 258 return this.data == rhs.data; 259 else 260 static if (rhsSize > size) 261 return this.toSize!rhsSize.data == rhs.data; 262 else 263 return this.data == rhs.toSize!size.data; 264 } 265 266 static if (size >= 64) 267 /// ditto 268 auto opEquals(ulong rhs) const 269 { 270 return opEquals(UInt!size(rhs)); 271 } 272 else 273 auto opEquals(uint rhs) const 274 { 275 return opEquals(UInt!size(rhs)); 276 } 277 278 /++ 279 +/ 280 auto opCmp(UInt!size rhs) const 281 { 282 foreach_reverse(i; 0 .. data.length) 283 { 284 if (this.data[i] < rhs.data[i]) 285 return -1; 286 if (this.data[i] > rhs.data[i]) 287 return +1; 288 } 289 return 0; 290 } 291 292 static if (size >= 64) 293 /// ditto 294 auto opCmp(ulong rhs) const scope 295 { 296 return opCmp(UInt!size(rhs)); 297 } 298 else 299 auto opCmp(uint rhs) const scope 300 { 301 return opCmp(UInt!size(rhs)); 302 } 303 304 static if (size >= 64) 305 /++ 306 +/ 307 ref UInt!size opAssign(ulong rhs) scope return 308 @safe pure nothrow @nogc 309 { 310 this.data = UInt!size(rhs).data; 311 return this; 312 } 313 else 314 /// 315 ref UInt!size opAssign(uint rhs) scope return 316 @safe pure nothrow @nogc 317 { 318 this.data = UInt!size(rhs).data; 319 return this; 320 } 321 322 /++ 323 +/ 324 ref UInt!size opAssign(uint rhsSize)(UInt!rhsSize rhs) scope return 325 @safe pure nothrow @nogc 326 { 327 this.data = UInt!size(rhs).data; 328 return this; 329 } 330 331 /++ 332 `bool overflow = a += b ` and `bool overflow = a -= b` operations. 333 +/ 334 bool opOpAssign(string op)(UInt!size rhs, bool overflow = false) 335 @safe pure nothrow @nogc scope 336 if (op == "+" || op == "-") 337 { 338 return view.opOpAssign!op(rhs.view, overflow); 339 } 340 341 /// ditto 342 bool opOpAssign(string op)(size_t rhs) 343 @safe pure nothrow @nogc scope 344 if (op == "+" || op == "-") 345 { 346 return view.opOpAssign!op(rhs); 347 } 348 349 static if (size_t.sizeof < ulong.sizeof) 350 /// ditto 351 bool opOpAssign(string op)(ulong rhs) 352 @safe pure nothrow @nogc scope 353 if (op == "+" || op == "-") 354 { 355 return opOpAssign!op(UInt!size(rhs)); 356 } 357 358 /// ditto 359 bool opOpAssign(string op, uint rsize)(UInt!rsize rhs, bool overflow = false) 360 @safe pure nothrow @nogc scope 361 if ((op == "+" || op == "-") && rsize < size) 362 { 363 return opOpAssign!op(rhs.toSize!size, overflow); 364 } 365 366 /++ 367 Returns: overflow value of multiplication 368 +/ 369 size_t opOpAssign(string op : "*")(size_t rhs, size_t carry = 0) 370 @safe pure nothrow @nogc scope 371 { 372 return view.opOpAssign!op(rhs, carry); 373 } 374 375 static if (size_t.sizeof == 4) 376 /// ditto 377 auto opOpAssign(string op : "*")(ulong rhs) 378 @safe pure nothrow @nogc scope 379 { 380 return opOpAssign!op(UInt!64(rhs)); 381 } 382 383 384 /++ 385 Returns: overflow value of multiplication 386 +/ 387 void opOpAssign(string op : "*", size_t rhsSize)(UInt!rhsSize rhs) 388 @safe pure nothrow @nogc scope 389 if (rhsSize <= size) 390 { 391 this = extendedMul(this, rhs).toSize!size; 392 } 393 394 /++ 395 Performs `uint remainder = (overflow$big) /= scalar` operatrion, where `$` denotes big-endian concatenation. 396 Precondition: `overflow < rhs` 397 Params: 398 rhs = unsigned value to devide by 399 overflow = initial unsigned overflow 400 Returns: 401 unsigned remainder value (evaluated overflow) 402 +/ 403 uint opOpAssign(string op : "/")(uint rhs, uint overflow = 0) 404 @safe pure nothrow @nogc scope 405 { 406 assert(overflow < rhs); 407 auto work = view.normalized; 408 if (worl.coefficients.length) 409 return work.opOpAssign!op(rhs, overflow); 410 return overflow; 411 } 412 413 /++ 414 Performs division & extracts the remainder. 415 Params: 416 rhs = unsigned value to divide by 417 Returns: quotient, sets `rhs` to remainder 418 +/ 419 ref divMod(size_t rhsSize)(scope ref UInt!rhsSize rhs) 420 @safe pure nothrow @nogc scope return 421 { 422 import mir.bignum.internal.kernel: divMod, divisionRequiredBuffSize; 423 424 UInt!size quotient; 425 426 auto dividendV = this.view; 427 auto divisorV = rhs.view; 428 divisorV = divisorV.normalized; 429 dividendV = dividendV.normalized; 430 431 import mir.utility: min; 432 enum vlen = min(rhs.data.length, data.length); 433 size_t[divisionRequiredBuffSize(data.length, vlen)] buffer = void; 434 435 divMod( 436 quotient.data, 437 divisorV.coefficients, 438 dividendV.coefficients, 439 divisorV.coefficients, 440 buffer); 441 this = quotient; 442 return this; 443 } 444 445 /++ 446 Performs `big /= rhs` operation. 447 Params: 448 rhs = unsigned value to divide by 449 Returns: 450 quotient from division 451 +/ 452 ref opOpAssign(string op : "/", size_t rhsSize)(UInt!rhsSize rhs) 453 @safe pure nothrow @nogc scope return 454 { 455 return this.divMod(rhs); 456 } 457 458 /// ditto 459 ref opOpAssign(string op : "/")(ulong rhs) 460 @safe pure nothrow @nogc scope return 461 { 462 return opOpAssign!(op, ulong.sizeof * 8)(UInt!(ulong.sizeof * 8)(rhs)); 463 } 464 465 /++ 466 Performs `big %= rhs` operation. 467 Params: 468 rhs = unsigned value to divide by 469 Returns: 470 remainder from division 471 +/ 472 ref opOpAssign(string op : "%", size_t rhsSize)(UInt!rhsSize rhs) 473 @safe pure nothrow @nogc scope return 474 { 475 this.divMod(rhs); 476 this = cast(UInt!size)rhs; 477 } 478 479 /// ditto 480 ref opOpAssign(string op : "%")(ulong rhs) 481 @safe pure nothrow @nogc scope 482 { 483 return opOpAssign!(op, ulong.sizeof * 8)(UInt!(ulong.sizeof * 8)(rhs)); 484 } 485 486 static if (size == 128) 487 /// 488 version(mir_bignum_test) 489 @safe pure @nogc 490 unittest 491 { 492 auto a = UInt!128.fromHexString("e3251bacb112c88b71ad3f85a970a314"); 493 auto b = UInt!128.fromHexString("dfbbfae3cd0aff2714a1de7022b0029d"); 494 assert(a / b == UInt!128.fromHexString("1")); 495 assert(a % b == UInt!128.fromHexString("36920c8e407c9645d0b611586c0a077")); 496 } 497 498 /// 499 ref UInt!size opOpAssign(string op)(UInt!size rhs) nothrow return 500 if (op == "^" || op == "|" || op == "&") 501 { 502 static foreach (i; 0 .. data.length) 503 mixin(`data[i] ` ~ op ~ `= rhs.data[i];`); 504 return this; 505 } 506 507 static if (size == 128) 508 /// 509 version(mir_bignum_test) 510 @safe pure @nogc 511 unittest 512 { 513 auto a = UInt!128.fromHexString("dfbbfae3cd0aff2714a1de7022b0029d"); 514 auto b = UInt!128.fromHexString("e3251bacb112c88b71ad3f85a970a314"); 515 assert((a.opBinary!"|"(b)) == UInt!128.fromHexString("ffbffbeffd1affaf75adfff5abf0a39d")); 516 } 517 518 /// 519 ref UInt!size opOpAssign(string op)(size_t rhs) nothrow return scope 520 if (op == "^" || op == "|" || op == "&") 521 { 522 mixin(`view.coefficients[0] ` ~ op ~ `= rhs;`); 523 return this; 524 } 525 526 static if (size_t.sizeof < ulong.sizeof) 527 /// ditto 528 ref opOpAssign(string op)(ulong rhs) return 529 @safe pure nothrow @nogc scope 530 if (op == "^" || op == "|" || op == "&") 531 { 532 return opOpAssign!op(UInt!size(rhs)); 533 } 534 535 /// 536 ref UInt!size opOpAssign(string op)(size_t shift) 537 @safe pure nothrow @nogc return 538 if (op == "<<" || op == ">>") 539 { 540 auto d = view.coefficients; 541 assert(shift < size); 542 auto index = shift / (size_t.sizeof * 8); 543 auto bs = shift % (size_t.sizeof * 8); 544 auto ss = size_t.sizeof * 8 - bs; 545 static if (op == ">>") 546 { 547 if (bs) 548 { 549 foreach (j; 0 .. data.length - (index + 1)) 550 { 551 d[j] = (d[j + index] >>> bs) | (d[j + (index + 1)] << ss); 552 } 553 } 554 else 555 { 556 foreach (j; 0 .. data.length - (index + 1)) 557 { 558 d[j] = d[j + index]; 559 } 560 } 561 d[$ - (index + 1)] = d[$ - 1] >>> bs; 562 foreach (j; data.length - index .. data.length) 563 { 564 d[j] = 0; 565 } 566 } 567 else 568 { 569 if (bs) 570 { 571 foreach_reverse (j; index + 1 .. data.length) 572 { 573 d[j] = (d[j - index] << bs) | (d[j - (index + 1)] >> ss); 574 } 575 } 576 else 577 { 578 foreach_reverse (j; index + 1 .. data.length) 579 { 580 d[j] = d[j - index]; 581 } 582 } 583 d[index] = d[0] << bs; 584 foreach_reverse (j; 0 .. index) 585 { 586 d[j] = 0; 587 } 588 } 589 return this; 590 } 591 592 /++ 593 `auto c = a << b` operation. 594 +/ 595 UInt!size opBinary(string op)(size_t rhs) 596 const @safe pure nothrow @nogc 597 if (op == "<<" || op == ">>>" || op == ">>") 598 { 599 UInt!size ret = this; 600 ret.opOpAssign!op(rhs); 601 return ret; 602 } 603 604 static if (size == 128) 605 /// 606 version(mir_bignum_test) 607 @safe pure @nogc 608 unittest 609 { 610 auto a = UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"); 611 assert(a << 0 == a); 612 assert(a << 4 == UInt!128.fromHexString("fbbfae3cd0aff2714a1de7022b0029d0")); 613 assert(a << 68 == UInt!128.fromHexString("4a1de7022b0029d00000000000000000")); 614 assert(a << 127 == UInt!128.fromHexString("80000000000000000000000000000000")); 615 assert(a >> 0 == a); 616 assert(a >> 4 == UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029")); 617 assert(a >> 68 == UInt!128.fromHexString("afbbfae3cd0aff2")); 618 assert(a >> 127 == UInt!128(1)); 619 } 620 621 /++ 622 Binary operations 623 +/ 624 template opBinary(string op) 625 if (op == "^" || op == "|" || op == "&" || op == "+" || op == "-" || op == "*" || op == "/" || op == "%") 626 { 627 /// 628 UInt!size opBinary(size_t rsize)(UInt!rsize rhs) 629 const @safe pure nothrow @nogc 630 if (rsize <= size) 631 { 632 UInt!size ret = this; 633 ret.opOpAssign!op(rhs); 634 return ret; 635 } 636 637 /// ditto 638 UInt!size opBinary(ulong rhs) 639 const @safe pure nothrow @nogc 640 { 641 UInt!size ret = this; 642 ret.opOpAssign!op(rhs); 643 return ret; 644 } 645 } 646 647 static if (size == 128) 648 /// 649 version(mir_bignum_test) 650 @safe pure @nogc 651 unittest 652 { 653 auto a = UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"); 654 assert(a / UInt!128.fromHexString("5") == UInt!128.fromHexString("23259893f5ceffd49db9f949a0899a1f")); 655 assert(a == UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d")); 656 assert(a % UInt!128.fromHexString("5") == UInt!128.fromHexString("2")); 657 assert(a == UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d")); 658 659 assert(a / 5 == UInt!128.fromHexString("23259893f5ceffd49db9f949a0899a1f")); 660 assert(a % 5 == UInt!64.fromHexString("2")); 661 assert(a % 5 == 2); 662 } 663 664 /// ditto 665 template opBinaryRight(string op) 666 if (op == "^" || op == "|" || op == "&" || op == "+" || op == "*") 667 { 668 /// 669 UInt!size opBinaryRight(size_t lsize)(UInt!lsize lhs) 670 const @safe pure nothrow @nogc 671 if (lsize < size) 672 { 673 UInt!size ret = this; 674 ret.opOpAssign!op(lhs); 675 return ret; 676 } 677 678 /// ditto 679 UInt!size opBinaryRight(ulong lhs) 680 const @safe pure nothrow @nogc 681 { 682 UInt!size ret = this; 683 ret.opOpAssign!op(lhs); 684 return ret; 685 } 686 } 687 688 /++ 689 Shifts left using at most `size_t.sizeof * 8 - 1` bits 690 +/ 691 UInt!size smallLeftShift(uint shift) const 692 { 693 assert(shift < size_t.sizeof * 8); 694 UInt!size ret = this; 695 if (shift) 696 { 697 auto csh = size_t.sizeof * 8 - shift; 698 static foreach_reverse (i; 1 .. data.length) 699 { 700 ret.data[i] = (ret.data[i] << shift) | (ret.data[i - 1] >>> csh); 701 } 702 ret.data[0] = ret.data[0] << shift; 703 } 704 return ret; 705 } 706 707 static if (size == 128) 708 /// 709 version(mir_bignum_test) 710 @safe pure @nogc 711 unittest 712 { 713 auto a = UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"); 714 assert(a.smallLeftShift(4) == UInt!128.fromHexString("fbbfae3cd0aff2714a1de7022b0029d0")); 715 } 716 717 /++ 718 Shifts right using at most `size_t.sizeof * 8 - 1` bits 719 +/ 720 UInt!size smallRightShift(uint shift) const 721 { 722 assert(shift < size_t.sizeof * 8); 723 UInt!size ret = this; 724 if (shift) 725 { 726 auto csh = size_t.sizeof * 8 - shift; 727 static foreach (i; 0 .. data.length - 1) 728 { 729 ret.data[i] = (ret.data[i] >>> shift) | (ret.data[i + 1] << csh); 730 } 731 ret.data[$ - 1] = ret.data[$ - 1] >>> shift; 732 } 733 return ret; 734 } 735 736 static if (size == 128) 737 /// 738 version(mir_bignum_test) 739 @safe pure @nogc 740 unittest 741 { 742 auto a = UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"); 743 assert(a.smallRightShift(4) == UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029")); 744 } 745 746 /++ 747 +/ 748 T opCast(T)() const 749 if (is(Unqual!T == bool)) 750 { 751 static foreach (i; 0 .. data.length) 752 { 753 if (data[i]) 754 return true; 755 } 756 return false; 757 } 758 759 /++ 760 +/ 761 T opCast(T)() const 762 if (is(Unqual!T == ulong)) 763 { 764 static if (size_t.sizeof == ulong.sizeof) 765 { 766 return data[0]; 767 } 768 else 769 { 770 return data[0] | (ulong(data[1]) << 32); 771 } 772 } 773 774 /++ 775 +/ 776 T opCast(T)() const @safe pure nothrow @nogc 777 if (is(T == UInt!newSize, uint newSize)) 778 { 779 enum newLength = typeof(return).data.length; 780 static if (newLength <= data.length) 781 { 782 return typeof(return)(data[0 .. newLength]); 783 } 784 else 785 { 786 typeof(return) ret; 787 ret.data[0 .. data.length] = data; 788 return ret; 789 } 790 } 791 792 /++ 793 +/ 794 T opCast(T)() const 795 if (is(Unqual!T == uint)) 796 { 797 return cast(uint) data[0]; 798 } 799 800 /++ 801 Returns: 802 the number with shrinked or extended size. 803 +/ 804 UInt!newSize toSize(size_t newSize, bool lowerBits = true)() 805 const @safe pure @nogc nothrow 806 { 807 typeof(return) ret; 808 import mir.utility: min; 809 enum N = min(ret.data.length, data.length); 810 static if (lowerBits) 811 { 812 ret.data[0 .. N] = data[0 .. N]; 813 } 814 else 815 { 816 ret.data[0 .. N] = data[$ - N .. $]; 817 } 818 return ret; 819 } 820 821 /// 822 UInt!(size + additionalRightBits) rightExtend(size_t additionalRightBits)() 823 const @safe pure @nogc nothrow 824 { 825 static if (additionalRightBits) 826 { 827 typeof(return) ret; 828 version (BigEndian) 829 ret.data[0 .. data.length] = data; 830 else 831 ret.data[$ - data.length .. $] = data; 832 return ret; 833 } 834 else 835 { 836 return this; 837 } 838 } 839 840 /++ 841 +/ 842 bool bt(size_t position) const 843 @safe pure nothrow @nogc 844 { 845 assert(position < data.sizeof * 8); 846 return view.bt(position); 847 } 848 849 static if (size == 128) 850 /// 851 version(mir_bignum_test) 852 @safe pure @nogc 853 unittest 854 { 855 auto a = UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"); 856 assert(a.bt(127) == 1); 857 assert(a.bt(126) == 0); 858 assert(a.bt(125) == 1); 859 assert(a.bt(124) == 0); 860 assert(a.bt(0) == 1); 861 assert(a.bt(1) == 0); 862 assert(a.bt(2) == 1); 863 assert(a.bt(3) == 1); 864 } 865 866 /++ 867 +/ 868 size_t ctlz() const scope @property 869 @safe pure nothrow @nogc 870 { 871 return view.ctlz; 872 } 873 874 static if (size == 128) 875 /// 876 version(mir_bignum_test) 877 @safe pure @nogc 878 unittest 879 { 880 auto a = UInt!128.fromHexString("dfbbfae3cd0aff2714a1de7022b0029d"); 881 assert (a.ctlz == 0); 882 a = UInt!128.init; 883 assert (a.ctlz == 128); 884 a = UInt!128.fromHexString("3"); 885 assert (a.ctlz == 126); 886 } 887 888 /++ 889 +/ 890 size_t cttz() const @property 891 @safe pure nothrow @nogc 892 { 893 return view.cttz; 894 } 895 896 static if (size == 128) 897 /// 898 version(mir_bignum_test) 899 @safe pure @nogc 900 unittest 901 { 902 auto a = UInt!128.fromHexString("d"); 903 assert (a.cttz == 0); 904 a = UInt!128.init; 905 assert (a.cttz == 128); 906 a = UInt!128.fromHexString("300000000000000000"); 907 assert (a.cttz == 68); 908 } 909 910 /++ 911 +/ 912 bool signBit() const @property 913 { 914 return data[$ - 1] >> (size_t.sizeof * 8 - 1); 915 } 916 917 /// ditto 918 void signBit(bool value) @property 919 { 920 enum signMask = ptrdiff_t.max; 921 data[$ - 1] = (data[$ - 1] & ptrdiff_t.max) | (size_t(value) << (size_t.sizeof * 8 - 1)); 922 } 923 924 static if (size == 128) 925 /// 926 version(mir_bignum_test) 927 unittest 928 { 929 auto a = UInt!128.fromHexString("dfbbfae3cd0aff2714a1de7022b0029d"); 930 assert(a.signBit); 931 a.signBit = false; 932 assert(a == UInt!128.fromHexString("5fbbfae3cd0aff2714a1de7022b0029d")); 933 assert(!a.signBit); 934 a.signBit = true; 935 assert(a == UInt!128.fromHexString("dfbbfae3cd0aff2714a1de7022b0029d")); 936 } 937 } 938 939 /++ 940 +/ 941 UInt!sizeB extendedMulHigh(size_t sizeA, size_t sizeB)(UInt!sizeA a, UInt!sizeB b) 942 @safe pure nothrow @nogc 943 { 944 return (extendedMul(a, b) >> sizeA).toSize!sizeB; 945 } 946 947 /++ 948 +/ 949 UInt!(sizeA + sizeB) extendedMul(size_t sizeA, size_t sizeB)(UInt!sizeA a, UInt!sizeB b) 950 @safe pure nothrow @nogc 951 { 952 UInt!(sizeA + sizeB) ret; 953 enum al = a.data.length; 954 enum alp1 = a.data.length + 1; 955 ret.data[0 .. alp1] = extendedMul(a, b.data[0]).data; 956 static foreach ( i; 1 .. b.data.length) 957 ret.data[i .. i + alp1] = extendedMulAdd(a, b.data[i], UInt!sizeA(ret.data[i .. i + al])).data; 958 return ret; 959 } 960 961 /// ditto 962 UInt!(size + size_t.sizeof * 8) 963 extendedMul(size_t size)(UInt!size a, size_t b) 964 @safe pure nothrow @nogc 965 { 966 size_t overflow = a.view *= b; 967 auto ret = a.toSize!(size + size_t.sizeof * 8); 968 ret.data[$ - 1] = overflow; 969 return ret; 970 } 971 972 /// ditto 973 auto extendedMul()(ulong a, ulong b) 974 @safe pure nothrow @nogc 975 { 976 static if (size_t.sizeof == ulong.sizeof) 977 { 978 import mir.utility: extMul; 979 auto e = extMul(a, b); 980 return UInt!128([e.low, e.high]); 981 } 982 else 983 { 984 return extendedMul(UInt!64(a), UInt!64(b)); 985 } 986 } 987 988 /// ditto 989 auto extendedMul()(uint a, uint b) 990 @safe pure nothrow @nogc 991 { 992 static if (size_t.sizeof == uint.sizeof) 993 { 994 import mir.utility: extMul; 995 auto e = extMul(a, b); 996 version(LittleEndian) 997 return UInt!64([e.low, e.high]); 998 else 999 return UInt!64([e.high, e.low]); 1000 } 1001 else 1002 { 1003 return UInt!64([ulong(a) * b]); 1004 } 1005 } 1006 1007 /// 1008 version(mir_bignum_test) 1009 @safe pure @nogc 1010 unittest 1011 { 1012 auto a = UInt!128.max; 1013 auto b = UInt!256.max; 1014 auto c = UInt!384.max; 1015 assert(extendedMul(a, a) == UInt!256.max - UInt!128.max - UInt!128.max); 1016 assert(extendedMul(a, b) == UInt!384.max - UInt!128.max - UInt!256.max); 1017 assert(extendedMul(b, a) == UInt!384.max - UInt!128.max - UInt!256.max); 1018 1019 a = UInt!128.fromHexString("dfbbfae3cd0aff2714a1de7022b0029d"); 1020 b = UInt!256.fromHexString("3fe48f2dc8aad570d037bc9b323fc0cfa312fcc2f63cb521bd8a4ca6157ef619"); 1021 c = UInt!384.fromHexString("37d7034b86e8d58a9fc564463fcedef9e2ad1126dd2c0f803e61c72852a9917ef74fa749e7936a9e4e224aeeaff91f55"); 1022 assert(extendedMul(a, b) == c); 1023 assert(extendedMul(b, a) == c); 1024 1025 a = UInt!128.fromHexString("23edf5ff44ee3a4feafc652607aa1eb9"); 1026 b = UInt!256.fromHexString("d3d79144b8941fb50c9102e3251bacb112c88b71ad3f85a970a31458ce24297b"); 1027 c = UInt!384.fromHexString("1dbb62fe6ca5fed101068eda7222d6a9857633ecdfed37a2d156ff6309065ecc633f31465727677a93a7acbd1dac63e3"); 1028 assert(extendedMul(a, b) == c); 1029 assert(extendedMul(b, a) == c); 1030 } 1031 1032 /// ulong 1033 version(mir_bignum_test) 1034 @safe pure @nogc 1035 unittest 1036 { 1037 ulong a = 0xdfbbfae3cd0aff27; 1038 ulong b = 0x14a1de7022b0029d; 1039 auto c = UInt!128.fromHexString("120827399968ea2a2db185d16e8cc8eb"); 1040 assert(extendedMul(a, b) == c); 1041 assert(extendedMul(b, a) == c); 1042 } 1043 1044 /// uint 1045 version(mir_bignum_test) 1046 @safe pure @nogc 1047 unittest 1048 { 1049 uint a = 0xdfbbfae3; 1050 uint b = 0xcd0aff27; 1051 auto c = UInt!64.fromHexString("b333243de8695595"); 1052 assert(extendedMul(a, b) == c); 1053 assert(extendedMul(b, a) == c); 1054 } 1055 1056 /++ 1057 +/ 1058 UInt!(size + size_t.sizeof * 8) 1059 extendedMulAdd(size_t size)(UInt!size a, size_t b, UInt!size c) 1060 { 1061 auto ret = extendedMul(a, b); 1062 auto view = ret.view; 1063 view.coefficients[$ - 1] += view.topLeastSignificantPart(a.data.length) += c.view; 1064 return ret; 1065 }