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 }