1 /++ 2 Low-level betterC utilities for big integer arithmetic libraries. 3 4 The module provides $(REF BigUIntAccumulator), $(REF BigUIntView), and $(LREF BigIntView), $(REF DecimalView). 5 6 Note: 7 The module doesn't provide full arithmetic API for now. 8 +/ 9 module mir.bignum.low_level_view; 10 11 import mir.checkedint; 12 import std.traits; 13 14 version(LDC) import ldc.attributes: optStrategy; 15 else struct optStrategy { string opt; } 16 17 private alias cop(string op : "-") = subu; 18 private alias cop(string op : "+") = addu; 19 private enum inverseSign(string op) = op == "+" ? "-" : "+"; 20 21 package immutable hexStringErrorMsg = "Incorrect hex string for BigUIntView.fromHexString"; 22 version (D_Exceptions) 23 { 24 package immutable hexStringException = new Exception(hexStringErrorMsg); 25 } 26 27 /++ 28 +/ 29 enum WordEndian 30 { 31 /// 32 little, 33 /// 34 big, 35 } 36 37 version(LittleEndian) 38 { 39 /++ 40 +/ 41 enum TargetEndian = WordEndian.little; 42 } 43 else 44 { 45 /++ 46 +/ 47 enum TargetEndian = WordEndian.big; 48 } 49 50 package template MaxWordPow10(T) 51 { 52 static if (is(T == ubyte)) 53 enum MaxWordPow10 = 2; 54 else 55 static if (is(T == ushort)) 56 enum MaxWordPow10 = 4; 57 else 58 static if (is(T == uint)) 59 enum MaxWordPow10 = 9; 60 else 61 static if (is(T == ulong)) 62 enum MaxWordPow10 = 19; 63 else 64 static assert(0); 65 } 66 67 package template MaxWordPow5(T) 68 { 69 static if (is(T == ubyte)) 70 enum MaxWordPow5 = 3; 71 else 72 static if (is(T == ushort)) 73 enum MaxWordPow5 = 6; 74 else 75 static if (is(T == uint)) 76 enum MaxWordPow5 = 13; 77 else 78 static if (is(T == ulong)) 79 enum MaxWordPow5 = 27; 80 else 81 static assert(0); 82 } 83 84 package template MaxFpPow5(T) 85 { 86 static if (T.mant_dig == 24) 87 enum MaxFpPow5 = 6; 88 else 89 static if (T.mant_dig == 53) 90 enum MaxFpPow5 = 10; 91 else 92 static if (T.mant_dig == 64) 93 enum MaxFpPow5 = 27; 94 else 95 static if (T.mant_dig == 113) 96 enum MaxFpPow5 = 48; 97 else 98 static assert(0, "floating point format isn't supported"); 99 } 100 101 /++ 102 Fast integer computation of `ceil(log10(exp2(e)))` with 64-bit mantissa precision. 103 The result is guaranted to be greater then `log10(exp2(e))`, which is irrational number. 104 +/ 105 T ceilLog10Exp2(T)(const T e) 106 @safe pure nothrow @nogc 107 if (is(T == ubyte) || is(T == ushort) || is(T == uint) || is(T == ulong)) 108 { 109 import mir.utility: extMul; 110 auto result = extMul(0x9a209a84fbcff799UL, e); 111 return cast(T) ((result.high >> 1) + ((result.low != 0) | (result.high & 1))); 112 } 113 114 /// 115 version(mir_bignum_test) 116 @safe pure nothrow @nogc unittest 117 { 118 assert(ceilLog10Exp2(ubyte(10)) == 4); // ubyte 119 assert(ceilLog10Exp2(10U) == 4); // uint 120 assert(ceilLog10Exp2(10UL) == 4); // ulong 121 } 122 123 /++ 124 Arbitrary length unsigned integer view. 125 +/ 126 struct BigUIntView(W, WordEndian endian = TargetEndian) 127 if (__traits(isUnsigned, W)) 128 { 129 import mir.bignum.fp: Fp, half; 130 import mir.bignum.fixed: UInt; 131 132 /++ 133 A group of coefficients for a radix `W.max + 1`. 134 135 The order corresponds to endianness. 136 +/ 137 W[] coefficients; 138 139 /++ 140 Retrurns: signed integer view using the same data payload 141 +/ 142 BigIntView!(W, endian) signed()() @safe pure nothrow @nogc scope @property 143 { 144 return typeof(return)(this); 145 } 146 147 /// 148 T opCast(T, bool wordNormalized = false, bool nonZero = false)() scope const 149 if (isFloatingPoint!T && isMutable!T) 150 { 151 import mir.bignum.fp; 152 enum md = T.mant_dig; 153 enum b = size_t.sizeof * 8; 154 enum n = md / b + (md % b != 0); 155 enum s = n * b; 156 return this.opCast!(Fp!s, s - md, wordNormalized, nonZero).opCast!(T, true); 157 } 158 159 static if (W.sizeof == size_t.sizeof && endian == TargetEndian) 160 /// 161 version(mir_bignum_test) 162 unittest 163 { 164 auto a = cast(double) BigUIntView!size_t.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"); 165 assert(a == 0xa.fbbfae3cd0bp+124); 166 assert(cast(double) BigUIntView!size_t.init == 0); 167 assert(cast(double) BigUIntView!size_t([0]) == 0); 168 } 169 170 /// 171 @safe 172 T opCast(T : Fp!coefficientSize, size_t internalRoundLastBits = 0, bool wordNormalized = false, bool nonZero = false, size_t coefficientSize)() scope const 173 if (internalRoundLastBits < size_t.sizeof * 8 && (size_t.sizeof >= W.sizeof || endian == TargetEndian)) 174 { 175 static if (isMutable!W) 176 { 177 return lightConst.opCast!(T, internalRoundLastBits, wordNormalized, nonZero); 178 } 179 else 180 static if (W.sizeof > size_t.sizeof) 181 { 182 return lightConst.opCast!(BigUIntView!size_t).opCast!(T, internalRoundLastBits, false, nonZero); 183 } 184 else 185 { 186 import mir.utility: _expect; 187 import mir.bitop: ctlz; 188 Fp!coefficientSize ret; 189 auto integer = lightConst; 190 static if (!wordNormalized) 191 integer = integer.normalized; 192 static if (!nonZero) 193 if (integer.coefficients.length == 0) 194 goto R; 195 { 196 assert(integer.coefficients.length); 197 enum N = ret.coefficient.data.length; 198 sizediff_t size = integer.coefficients.length * (W.sizeof * 8); 199 sizediff_t expShift = size - coefficientSize; 200 ret.exponent = expShift; 201 if (_expect(expShift <= 0, true)) 202 { 203 static if (N == 1 && W.sizeof == size_t.sizeof) 204 { 205 ret.coefficient.data[0] = integer.mostSignificant; 206 } 207 else 208 { 209 BigUIntView!size_t(ret.coefficient.data) 210 .opCast!(BigUIntView!(Unqual!W)) 211 .leastSignificantFirst 212 [$ - integer.coefficients.length .. $] = integer.leastSignificantFirst; 213 } 214 auto c = cast(uint) ctlz(ret.coefficient.view.mostSignificant); 215 ret.exponent -= c; 216 ret.coefficient = ret.coefficient.smallLeftShift(c); 217 } 218 else 219 { 220 UInt!(coefficientSize + size_t.sizeof * 8) holder; 221 222 223 static if (N == 1 && W.sizeof == size_t.sizeof) 224 { 225 version (BigEndian) 226 { 227 holder.data[0] = integer.mostSignificantFirst[0]; 228 holder.data[1] = integer.mostSignificantFirst[1]; 229 } 230 else 231 { 232 holder.data[0] = integer.mostSignificantFirst[1]; 233 holder.data[1] = integer.mostSignificantFirst[0]; 234 } 235 } 236 else 237 { 238 auto holderView = holder 239 .view 240 .opCast!(BigUIntView!(Unqual!W)) 241 .leastSignificantFirst; 242 import mir.utility: min; 243 auto minLength = min(integer.coefficients.length, holderView.length); 244 holderView[$ - minLength .. $] = integer.leastSignificantFirst[$ - minLength .. $]; 245 } 246 247 auto c = cast(uint) ctlz(holder.view.mostSignificant); 248 ret.exponent -= c; 249 holder = holder.smallLeftShift(c); 250 ret.coefficient = holder.toSize!(coefficientSize, false); 251 auto tail = BigUIntView!size_t(holder.data).leastSignificant; 252 253 bool nonZeroTail() 254 { 255 while(_expect(integer.leastSignificant == 0, false)) 256 { 257 integer.popLeastSignificant; 258 assert(integer.coefficients.length); 259 } 260 return integer.coefficients.length > (N + 1) * (size_t.sizeof / W.sizeof); 261 } 262 263 static if (internalRoundLastBits) 264 { 265 enum half = size_t(1) << (internalRoundLastBits - 1); 266 enum mask0 = (size_t(1) << internalRoundLastBits) - 1; 267 auto tail0 = BigUIntView!size_t(ret.coefficient.data).leastSignificant & mask0; 268 BigUIntView!size_t(ret.coefficient.data).leastSignificant &= ~mask0; 269 auto condInc = tail0 >= half 270 && ( tail0 > half 271 || tail 272 || (BigUIntView!size_t(ret.coefficient.data).leastSignificant & 1) 273 || nonZeroTail); 274 } 275 else 276 { 277 enum half = cast(size_t)Signed!size_t.min; 278 auto condInc = tail >= half 279 && ( tail > half 280 || (BigUIntView!size_t(ret.coefficient.data).leastSignificant & 1) 281 || nonZeroTail); 282 } 283 284 if (condInc) 285 { 286 enum inc = size_t(1) << internalRoundLastBits; 287 if (auto overflow = ret.coefficient += inc) 288 { 289 import mir.bignum.fp: half; 290 ret.coefficient = half!coefficientSize; 291 ret.exponent++; 292 } 293 } 294 } 295 } 296 R: 297 return ret; 298 } 299 } 300 301 static if (W.sizeof == size_t.sizeof && endian == TargetEndian) 302 /// 303 version(mir_bignum_test) 304 @safe pure 305 unittest 306 { 307 import mir.bignum.fp: Fp; 308 import mir.bignum.fixed: UInt; 309 310 auto fp = cast(Fp!128) BigUIntView!ulong.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"); 311 assert(fp.exponent == 0); 312 assert(fp.coefficient == UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d")); 313 314 fp = cast(Fp!128) BigUIntView!uint.fromHexString("ae3cd0aff2714a1de7022b0029d"); 315 assert(fp.exponent == -20); 316 assert(fp.coefficient == UInt!128.fromHexString("ae3cd0aff2714a1de7022b0029d00000")); 317 318 fp = cast(Fp!128) BigUIntView!ushort.fromHexString("e7022b0029d"); 319 assert(fp.exponent == -84); 320 assert(fp.coefficient == UInt!128.fromHexString("e7022b0029d000000000000000000000")); 321 322 fp = cast(Fp!128) BigUIntView!ubyte.fromHexString("e7022b0029d"); 323 assert(fp.exponent == -84); 324 assert(fp.coefficient == UInt!128.fromHexString("e7022b0029d000000000000000000000")); 325 326 fp = cast(Fp!128) BigUIntView!size_t.fromHexString("e7022b0029d"); 327 assert(fp.exponent == -84); 328 assert(fp.coefficient == UInt!128.fromHexString("e7022b0029d000000000000000000000")); 329 330 fp = cast(Fp!128) BigUIntView!size_t.fromHexString("ffffffffffffffffffffffffffffffff1000000000000000"); 331 assert(fp.exponent == 64); 332 assert(fp.coefficient == UInt!128.fromHexString("ffffffffffffffffffffffffffffffff")); 333 334 fp = cast(Fp!128) BigUIntView!size_t.fromHexString("ffffffffffffffffffffffffffffffff8000000000000000"); 335 assert(fp.exponent == 65); 336 assert(fp.coefficient == UInt!128.fromHexString("80000000000000000000000000000000")); 337 338 fp = cast(Fp!128) BigUIntView!size_t.fromHexString("fffffffffffffffffffffffffffffffe8000000000000000"); 339 assert(fp.exponent == 64); 340 assert(fp.coefficient == UInt!128.fromHexString("fffffffffffffffffffffffffffffffe")); 341 342 fp = cast(Fp!128) BigUIntView!size_t.fromHexString("fffffffffffffffffffffffffffffffe8000000000000001"); 343 assert(fp.exponent == 64); 344 assert(fp.coefficient == UInt!128.fromHexString("ffffffffffffffffffffffffffffffff")); 345 } 346 347 348 /// 349 T opCast(T, bool nonZero = false)() const scope 350 if (isIntegral!T && isUnsigned!T && isMutable!T) 351 { 352 auto work = lightConst; 353 static if (!nonZero) 354 { 355 if (coefficients.length == 0) 356 { 357 return 0; 358 } 359 } 360 static if (T.sizeof <= W.sizeof) 361 { 362 return cast(T) work.leastSignificant; 363 } 364 else 365 { 366 T ret; 367 do 368 { 369 ret <<= W.sizeof * 8; 370 ret |= work.mostSignificant; 371 work.popMostSignificant; 372 } 373 while(work.coefficients.length); 374 return ret; 375 } 376 } 377 378 static if (W.sizeof == size_t.sizeof && endian == TargetEndian) 379 /// 380 version(mir_bignum_test) 381 @safe pure 382 unittest 383 { 384 auto view = BigUIntView!ulong.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"); 385 assert(cast(ulong) view == 0x14a1de7022b0029d); 386 assert(cast(uint) view == 0x22b0029d); 387 assert(cast(ubyte) view == 0x9d); 388 } 389 390 static if (W.sizeof == size_t.sizeof && endian == TargetEndian) 391 version(mir_bignum_test) 392 @safe pure 393 unittest 394 { 395 auto view = BigUIntView!ushort.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"); 396 assert(cast(ulong) view == 0x14a1de7022b0029d); 397 assert(cast(uint) view == 0x22b0029d); 398 assert(cast(ubyte) view == 0x9d); 399 } 400 401 static if (W.sizeof == size_t.sizeof && endian == TargetEndian) 402 version(mir_bignum_test) 403 @safe pure 404 unittest 405 { 406 auto view = BigUIntView!uint.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"); 407 assert(cast(ulong) view == 0x14a1de7022b0029d); 408 assert(cast(uint) view == 0x22b0029d); 409 assert(cast(ubyte) view == 0x9d); 410 } 411 412 static if (endian == TargetEndian) 413 /// 414 @trusted pure nothrow @nogc 415 BigUIntView!V opCast(T : BigUIntView!V, V)() scope return 416 if (V.sizeof <= W.sizeof) 417 { 418 return typeof(return)(cast(V[])this.coefficients); 419 } 420 421 /// 422 BigUIntView!(const W, endian) lightConst()() 423 const @safe pure nothrow @nogc @property scope return 424 { 425 return typeof(return)(coefficients); 426 } 427 ///ditto 428 alias lightConst this; 429 430 /++ 431 +/ 432 sizediff_t opCmp(scope BigUIntView!(const W, endian) rhs) 433 const @safe pure nothrow @nogc scope 434 { 435 import mir.algorithm.iteration: cmp; 436 auto l = this.lightConst.normalized; 437 auto r = rhs.lightConst.normalized; 438 if (sizediff_t d = l.coefficients.length - r.coefficients.length) 439 return d; 440 return cmp(l.mostSignificantFirst, r.mostSignificantFirst); 441 } 442 443 /// 444 bool opEquals(scope BigUIntView!(const W, endian) rhs) 445 const @safe pure nothrow @nogc scope 446 { 447 return this.coefficients == rhs.coefficients; 448 } 449 450 /++ 451 +/ 452 ref inout(W) mostSignificant() inout @property scope return 453 { 454 static if (endian == WordEndian.big) 455 return coefficients[0]; 456 else 457 return coefficients[$ - 1]; 458 } 459 460 /++ 461 +/ 462 ref inout(W) leastSignificant() inout @property scope return 463 { 464 static if (endian == WordEndian.little) 465 return coefficients[0]; 466 else 467 return coefficients[$ - 1]; 468 } 469 470 /++ 471 +/ 472 void popMostSignificant() scope 473 { 474 static if (endian == WordEndian.big) 475 coefficients = coefficients[1 .. $]; 476 else 477 coefficients = coefficients[0 .. $ - 1]; 478 } 479 480 /++ 481 +/ 482 void popLeastSignificant() scope 483 { 484 static if (endian == WordEndian.little) 485 coefficients = coefficients[1 .. $]; 486 else 487 coefficients = coefficients[0 .. $ - 1]; 488 } 489 490 /++ 491 +/ 492 BigUIntView topMostSignificantPart(size_t length) scope return 493 { 494 static if (endian == WordEndian.big) 495 return BigUIntView(coefficients[0 .. length]); 496 else 497 return BigUIntView(coefficients[$ - length .. $]); 498 } 499 500 /++ 501 +/ 502 BigUIntView topLeastSignificantPart(size_t length) scope return 503 { 504 static if (endian == WordEndian.little) 505 return BigUIntView(coefficients[0 .. length]); 506 else 507 return BigUIntView(coefficients[$ - length .. $]); 508 } 509 510 /++ 511 Shifts left using at most `size_t.sizeof * 8 - 1` bits 512 +/ 513 void smallLeftShiftInPlace()(uint shift) scope 514 { 515 assert(shift < W.sizeof * 8); 516 if (shift == 0) 517 return; 518 auto csh = W.sizeof * 8 - shift; 519 auto d = leastSignificantFirst; 520 assert(d.length); 521 foreach_reverse (i; 1 .. d.length) 522 d[i] = (d[i] << shift) | (d[i - 1] >>> csh); 523 d.front <<= shift; 524 } 525 526 static if (W.sizeof == size_t.sizeof && endian == TargetEndian) 527 /// 528 version(mir_bignum_test) 529 @safe pure 530 unittest 531 { 532 auto a = BigUIntView!size_t.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"); 533 a.smallLeftShiftInPlace(4); 534 assert(a == BigUIntView!size_t.fromHexString("fbbfae3cd0aff2714a1de7022b0029d0")); 535 a.smallLeftShiftInPlace(0); 536 assert(a == BigUIntView!size_t.fromHexString("fbbfae3cd0aff2714a1de7022b0029d0")); 537 } 538 539 /++ 540 Shifts right using at most `size_t.sizeof * 8 - 1` bits 541 +/ 542 void smallRightShiftInPlace()(uint shift) 543 { 544 assert(shift < W.sizeof * 8); 545 if (shift == 0) 546 return; 547 auto csh = W.sizeof * 8 - shift; 548 auto d = leastSignificantFirst; 549 assert(d.length); 550 foreach (i; 0 .. d.length - 1) 551 d[i] = (d[i] >>> shift) | (d[i + 1] << csh); 552 d.back >>>= shift; 553 } 554 555 static if (W.sizeof == size_t.sizeof && endian == TargetEndian) 556 /// 557 version(mir_bignum_test) 558 @safe pure 559 unittest 560 { 561 auto a = BigUIntView!size_t.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"); 562 a.smallRightShiftInPlace(4); 563 assert(a == BigUIntView!size_t.fromHexString("afbbfae3cd0aff2714a1de7022b0029")); 564 } 565 566 /++ 567 +/ 568 static BigUIntView fromHexString(C, bool allowUnderscores = false)(scope const(C)[] str) 569 @trusted pure 570 if (isSomeChar!C) 571 { 572 auto length = str.length / (W.sizeof * 2) + (str.length % (W.sizeof * 2) != 0); 573 auto data = new Unqual!W[length]; 574 auto view = BigUIntView!(Unqual!W, endian)(data); 575 if (view.fromHexStringImpl!(C, allowUnderscores)(str)) 576 return BigUIntView(cast(W[])view.coefficients); 577 version(D_Exceptions) 578 throw hexStringException; 579 else 580 assert(0, hexStringErrorMsg); 581 } 582 583 static if (isMutable!W) 584 /++ 585 +/ 586 bool fromHexStringImpl(C, bool allowUnderscores = false)(scope const(C)[] str) 587 @safe pure @nogc nothrow scope 588 if (isSomeChar!C) 589 { 590 pragma(inline, false); 591 import mir.utility: _expect; 592 static if (allowUnderscores) { 593 if (_expect(str.length == 0, false)) // can't tell how big the coeff array needs to be, rely on a runtime check 594 return false; 595 } else { 596 if (_expect(str.length == 0 || str.length > coefficients.length * W.sizeof * 2, false)) 597 return false; 598 } 599 600 leastSignificant = 0; 601 auto work = topLeastSignificantPart(1); 602 W current; 603 size_t i, j; 604 static if (allowUnderscores) bool recentUnderscore; 605 606 do 607 { 608 ubyte c; 609 switch(str[$ - ++i]) 610 { 611 case '0': c = 0x0; break; 612 case '1': c = 0x1; break; 613 case '2': c = 0x2; break; 614 case '3': c = 0x3; break; 615 case '4': c = 0x4; break; 616 case '5': c = 0x5; break; 617 case '6': c = 0x6; break; 618 case '7': c = 0x7; break; 619 case '8': c = 0x8; break; 620 case '9': c = 0x9; break; 621 case 'A': 622 case 'a': c = 0xA; break; 623 case 'B': 624 case 'b': c = 0xB; break; 625 case 'C': 626 case 'c': c = 0xC; break; 627 case 'D': 628 case 'd': c = 0xD; break; 629 case 'E': 630 case 'e': c = 0xE; break; 631 case 'F': 632 case 'f': c = 0xF; break; 633 static if (allowUnderscores) 634 { 635 case '_': 636 if (recentUnderscore) return false; 637 recentUnderscore = true; 638 continue; 639 } 640 default: return false; 641 } 642 ++j; 643 static if (allowUnderscores) recentUnderscore = false; 644 // how far do we need to shift to get to the top 4 bits 645 enum s = W.sizeof * 8 - 4; 646 // shift number to the top most 4 bits 647 W cc = cast(W)(W(c) << s); 648 // shift unsigned right 4 bits 649 current >>>= 4; 650 // add number to top most 4 bits of current var 651 current |= cc; 652 if (j % (W.sizeof * 2) == 0) // is this packed var full? 653 { 654 work.mostSignificant = current; 655 current = 0; 656 if (_expect(work.coefficients.length < coefficients.length, true)) 657 { 658 work = topLeastSignificantPart(work.coefficients.length + 1); 659 } 660 else if (i < str.length) // if we've run out of coefficients before reaching the end of the string, error 661 { 662 return false; 663 } 664 } 665 } 666 while(i < str.length); 667 668 static if (allowUnderscores) 669 { 670 // check for a underscore at the beginning or the end 671 if (recentUnderscore || str[$ - 1] == '_') return false; 672 } 673 674 if (current) 675 { 676 current >>>= 4 * (W.sizeof * 2 - j % (W.sizeof * 2)); 677 work.mostSignificant = current; 678 } 679 680 coefficients = coefficients[0 .. (j / (W.sizeof * 2) + (j % (W.sizeof * 2) != 0))]; 681 682 return true; 683 } 684 685 static if (W.sizeof == size_t.sizeof && endian == TargetEndian) 686 /// 687 version(mir_bignum_test) 688 @safe pure 689 unittest 690 { 691 auto view = BigUIntView!size_t.fromHexString!(char, true)("abcd_efab_cdef"); 692 assert(cast(ulong)view == 0xabcd_efab_cdef); 693 } 694 695 static if (W.sizeof == size_t.sizeof && endian == TargetEndian) 696 /// 697 version(mir_bignum_test) 698 @safe pure 699 unittest 700 { 701 // Check that invalid underscores in hex literals throw an error. 702 void expectThrow(const(char)[] input) { 703 bool caught = false; 704 try { 705 auto view = BigUIntView!size_t.fromHexString!(char, true)(input); 706 } catch (Exception e) { 707 caught = true; 708 } 709 710 assert(caught); 711 } 712 713 expectThrow("abcd_efab_cef_"); 714 expectThrow("abcd__efab__cef"); 715 expectThrow("_abcd_efab_cdef"); 716 expectThrow("_abcd_efab_cdef_"); 717 expectThrow("_abcd_efab_cdef__"); 718 expectThrow("__abcd_efab_cdef"); 719 expectThrow("__abcd_efab_cdef_"); 720 expectThrow("__abcd_efab_cdef__"); 721 expectThrow("__abcd__efab_cdef__"); 722 expectThrow("__abcd__efab__cdef__"); 723 } 724 725 static if (isMutable!W && W.sizeof >= 4) 726 /++ 727 Returns: false in case of overflow or incorrect string. 728 Precondition: non-empty coefficients 729 Note: doesn't support signs. 730 +/ 731 bool fromStringImpl(C)(scope const(C)[] str) 732 scope @trusted pure @nogc nothrow 733 if (isSomeChar!C) 734 { 735 import mir.utility: _expect; 736 737 assert(coefficients.length); 738 739 if (_expect(str.length == 0, false)) 740 return false; 741 742 leastSignificant = 0; 743 uint d = str[0] - '0'; 744 str = str[1 .. $]; 745 746 W v; 747 W t = 1; 748 749 if (d == 0) 750 { 751 if (str.length == 0) 752 { 753 coefficients = null; 754 return true; 755 } 756 return false; 757 } 758 else 759 if (d >= 10) 760 return false; 761 762 auto work = topLeastSignificantPart(1); 763 goto S; 764 765 for(;;) 766 { 767 enum mp10 = W(10) ^^ MaxWordPow10!W; 768 d = str[0] - '0'; 769 str = str[1 .. $]; 770 if (_expect(d > 10, false)) 771 break; 772 v *= 10; 773 S: 774 t *= 10; 775 v += d; 776 777 if (_expect(t == mp10 || str.length == 0, false)) 778 { 779 L: 780 if (auto overflow = work.opOpAssign!"*"(t, v)) 781 { 782 if (_expect(work.coefficients.length < coefficients.length, true)) 783 { 784 work = topLeastSignificantPart(work.coefficients.length + 1); 785 work.mostSignificant = overflow; 786 } 787 else 788 { 789 return false; 790 } 791 } 792 v = 0; 793 t = 1; 794 if (str.length == 0) 795 { 796 this = work; 797 return true; 798 } 799 } 800 } 801 return false; 802 } 803 804 static if (isMutable!W && W.sizeof >= 4) 805 /++ 806 Performs `bool overflow = big +(-)= big` operatrion. 807 Params: 808 rhs = value to add with non-empty coefficients 809 overflow = (overflow) initial iteration overflow 810 Precondition: non-empty coefficients length of greater or equal to the `rhs` coefficients length. 811 Returns: 812 true in case of unsigned overflow 813 +/ 814 bool opOpAssign(string op)(scope BigUIntView!(const W, endian) rhs, bool overflow = false) 815 @safe pure nothrow @nogc scope 816 if (op == "+" || op == "-") 817 { 818 assert(this.coefficients.length > 0); 819 assert(rhs.coefficients.length <= this.coefficients.length); 820 auto ls = this.leastSignificantFirst; 821 auto rs = rhs.leastSignificantFirst; 822 do 823 { 824 bool overflowM, overflowG; 825 ls.front = ls.front.cop!op(rs.front, overflowM).cop!op(overflow, overflowG); 826 overflow = overflowG | overflowM; 827 ls.popFront; 828 rs.popFront; 829 } 830 while(rs.length); 831 if (overflow && ls.length) 832 return topMostSignificantPart(ls.length).opOpAssign!op(W(overflow)); 833 return overflow; 834 } 835 836 static if (isMutable!W && W.sizeof >= 4) 837 /// ditto 838 bool opOpAssign(string op)(scope BigIntView!(const W, endian) rhs, bool overflow = false) 839 @safe pure nothrow @nogc scope 840 if (op == "+" || op == "-") 841 { 842 return rhs.sign == false ? 843 opOpAssign!op(rhs.unsigned, overflow): 844 opOpAssign!(inverseSign!op)(rhs.unsigned, overflow); 845 } 846 847 static if (isMutable!W && W.sizeof >= 4) 848 /++ 849 Performs `bool Overflow = big +(-)= scalar` operatrion. 850 Precondition: non-empty coefficients 851 Params: 852 rhs = value to add 853 Returns: 854 true in case of unsigned overflow 855 +/ 856 bool opOpAssign(string op, T)(const T rhs) 857 @safe pure nothrow @nogc scope 858 if ((op == "+" || op == "-") && is(T == W)) 859 { 860 assert(this.coefficients.length > 0); 861 auto ns = this.leastSignificantFirst; 862 W additive = rhs; 863 do 864 { 865 bool overflow; 866 ns.front = ns.front.cop!op(additive, overflow); 867 if (!overflow) 868 return overflow; 869 additive = overflow; 870 ns.popFront; 871 } 872 while (ns.length); 873 return true; 874 } 875 876 static if (isMutable!W && W.sizeof >= 4) 877 /// ditto 878 bool opOpAssign(string op, T)(const T rhs) 879 @safe pure nothrow @nogc scope 880 if ((op == "+" || op == "-") && is(T == Signed!W)) 881 { 882 return rhs >= 0 ? 883 opOpAssign!op(cast(W)rhs): 884 opOpAssign!(inverseSign!op)(cast(W)(-rhs)); 885 } 886 887 static if (isMutable!W && W.sizeof >= 4) 888 /++ 889 Performs `W overflow = (big += overflow) *= scalar` operatrion. 890 Precondition: non-empty coefficients 891 Params: 892 rhs = unsigned value to multiply by 893 overflow = initial overflow 894 Returns: 895 unsigned overflow value 896 +/ 897 W opOpAssign(string op : "*")(W rhs, W overflow = 0u) 898 @safe pure nothrow @nogc scope 899 { 900 assert(coefficients.length); 901 auto ns = this.leastSignificantFirst; 902 do 903 { 904 import mir.utility: extMul; 905 auto ext = ns.front.extMul(rhs); 906 bool overflowM; 907 ns.front = ext.low.cop!"+"(overflow, overflowM); 908 overflow = ext.high + overflowM; 909 ns.popFront; 910 } 911 while (ns.length); 912 return overflow; 913 } 914 915 static if (isMutable!W && W.sizeof == 4 || W.sizeof == 8 && endian == TargetEndian) 916 /++ 917 Performs `uint remainder = (overflow$big) /= scalar` operatrion, where `$` denotes big-endian concatenation. 918 Precondition: non-empty coefficients, `overflow < rhs` 919 Params: 920 rhs = unsigned value to devide by 921 overflow = initial unsigned overflow 922 Returns: 923 unsigned remainder value (evaluated overflow) 924 +/ 925 uint opOpAssign(string op : "/")(uint rhs, uint overflow = 0) 926 @safe pure nothrow @nogc scope 927 { 928 assert(overflow < rhs); 929 assert(coefficients.length); 930 static if (W.sizeof == 4) 931 { 932 auto ns = this.mostSignificantFirst; 933 size_t i; 934 do 935 { 936 auto ext = (ulong(overflow) << 32) ^ ns[i]; 937 ns[i] = cast(uint)(ext / rhs); 938 overflow = ext % rhs; 939 } 940 while (++i < ns.length); 941 if (mostSignificant == 0) 942 popMostSignificant; 943 return overflow; 944 } 945 else 946 { 947 auto work = opCast!(BigUIntView!uint); 948 if (work.mostSignificant == 0) 949 work.popMostSignificant; 950 auto remainder = work.opOpAssign!op(rhs, overflow); 951 coefficients = coefficients[0 .. work.coefficients.length / 2 + work.coefficients.length % 2]; 952 return remainder; 953 } 954 } 955 956 static if (isMutable!W && W.sizeof == size_t.sizeof) 957 /++ 958 Performs `W overflow = (big += overflow) *= scalar` operatrion. 959 Precondition: non-empty coefficients 960 Params: 961 rhs = unsigned fixed-length integer to multiply by 962 overflow = initial overflow 963 Returns: 964 unsigned fixed-length integer overflow value 965 +/ 966 UInt!size 967 opOpAssign(string op : "*", size_t size)(UInt!size rhs, UInt!size overflow = 0) 968 @safe pure nothrow @nogc scope 969 { 970 assert(coefficients.length); 971 auto ns = this.leastSignificantFirst; 972 do 973 { 974 auto t = rhs; 975 auto overflowW = t.view *= ns.front; 976 auto overflowM = t += overflow; 977 overflowW += overflowM; 978 ns.front = cast(size_t) t; 979 static if (size > size_t.sizeof * 8) 980 overflow = t.toSize!(size - size_t.sizeof * 8, false).toSize!size; 981 BigUIntView!size_t(overflow.data).mostSignificant = overflowW; 982 ns.popFront; 983 } 984 while (ns.length); 985 return overflow; 986 } 987 988 /++ 989 Returns: the same intger view with inversed sign 990 +/ 991 BigIntView!(W, endian) opUnary(string op : "-")() scope return 992 { 993 return typeof(return)(this, true); 994 } 995 996 static if (isMutable!W && W.sizeof >= 4) 997 /++ 998 +/ 999 void bitwiseNotInPlace() scope 1000 { 1001 foreach (ref coefficient; this.coefficients) 1002 coefficient = cast(W)~(0 + coefficient); 1003 } 1004 1005 static if (isMutable!W && W.sizeof >= 4) 1006 /++ 1007 Performs `number=-number` operatrion. 1008 Precondition: non-empty coefficients 1009 Returns: 1010 true if 'number=-number=0' and false otherwise 1011 +/ 1012 bool twoComplementInPlace() scope 1013 { 1014 assert(coefficients.length); 1015 bitwiseNotInPlace(); 1016 return this.opOpAssign!"+"(W(1)); 1017 } 1018 1019 /++ 1020 Returns: a slice of coefficients starting from the least significant. 1021 +/ 1022 auto leastSignificantFirst() 1023 @safe pure nothrow @nogc @property scope return 1024 { 1025 import mir.ndslice.slice: sliced; 1026 static if (endian == WordEndian.little) 1027 { 1028 return coefficients.sliced; 1029 } 1030 else 1031 { 1032 import mir.ndslice.topology: retro; 1033 return coefficients.sliced.retro; 1034 } 1035 } 1036 1037 /// 1038 auto leastSignificantFirst() 1039 const @safe pure nothrow @nogc @property scope return 1040 { 1041 import mir.ndslice.slice: sliced; 1042 static if (endian == WordEndian.little) 1043 { 1044 return coefficients.sliced; 1045 } 1046 else 1047 { 1048 import mir.ndslice.topology: retro; 1049 return coefficients.sliced.retro; 1050 } 1051 } 1052 1053 /++ 1054 Returns: a slice of coefficients starting from the most significant. 1055 +/ 1056 auto mostSignificantFirst() 1057 @safe pure nothrow @nogc @property scope return 1058 { 1059 import mir.ndslice.slice: sliced; 1060 static if (endian == WordEndian.big) 1061 { 1062 return coefficients.sliced; 1063 } 1064 else 1065 { 1066 import mir.ndslice.topology: retro; 1067 return coefficients.sliced.retro; 1068 } 1069 } 1070 1071 /// 1072 auto mostSignificantFirst() 1073 const @safe pure nothrow @nogc @property scope return 1074 { 1075 import mir.ndslice.slice: sliced; 1076 static if (endian == WordEndian.big) 1077 { 1078 return coefficients.sliced; 1079 } 1080 else 1081 { 1082 import mir.ndslice.topology: retro; 1083 return coefficients.sliced.retro; 1084 } 1085 } 1086 1087 /++ 1088 Strips most significant zero coefficients. 1089 +/ 1090 BigUIntView normalized() scope return 1091 { 1092 auto number = this; 1093 if (number.coefficients.length) do 1094 { 1095 static if (endian == WordEndian.big) 1096 { 1097 if (number.coefficients[0]) 1098 break; 1099 number.coefficients = number.coefficients[1 .. $]; 1100 } 1101 else 1102 { 1103 if (number.coefficients[$ - 1]) 1104 break; 1105 number.coefficients = number.coefficients[0 .. $ - 1]; 1106 } 1107 } 1108 while (number.coefficients.length); 1109 return number; 1110 } 1111 1112 ///ditto 1113 BigUIntView!(const W, endian) normalized() scope const 1114 { 1115 return lightConst.normalized; 1116 } 1117 1118 /++ 1119 +/ 1120 bool bt()(size_t position) scope 1121 { 1122 import mir.ndslice.topology: bitwise; 1123 assert(position < coefficients.length * W.sizeof * 8); 1124 return leastSignificantFirst.bitwise[position]; 1125 } 1126 1127 /++ 1128 +/ 1129 size_t ctlz()() scope const @property 1130 @safe pure nothrow @nogc 1131 { 1132 import mir.bitop: ctlz; 1133 assert(coefficients.length); 1134 auto d = mostSignificantFirst; 1135 size_t ret; 1136 do 1137 { 1138 if (auto c = d.front) 1139 { 1140 ret += ctlz(c); 1141 break; 1142 } 1143 ret += W.sizeof * 8; 1144 d.popFront; 1145 } 1146 while(d.length); 1147 return ret; 1148 } 1149 1150 /++ 1151 +/ 1152 size_t cttz()() scope const @property 1153 @safe pure nothrow @nogc 1154 { 1155 import mir.bitop: cttz; 1156 assert(coefficients.length); 1157 auto d = leastSignificantFirst; 1158 size_t ret; 1159 do 1160 { 1161 if (auto c = d.front) 1162 { 1163 ret += cttz(c); 1164 break; 1165 } 1166 ret += W.sizeof * 8; 1167 d.popFront; 1168 } 1169 while(d.length); 1170 return ret; 1171 } 1172 1173 /// 1174 BigIntView!(W, endian) withSign()(bool sign) 1175 { 1176 return typeof(return)(this, sign); 1177 } 1178 1179 /++ 1180 Params: 1181 value = (out) unsigned integer 1182 Returns: true on success 1183 +/ 1184 bool get(U)(scope out U value) 1185 @safe pure nothrow @nogc scope const 1186 if (isUnsigned!U) 1187 { 1188 auto d = lightConst.mostSignificantFirst; 1189 if (d.length == 0) 1190 return false; 1191 static if (U.sizeof > W.sizeof) 1192 { 1193 size_t i; 1194 for(;;) 1195 { 1196 value |= d[0]; 1197 d = d[1 .. $]; 1198 if (d.length == 0) 1199 return false; 1200 i += cast(bool)value; 1201 value <<= W.sizeof * 8; 1202 import mir.utility: _expect; 1203 if (_expect(i >= U.sizeof / W.sizeof, false)) 1204 return true; 1205 } 1206 } 1207 else 1208 { 1209 for(;;) 1210 { 1211 W f = d[0]; 1212 d = d[1 .. $]; 1213 if (d.length == 0) 1214 { 1215 value = cast(U)f; 1216 static if (U.sizeof < W.sizeof) 1217 { 1218 if (value != f) 1219 return true; 1220 } 1221 return false; 1222 } 1223 if (f) 1224 return true; 1225 } 1226 } 1227 } 1228 1229 /++ 1230 Returns: true if the integer and equals to `rhs`. 1231 +/ 1232 bool opEquals(ulong rhs) 1233 @safe pure nothrow @nogc const scope 1234 { 1235 foreach (d; lightConst.leastSignificantFirst) 1236 { 1237 static if (W.sizeof >= ulong.sizeof) 1238 { 1239 if (d != rhs) 1240 return false; 1241 rhs = 0; 1242 } 1243 else 1244 { 1245 if (d != (rhs & W.max)) 1246 return false; 1247 rhs >>>= W.sizeof * 8; 1248 } 1249 } 1250 return rhs == 0; 1251 } 1252 1253 static if (W.sizeof == size_t.sizeof && endian == TargetEndian) 1254 /// 1255 version(mir_bignum_test) 1256 @safe pure 1257 unittest 1258 { 1259 auto view2 = BigUIntView!(const(ubyte), WordEndian.big)([1, 0]); 1260 assert(view2 == 256); // false 1261 assert(cast(ulong)view2 == 256); // true 1262 auto view = BigUIntView!(const(ubyte), WordEndian.big)([15, 255, 255]); 1263 assert(view == 1048575); // false 1264 assert(cast(ulong)view == 1048575); // true 1265 } 1266 1267 static if (isMutable!W && W.sizeof >= 4) 1268 /++ 1269 Params: 1270 str = string buffer, the tail paer 1271 Precondition: mutable number with word size at least 4 bytes 1272 Postconditoin: the number is destroyed 1273 Returns: last N bytes used in the buffer 1274 +/ 1275 size_t toStringImpl(C)(scope C[] str) 1276 @safe pure nothrow @nogc 1277 if (isSomeChar!C && isMutable!C) 1278 { 1279 assert(str.length); 1280 assert(str.length >= ceilLog10Exp2(coefficients.length * (W.sizeof * 8))); 1281 1282 size_t i = str.length; 1283 while(coefficients.length > 1) 1284 { 1285 uint rem = this /= 1_000_000_000; 1286 foreach (_; 0 .. 9) 1287 { 1288 str[--i] = cast(char)(rem % 10 + '0'); 1289 rem /= 10; 1290 } 1291 } 1292 1293 W rem = coefficients.length == 1 ? coefficients[0] : W(0); 1294 do 1295 { 1296 str[--i] = cast(char)(rem % 10 + '0'); 1297 rem /= 10; 1298 } 1299 while(rem); 1300 1301 return str.length - i; 1302 } 1303 1304 static if (W.sizeof == size_t.sizeof && endian == TargetEndian) 1305 /// 1306 version(mir_bignum_test) 1307 @safe pure @nogc 1308 unittest 1309 { 1310 import mir.bignum.integer; 1311 1312 auto a = BigInt!2("123456789098765432123456789098765432100"); 1313 char[ceilLog10Exp2(a.data.length * (size_t.sizeof * 8))] buffer; 1314 auto len = a.view.unsigned.toStringImpl(buffer); 1315 assert(buffer[$ - len .. $] == "123456789098765432123456789098765432100"); 1316 } 1317 } 1318 1319 /// 1320 version(mir_bignum_test) 1321 @safe pure nothrow 1322 unittest 1323 { 1324 import std.traits; 1325 alias AliasSeq(T...) = T; 1326 1327 foreach (T; AliasSeq!(ubyte, ushort, uint, ulong)) 1328 foreach (endian; AliasSeq!(WordEndian.little, WordEndian.big)) 1329 { 1330 static if (endian == WordEndian.little) 1331 { 1332 T[3] lhsData = [1, T.max-1, 0]; 1333 T[3] rhsData = [T.max, T.max, 0]; 1334 } 1335 else 1336 { 1337 T[3] lhsData = [0, T.max-1, 1]; 1338 T[3] rhsData = [0, T.max, T.max]; 1339 } 1340 1341 auto lhs = BigUIntView!(T, endian)(lhsData).normalized; 1342 1343 /// bool overflow = bigUInt op= scalar 1344 assert(lhs.leastSignificantFirst == [1, T.max-1]); 1345 assert(lhs.mostSignificantFirst == [T.max-1, 1]); 1346 static if (T.sizeof >= 4) 1347 { 1348 assert((lhs += T.max) == false); 1349 assert(lhs.leastSignificantFirst == [0, T.max]); 1350 assert((lhs += T.max) == false); 1351 assert((lhs += T.max) == true); // overflow bit 1352 assert(lhs.leastSignificantFirst == [T.max-1, 0]); 1353 assert((lhs -= T(1)) == false); 1354 assert(lhs.leastSignificantFirst == [T.max-2, 0]); 1355 assert((lhs -= T.max) == true); // underflow bit 1356 assert(lhs.leastSignificantFirst == [T.max-1, T.max]); 1357 assert((lhs -= Signed!T(-4)) == true); // overflow bit 1358 assert(lhs.leastSignificantFirst == [2, 0]); 1359 assert((lhs += Signed!T.max) == false); // overflow bit 1360 assert(lhs.leastSignificantFirst == [Signed!T.max + 2, 0]); 1361 1362 /// bool overflow = bigUInt op= bigUInt/bigInt 1363 lhs = BigUIntView!(T, endian)(lhsData); 1364 auto rhs = BigUIntView!(T, endian)(rhsData).normalized; 1365 assert(lhs.leastSignificantFirst == [Signed!T.max + 2, 0, 0]); 1366 assert(rhs.leastSignificantFirst == [T.max, T.max]); 1367 assert((lhs += rhs) == false); 1368 assert(lhs.leastSignificantFirst == [Signed!T.max + 1, 0, 1]); 1369 assert((lhs -= rhs) == false); 1370 assert(lhs.leastSignificantFirst == [Signed!T.max + 2, 0, 0]); 1371 assert((lhs += -rhs) == true); 1372 assert(lhs.leastSignificantFirst == [Signed!T.max + 3, 0, T.max]); 1373 assert((lhs += -(-rhs)) == true); 1374 assert(lhs.leastSignificantFirst == [Signed!T.max + 2, 0, 0]); 1375 1376 /// W overflow = bigUInt *= scalar 1377 assert((lhs *= T.max) == 0); 1378 assert((lhs += T(Signed!T.max + 2)) == false); 1379 assert(lhs.leastSignificantFirst == [0, Signed!T.max + 2, 0]); 1380 lhs = lhs.normalized; 1381 lhs.leastSignificantFirst[1] = T.max / 2 + 3; 1382 assert(lhs.leastSignificantFirst == [0, T.max / 2 + 3]); 1383 assert((lhs *= 8u) == 4); 1384 assert(lhs.leastSignificantFirst == [0, 16]); 1385 } 1386 } 1387 } 1388 1389 /++ 1390 Arbitrary length signed integer view. 1391 +/ 1392 struct BigIntView(W, WordEndian endian = TargetEndian) 1393 if (is(Unqual!W == ubyte) || is(Unqual!W == ushort) || is(Unqual!W == uint) || is(Unqual!W == ulong)) 1394 { 1395 import mir.bignum.fp: Fp; 1396 1397 /++ 1398 Self-assigned to unsigned integer view $(MREF BigUIntView). 1399 1400 Sign is stored in the most significant bit. 1401 1402 The number is encoded as pair of `unsigned` and `sign`. 1403 +/ 1404 BigUIntView!(W, endian) unsigned; 1405 1406 /++ 1407 Sign bit 1408 +/ 1409 bool sign; 1410 1411 /// 1412 inout(W)[] coefficients() inout @property scope return 1413 { 1414 return unsigned.coefficients; 1415 } 1416 1417 /// 1418 this(W[] coefficients, bool sign = false) 1419 { 1420 this(BigUIntView!(W, endian)(coefficients), sign); 1421 } 1422 1423 /// 1424 this(BigUIntView!(W, endian) unsigned, bool sign = false) 1425 { 1426 this.unsigned = unsigned; 1427 this.sign = sign; 1428 } 1429 1430 static if (isMutable!W && W.sizeof >= 4) 1431 /++ 1432 Returns: false in case of overflow or incorrect string. 1433 Precondition: non-empty coefficients. 1434 +/ 1435 bool fromStringImpl(C)(scope const(C)[] str) 1436 scope @trusted pure @nogc nothrow 1437 if (isSomeChar!C) 1438 { 1439 import mir.utility: _expect; 1440 1441 if (_expect(str.length == 0, false)) 1442 return false; 1443 1444 if (str[0] == '-') 1445 { 1446 sign = true; 1447 str = str[1 .. $]; 1448 } 1449 else 1450 if (_expect(str[0] == '+', false)) 1451 { 1452 str = str[1 .. $]; 1453 } 1454 1455 return unsigned.fromStringImpl(str); 1456 } 1457 1458 /++ 1459 +/ 1460 static BigIntView fromHexString(C, bool allowUnderscores = false)(scope const(C)[] str) 1461 @trusted pure 1462 if (isSomeChar!C) 1463 { 1464 auto length = str.length / (W.sizeof * 2) + (str.length % (W.sizeof * 2) != 0); 1465 auto ret = BigIntView!(Unqual!W, endian)(new Unqual!W[length]); 1466 if (ret.fromHexStringImpl!(C, allowUnderscores)(str)) 1467 return cast(BigIntView) ret; 1468 version(D_Exceptions) 1469 throw hexStringException; 1470 else 1471 assert(0, hexStringErrorMsg); 1472 } 1473 1474 static if (isMutable!W) 1475 /++ 1476 +/ 1477 bool fromHexStringImpl(C, bool allowUnderscores = false)(scope const(C)[] str) 1478 @safe pure @nogc nothrow 1479 if (isSomeChar!C) 1480 { 1481 pragma(inline, false); 1482 import mir.utility: _expect; 1483 1484 assert(unsigned.coefficients.length); 1485 1486 if (_expect(str.length == 0, false)) 1487 return false; 1488 1489 sign = false; 1490 1491 if (str[0] == '-') 1492 { 1493 sign = true; 1494 str = str[1 .. $]; 1495 } 1496 else 1497 if (_expect(str[0] == '+', false)) 1498 { 1499 str = str[1 .. $]; 1500 } 1501 1502 return unsigned.fromHexStringImpl!(C, allowUnderscores)(str); 1503 } 1504 1505 /// 1506 T opCast(T, bool wordNormalized = false, bool nonZero = false)() scope const 1507 if (isFloatingPoint!T && isMutable!T) 1508 { 1509 auto ret = this.unsigned.opCast!(T, wordNormalized, nonZero); 1510 if (sign) 1511 ret = -ret; 1512 return ret; 1513 } 1514 1515 static if (W.sizeof == size_t.sizeof && endian == TargetEndian) 1516 /// 1517 version(mir_bignum_test) 1518 @safe pure 1519 unittest 1520 { 1521 auto a = cast(double) BigIntView!size_t.fromHexString("-afbbfae3cd0aff2714a1de7022b0029d"); 1522 assert(a == -0xa.fbbfae3cd0bp+124); 1523 } 1524 1525 static if (W.sizeof == size_t.sizeof && endian == TargetEndian) 1526 /// 1527 version(mir_bignum_test) 1528 @safe pure 1529 unittest 1530 { 1531 auto a = cast(double) BigIntView!size_t.fromHexString!(char, true)("-afbb_fae3_cd0a_ff27_14a1_de70_22b0_029d"); 1532 assert(a == -0xa.fbbfae3cd0bp+124); 1533 } 1534 1535 /// 1536 T opCast(T, bool nonZero = false)() scope const 1537 if (is(T == long) || is(T == int)) 1538 { 1539 auto ret = this.unsigned.opCast!(Unsigned!T, nonZero); 1540 if (sign) 1541 ret = -ret; 1542 return ret; 1543 } 1544 1545 static if (W.sizeof == size_t.sizeof && endian == TargetEndian) 1546 /// 1547 version(mir_bignum_test) 1548 @safe pure 1549 unittest 1550 { 1551 auto view = BigIntView!size_t.fromHexString("-afbbfae3cd0aff2714a1de7022b0021d"); 1552 assert(cast(long) view == -0x14a1de7022b0021d); 1553 assert(cast(int) view == -0x22b0021d); 1554 } 1555 1556 static if (W.sizeof == size_t.sizeof && endian == TargetEndian) 1557 /// 1558 version(mir_bignum_test) 1559 @safe pure 1560 unittest 1561 { 1562 auto view = BigIntView!size_t.fromHexString!(char, true)("-afbb_fae3_cd0a_ff27_14a1_de70_22b0_021d"); 1563 assert(cast(long) view == -0x14a1de7022b0021d); 1564 assert(cast(int) view == -0x22b0021d); 1565 } 1566 1567 static if (W.sizeof == size_t.sizeof && endian == TargetEndian) 1568 version(mir_bignum_test) 1569 @safe pure 1570 unittest 1571 { 1572 auto view = BigIntView!ushort.fromHexString("-afbbfae3cd0aff2714a1de7022b0021d"); 1573 assert(cast(long) view == -0x14a1de7022b0021d); 1574 assert(cast(int) view == -0x22b0021d); 1575 } 1576 1577 static if (W.sizeof == size_t.sizeof && endian == TargetEndian) 1578 version(mir_bignum_test) 1579 @safe pure 1580 unittest 1581 { 1582 auto view = BigIntView!ushort.fromHexString!(char, true)("-afbb_fae3_cd0a_ff27_14a1_de70_22b0_021d"); 1583 assert(cast(long) view == -0x14a1de7022b0021d); 1584 assert(cast(int) view == -0x22b0021d); 1585 } 1586 1587 static if (W.sizeof == size_t.sizeof && endian == TargetEndian) 1588 version(mir_bignum_test) 1589 @safe pure 1590 unittest 1591 { 1592 auto view = BigIntView!ubyte.fromHexString("-afbbfae3cd0aff2714a1de7022b0021d"); 1593 assert(cast(long) view == -0x14a1de7022b0021d); 1594 assert(cast(int) view == -0x22b0021d); 1595 } 1596 1597 static if (W.sizeof == size_t.sizeof && endian == TargetEndian) 1598 version(mir_bignum_test) 1599 @safe pure 1600 unittest 1601 { 1602 auto view = BigIntView!ubyte.fromHexString!(char, true)("-afbb_fae3_cd0a_ff27_14a1_de70_22b0_021d"); 1603 assert(cast(long) view == -0x14a1de7022b0021d); 1604 assert(cast(int) view == -0x22b0021d); 1605 } 1606 1607 /++ 1608 +/ 1609 T opCast(T : Fp!coefficientSize, size_t internalRoundLastBits = 0, bool wordNormalized = false, bool nonZero = false, size_t coefficientSize)() scope const 1610 if (internalRoundLastBits < size_t.sizeof * 8 && (size_t.sizeof >= W.sizeof || endian == TargetEndian)) 1611 { 1612 auto ret = unsigned.opCast!(Fp!coefficientSize, internalRoundLastBits, wordNormalized, nonZero); 1613 ret.sign = sign; 1614 return ret; 1615 } 1616 1617 static if (W.sizeof == size_t.sizeof && endian == TargetEndian) 1618 /// 1619 version(mir_bignum_test) 1620 @safe pure 1621 unittest 1622 { 1623 import mir.bignum.fixed: UInt; 1624 import mir.bignum.fp: Fp; 1625 1626 auto fp = cast(Fp!128) BigIntView!size_t.fromHexString("-afbbfae3cd0aff2714a1de7022b0029d"); 1627 assert(fp.sign); 1628 assert(fp.exponent == 0); 1629 assert(fp.coefficient == UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d")); 1630 } 1631 1632 static if (W.sizeof == size_t.sizeof && endian == TargetEndian) 1633 version(mir_bignum_test) 1634 @safe pure 1635 unittest 1636 { 1637 import mir.bignum.fixed: UInt; 1638 import mir.bignum.fp: Fp; 1639 1640 auto fp = cast(Fp!128) BigIntView!size_t.fromHexString!(char, true)("-afbb_fae3_cd0a_ff27_14a1_de70_22b0_029d"); 1641 assert(fp.sign); 1642 assert(fp.exponent == 0); 1643 assert(fp.coefficient == UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d")); 1644 } 1645 1646 static if (endian == TargetEndian) 1647 /// 1648 BigIntView!V opCast(T : BigIntView!V, V)() scope return 1649 if (V.sizeof <= W.sizeof) 1650 { 1651 return typeof(return)(this.unsigned.opCast!(BigUIntView!V), sign); 1652 } 1653 1654 /// 1655 BigIntView!(const W, endian) lightConst()() scope return 1656 const @safe pure nothrow @nogc @property 1657 { 1658 return typeof(return)(unsigned.lightConst, sign); 1659 } 1660 1661 ///ditto 1662 alias lightConst this; 1663 1664 /++ 1665 +/ 1666 sizediff_t opCmp(BigIntView!(const W, endian) rhs) 1667 const @safe pure nothrow @nogc scope 1668 { 1669 import mir.algorithm.iteration: cmp; 1670 if (auto s = rhs.sign - this.sign) 1671 { 1672 if (this.unsigned.coefficients.length && rhs.unsigned.coefficients.length) 1673 return s; 1674 } 1675 auto d = this.unsigned.opCmp(rhs.unsigned); 1676 return sign ? -d : d; 1677 } 1678 1679 /// 1680 bool opEquals(BigIntView!(const W, endian) rhs) 1681 const @safe pure nothrow @nogc scope 1682 { 1683 return (this.sign == rhs.sign || unsigned.coefficients.length == 0) && this.unsigned == rhs.unsigned; 1684 } 1685 1686 /++ 1687 Returns: true if the integer and equals to `rhs`. 1688 +/ 1689 bool opEquals(long rhs) 1690 @safe pure nothrow @nogc const scope 1691 { 1692 if (rhs == 0 && unsigned.coefficients.length == 0) 1693 return true; 1694 bool sign = rhs < 0; 1695 ulong urhs = sign ? -rhs : rhs; 1696 return sign == this.sign && unsigned == urhs; 1697 } 1698 1699 /++ 1700 +/ 1701 BigIntView topMostSignificantPart(size_t length) 1702 { 1703 return BigIntView(unsigned.topMostSignificantPart(length), sign); 1704 } 1705 1706 /++ 1707 +/ 1708 BigIntView topLeastSignificantPart(size_t length) 1709 { 1710 return BigIntView(unsigned.topLeastSignificantPart(length), sign); 1711 } 1712 1713 static if (isMutable!W && W.sizeof >= 4) 1714 /++ 1715 Performs `bool overflow = big +(-)= big` operatrion. 1716 Params: 1717 rhs = value to add with non-empty coefficients 1718 overflow = (overflow) initial iteration overflow 1719 Precondition: non-empty coefficients length of greater or equal to the `rhs` coefficients length. 1720 Returns: 1721 true in case of unsigned overflow 1722 +/ 1723 bool opOpAssign(string op)(scope BigIntView!(const W, endian) rhs, bool overflow = false) 1724 @safe pure nothrow @nogc 1725 if (op == "+" || op == "-") 1726 { 1727 assert(rhs.coefficients.length > 0); 1728 import mir.conv; 1729 debug assert(this.coefficients.length >= rhs.coefficients.length, this.coefficients.length.to!string ~ " " ~ rhs.coefficients.length.to!string); 1730 enum sum = op == "+"; 1731 // pos += pos 1732 // neg += neg 1733 // neg -= pos 1734 // pos -= neg 1735 if ((sign == rhs.sign) == sum) 1736 return unsigned.opOpAssign!"+"(rhs.unsigned, overflow); 1737 // pos -= pos 1738 // pos += neg 1739 // neg += pos 1740 // neg -= neg 1741 if (unsigned.opOpAssign!"-"(rhs.unsigned, overflow)) 1742 { 1743 sign = !sign; 1744 unsigned.twoComplementInPlace; 1745 } 1746 return false; 1747 } 1748 1749 static if (isMutable!W && W.sizeof >= 4) 1750 /// ditto 1751 bool opOpAssign(string op)(scope BigUIntView!(const W, endian) rhs, bool overflow = false) 1752 @safe pure nothrow @nogc 1753 if (op == "+" || op == "-") 1754 { 1755 return opOpAssign!op(rhs.signed, overflow); 1756 } 1757 1758 static if (isMutable!W && W.sizeof >= 4) 1759 /++ 1760 Performs `bool overflow = big +(-)= scalar` operatrion. 1761 Precondition: non-empty coefficients 1762 Params: 1763 rhs = value to add 1764 Returns: 1765 true in case of unsigned overflow 1766 +/ 1767 bool opOpAssign(string op, T)(const T rhs) 1768 @safe pure nothrow @nogc 1769 if ((op == "+" || op == "-") && is(T == Signed!W)) 1770 { 1771 assert(this.coefficients.length > 0); 1772 enum sum = op == "+"; 1773 // pos += pos 1774 // neg += neg 1775 // neg -= pos 1776 // pos -= neg 1777 auto urhs = cast(W) (rhs < 0 ? -rhs : rhs); 1778 if ((sign == (rhs < 0)) == sum) 1779 return unsigned.opOpAssign!"+"(urhs); 1780 // pos -= pos 1781 // pos += neg 1782 // neg += pos 1783 // neg -= neg 1784 if (unsigned.opOpAssign!"-"(urhs)) 1785 { 1786 sign = !sign; 1787 unsigned.twoComplementInPlace; 1788 } 1789 return false; 1790 } 1791 1792 static if (isMutable!W && W.sizeof >= 4) 1793 /// ditto 1794 bool opOpAssign(string op, T)(const T rhs) 1795 @safe pure nothrow @nogc 1796 if ((op == "+" || op == "-") && is(T == W)) 1797 { 1798 assert(this.coefficients.length > 0); 1799 enum sum = op == "+"; 1800 // pos += pos 1801 // neg -= pos 1802 if ((sign == false) == sum) 1803 return unsigned.opOpAssign!"+"(rhs); 1804 // pos -= pos 1805 // neg += pos 1806 if (unsigned.opOpAssign!"-"(rhs)) 1807 { 1808 sign = !sign; 1809 unsigned.twoComplementInPlace; 1810 } 1811 return false; 1812 } 1813 1814 static if (isMutable!W && W.sizeof >= 4) 1815 /++ 1816 Performs `W overflow = (big += overflow) *= scalar` operatrion. 1817 Precondition: non-empty coefficients 1818 Params: 1819 rhs = unsigned value to multiply by 1820 overflow = initial overflow 1821 Returns: 1822 unsigned overflow value 1823 +/ 1824 W opOpAssign(string op : "*")(W rhs, W overflow = 0u) 1825 @safe pure nothrow @nogc 1826 { 1827 return unsigned.opOpAssign!op(rhs, overflow); 1828 } 1829 1830 /++ 1831 Returns: the same intger view with inversed sign 1832 +/ 1833 BigIntView opUnary(string op : "-")() 1834 { 1835 return BigIntView(unsigned, !sign); 1836 } 1837 1838 /++ 1839 Returns: a slice of coefficients starting from the least significant. 1840 +/ 1841 auto leastSignificantFirst() 1842 @safe pure nothrow @nogc @property 1843 { 1844 return unsigned.leastSignificantFirst; 1845 } 1846 1847 /++ 1848 Returns: a slice of coefficients starting from the most significant. 1849 +/ 1850 auto mostSignificantFirst() 1851 @safe pure nothrow @nogc @property 1852 { 1853 return unsigned.mostSignificantFirst; 1854 } 1855 1856 /++ 1857 Strips zero most significant coefficients. 1858 Strips most significant zero coefficients. 1859 Sets sign to zero if no coefficients were left. 1860 +/ 1861 BigIntView normalized() 1862 { 1863 auto number = this; 1864 number.unsigned = number.unsigned.normalized; 1865 number.sign = number.coefficients.length == 0 ? false : number.sign; 1866 return number; 1867 } 1868 1869 ///ditto 1870 BigIntView!(const W, endian) normalized() const 1871 { 1872 return lightConst.normalized; 1873 } 1874 } 1875 1876 /// 1877 version(mir_bignum_test) 1878 @safe pure nothrow 1879 unittest 1880 { 1881 import std.traits; 1882 alias AliasSeq(T...) = T; 1883 1884 foreach (T; AliasSeq!(ubyte, ushort, uint, ulong)) 1885 foreach (endian; AliasSeq!(WordEndian.little, WordEndian.big)) 1886 { 1887 static if (endian == WordEndian.little) 1888 { 1889 T[3] lhsData = [1, T.max-1, 0]; 1890 T[3] rhsData = [T.max, T.max, 0]; 1891 } 1892 else 1893 { 1894 T[3] lhsData = [0, T.max-1, 1]; 1895 T[3] rhsData = [0, T.max, T.max]; 1896 } 1897 1898 auto lhs = BigIntView!(T, endian)(lhsData).normalized; 1899 1900 /// bool overflow = bigUInt op= scalar 1901 assert(lhs.leastSignificantFirst == [1, T.max-1]); 1902 assert(lhs.mostSignificantFirst == [T.max-1, 1]); 1903 1904 static if (T.sizeof >= 4) 1905 { 1906 1907 assert((lhs += T.max) == false); 1908 assert(lhs.leastSignificantFirst == [0, T.max]); 1909 assert((lhs += T.max) == false); 1910 assert((lhs += T.max) == true); // overflow bit 1911 assert(lhs.leastSignificantFirst == [T.max-1, 0]); 1912 assert((lhs -= T(1)) == false); 1913 assert(lhs.leastSignificantFirst == [T.max-2, 0]); 1914 assert((lhs -= T.max) == false); 1915 assert(lhs.leastSignificantFirst == [2, 0]); 1916 assert(lhs.sign); 1917 assert((lhs -= Signed!T(-4)) == false); 1918 assert(lhs.leastSignificantFirst == [2, 0]); 1919 assert(lhs.sign == false); 1920 assert((lhs += Signed!T.max) == false); 1921 assert(lhs.leastSignificantFirst == [Signed!T.max + 2, 0]); 1922 1923 /// bool overflow = bigUInt op= bigUInt/bigInt 1924 lhs = BigIntView!(T, endian)(lhsData); 1925 auto rhs = BigUIntView!(T, endian)(rhsData).normalized; 1926 assert(lhs.leastSignificantFirst == [Signed!T.max + 2, 0, 0]); 1927 assert(rhs.leastSignificantFirst == [T.max, T.max]); 1928 assert((lhs += rhs) == false); 1929 assert(lhs.leastSignificantFirst == [Signed!T.max + 1, 0, 1]); 1930 assert((lhs -= rhs) == false); 1931 assert(lhs.leastSignificantFirst == [Signed!T.max + 2, 0, 0]); 1932 assert((lhs += -rhs) == false); 1933 assert(lhs.sign); 1934 assert(lhs.leastSignificantFirst == [T.max - (Signed!T.max + 2), T.max, 0]); 1935 assert(lhs.sign); 1936 assert((lhs -= -rhs) == false); 1937 assert(lhs.leastSignificantFirst == [Signed!T.max + 2, 0, 0]); 1938 assert(lhs.sign == false); 1939 } 1940 } 1941 } 1942 1943 /// 1944 version(mir_bignum_test) 1945 unittest 1946 { 1947 import mir.bignum.fixed: UInt; 1948 import mir.bignum.low_level_view: BigUIntView; 1949 auto bigView = BigUIntView!size_t.fromHexString("55a325ad18b2a77120d870d987d5237473790532acab45da44bc07c92c92babf0b5e2e2c7771cd472ae5d7acdb159a56fbf74f851a058ae341f69d1eb750d7e3"); 1950 auto fixed = UInt!256.fromHexString("55e5669576d31726f4a9b58a90159de5923adc6c762ebd3c4ba518d495229072"); 1951 auto overflow = bigView *= fixed; 1952 assert(overflow == UInt!256.fromHexString("1cbbe8c42dc21f936e4ce5b2f52ac404439857f174084012fcd1b71fdec2a398")); 1953 assert(bigView == BigUIntView!size_t.fromHexString("c73fd2b26f2514c103c324943b6c90a05d2732118d5f0099c36a69a8051bb0573adc825b5c9295896c70280faa4c4d369df8e92f82bfffafe078b52ae695d316")); 1954 1955 } 1956 1957 /// 1958 version(mir_bignum_test) 1959 unittest 1960 { 1961 import mir.bignum.fixed: UInt; 1962 import mir.bignum.low_level_view: BigUIntView; 1963 auto bigView2 = BigUIntView!size_t.fromHexString("55a325ad18b2a77120d870d987d5237473790532acab45da44bc07c92c92babf0b5e2e2c7771cd472ae5d7acdb159a56fbf74f851a058ae341f69d1eb750d7e3"); 1964 auto bigView = BigUIntView!size_t.fromHexString!(char, true)("55a3_25ad_18b2_a771_20d8_70d9_87d5_2374_7379_0532_acab_45da_44bc_07c9_2c92_babf_0b5e_2e2c_7771_cd47_2ae5_d7ac_db15_9a56_fbf7_4f85_1a05_8ae3_41f6_9d1e_b750_d7e3"); 1965 auto fixed = UInt!256.fromHexString!(true)("55e5_6695_76d3_1726_f4a9_b58a_9015_9de5_923a_dc6c_762e_bd3c_4ba5_18d4_9522_9072"); 1966 auto overflow = bigView *= fixed; 1967 assert(overflow == UInt!256.fromHexString("1cbbe8c42dc21f936e4ce5b2f52ac404439857f174084012fcd1b71fdec2a398")); 1968 assert(bigView == BigUIntView!size_t.fromHexString("c73fd2b26f2514c103c324943b6c90a05d2732118d5f0099c36a69a8051bb0573adc825b5c9295896c70280faa4c4d369df8e92f82bfffafe078b52ae695d316")); 1969 } 1970 1971 /++ 1972 An utility type to wrap a local buffer to accumulate unsigned numbers. 1973 +/ 1974 struct BigUIntAccumulator(W, WordEndian endian = TargetEndian) 1975 if (is(Unqual!W == uint) || is(Unqual!W == ulong)) 1976 { 1977 /++ 1978 A group of coefficients for a $(MREF DecimalRadix)`!W`. 1979 1980 The order corresponds to endianness. 1981 1982 The unused part can be uninitialized. 1983 +/ 1984 W[] coefficients; 1985 1986 /++ 1987 Current length of initialized coefficients. 1988 1989 The initialization order corresponds to endianness. 1990 1991 The `view` method may return a view with empty coefficients, which isn't usable. 1992 Put `0` or another number first to make the accumulator maintain a non-empty view. 1993 +/ 1994 size_t length; 1995 1996 /++ 1997 Returns: 1998 Current unsigned integer view. 1999 Note: 2000 The method may return a view with empty coefficients, which isn't usable. 2001 Put `0` or another number first to make the accumulator maintain a non-empty view. 2002 +/ 2003 BigUIntView!(W, endian) view() @safe pure nothrow @nogc @property 2004 { 2005 static if (endian == WordEndian.little) 2006 return typeof(return)(coefficients[0 .. length]); 2007 else 2008 return typeof(return)(coefficients[$ - length .. $]); 2009 } 2010 2011 /++ 2012 Returns: 2013 True if the accumulator can accept next most significant coefficient 2014 +/ 2015 bool canPut() @property 2016 { 2017 return length < coefficients.length; 2018 } 2019 2020 /++ 2021 Places coefficient to the next most significant position. 2022 +/ 2023 void put(W coeffecient) 2024 in { 2025 assert(length < coefficients.length); 2026 } 2027 do { 2028 static if (endian == WordEndian.little) 2029 coefficients[length++] = coeffecient; 2030 else 2031 coefficients[$ - ++length] = coeffecient; 2032 } 2033 2034 /++ 2035 Strips most significant zero coefficients from the current `view`. 2036 Note: 2037 The `view` method may return a view with empty coefficients, which isn't usable. 2038 Put `0` or another number first to make the accumulator maintain a non-empty view. 2039 +/ 2040 void normalize() 2041 { 2042 length = view.normalized.coefficients.length; 2043 } 2044 2045 /// 2046 bool canPutN(size_t n) 2047 { 2048 return length + n <= coefficients.length; 2049 } 2050 2051 /// 2052 bool approxCanMulPow5(size_t degree) 2053 { 2054 // TODO: more precise result 2055 enum n = MaxWordPow5!W; 2056 return canPutN(degree / n + (degree % n != 0)); 2057 } 2058 2059 /// 2060 bool canMulPow2(size_t degree) 2061 { 2062 import mir.bitop: ctlz; 2063 enum n = W.sizeof * 8; 2064 return canPutN(degree / n + (degree % n > ctlz(view.mostSignificant))); 2065 } 2066 2067 /// 2068 void mulPow5(size_t degree) 2069 { 2070 // assert(approxCanMulPow5(degree)); 2071 enum n = MaxWordPow5!W; 2072 enum wordInit = W(5) ^^ n; 2073 W word = wordInit; 2074 while(degree) 2075 { 2076 if (degree >= n) 2077 { 2078 degree -= n; 2079 } 2080 else 2081 { 2082 word = 1; 2083 do word *= 5; 2084 while(--degree); 2085 } 2086 if (auto overflow = view *= word) 2087 { 2088 put(overflow); 2089 } 2090 } 2091 } 2092 2093 /// 2094 void mulPow2(size_t degree) scope @safe 2095 { 2096 import mir.bitop: ctlz; 2097 assert(canMulPow2(degree)); 2098 enum n = W.sizeof * 8; 2099 auto ws = degree / n; 2100 auto oldLength = length; 2101 length += ws; 2102 if (ws) 2103 { 2104 auto v = view.leastSignificantFirst; 2105 foreach_reverse (i; 0 .. oldLength) 2106 { 2107 v[i + ws] = v[i]; 2108 } 2109 do v[--ws] = 0; 2110 while(ws); 2111 } 2112 2113 if (auto tail = cast(uint)(degree % n)) 2114 { 2115 if (tail > ctlz(view.mostSignificant)) 2116 { 2117 put(0); 2118 oldLength++; 2119 } 2120 view.topMostSignificantPart(oldLength).smallLeftShiftInPlace(tail); 2121 } 2122 } 2123 } 2124 2125 /// 2126 version(mir_bignum_test) 2127 @safe pure 2128 unittest 2129 { 2130 import std.traits; 2131 alias AliasSeq(T...) = T; 2132 2133 foreach (T; AliasSeq!(uint, ulong)) 2134 foreach (endian; AliasSeq!(WordEndian.little, WordEndian.big)) 2135 { 2136 T[16 / T.sizeof] buffer; 2137 auto accumulator = BigUIntAccumulator!(T, endian)(buffer); 2138 assert(accumulator.length == 0); 2139 assert(accumulator.coefficients.length == buffer.length); 2140 assert(accumulator.view.coefficients.length == 0); 2141 // needs to put a number before any operations on `.view` 2142 accumulator.put(1); 2143 // compute N factorial 2144 auto N = 30; 2145 foreach(i; 1 .. N + 1) 2146 { 2147 if (auto overflow = accumulator.view *= i) 2148 { 2149 if (!accumulator.canPut) 2150 throw new Exception("Factorial buffer overflow"); 2151 accumulator.put(overflow); 2152 } 2153 } 2154 assert(accumulator.view == BigUIntView!(T, endian).fromHexString("D13F6370F96865DF5DD54000000")); 2155 } 2156 } 2157 2158 /// Computes `13 * 10^^60` 2159 version(mir_bignum_test) 2160 @safe pure 2161 unittest 2162 { 2163 uint[7] buffer; 2164 auto accumulator = BigUIntAccumulator!uint(buffer); 2165 accumulator.put(13); // initial value 2166 assert(accumulator.approxCanMulPow5(60)); 2167 accumulator.mulPow5(60); 2168 assert(accumulator.canMulPow2(60)); 2169 accumulator.mulPow2(60); 2170 assert(accumulator.view == BigUIntView!uint.fromHexString("81704fcef32d3bd8117effd5c4389285b05d000000000000000")); 2171 } 2172 2173 /// 2174 enum DecimalExponentKey 2175 { 2176 /// 2177 none = 0, 2178 /// 2179 infinity = 1, 2180 /// 2181 nan = 2, 2182 /// 2183 dot = '.' - '0', 2184 /// 2185 d = 'd' - '0', 2186 /// 2187 e = 'e' - '0', 2188 /// 2189 D = 'D' - '0', 2190 /// 2191 E = 'E' - '0', 2192 } 2193 2194 /++ 2195 +/ 2196 struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t) 2197 if (isUnsigned!W) 2198 { 2199 /// 2200 bool sign; 2201 /// 2202 Exp exponent; 2203 /// 2204 BigUIntView!(W, endian) coefficient; 2205 2206 static if (isMutable!W && W.sizeof >= 4) 2207 /++ 2208 Returns: false in case of overflow or incorrect string. 2209 Precondition: non-empty coefficients 2210 +/ 2211 bool fromStringImpl(C, 2212 bool allowSpecialValues = true, 2213 bool allowDotOnBounds = true, 2214 bool allowDExponent = true, 2215 bool allowStartingPlus = true, 2216 bool allowUnderscores = true, 2217 bool allowLeadingZeros = true, 2218 bool allowExponent = true, 2219 bool checkEmpty = true, 2220 ) 2221 (scope const(C)[] str, out DecimalExponentKey key, int exponentShift = 0) 2222 scope @trusted pure @nogc nothrow 2223 if (isSomeChar!C) 2224 { 2225 import mir.utility: _expect; 2226 2227 version(LDC) 2228 { 2229 static if ((allowSpecialValues && allowDExponent && allowStartingPlus && allowDotOnBounds && checkEmpty) == false) 2230 pragma(inline, true); 2231 } 2232 2233 assert(coefficient.coefficients.length); 2234 2235 coefficient.leastSignificant = 0; 2236 auto work = coefficient.topLeastSignificantPart(1); 2237 2238 static if (checkEmpty) 2239 { 2240 if (_expect(str.length == 0, false)) 2241 return false; 2242 } 2243 2244 if (str[0] == '-') 2245 { 2246 sign = true; 2247 str = str[1 .. $]; 2248 if (_expect(str.length == 0, false)) 2249 return false; 2250 } 2251 else 2252 static if (allowStartingPlus) 2253 { 2254 if (_expect(str[0] == '+', false)) 2255 { 2256 str = str[1 .. $]; 2257 if (_expect(str.length == 0, false)) 2258 return false; 2259 } 2260 } 2261 2262 uint d = str[0] - '0'; 2263 str = str[1 .. $]; 2264 2265 W v; 2266 W t = 1; 2267 bool dot; 2268 2269 static if (allowUnderscores) 2270 { 2271 bool recentUnderscore; 2272 } 2273 2274 static if (!allowLeadingZeros) 2275 { 2276 if (d == 0) 2277 { 2278 if (str.length == 0) 2279 { 2280 coefficient = coefficient.init; 2281 return true; 2282 } 2283 if (str[0] >= '0' && str[0] <= '9') 2284 return false; 2285 goto S; 2286 } 2287 } 2288 2289 if (d < 10) 2290 { 2291 goto S; 2292 } 2293 2294 static if (allowDotOnBounds) 2295 { 2296 if (d == '.' - '0') 2297 { 2298 if (str.length == 0) 2299 return false; 2300 key = DecimalExponentKey.dot; 2301 dot = true; 2302 goto F; 2303 } 2304 } 2305 2306 static if (allowSpecialValues) 2307 { 2308 goto NI; 2309 } 2310 else 2311 { 2312 return false; 2313 } 2314 2315 F: for(;;) 2316 { 2317 enum mp10 = W(10) ^^ MaxWordPow10!W; 2318 d = str[0] - '0'; 2319 str = str[1 .. $]; 2320 2321 if (_expect(d <= 10, true)) 2322 { 2323 static if (allowUnderscores) 2324 { 2325 recentUnderscore = false; 2326 } 2327 v *= 10; 2328 S: 2329 t *= 10; 2330 v += d; 2331 exponentShift -= dot; 2332 2333 if (_expect(t == mp10 || str.length == 0, false)) 2334 { 2335 L: 2336 if (auto overflow = work.opOpAssign!"*"(t, v)) 2337 { 2338 if (_expect(work.coefficients.length < coefficient.coefficients.length, true)) 2339 { 2340 work = coefficient.topLeastSignificantPart(work.coefficients.length + 1); 2341 work.mostSignificant = overflow; 2342 } 2343 else 2344 { 2345 return false; 2346 } 2347 } 2348 2349 v = 0; 2350 t = 1; 2351 if (str.length == 0) 2352 { 2353 M: 2354 exponent += exponentShift; 2355 coefficient = work.mostSignificant == 0 ? coefficient.init : work; 2356 static if (allowUnderscores) 2357 { 2358 return !recentUnderscore; 2359 } 2360 else 2361 { 2362 return true; 2363 } 2364 } 2365 } 2366 2367 continue; 2368 } 2369 static if (allowUnderscores) 2370 { 2371 if (recentUnderscore) 2372 return false; 2373 } 2374 switch (d) 2375 { 2376 case DecimalExponentKey.dot: 2377 key = DecimalExponentKey.dot; 2378 if (_expect(dot, false)) 2379 break; 2380 dot = true; 2381 if (str.length) 2382 { 2383 static if (allowUnderscores) 2384 { 2385 recentUnderscore = true; 2386 } 2387 continue; 2388 } 2389 static if (allowDotOnBounds) 2390 { 2391 goto L; 2392 } 2393 else 2394 { 2395 return false; 2396 } 2397 static if (allowExponent) 2398 { 2399 static if (allowDExponent) 2400 { 2401 case DecimalExponentKey.d: 2402 case DecimalExponentKey.D: 2403 goto case DecimalExponentKey.e; 2404 } 2405 case DecimalExponentKey.e: 2406 case DecimalExponentKey.E: 2407 import mir.parse: parse; 2408 key = cast(DecimalExponentKey)d; 2409 if (parse(str, exponent) && str.length == 0) 2410 { 2411 if (t != 1) 2412 goto L; 2413 goto M; 2414 } 2415 break; 2416 } 2417 static if (allowUnderscores) 2418 { 2419 case '_' - '0': 2420 recentUnderscore = true; 2421 if (str.length) 2422 continue; 2423 break; 2424 } 2425 default: 2426 } 2427 break; 2428 } 2429 return false; 2430 2431 static if (allowSpecialValues) 2432 { 2433 NI: 2434 exponent = exponent.max; 2435 if (str.length == 2) 2436 { 2437 auto stail = cast(C[2])str[0 .. 2]; 2438 if (d == 'i' - '0' && stail == cast(C[2])"nf" || d == 'I' - '0' && (stail == cast(C[2])"nf" || stail == cast(C[2])"NF")) 2439 { 2440 coefficient = coefficient.init; 2441 key = DecimalExponentKey.infinity; 2442 return true; 2443 } 2444 if (d == 'n' - '0' && stail == cast(C[2])"an" || d == 'N' - '0' && (stail == cast(C[2])"aN" || stail == cast(C[2])"AN")) 2445 { 2446 coefficient.leastSignificant = 1; 2447 coefficient = coefficient.topLeastSignificantPart(1); 2448 key = DecimalExponentKey.nan; 2449 return true; 2450 } 2451 } 2452 return false; 2453 } 2454 } 2455 2456 /// 2457 DecimalView!(const W, endian, Exp) lightConst()() 2458 const @safe pure nothrow @nogc @property 2459 { 2460 return typeof(return)(sign, exponent, coefficient.lightConst); 2461 } 2462 ///ditto 2463 alias lightConst this; 2464 2465 /++ 2466 +/ 2467 BigIntView!(W, endian) signedCoefficient() 2468 { 2469 return typeof(return)(coefficient, sign); 2470 } 2471 2472 /++ 2473 Mir parsing supports up-to quadruple precision. The conversion error is 0 ULP for normal numbers. 2474 Subnormal numbers with an exponent greater than or equal to -512 have upper error bound equal to 1 ULP. 2475 +/ 2476 T opCast(T, bool wordNormalized = false, bool nonZero = false)() scope const 2477 if (isFloatingPoint!T && isMutable!T) 2478 { 2479 version(LDC) 2480 { 2481 static if (wordNormalized) 2482 pragma(inline, true); 2483 } 2484 2485 import mir.bignum.fixed: UInt; 2486 import mir.bignum.fp: Fp, extendedMul; 2487 import mir.bignum.internal.dec2flt_table; 2488 import mir.math.common: floor; 2489 import mir.utility: _expect; 2490 2491 auto coeff = coefficient.lightConst; 2492 T ret = 0; 2493 2494 static if (!wordNormalized) 2495 coeff = coeff.normalized; 2496 2497 if (_expect(exponent == exponent.max, false)) 2498 { 2499 ret = coeff.coefficients.length ? T.nan : T.infinity; 2500 goto R; 2501 } 2502 2503 static if (!nonZero) 2504 if (coeff.coefficients.length == 0) 2505 goto R; 2506 2507 enum S = 9; 2508 2509 static if (T.mant_dig < 64) 2510 { 2511 Fp!64 load(Exp e) 2512 { 2513 auto p10coeff = p10_coefficients[cast(sizediff_t)e - min_p10_e][0]; 2514 auto p10exp = p10_exponents[cast(sizediff_t)e - min_p10_e]; 2515 return Fp!64(false, p10exp, UInt!64(p10coeff)); 2516 } 2517 { 2518 auto expSign = exponent < 0; 2519 if (_expect((expSign ? -exponent : exponent) >>> S == 0, true)) 2520 { 2521 enum ulong mask = (1UL << (64 - T.mant_dig)) - 1; 2522 enum ulong half = (1UL << (64 - T.mant_dig - 1)); 2523 enum ulong bound = ulong(1) << T.mant_dig; 2524 2525 auto c = coeff.opCast!(Fp!64, 0, true, true); 2526 auto z = c.extendedMul(load(exponent)); 2527 ret = cast(T) z; 2528 auto slop = (coeff.coefficients.length > (ulong.sizeof / W.sizeof)) + 3 * expSign; 2529 long bitsDiff = (cast(ulong) z.opCast!(Fp!64).coefficient & mask) - half; 2530 if (_expect((bitsDiff < 0 ? -bitsDiff : bitsDiff) > slop, true)) 2531 goto R; 2532 if (slop == 0 && exponent <= MaxWordPow5!ulong || exponent == 0) 2533 goto R; 2534 if (slop == 3 && MaxFpPow5!T >= -exponent && cast(ulong)c.coefficient < bound) 2535 { 2536 auto e = load(-exponent); 2537 ret = c.opCast!(T, true) / cast(T) (cast(ulong)e.coefficient >> e.exponent); 2538 goto R; 2539 } 2540 ret = algoR!T(ret, coeff, cast(int) exponent); 2541 goto R; 2542 } 2543 ret = expSign ? 0 : T.infinity; 2544 goto R; 2545 } 2546 } 2547 else 2548 { 2549 enum P = 1 << S; 2550 static assert(min_p10_e <= -P); 2551 static assert(max_p10_e >= P); 2552 Fp!128 load(Exp e) 2553 { 2554 auto idx = cast(sizediff_t)e - min_p10_e; 2555 ulong h = p10_coefficients[idx][0]; 2556 ulong l = p10_coefficients[idx][1]; 2557 if (l >= cast(ulong)long.min) 2558 h--; 2559 version(BigEndian) 2560 auto p10coeff = UInt!128(cast(ulong[2])[h, l]); 2561 else 2562 auto p10coeff = UInt!128(cast(ulong[2])[l, h]); 2563 auto p10exp = p10_exponents[idx] - 64; 2564 return Fp!128(false, p10exp, p10coeff); 2565 } 2566 2567 { 2568 auto expSign = exponent < 0; 2569 Unsigned!Exp exp = exponent; 2570 exp = expSign ? -exp : exp; 2571 if (exp >= 5000) 2572 { 2573 ret = expSign ? 0 : T.infinity; 2574 goto R; 2575 } 2576 Exp index = exp & 0x1FF; 2577 bool gotoAlgoR; 2578 auto c = load(expSign ? -index : index); 2579 { 2580 exp >>= S; 2581 gotoAlgoR = exp != 0; 2582 if (_expect(gotoAlgoR, false)) 2583 { 2584 auto v = load(expSign ? -P : P); 2585 do 2586 { 2587 if (exp & 1) 2588 c *= v; 2589 exp >>>= 1; 2590 if (exp == 0) 2591 break; 2592 v *= v; 2593 } 2594 while(true); 2595 } 2596 } 2597 auto z = coeff.opCast!(Fp!128, 0, true, true).extendedMul(c); 2598 ret = cast(T) z; 2599 if (!gotoAlgoR) 2600 { 2601 static if (T.mant_dig == 64) 2602 enum ulong mask = ulong.max; 2603 else 2604 enum ulong mask = (1UL << (128 - T.mant_dig)) - 1; 2605 enum ulong half = (1UL << (128 - T.mant_dig - 1)); 2606 enum UInt!128 bound = UInt!128(1) << T.mant_dig; 2607 2608 auto slop = (coeff.coefficients.length > (ulong.sizeof * 2 / W.sizeof)) + 3 * expSign; 2609 long bitsDiff = (cast(ulong) z.opCast!(Fp!128).coefficient & mask) - half; 2610 if (_expect((bitsDiff < 0 ? -bitsDiff : bitsDiff) > slop, true)) 2611 goto R; 2612 if (slop == 0 && exponent <= 55 || exponent == 0) 2613 goto R; 2614 if (slop == 3 && MaxFpPow5!T >= -exponent && c.coefficient < bound) 2615 { 2616 auto e = load(-exponent); 2617 ret = c.opCast!(T, true) / cast(T) e; 2618 goto R; 2619 } 2620 } 2621 ret = algoR!T(ret, coeff, cast(int) exponent); 2622 goto R; 2623 } 2624 } 2625 R: 2626 if (sign) 2627 ret = -ret; 2628 return ret; 2629 } 2630 } 2631 2632 @optStrategy("minsize") 2633 package T algoR(T, W, WordEndian endian)(T ret, scope BigUIntView!(const W, endian) coeff, int exponent) 2634 { 2635 pragma(inline, false); 2636 2637 import mir.bignum.fixed: UInt; 2638 import mir.bignum.integer: BigInt; 2639 import mir.math.common: floor; 2640 import mir.math.ieee: ldexp, frexp, nextDown, nextUp; 2641 import mir.utility: _expect; 2642 2643 BigInt!256 x = void, y = void; // max value is 2^(2^14)-1 2644 if (exponent >= 0) 2645 { 2646 if (!x.copyFrom(coeff) && !x.mulPow5(exponent)) // if no overflow 2647 ret = ldexp(cast(T) x, exponent); 2648 } 2649 else do 2650 { 2651 if (ret < ret.min_normal) 2652 break; 2653 int k; 2654 auto m0 = frexp(ret, k); 2655 k -= T.mant_dig; 2656 static if (T.mant_dig <= 64) 2657 { 2658 enum p2 = T(2) ^^ T.mant_dig; 2659 auto m = UInt!64(cast(ulong) (m0 * p2)); 2660 } 2661 else 2662 { 2663 enum p2h = T(2) ^^ (T.mant_dig - 64); 2664 enum p2l = T(2) ^^ 64; 2665 m0 *= p2h; 2666 auto mhf = floor(m0); 2667 auto mh = cast(ulong) mhf; 2668 m0 -= mhf; 2669 m0 *= p2l; 2670 auto ml = cast(ulong) m0; 2671 auto m = UInt!128(mh); 2672 m <<= 64; 2673 m |= UInt!128(ml); 2674 } 2675 auto mtz = m.cttz; 2676 if (mtz != m.sizeof * 8) 2677 { 2678 m >>= mtz; 2679 k += mtz; 2680 } 2681 2682 if (x.copyFrom(coeff)) // if overflow 2683 break; 2684 y.__ctor(m); 2685 y.mulPow5(-exponent); 2686 auto shift = k - exponent; 2687 (shift >= 0 ? y : x) <<= shift >= 0 ? shift : -shift; 2688 x -= y; 2689 if (x.length == 0) 2690 break; 2691 x <<= 1; 2692 x *= m; 2693 auto cond = mtz == T.mant_dig - 1 && x.sign; 2694 auto cmp = x.view.unsigned.opCmp(y.view.unsigned); 2695 if (cmp < 0) 2696 { 2697 if (!cond) 2698 break; 2699 x <<= 1; 2700 if (x.view.unsigned <= y.view.unsigned) 2701 break; 2702 } 2703 else 2704 if (!cmp && !cond && !mtz) 2705 break; 2706 ret = x.sign ? nextDown(ret) : nextUp(ret); 2707 if (ret == 0) 2708 break; 2709 } 2710 while (T.mant_dig >= 64 && exponent < -512); 2711 return ret; 2712 } 2713 2714 /// 2715 version(mir_bignum_test) 2716 unittest 2717 { 2718 alias AliasSeq(T...) = T; 2719 2720 foreach (T; AliasSeq!(float, double, real)) 2721 {{ 2722 T value = 3.518437208883201171875E+013; 2723 2724 }} 2725 2726 foreach(E; AliasSeq!(WordEndian.little, WordEndian.big)) 2727 foreach(W; AliasSeq!(ulong, uint, ushort, ubyte)) 2728 static if (!(E != TargetEndian && (W.sizeof > size_t.sizeof || W.sizeof == 1))) 2729 {{ 2730 alias Args = AliasSeq!(W, E); 2731 2732 auto view = DecimalView!Args(false, -8, BigUIntView!Args.fromHexString("BEBC2000000011E1A3")); 2733 2734 assert (cast(float)view == 3.518437208883201171875E+013f); 2735 assert (cast(double)view == 3.518437208883201171875E+013); 2736 static if (real.mant_dig >= 64) 2737 assert (cast(real)view == 3.518437208883201171875E+013L); 2738 2739 view = DecimalView!Args(true, -169, BigUIntView!Args.fromHexString("5A174AEDA65CC")); 2740 assert (cast(float)view == -0); 2741 assert (cast(double)view == -0x1.1p-511); 2742 static if (real.mant_dig >= 64) 2743 assert (cast(real)view == -0x8.80000000000019fp-514L); 2744 2745 view = DecimalView!Args(true, 293, BigUIntView!Args.fromHexString("36496F6C4ED38")); 2746 assert (cast(float)view == -float.infinity); 2747 assert (cast(double)view == -9.55024478104888e+307); 2748 static if (real.mant_dig >= 64) 2749 assert (cast(real)view == -9.55024478104888e+307L); 2750 2751 view = DecimalView!Args(false, 0, BigUIntView!Args.fromHexString("1")); 2752 assert (cast(float)view == 1); 2753 assert (cast(double)view == 1); 2754 static if (real.mant_dig >= 64) 2755 assert (cast(real)view == 1L); 2756 2757 view = DecimalView!Args(false, -5, BigUIntView!Args.fromHexString("3")); 2758 assert (cast(float)view == 3e-5f); 2759 assert (cast(double)view == 3e-5); 2760 static if (real.mant_dig >= 64) 2761 assert (cast(real)view == 3e-5L); 2762 2763 view = DecimalView!Args(false, -1, BigUIntView!Args.fromHexString("1")); 2764 assert (cast(float)view == 0.1f); 2765 assert (cast(double)view == 0.1); 2766 static if (real.mant_dig >= 64) 2767 assert (cast(real)view == 0.1L); 2768 2769 view = DecimalView!Args(false, 0, BigUIntView!Args.fromHexString("3039")); 2770 assert (cast(float)view == 12345.0f); 2771 assert (cast(double)view == 12345.0); 2772 static if (real.mant_dig >= 64) 2773 assert (cast(real)view == 12345.0L); 2774 2775 view = DecimalView!Args(false, -7, BigUIntView!Args.fromHexString("98967F")); 2776 assert (cast(float)view == 0.9999999f); 2777 assert (cast(double)view == 0.9999999); 2778 static if (real.mant_dig >= 64) 2779 assert (cast(real)view == 0.9999999L); 2780 2781 view = DecimalView!Args(false, -324, BigUIntView!Args.fromHexString("4F0CEDC95A718E")); 2782 assert (cast(float)view == 0); 2783 assert (cast(double)view == 2.2250738585072014e-308); 2784 static if (real.mant_dig >= 64) 2785 assert (cast(real)view == 2.2250738585072014e-308L); 2786 2787 view = DecimalView!Args(false, 0, BigUIntView!Args.fromHexString("1FFFFFFFFFFFFFFFD")); 2788 assert (cast(float)view == 36893488147419103229f); 2789 assert (cast(double)view == 36893488147419103229.0); 2790 static if (real.mant_dig >= 64) 2791 assert (cast(real)view == 0x1FFFFFFFFFFFFFFFDp0L); 2792 2793 view = DecimalView!Args(false, -33, BigUIntView!Args.fromHexString("65")); 2794 assert (cast(float)view == 101e-33f); 2795 assert (cast(double)view == 101e-33); 2796 static if (real.mant_dig >= 64) 2797 assert (cast(real)view == 101e-33L); 2798 2799 view = DecimalView!Args(false, 23, BigUIntView!Args.fromHexString("1")); 2800 assert (cast(float)view == 1e23f); 2801 assert (cast(double)view == 1e23); 2802 static if (real.mant_dig >= 64) 2803 assert (cast(real)view == 1e23L); 2804 2805 view = DecimalView!Args(false, 23, BigUIntView!Args.fromHexString("81B")); 2806 assert (cast(float)view == 2075e23f); 2807 assert (cast(double)view == 0xaba3d58a1f1a98p+32); 2808 static if (real.mant_dig >= 64) 2809 assert (cast(real)view == 0xaba3d58a1f1a9cp+32L); 2810 2811 view = DecimalView!Args(false, -23, BigUIntView!Args.fromHexString("2209")); 2812 assert (cast(float)view == 8713e-23f); 2813 assert (cast(double)view == 0x1.9b75b4e7de2b9p-64); 2814 static if (real.mant_dig >= 64) 2815 assert (cast(real)view == 0xc.dbada73ef15c401p-67L); 2816 2817 view = DecimalView!Args(false, 300, BigUIntView!Args.fromHexString("1")); 2818 assert (cast(float)view == float.infinity); 2819 assert (cast(double)view == 0x1.7e43c8800759cp+996); 2820 static if (real.mant_dig >= 64) 2821 assert (cast(real)view == 0xb.f21e44003acdd2dp+993L); 2822 2823 view = DecimalView!Args(false, 245, BigUIntView!Args.fromHexString("B3A73CEB227")); 2824 assert (cast(float)view == float.infinity); 2825 assert (cast(double)view == 0x1.48e3735333cb6p+857); 2826 static if (real.mant_dig >= 64) 2827 assert (cast(real)view == 0xa.471b9a999e5b01ep+854L); 2828 2829 view = DecimalView!Args(false, 0, BigUIntView!Args.fromHexString("88BF4748507FB9900ADB624CCFF8D78897DC900FB0460327D4D86D327219")); 2830 assert (cast(float)view == float.infinity); 2831 assert (cast(double)view == 0x1.117e8e90a0ff7p+239); 2832 static if (real.mant_dig >= 64) 2833 assert (cast(real)view == 0x8.8bf4748507fb99p+236L); 2834 2835 view = DecimalView!Args(false, -324, BigUIntView!Args.fromHexString("5")); 2836 assert (cast(float)view == 0); 2837 assert (cast(double)view == 0x0.0000000000001p-1022); 2838 static if (real.mant_dig >= 64) 2839 assert (cast(real)view == 0x8.18995ce7aa0e1b2p-1077L); 2840 2841 view = DecimalView!Args(false, -324, BigUIntView!Args.fromHexString("5B")); 2842 assert (cast(float)view == 0); 2843 assert (cast(double)view == 0x0.0000000000012p-1022); 2844 static if (real.mant_dig >= 64) 2845 assert (cast(real)view == 0x9.3594d9adeb09a55p-1073L); 2846 2847 view = DecimalView!Args(false, -322, BigUIntView!Args.fromHexString("1")); 2848 assert (cast(float)view == 0); 2849 assert (cast(double)view == 0x0.0000000000014p-1022); 2850 static if (real.mant_dig >= 64) 2851 assert (cast(real)view == 0xa.1ebfb4219491a1fp-1073L); 2852 2853 view = DecimalView!Args(false, -320, BigUIntView!Args.fromHexString("CA1CCB")); 2854 assert (cast(float)view == 0); 2855 assert (cast(double)view == 0x0.000063df832d9p-1022); 2856 static if (real.mant_dig >= 64) 2857 assert (cast(real)view == 0xc.7bf065b215888c7p-1043L); 2858 2859 view = DecimalView!Args(false, -319, BigUIntView!Args.fromHexString("33CE7943FB")); 2860 assert (cast(float)view == 0); 2861 assert (cast(double)view == 0x1.000000000162p-1022); 2862 static if (real.mant_dig >= 64) 2863 assert (cast(real)view == 0x8.000000000b103b6p-1025L); 2864 2865 view = DecimalView!Args(false, -309, BigUIntView!Args.fromHexString("15")); 2866 assert (cast(float)view == 0); 2867 assert (cast(double)view == 0x0.f19c2629ccf53p-1022); 2868 static if (real.mant_dig >= 64) 2869 assert (cast(real)view == 0xf.19c2629ccf52fc4p-1026L); 2870 2871 view = DecimalView!Args(false, -340, BigUIntView!Args.fromHexString("AF87023B9BF0EE")); 2872 assert (cast(float)view == 0); 2873 assert (cast(double)view == 0x0.0000000000001p-1022); 2874 static if (real.mant_dig >= 64) 2875 assert (cast(real)view == 0xf.fffffffffffff64p-1078L); 2876 2877 view = DecimalView!Args(false, 400, BigUIntView!Args.fromHexString("1")); 2878 assert (cast(float)view == float.infinity); 2879 assert (cast(double)view == double.infinity); 2880 static if (real.mant_dig >= 64) 2881 assert (cast(real)view == 0xd.a763fc8cb9ff9e6p+1325L); 2882 2883 view = DecimalView!Args(false, 309, BigUIntView!Args.fromHexString("1")); 2884 assert (cast(float)view == float.infinity); 2885 assert (cast(double)view == double.infinity); 2886 static if (real.mant_dig >= 64) 2887 assert (cast(real)view == 0xb.201833b35d63f73p+1023L); 2888 2889 view = DecimalView!Args(false, 308, BigUIntView!Args.fromHexString("2")); 2890 assert (cast(float)view == float.infinity); 2891 assert (cast(double)view == double.infinity); 2892 static if (real.mant_dig >= 64) 2893 assert (cast(real)view == 0x8.e679c2f5e44ff8fp+1021L); 2894 2895 view = DecimalView!Args(false, 308, BigUIntView!Args.fromHexString("2")); 2896 assert (cast(float)view == float.infinity); 2897 assert (cast(double)view == double.infinity); 2898 static if (real.mant_dig >= 64) 2899 assert (cast(real)view == 0x8.e679c2f5e44ff8fp+1021L); 2900 2901 view = DecimalView!Args(false, 295, BigUIntView!Args.fromHexString("1059949B7090")); 2902 assert (cast(float)view == float.infinity); 2903 assert (cast(double)view == double.infinity); 2904 static if (real.mant_dig >= 64) 2905 assert (cast(real)view == 0x8.00000000006955ap+1021L); 2906 2907 view = DecimalView!Args(false, 0, BigUIntView!Args.fromHexString("0")); 2908 assert (cast(float)view == 0); 2909 assert (cast(double)view == 0); 2910 static if (real.mant_dig >= 64) 2911 assert (cast(real)view == 0L); 2912 2913 view = DecimalView!Args(false, 0, BigUIntView!Args.init); 2914 assert (cast(float)view == 0); 2915 assert (cast(double)view == 0); 2916 static if (real.mant_dig >= 64) 2917 assert (cast(real)view == 0L); 2918 2919 view = DecimalView!Args(false, -325, BigUIntView!Args.fromHexString("1")); 2920 assert (cast(float)view == 0); 2921 assert (cast(double)view == 0); 2922 static if (real.mant_dig >= 64) 2923 assert (cast(real)view == 0xa.5ced43b7e3e9188p-1083L); 2924 2925 view = DecimalView!Args(false, -326, BigUIntView!Args.fromHexString("1")); 2926 assert (cast(float)view == 0); 2927 assert (cast(double)view == 0); 2928 static if (real.mant_dig >= 64) 2929 assert (cast(real)view == 0x8.4a57695fe98746dp-1086L); 2930 2931 view = DecimalView!Args(false, -500, BigUIntView!Args.fromHexString("1")); 2932 assert (cast(float)view == 0); 2933 assert (cast(double)view == 0); 2934 static if (real.mant_dig >= 64) 2935 assert (cast(real)view == 0x8.33ada2003db9a8p-1664L); 2936 2937 view = DecimalView!Args(false, -1000, BigUIntView!Args.fromHexString("1")); 2938 assert (cast(float)view == 0); 2939 assert (cast(double)view == 0); 2940 static if (real.mant_dig >= 64) 2941 assert (cast(real)view == 0x8.68a9188a89e1467p-3325L); 2942 2943 view = DecimalView!Args(false, -4999, BigUIntView!Args.fromHexString("1")); 2944 assert (cast(float)view == 0); 2945 assert (cast(double)view == 0); 2946 static if (real.mant_dig >= 64) 2947 assert (cast(real)view == 0L); 2948 2949 view = DecimalView!Args(false, -10000, BigUIntView!Args.fromHexString("1")); 2950 assert (cast(float)view == 0); 2951 assert (cast(double)view == 0); 2952 static if (real.mant_dig >= 64) 2953 assert (cast(real)view == 0L); 2954 2955 view = DecimalView!Args(false, -4969, BigUIntView!Args.fromHexString("329659A941466C6B")); 2956 assert (cast(float)view == 0); 2957 assert (cast(double)view == 0); 2958 static if (real.mant_dig >= 64) 2959 assert (cast(real)view == real.min_normal * real.epsilon); 2960 2961 view = DecimalView!Args(false, -15, BigUIntView!Args.fromHexString("525DB0200FFAB")); 2962 assert (cast(float)view == 1.448997445238699f); 2963 assert (cast(double)view == 0x1.72f17f1f49aadp+0); 2964 static if (real.mant_dig >= 64) 2965 assert (cast(real)view == 0xb.978bf8fa4d56cp-3L); 2966 2967 view = DecimalView!Args(false, -15, BigUIntView!Args.fromHexString("525DB0200FFAB")); 2968 assert (cast(float)view == 1.448997445238699f); 2969 assert (cast(double)view == 0x1.72f17f1f49aadp+0); 2970 static if (real.mant_dig >= 64) 2971 assert (cast(real)view == 0xb.978bf8fa4d56cp-3L); 2972 2973 view = DecimalView!Args(false, -325, BigUIntView!Args.fromHexString("1")); 2974 assert (cast(float)view == 0); 2975 assert (cast(double)view == 0); 2976 static if (real.mant_dig >= 64) 2977 assert (cast(real)view == 0xa.5ced43b7e3e9188p-1083L); 2978 2979 view = DecimalView!Args(false, -326, BigUIntView!Args.fromHexString("1")); 2980 assert (cast(float)view == 0); 2981 assert (cast(double)view == 0); 2982 static if (real.mant_dig >= 64) 2983 assert (cast(real)view == 0x8.4a57695fe98746dp-1086L); 2984 2985 view = DecimalView!Args(false, 0, BigUIntView!Args.fromHexString("1")); 2986 assert (cast(float)view == 1); 2987 assert (cast(double)view == 0x1p+0); 2988 static if (real.mant_dig >= 64) 2989 assert (cast(real)view == 0x8p-3L); 2990 2991 view = DecimalView!Args(false, -5, BigUIntView!Args.fromHexString("3")); 2992 assert (cast(float)view == 3e-5f); 2993 assert (cast(double)view == 0x1.f75104d551d69p-16); 2994 static if (real.mant_dig >= 64) 2995 assert (cast(real)view == 0xf.ba8826aa8eb4635p-19L); 2996 2997 view = DecimalView!Args(false, -1, BigUIntView!Args.fromHexString("1")); 2998 assert (cast(float)view == 0.1f); 2999 assert (cast(double)view == 0x1.999999999999ap-4); 3000 static if (real.mant_dig >= 64) 3001 assert (cast(real)view == 0xc.ccccccccccccccdp-7L); 3002 3003 view = DecimalView!Args(false, -7, BigUIntView!Args.fromHexString("98967F")); 3004 assert (cast(float)view == 0.9999999f); 3005 assert (cast(double)view == 0x1.fffffca501acbp-1); 3006 static if (real.mant_dig >= 64) 3007 assert (cast(real)view == 0xf.ffffe5280d65435p-4L); 3008 }} 3009 } 3010 3011 /++ 3012 +/ 3013 struct BinaryView(W, WordEndian endian = TargetEndian, Exp = int) 3014 { 3015 /// 3016 bool sign; 3017 /// 3018 Exp exponent; 3019 /// 3020 BigUIntView!(W, endian) coefficient; 3021 3022 /// 3023 DecimalView!(const W, endian, Exp) lightConst()() 3024 const @safe pure nothrow @nogc @property 3025 { 3026 return typeof(return)(sign, exponent, coefficient.lightConst); 3027 } 3028 ///ditto 3029 alias lightConst this; 3030 3031 /++ 3032 +/ 3033 BigIntView!(W, endian) signedCoefficient() 3034 { 3035 return typeof(return)(sign, coefficients); 3036 } 3037 }