The OpenD Programming Language

1 /++
2 Low-level betterC utilities for big integer arithmetic libraries.
3 
4 The module provides $(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 private alias cop(string op : "-") = subu;
15 private alias cop(string op : "+") = addu;
16 private enum inverseSign(string op) = op == "+" ? "-" : "+";
17 
18 package immutable hexStringErrorMsg = "Incorrect hex string for BigUIntView.fromHexString";
19 package immutable binaryStringErrorMsg = "Incorrect binary string for BigUIntView.fromBinaryString";
20 version (D_Exceptions)
21 {
22     package immutable hexStringException = new Exception(hexStringErrorMsg);
23     package immutable binaryStringException = new Exception(binaryStringErrorMsg);
24 }
25 
26 package template MaxWordPow10(T)
27 {
28     static if (is(T == ubyte))
29         enum MaxWordPow10 = 2;
30     else
31     static if (is(T == ushort))
32         enum MaxWordPow10 = 4;
33     else
34     static if (is(T == uint))
35         enum MaxWordPow10 = 9;
36     else
37     static if (is(T == ulong))
38         enum MaxWordPow10 = 19;
39     else
40         static assert(0);
41 }
42 
43 /++
44 Fast integer computation of `ceil(log10(exp2(e)))` with 64-bit mantissa precision.
45 The result is guaranted to be greater then `log10(exp2(e))`, which is irrational number.
46 +/
47 T ceilLog10Exp2(T)(const T e)
48     @safe pure nothrow @nogc
49     if (is(T == ubyte) || is(T == ushort) || is(T == uint) || is(T == ulong))
50 {
51     import mir.utility: extMul;
52     auto result = extMul(0x9a209a84fbcff799UL, e);
53     return  cast(T) ((result.high >> 1) + ((result.low != 0) | (result.high & 1)));
54 }
55 
56 ///
57 version(mir_bignum_test_llv)
58 @safe pure nothrow @nogc unittest
59 {
60     assert(ceilLog10Exp2(ubyte(10)) == 4); // ubyte
61     assert(ceilLog10Exp2(10U) == 4); // uint
62     assert(ceilLog10Exp2(10UL) == 4); // ulong
63 }
64 
65 /++
66 Arbitrary length unsigned integer view.
67 +/
68 struct BigUIntView(W)
69     if (__traits(isUnsigned, W))
70 {
71     import mir.bignum.fp: Fp, half;
72     import mir.bignum.fixed: UInt;
73 
74     /++
75     A group of coefficients for a radix `W.max + 1`.
76 
77     The order corresponds to endianness.
78     +/
79     W[] coefficients;
80 
81 @safe:
82 
83     /++
84     Retrurns: signed integer view using the same data payload
85     +/
86     size_t length()() @safe pure nothrow @nogc const @property
87     {
88         return coefficients.length;
89     }
90 
91     /++
92     Retrurns: signed integer view using the same data payload
93     +/
94     BigIntView!W signed()(bool sign = false) @safe pure nothrow @nogc return scope @property
95     {
96         return typeof(return)(this, sign);
97     }
98 
99     static if (W.sizeof >= size_t.sizeof)
100     ///
101     T opCast(T)() const scope
102         if (isFloatingPoint!T && isMutable!T)
103     {
104         import mir.bignum.internal.dec2float: binaryTo;
105         return normalized.coefficients.binaryTo!T;
106     }
107 
108     static if (W.sizeof >= size_t.sizeof)
109     ///
110     @safe
111     T opCast(T : Fp!coefficientSize, uint coefficientSize)() const scope
112     {
113         static if (isMutable!W)
114         {
115             return lightConst.opCast!T;
116         }
117         else
118         static if (W.sizeof > size_t.sizeof)
119         {
120             return lightConst.opCast!(BigUIntView!(const size_t)).opCast!T;
121         }
122         else
123         {
124             import mir.bignum.internal.dec2float: binaryToFp;
125             auto coefficients = normalized.coefficients;
126             return coefficients.length
127                 ? coefficients.binaryToFp!coefficientSize
128                 : Fp!coefficientSize.init;
129         }
130     }
131 
132     ///
133     T opCast(T, bool nonZero = false)() const scope
134         if (isIntegral!T && isUnsigned!T && isMutable!T)
135     {
136         auto work = lightConst;
137         static if (!nonZero)
138         {
139             if (coefficients.length == 0)
140             {
141                 return 0;
142             }
143         }
144         static if (T.sizeof <= W.sizeof)
145         {
146             return cast(T) work.coefficients[0];
147         }
148         else
149         {
150             T ret;
151             do
152             {
153                 ret <<= W.sizeof * 8;
154                 ret |= work.coefficients[$ - 1];
155                 work.popMostSignificant;
156             }
157             while(work.coefficients.length);
158             return ret;
159         }
160     }
161 
162     ///
163     pure nothrow @nogc
164     BigUIntView!V opCast(T : BigUIntView!V, V)() return scope
165         if (V.sizeof <= W.sizeof)
166     {
167         return typeof(return)(cast(V[])this.coefficients);
168     }
169 
170     pure nothrow @nogc
171     BigUIntView!V opCast(T : BigUIntView!V, V)() const return scope
172         if (V.sizeof <= W.sizeof)
173     {
174         return typeof(return)(cast(V[])this.coefficients);
175     }
176 
177     ///
178     BigUIntView!(const W) lightConst()() return scope
179         const @safe pure nothrow @nogc @property
180     {
181         return typeof(return)(coefficients);
182     }
183     ///ditto
184     alias lightConst this;
185 
186     /++
187     +/
188     sizediff_t opCmp(scope BigUIntView!(const W) rhs)
189         const @safe pure nothrow @nogc scope
190     {
191         import mir.algorithm.iteration: cmp;
192         auto l = this.lightConst.normalized;
193         auto r = rhs.lightConst.normalized;
194         if (sizediff_t d = l.coefficients.length - r.coefficients.length)
195             return d;
196         return cmp(l.mostSignificantFirst, r.mostSignificantFirst);
197     }
198 
199     ///
200     bool opEquals(BigUIntView!(const W) rhs)
201         const @safe pure nothrow @nogc scope
202     {
203         return this.coefficients == rhs.coefficients;
204     }
205 
206     /++
207     +/
208     void popMostSignificant() scope
209     {
210         coefficients = coefficients[0 .. $ - 1];
211     }
212 
213     /++
214     +/
215     void popLeastSignificant() scope
216     {
217         coefficients = coefficients[1 .. $];
218     }
219 
220     /++
221     +/
222     BigUIntView topMostSignificantPart(size_t length)
223         in (length <= coefficients.length)
224     {
225         return BigUIntView(coefficients[$ - length .. $]);
226     }
227 
228     /++
229     +/
230     BigUIntView topLeastSignificantPart(size_t length)
231         in (length <= coefficients.length)
232     {
233         return BigUIntView(coefficients[0 .. length]);
234     }
235 
236     /++
237     Shifts left using at most `size_t.sizeof * 8 - 1` bits
238     +/
239     void smallLeftShiftInPlace()(uint shift) scope
240     {
241         assert(shift < W.sizeof * 8);
242         if (shift == 0)
243             return;
244         auto csh = W.sizeof * 8 - shift;
245         auto d = coefficients[];
246         assert(d.length);
247         foreach_reverse (i; 1 .. d.length)
248             d[i] = (d[i] << shift) | (d[i - 1] >>> csh);
249         d[0] <<= shift;
250     }
251 
252     /++
253     Shifts right using at most `size_t.sizeof * 8 - 1` bits
254     +/
255     void smallRightShiftInPlace()(uint shift)
256     {
257         assert(shift < W.sizeof * 8);
258         if (shift == 0)
259             return;
260         auto csh = W.sizeof * 8 - shift;
261         auto d = coefficients[];
262         assert(d.length);
263         foreach (i; 0 .. d.length - 1)
264             d[i] = (d[i] >>> shift) | (d[i + 1] << csh);
265         d[$ - 1] >>>= shift;
266     }
267 
268     /++
269     +/
270     static BigUIntView fromHexString(C, bool allowUnderscores = false)(scope const(C)[] str)
271         @trusted pure
272         if (isSomeChar!C)
273     {
274         auto length = str.length / (W.sizeof * 2) + (str.length % (W.sizeof * 2) != 0);
275         auto data = new Unqual!W[length];
276         auto view = BigUIntView!(Unqual!W)(data);
277         if (view.fromHexStringImpl!(C, allowUnderscores)(str))
278             return BigUIntView(cast(W[])view.coefficients);
279         version(D_Exceptions)
280             { import mir.exception : toMutable; throw hexStringException.toMutable; }
281         else
282             assert(0, hexStringErrorMsg);
283     }
284 
285     static if (isMutable!W)
286     /++
287     +/
288     bool fromHexStringImpl(C, bool allowUnderscores = false)(scope const(C)[] str)
289         @safe pure @nogc nothrow scope
290         if (isSomeChar!C)
291     {
292         pragma(inline, false);
293         import mir.utility: _expect;
294         static if (allowUnderscores) {
295             if (_expect(str.length == 0, false)) // can't tell how big the coeff array needs to be, rely on a runtime check
296                 return false;
297         } else {
298             if (_expect(str.length == 0 || str.length > coefficients.length * W.sizeof * 2, false))
299                 return false;
300         }
301 
302         coefficients[0] = 0;
303         auto work = topLeastSignificantPart(1);
304         W current;
305         size_t i, j;
306         static if (allowUnderscores) bool recentUnderscore;
307 
308         do
309         {
310             ubyte c;
311             switch(str[$ - ++i])
312             {
313                 case '0': c = 0x0; break;
314                 case '1': c = 0x1; break;
315                 case '2': c = 0x2; break;
316                 case '3': c = 0x3; break;
317                 case '4': c = 0x4; break;
318                 case '5': c = 0x5; break;
319                 case '6': c = 0x6; break;
320                 case '7': c = 0x7; break;
321                 case '8': c = 0x8; break;
322                 case '9': c = 0x9; break;
323                 case 'A':
324                 case 'a': c = 0xA; break;
325                 case 'B':
326                 case 'b': c = 0xB; break;
327                 case 'C':
328                 case 'c': c = 0xC; break;
329                 case 'D':
330                 case 'd': c = 0xD; break;
331                 case 'E':
332                 case 'e': c = 0xE; break;
333                 case 'F':
334                 case 'f': c = 0xF; break;
335                 static if (allowUnderscores) 
336                 {
337                     case '_': 
338                         if (recentUnderscore) return false;
339                         recentUnderscore = true;
340                         continue;
341                 }
342                 default: return false;
343             }
344             ++j;
345             static if (allowUnderscores) recentUnderscore = false;
346             // how far do we need to shift to get to the top 4 bits
347             enum s = W.sizeof * 8 - 4;
348             // shift number to the top most 4 bits
349             W cc = cast(W)(W(c) << s);
350             // shift unsigned right 4 bits
351             current >>>= 4;
352             // add number to top most 4 bits of current var
353             current |= cc;
354             if (j % (W.sizeof * 2) == 0) // is this packed var full? 
355             {
356                 work.coefficients[$ - 1] = current;
357                 current = 0;
358                 if (_expect(work.coefficients.length < coefficients.length, true))
359                 {
360                     work = topLeastSignificantPart(work.coefficients.length + 1);
361                 }
362                 else if (i < str.length) // if we've run out of coefficients before reaching the end of the string, error
363                 {
364                     return false;
365                 }
366             }
367         }
368         while(i < str.length);
369 
370         static if (allowUnderscores) 
371         {
372             // check for a underscore at the beginning or the end
373             if (recentUnderscore || str[$ - 1] == '_') return false;
374         }
375 
376         if (current)
377         {
378             current >>>= 4 * (W.sizeof * 2 - j % (W.sizeof * 2));
379             work.coefficients[$ - 1] = current;
380         }
381         else
382         {
383             work.coefficients = work.coefficients[0 .. (j / (W.sizeof * 2) + (j % (W.sizeof * 2) != 0))];
384             work = work.normalized;
385         }
386 
387         ()@trusted {this = work;}();
388         return true;
389     }
390 
391     /++
392     +/
393     static BigUIntView fromBinaryString(C, bool allowUnderscores = false)(scope const(C)[] str)
394         @trusted pure
395         if (isSomeChar!C)
396     {
397         auto length = str.length / (W.sizeof * 8) + (str.length % (W.sizeof * 8) != 0);
398         auto data = new Unqual!W[length];
399         auto view = BigUIntView!(Unqual!W)(data);
400         if (view.fromBinaryStringImpl!(C, allowUnderscores)(str))
401             return BigUIntView(cast(W[])view.coefficients);
402         version(D_Exceptions)
403             { import mir.exception : toMutable; throw binaryStringException.toMutable; }
404         else
405             assert(0, binaryStringErrorMsg);
406     }
407 
408     static if (isMutable!W)
409     /++
410     +/
411     bool fromBinaryStringImpl(C, bool allowUnderscores = false)(scope const(C)[] str)
412         @safe pure @nogc nothrow scope
413         if (isSomeChar!C)
414     {
415         pragma(inline, false);
416         import mir.utility: _expect;
417         static if (allowUnderscores) {
418             if (_expect(str.length == 0, false)) // can't tell how big the coeff array needs to be, rely on a runtime check
419                 return false;
420         } else {
421             if (_expect(str.length == 0 || str.length > coefficients.length * W.sizeof * 8, false))
422                 return false;
423         }
424 
425         coefficients[0] = 0;
426         auto work = topLeastSignificantPart(1);
427         W current;
428         size_t i, j;
429         static if (allowUnderscores) bool recentUnderscore;
430 
431         do
432         {
433             ubyte c;
434             switch(str[$ - ++i])
435             {
436                 case '0': c = 0x0; break;
437                 case '1': c = 0x1; break;
438                 static if (allowUnderscores) 
439                 {
440                     case '_': 
441                         if (recentUnderscore) return false;
442                         recentUnderscore = true;
443                         continue;
444                 }
445                 default: return false;
446             }
447             ++j;
448             static if (allowUnderscores) recentUnderscore = false;
449             // how far do we need to shift to get to the top bit?
450             enum s = W.sizeof * 8 - 1;
451             // shift number to the top most bit
452             W cc = cast(W)(W(c) << s);
453             // shift unsigned right 1 bit
454             current >>>= 1;
455             // add number to top most bit of current var
456             current |= cc;
457             if (j % (W.sizeof * 8) == 0) // is this packed var full? 
458             {
459                 work.coefficients[$ - 1] = current;
460                 current = 0;
461                 if (_expect(work.coefficients.length < coefficients.length, true))
462                 {
463                     work = topLeastSignificantPart(work.coefficients.length + 1);
464                 }
465                 else if (i < str.length) // if we've run out of coefficients before reaching the end of the string, error
466                 {
467                     return false;
468                 }
469             }
470         }
471         while(i < str.length);
472 
473         static if (allowUnderscores) 
474         {
475             // check for a underscore at the beginning or the end
476             if (recentUnderscore || str[$ - 1] == '_') return false;
477         }
478 
479         if (current)
480         {
481             current >>>= (W.sizeof * 8 - j % (W.sizeof * 8));
482             work.coefficients[$ - 1] = current;
483         }
484         else
485         {
486             work.coefficients = work.coefficients[0 .. (j / (W.sizeof * 8) + (j % (W.sizeof * 8) != 0))];
487             work = work.normalized;
488         }
489 
490         ()@trusted {this = work;}();
491         return true;
492     }
493 
494     static if (isMutable!W && W.sizeof >= 4)
495     /++
496     Returns: false in case of overflow or incorrect string.
497     Precondition: non-empty coefficients
498     Note: doesn't support signs.
499     +/
500     bool fromStringImpl(C)(scope const(C)[] str)
501         scope @trusted pure @nogc nothrow
502         if (isSomeChar!C)
503     {
504         import mir.utility: _expect;
505 
506         assert(coefficients.length);
507 
508         if (_expect(str.length == 0, false))
509             return false;
510 
511         coefficients[0] = 0;
512         uint d = str[0] - '0';
513         str = str[1 .. $];
514 
515         W v;
516         W t = 1;
517 
518         if (d == 0)
519         {
520             if (str.length == 0)
521             {
522                 coefficients = null;
523                 return true;
524             }
525             return false;
526         }
527         else
528         if (d >= 10)
529             return false;
530 
531         size_t len = 1;
532         goto S;
533 
534         for(;;)
535         {
536             enum mp10 = W(10) ^^ MaxWordPow10!W;
537             d = str[0] - '0';
538             str = str[1 .. $];
539             if (_expect(d > 10, false))
540                 break;
541             v *= 10;
542     S:
543             t *= 10;
544             v += d;
545 
546             if (_expect(t == mp10 || str.length == 0, false))
547             {
548             L:
549                 if (auto overflow = topLeastSignificantPart(len).opOpAssign!"*"(t, v))
550                 {
551                     if (_expect(len < coefficients.length, true))
552                     {
553                         coefficients[len++] = overflow;
554                     }
555                     else
556                     {
557                         return false;
558                     }
559                 }
560                 v = 0;
561                 t = 1;
562                 if (str.length == 0)
563                 {
564                     this = topLeastSignificantPart(len);
565                     return true;
566                 }
567             }
568         }
569         return false;
570     }
571 
572     static if (isMutable!W && W.sizeof >= 4)
573     /++
574     Performs `bool overflow = big +(-)= big` operatrion.
575     Params:
576         rhs = value to add with non-empty coefficients
577         overflow = (overflow) initial iteration overflow
578     Precondition: non-empty coefficients length of greater or equal to the `rhs` coefficients length.
579     Returns:
580         true in case of unsigned overflow
581     +/
582     bool opOpAssign(string op)(scope BigUIntView!(const W) rhs, bool overflow = false)
583     @safe pure nothrow @nogc scope
584         if (op == "+" || op == "-")
585     {
586         assert(this.coefficients.length > 0);
587         assert(rhs.coefficients.length <= this.coefficients.length);
588         auto ls = this.coefficients;
589         auto rs = rhs.coefficients;
590         do
591         {
592             bool overflowM, overflowG;
593             ls[0] = ls[0].cop!op(rs[0], overflowM).cop!op(overflow, overflowG);
594             overflow = overflowG | overflowM;
595             ls = ls[1 .. $];
596             rs = rs[1 .. $];
597         }
598         while(rs.length);
599         if (overflow && ls.length)
600             return topMostSignificantPart(ls.length).opOpAssign!op(W(overflow));
601         return overflow;
602     }
603 
604     static if (isMutable!W && W.sizeof >= 4)
605     /// ditto
606     bool opOpAssign(string op)(scope BigIntView!(const W) rhs, bool overflow = false)
607     @safe pure nothrow @nogc scope
608         if (op == "+" || op == "-")
609     {
610         return rhs.sign == false ?
611             opOpAssign!op(rhs.unsigned, overflow):
612             opOpAssign!(inverseSign!op)(rhs.unsigned, overflow);
613     }
614 
615     static if (isMutable!W && W.sizeof >= 4)
616     /++
617     Performs `bool Overflow = big +(-)= scalar` operatrion.
618     Precondition: non-empty coefficients
619     Params:
620         rhs = value to add
621     Returns:
622         true in case of unsigned overflow
623     +/
624     bool opOpAssign(string op, T)(const T rhs)
625         @safe pure nothrow @nogc scope
626         if ((op == "+" || op == "-") && is(T == W))
627     {
628         assert(this.coefficients.length > 0);
629         auto ns = this.coefficients;
630         W additive = rhs;
631         do
632         {
633             bool overflow;
634             ns[0] = ns[0].cop!op(additive, overflow);
635             if (!overflow)
636                 return overflow;
637             additive = overflow;
638             ns = ns[1 .. $];
639         }
640         while (ns.length);
641         return true;
642     }
643 
644     static if (isMutable!W && W.sizeof >= 4)
645     /// ditto
646     bool opOpAssign(string op, T)(const T rhs)
647         @safe pure nothrow @nogc scope
648         if ((op == "+" || op == "-") && is(T == Signed!W))
649     {
650         return rhs >= 0 ?
651             opOpAssign!op(cast(W)rhs):
652             opOpAssign!(inverseSign!op)(cast(W)(-rhs));
653     }
654 
655     static if (isMutable!W && W.sizeof >= 4)
656     /++
657     Performs `W overflow = (big += overflow) *= scalar` operatrion.
658     Precondition: non-empty coefficients
659     Params:
660         rhs = unsigned value to multiply by
661         overflow = initial overflow
662     Returns:
663         unsigned overflow value
664     +/
665     W opOpAssign(string op : "*")(W rhs, W overflow = 0u)
666         @safe pure nothrow @nogc scope
667     {
668         auto ns = this.coefficients;
669         while (ns.length)
670         {
671             import mir.utility: extMul;
672             auto ext = ns[0].extMul(rhs);
673             bool overflowM;
674             ns[0] = ext.low.cop!"+"(overflow, overflowM);
675             overflow = ext.high + overflowM;
676             ns = ns[1 .. $];
677         }
678         return overflow;
679     }
680 
681     static if (isMutable!W && W.sizeof == 4 || W.sizeof == 8)
682     /++
683     Performs `uint remainder = (overflow$big) /= scalar` operatrion, where `$` denotes big-endian concatenation.
684     Precondition: non-empty coefficients, `overflow < rhs`
685     Params:
686         rhs = unsigned value to devide by
687         overflow = initial unsigned overflow
688     Returns:
689         unsigned remainder value (evaluated overflow)
690     +/
691     uint opOpAssign(string op : "/")(uint rhs, uint overflow = 0)
692         @safe pure nothrow @nogc scope
693     {
694         assert(overflow < rhs);
695         assert(coefficients.length);
696         static if (W.sizeof == 4)
697         {
698             auto ns = this.mostSignificantFirst;
699             size_t i;
700             do
701             {
702                 auto ext = (ulong(overflow) << 32) ^ ns[i];
703                 ns[i] = cast(uint)(ext / rhs);
704                 overflow = ext % rhs;
705             }
706             while (++i < ns.length);
707             if (coefficients[$ - 1] == 0)
708                 popMostSignificant;
709             return overflow;
710         }
711         else
712         {
713             auto work = opCast!(BigUIntView!uint);
714             if (work.coefficients[$ - 1] == 0)
715                 work.popMostSignificant;
716             auto remainder = work.opOpAssign!op(rhs, overflow);
717             coefficients = coefficients[0 .. work.coefficients.length / 2 + work.coefficients.length % 2];
718             return remainder;
719         }
720     }
721 
722     static if (isMutable!W && W.sizeof == size_t.sizeof)
723     /++
724     Performs `W overflow = (big += overflow) *= scalar` operatrion.
725     Precondition: non-empty coefficients
726     Params:
727         rhs = unsigned fixed-length integer to multiply by
728         overflow = initial overflow
729     Returns:
730         unsigned fixed-length integer overflow value
731     +/
732     UInt!size
733     opOpAssign(string op : "*", size_t size)(UInt!size rhs, UInt!size overflow = 0)
734         @safe pure nothrow @nogc scope
735     {
736         assert(coefficients.length);
737         auto ns = this.coefficients;
738         do
739         {
740             auto t = rhs;
741             auto overflowW = t.view *= ns[0];
742             auto overflowM = t += overflow;
743             overflowW += overflowM;
744             ns[0] = cast(size_t) t;
745             static if (size > size_t.sizeof * 8)
746                 overflow = t.toSize!(size - size_t.sizeof * 8, false).toSize!size;
747             BigUIntView!size_t(overflow.data).coefficients[$ - 1] = overflowW;
748             ns = ns[1 .. $];
749         }
750         while (ns.length);
751         return overflow;
752     }
753 
754     /++
755     Returns: the same intger view with inversed sign
756     +/
757     BigIntView!W opUnary(string op : "-")()
758     {
759         return typeof(return)(this, true);
760     }
761 
762     static if (isMutable!W && W.sizeof >= 4)
763     /++
764     +/
765     void bitwiseNotInPlace() scope
766     {
767         foreach (ref coefficient; this.coefficients)
768             coefficient = cast(W)~(0 + coefficient);
769     }
770 
771     static if (isMutable!W && W.sizeof >= 4)
772     /++
773     Performs `number=-number` operatrion.
774     Precondition: non-empty coefficients
775     Returns:
776         true if 'number=-number=0' and false otherwise
777     +/
778     bool twoComplementInPlace() scope
779     {
780         assert(coefficients.length);
781         bitwiseNotInPlace();
782         return this.opOpAssign!"+"(W(1));
783     }
784 
785     /++
786     Returns: a slice of coefficients starting from the most significant.
787     +/
788     auto mostSignificantFirst()
789         @safe pure nothrow @nogc @property
790     {
791         import mir.ndslice.slice: sliced;
792         import mir.ndslice.topology: retro;
793         return coefficients.sliced.retro;
794     }
795 
796     ///
797     auto mostSignificantFirst()
798         const @safe pure nothrow @nogc @property
799     {
800         import mir.ndslice.slice: sliced;
801         import mir.ndslice.topology: retro;
802         return coefficients.sliced.retro;
803     }
804 
805     /++
806     Strips most significant zero coefficients.
807     +/
808     BigUIntView normalized() return scope
809     {
810         auto number = this;
811         if (number.coefficients.length) do
812         {
813             if (number.coefficients[$ - 1])
814                 break;
815             number.coefficients = number.coefficients[0 .. $ - 1];
816         }
817         while (number.coefficients.length);
818         return number;
819     }
820 
821     ///ditto
822     BigUIntView!(const W) normalized() const return scope
823     {
824         return lightConst.normalized;
825     }
826 
827     /++
828     +/
829     bool bt()(size_t position) scope
830     {
831         import mir.ndslice.topology: bitwise;
832         assert(position < coefficients.length * W.sizeof * 8);
833         return coefficients.bitwise[position];
834     }
835 
836     /++
837     +/
838     size_t ctlz()() scope const @property
839         @safe pure nothrow @nogc
840     {
841         import mir.bitop: ctlz;
842         assert(coefficients.length);
843         auto d = mostSignificantFirst;
844         size_t ret;
845         do
846         {
847             if (auto c = d[0])
848             {
849                 ret += ctlz(c);
850                 break;
851             }
852             ret += W.sizeof * 8;
853             d = d[1 .. $];
854         }
855         while(d.length);
856         return ret;
857     }
858 
859     /++
860     +/
861     size_t cttz()() scope const @property
862         @safe pure nothrow @nogc
863         in (coefficients.length)
864     {
865         import mir.bitop: cttz;
866         auto d = coefficients[];
867         size_t ret;
868         do
869         {
870             if (auto c = d[0])
871             {
872                 ret += cttz(c);
873                 break;
874             }
875             ret += W.sizeof * 8;
876             d = d[1 .. $];
877         }
878         while(d.length);
879         return ret;
880     }
881 
882     ///
883     BigIntView!W withSign()(bool sign) return scope
884     {
885         return typeof(return)(this, sign);
886     }
887 
888     /++
889     Params:
890         value = (out) unsigned integer
891     Returns: true on success
892     +/
893     bool get(U)(scope out U value)
894         @safe pure nothrow @nogc const
895         if (isUnsigned!U)
896     {
897         auto d = lightConst.mostSignificantFirst;
898         if (d.length == 0)
899             return false;
900         static if (U.sizeof > W.sizeof)
901         {
902             size_t i;
903             for(;;)
904             {
905                 value |= d[0];
906                 d = d[1 .. $];
907                 if (d.length == 0)
908                     return false;
909                 i += cast(bool)value;
910                 value <<= W.sizeof * 8;
911                 import mir.utility: _expect;
912                 if (_expect(i >= U.sizeof / W.sizeof, false))
913                     return true;
914             }
915         }
916         else
917         {
918             for(;;)
919             {
920                 W f = d[0];
921                 d = d[1 .. $];
922                 if (d.length == 0)
923                 {
924                     value = cast(U)f;
925                     static if (U.sizeof < W.sizeof)
926                     {
927                         if (value != f)
928                             return true;
929                     }
930                     return false;
931                 }
932                 if (f)
933                     return true;
934             }
935         }
936     }
937 
938     /++
939     Returns: true if the integer and equals to `rhs`.
940     +/
941     bool opEquals(ulong rhs)
942         @safe pure nothrow @nogc const scope
943     {
944         foreach (d; lightConst.coefficients)
945         {
946             static if (W.sizeof >= ulong.sizeof)
947             {
948                 if (d != rhs)
949                     return false;
950                 rhs = 0;
951             }
952             else
953             {
954                 if (d != (rhs & W.max))
955                     return false;
956                 rhs >>>= W.sizeof * 8;
957             }
958         }
959         return rhs == 0;
960     }
961 
962 
963     static if (isMutable!W && W.sizeof >= 4)
964     /++
965     Params:
966         str = string buffer, the tail paer 
967     Precondition: mutable number with word size at least 4 bytes
968     Postconditoin: the number is destroyed
969     Returns: last N bytes used in the buffer
970     +/
971     size_t toStringImpl(C)(scope C[] str)
972         @safe pure nothrow @nogc
973         if (isSomeChar!C && isMutable!C)
974     {
975         assert(str.length);
976         assert(str.length >= ceilLog10Exp2(coefficients.length * (W.sizeof * 8)));
977 
978         size_t i = str.length;
979         while(coefficients.length > 1)
980         {
981             uint rem = this /= 1_000_000_000;
982             foreach (_; 0 .. 9)
983             {
984                 str[--i] = cast(char)(rem % 10 + '0');
985                 rem /= 10;
986             }
987         }
988 
989         W rem = coefficients.length == 1 ? coefficients[0] : W(0);
990         do
991         {
992             str[--i] = cast(char)(rem % 10 + '0');
993             rem /= 10;
994         }
995         while(rem);
996 
997         return str.length - i;
998     }
999 
1000 }
1001 
1002 ///
1003 version(mir_bignum_test_llv)
1004 @safe pure @nogc
1005 unittest
1006 {
1007     import mir.bignum.integer;
1008 
1009     auto a = BigInt!2("123456789098765432123456789098765432100");
1010     char[ceilLog10Exp2(a.data.length * (size_t.sizeof * 8))] buffer;
1011     auto len = a.view.unsigned.toStringImpl(buffer);
1012     assert(buffer[$ - len .. $] == "123456789098765432123456789098765432100");
1013 }
1014 
1015 ///
1016 version(mir_bignum_test_llv)
1017 @safe pure
1018 unittest
1019 {
1020     import mir.test;
1021     auto view = BigUIntView!size_t.fromHexString!(char, true)("abcd_efab_cdef");
1022     (cast(ulong)view).should == 0xabcd_efab_cdef;
1023 }
1024 
1025 ///
1026 version(mir_bignum_test_llv)
1027 @safe pure
1028 unittest
1029 {
1030     auto a = BigUIntView!size_t.fromHexString("afbbfae3cd0aff2714a1de7022b0029d");
1031     a.smallLeftShiftInPlace(4);
1032     assert(a == BigUIntView!size_t.fromHexString("fbbfae3cd0aff2714a1de7022b0029d0"));
1033     a.smallLeftShiftInPlace(0);
1034     assert(a == BigUIntView!size_t.fromHexString("fbbfae3cd0aff2714a1de7022b0029d0"));
1035 }
1036 
1037 ///
1038 version(mir_bignum_test_llv)
1039 @safe pure
1040 unittest
1041 {
1042     auto a = BigUIntView!size_t.fromHexString("afbbfae3cd0aff2714a1de7022b0029d");
1043     a.smallRightShiftInPlace(4);
1044     assert(a == BigUIntView!size_t.fromHexString("afbbfae3cd0aff2714a1de7022b0029"));
1045 }
1046 
1047 
1048 ///
1049 version(mir_bignum_test_llv)
1050 @safe pure
1051 unittest
1052 {
1053     // Check that invalid underscores in hex literals throw an error.
1054     void expectThrow(const(char)[] input) {
1055         bool caught = false;
1056         try { 
1057             auto view = BigUIntView!size_t.fromHexString!(char, true)(input);
1058         } catch (Exception e) {
1059             caught = true;
1060         }
1061 
1062         assert(caught);
1063     }
1064 
1065     expectThrow("abcd_efab_cef_");
1066     expectThrow("abcd__efab__cef");
1067     expectThrow("_abcd_efab_cdef");
1068     expectThrow("_abcd_efab_cdef_");
1069     expectThrow("_abcd_efab_cdef__");
1070     expectThrow("__abcd_efab_cdef");
1071     expectThrow("__abcd_efab_cdef_");
1072     expectThrow("__abcd_efab_cdef__");
1073     expectThrow("__abcd__efab_cdef__");
1074     expectThrow("__abcd__efab__cdef__");
1075 }
1076 
1077 ///
1078 version(mir_bignum_test_llv)
1079 @safe pure
1080 unittest
1081 {
1082     auto view = BigUIntView!size_t.fromBinaryString!(char, true)("1111_0000_0101");
1083     assert(cast(ulong)view == 0b1111_0000_0101);
1084 }
1085 
1086 ///
1087 version(mir_bignum_test_llv)
1088 @safe pure
1089 unittest
1090 {
1091     // Check that invalid underscores in hex literals throw an error.
1092     void expectThrow(const(char)[] input) {
1093         bool caught = false;
1094         try { 
1095             auto view = BigUIntView!size_t.fromBinaryString!(char, true)(input);
1096         } catch (Exception e) {
1097             caught = true;
1098         }
1099 
1100         assert(caught);
1101     }
1102 
1103     expectThrow("abcd");
1104     expectThrow("0101__1011__0111");
1105     expectThrow("_0101_1011_0111");
1106     expectThrow("_0101_1011_0111_");
1107     expectThrow("_0101_1011_0111__");
1108     expectThrow("__0101_1011_0111_");
1109     expectThrow("__0101_1011_0111__");
1110     expectThrow("__0101__1011_0111__");
1111     expectThrow("__1011__0111__1011__");
1112 }
1113 
1114 ///
1115 version(mir_bignum_test_llv)
1116 unittest
1117 {
1118     auto a = cast(double) BigUIntView!size_t.fromHexString("afbbfae3cd0aff2714a1de7022b0029d");
1119     assert(a == 0xa.fbbfae3cd0bp+124);
1120     assert(cast(double) BigUIntView!size_t.init == 0);
1121     assert(cast(double) BigUIntView!size_t([0]) == 0);
1122 }
1123 
1124 ///
1125 version(mir_bignum_test_llv)
1126 @safe pure
1127 unittest
1128 {
1129     import mir.bignum.fp: Fp;
1130     import mir.bignum.fixed: UInt;
1131 
1132     auto fp = cast(Fp!128) BigUIntView!ulong.fromHexString("afbbfae3cd0aff2714a1de7022b0029d");
1133     assert(fp.exponent == 0);
1134     assert(fp.coefficient == UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"));
1135 
1136     fp = cast(Fp!128) BigUIntView!size_t.fromHexString("ae3cd0aff2714a1de7022b0029d");
1137     assert(fp.exponent == -20);
1138     assert(fp.coefficient == UInt!128.fromHexString("ae3cd0aff2714a1de7022b0029d00000"));
1139 
1140     fp = cast(Fp!128) BigUIntView!size_t.fromHexString("e7022b0029d");
1141     assert(fp.exponent == -84);
1142     assert(fp.coefficient == UInt!128.fromHexString("e7022b0029d000000000000000000000"));
1143 
1144     fp = cast(Fp!128) BigUIntView!size_t.fromHexString("e7022b0029d");
1145     assert(fp.exponent == -84);
1146     assert(fp.coefficient == UInt!128.fromHexString("e7022b0029d000000000000000000000"));
1147 
1148     fp = cast(Fp!128) BigUIntView!size_t.fromHexString("e7022b0029d");
1149     assert(fp.exponent == -84);
1150     assert(fp.coefficient == UInt!128.fromHexString("e7022b0029d000000000000000000000"));
1151 
1152     fp = cast(Fp!128) BigUIntView!size_t.fromHexString("ffffffffffffffffffffffffffffffff1000000000000000");
1153     assert(fp.exponent == 64);
1154     assert(fp.coefficient == UInt!128.fromHexString("ffffffffffffffffffffffffffffffff"));
1155 
1156     fp = cast(Fp!128) BigUIntView!size_t.fromHexString("ffffffffffffffffffffffffffffffff8000000000000000");
1157     assert(fp.exponent == 65);
1158     assert(fp.coefficient == UInt!128.fromHexString("80000000000000000000000000000000"));
1159 
1160     fp = cast(Fp!128) BigUIntView!size_t.fromHexString("fffffffffffffffffffffffffffffffe8000000000000000");
1161     assert(fp.exponent == 64);
1162     assert(fp.coefficient == UInt!128.fromHexString("fffffffffffffffffffffffffffffffe"));
1163 
1164     fp = cast(Fp!128) BigUIntView!size_t.fromHexString("fffffffffffffffffffffffffffffffe8000000000000001");
1165     assert(fp.exponent == 64);
1166     assert(fp.coefficient == UInt!128.fromHexString("ffffffffffffffffffffffffffffffff"));
1167 }
1168 
1169 ///
1170 version(mir_bignum_test_llv)
1171 @safe pure
1172 unittest
1173 {
1174     auto view = BigUIntView!ulong.fromHexString("afbbfae3cd0aff2714a1de7022b0029d");
1175     assert(cast(ulong) view == 0x14a1de7022b0029d);
1176     assert(cast(uint) view == 0x22b0029d);
1177     assert(cast(ubyte) view == 0x9d);
1178 }
1179 version(mir_bignum_test_llv)
1180 @safe pure
1181 unittest
1182 {
1183     auto view = BigUIntView!ushort.fromHexString("afbbfae3cd0aff2714a1de7022b0029d");
1184     assert(cast(ulong) view == 0x14a1de7022b0029d);
1185     assert(cast(uint) view == 0x22b0029d);
1186     assert(cast(ubyte) view == 0x9d);
1187 }
1188 
1189 version(mir_bignum_test_llv)
1190 @safe pure
1191 unittest
1192 {
1193     auto view = BigUIntView!uint.fromHexString("afbbfae3cd0aff2714a1de7022b0029d");
1194     assert(cast(ulong) view == 0x14a1de7022b0029d);
1195     assert(cast(uint) view == 0x22b0029d);
1196     assert(cast(ubyte) view == 0x9d);
1197 }
1198 
1199 
1200 ///
1201 version(mir_bignum_test_llv)
1202 @safe pure nothrow
1203 unittest
1204 {
1205     import std.traits;
1206     alias AliasSeq(T...) = T;
1207 
1208     foreach (T; AliasSeq!(ubyte, ushort, uint, ulong))
1209     {
1210         T[3] lhsData = [1, T.max-1, 0];
1211         T[3] rhsData = [T.max, T.max, 0];
1212 
1213         auto lhs = BigUIntView!T(lhsData).normalized;
1214 
1215         /// bool overflow = bigUInt op= scalar
1216         assert(lhs.coefficients == [1, T.max-1]);
1217         assert(lhs.mostSignificantFirst == [T.max-1, 1]);
1218         static if (T.sizeof >= 4)
1219         {
1220             assert((lhs += T.max) == false);
1221             assert(lhs.coefficients == [0, T.max]);
1222             assert((lhs += T.max) == false);
1223             assert((lhs += T.max) == true); // overflow bit
1224             assert(lhs.coefficients == [T.max-1, 0]);
1225             assert((lhs -= T(1)) == false);
1226             assert(lhs.coefficients == [T.max-2, 0]);
1227             assert((lhs -= T.max) == true); // underflow bit
1228             assert(lhs.coefficients == [T.max-1, T.max]);
1229             assert((lhs -= Signed!T(-4)) == true); // overflow bit
1230             assert(lhs.coefficients == [2, 0]);
1231             assert((lhs += Signed!T.max) == false); // overflow bit
1232             assert(lhs.coefficients == [Signed!T.max + 2, 0]);
1233 
1234             ///  bool overflow = bigUInt op= bigUInt/bigInt
1235             lhs = BigUIntView!T(lhsData);
1236             auto rhs = BigUIntView!T(rhsData).normalized;
1237             assert(lhs.coefficients == [Signed!T.max + 2, 0, 0]);
1238             assert(rhs.coefficients == [T.max, T.max]);
1239             assert((lhs += rhs) == false);
1240             assert(lhs.coefficients == [Signed!T.max + 1, 0, 1]);
1241             assert((lhs -= rhs) == false);
1242             assert(lhs.coefficients == [Signed!T.max + 2, 0, 0]);
1243             assert((lhs += -rhs) == true);
1244             assert(lhs.coefficients == [Signed!T.max + 3, 0, T.max]);
1245             assert((lhs += -(-rhs)) == true);
1246             assert(lhs.coefficients == [Signed!T.max + 2, 0, 0]);
1247 
1248             /// W overflow = bigUInt *= scalar
1249             assert((lhs *= T.max) == 0);
1250             assert((lhs += T(Signed!T.max + 2)) == false);
1251             assert(lhs.coefficients == [0, Signed!T.max + 2, 0]);
1252             lhs = lhs.normalized;
1253             lhs.coefficients[1] = T.max / 2 + 3;
1254             assert(lhs.coefficients == [0, T.max / 2 + 3]);
1255             assert((lhs *= 8u) == 4);
1256             assert(lhs.coefficients == [0, 16]);
1257         }
1258     }
1259 }
1260 
1261 /++
1262 Arbitrary length signed integer view.
1263 +/
1264 struct BigIntView(W)
1265     if (is(Unqual!W == ubyte) || is(Unqual!W == ushort) || is(Unqual!W == uint) || is(Unqual!W == ulong))
1266 {
1267     import mir.bignum.fp: Fp;
1268 
1269     /++
1270     Self-assigned to unsigned integer view $(MREF BigUIntView).
1271 
1272     Sign is stored in the most significant bit.
1273 
1274     The number is encoded as pair of `unsigned` and `sign`.
1275     +/
1276     BigUIntView!W unsigned;
1277 
1278     /++
1279     Sign bit
1280     +/
1281     bool sign;
1282 
1283 @safe:
1284 
1285     ///
1286     inout(W)[] coefficients() inout @property
1287     {
1288         return unsigned.coefficients;
1289     }
1290 
1291     ///
1292     this(W[] coefficients, bool sign = false)
1293     {
1294         this(BigUIntView!W(coefficients), sign);
1295     }
1296 
1297     ///
1298     this(BigUIntView!W unsigned, bool sign = false)
1299     {
1300         this.unsigned = unsigned;
1301         this.sign = sign;
1302     }
1303 
1304     static if (isMutable!W && W.sizeof >= 4)
1305     /++
1306     Returns: false in case of overflow or incorrect string.
1307     Precondition: non-empty coefficients.
1308     +/
1309     bool fromStringImpl(C)(scope const(C)[] str)
1310         scope @trusted pure @nogc nothrow
1311         if (isSomeChar!C)
1312     {
1313         import mir.utility: _expect;
1314 
1315         if (_expect(str.length == 0, false))
1316             return false;
1317 
1318         if (str[0] == '-')
1319         {
1320             sign = true;
1321             str = str[1 .. $];
1322         }
1323         else
1324         if (_expect(str[0] == '+', false))
1325         {
1326             str = str[1 .. $];
1327         }
1328 
1329         if (!unsigned.fromStringImpl(str))
1330             return false;
1331         sign &= unsigned.coefficients.length != 0;
1332         return true;
1333     }
1334 
1335     /++
1336     +/
1337     static BigIntView fromHexString(C, bool allowUnderscores = false)(scope const(C)[] str)
1338         @trusted pure
1339         if (isSomeChar!C)
1340     {
1341         auto length = str.length / (W.sizeof * 2) + (str.length % (W.sizeof * 2) != 0);
1342         auto ret = BigIntView!(Unqual!W)(new Unqual!W[length]);
1343         if (ret.fromHexStringImpl!(C, allowUnderscores)(str))
1344             return  cast(BigIntView) ret;
1345         version(D_Exceptions)
1346             { import mir.exception : toMutable; throw hexStringException.toMutable; }
1347         else
1348             assert(0, hexStringErrorMsg);
1349     }
1350 
1351     static if (isMutable!W)
1352     /++
1353     +/
1354     bool fromHexStringImpl(C, bool allowUnderscores = false)(scope const(C)[] str)
1355         @safe pure @nogc nothrow
1356         if (isSomeChar!C)
1357     {
1358         pragma(inline, false);
1359         import mir.utility: _expect;
1360 
1361         assert(unsigned.coefficients.length);
1362 
1363         if (_expect(str.length == 0, false))
1364             return false;
1365 
1366         sign = false;
1367 
1368         if (str[0] == '-')
1369         {
1370             sign = true;
1371             str = str[1 .. $];
1372         }
1373         else
1374         if (_expect(str[0] == '+', false))
1375         {
1376             str = str[1 .. $];
1377         }
1378 
1379         auto ret = unsigned.fromHexStringImpl!(C, allowUnderscores)(str);
1380         sign = sign && unsigned.coefficients.length;
1381         return ret;
1382     }
1383 
1384     /++
1385     +/
1386     static BigIntView fromBinaryString(C, bool allowUnderscores = false)(scope const(C)[] str)
1387         @trusted pure
1388         if (isSomeChar!C)
1389     {
1390         auto length = str.length / (W.sizeof * 8) + (str.length % (W.sizeof * 8) != 0);
1391         auto ret = BigIntView!(Unqual!W)(new Unqual!W[length]);
1392         if (ret.fromBinaryStringImpl!(C, allowUnderscores)(str))
1393             return cast(BigIntView) ret;
1394         version(D_Exceptions)
1395             { import mir.exception : toMutable; throw binaryStringException.toMutable; }
1396         else
1397             assert(0, binaryStringErrorMsg);
1398     }
1399 
1400     static if (isMutable!W)
1401     /++
1402     +/
1403     bool fromBinaryStringImpl(C, bool allowUnderscores = false)(scope const(C)[] str)
1404         @safe pure @nogc nothrow
1405         if (isSomeChar!C)
1406     {
1407         pragma(inline, false);
1408         import mir.utility: _expect;
1409 
1410         assert(unsigned.coefficients.length);
1411 
1412         if (_expect(str.length == 0, false))
1413             return false;
1414 
1415         sign = false;
1416 
1417         if (str[0] == '-')
1418         {
1419             sign = true;
1420             str = str[1 .. $];
1421         }
1422         else
1423         if (_expect(str[0] == '+', false))
1424         {
1425             str = str[1 .. $];
1426         }
1427 
1428         auto ret = unsigned.fromBinaryStringImpl!(C, allowUnderscores)(str);
1429         sign = sign && unsigned.coefficients.length;
1430         return ret;
1431     }
1432 
1433     ///
1434     T opCast(T)() const scope
1435         if (isFloatingPoint!T && isMutable!T)
1436     {
1437         auto ret = this.unsigned.opCast!T;
1438         if (sign)
1439             ret = -ret;
1440         return ret;
1441     }
1442 
1443     ///
1444     T opCast(T, bool nonZero = false)() const scope
1445         if (is(T == long) || is(T == int))
1446     {
1447         auto ret = this.unsigned.opCast!(Unsigned!T, nonZero);
1448         if (sign)
1449             ret = -ret;
1450         return ret;
1451     }
1452 
1453     static if (W.sizeof == size_t.sizeof)
1454     ///
1455     version(mir_bignum_test)
1456     @safe pure
1457     unittest
1458     {
1459         auto view = BigIntView!size_t.fromHexString("-afbbfae3cd0aff2714a1de7022b0021d");
1460         assert(cast(long) view == -0x14a1de7022b0021d);
1461         assert(cast(int) view == -0x22b0021d);
1462     }
1463 
1464     static if (W.sizeof == size_t.sizeof)
1465     ///
1466     version(mir_bignum_test)
1467     @safe pure
1468     unittest
1469     {
1470         auto view = BigIntView!size_t.fromHexString!(char, true)("-afbb_fae3_cd0a_ff27_14a1_de70_22b0_021d");
1471         assert(cast(long) view == -0x14a1de7022b0021d);
1472         assert(cast(int) view == -0x22b0021d);
1473     }
1474 
1475     static if (W.sizeof == size_t.sizeof)
1476     version(mir_bignum_test)
1477     @safe pure
1478     unittest
1479     {
1480         auto view = BigIntView!ushort.fromHexString("-afbbfae3cd0aff2714a1de7022b0021d");
1481         assert(cast(long) view == -0x14a1de7022b0021d);
1482         assert(cast(int) view == -0x22b0021d);
1483     }
1484     
1485     static if (W.sizeof == size_t.sizeof)
1486     version(mir_bignum_test_llv)
1487     @safe pure
1488     unittest
1489     {
1490         auto view = BigIntView!ushort.fromHexString!(char, true)("-afbb_fae3_cd0a_ff27_14a1_de70_22b0_021d");
1491         assert(cast(long) view == -0x14a1de7022b0021d);
1492         assert(cast(int) view == -0x22b0021d);
1493     }
1494 
1495     static if (W.sizeof == size_t.sizeof)
1496     version(mir_bignum_test)
1497     @safe pure
1498     unittest
1499     {
1500         auto view = BigIntView!ubyte.fromHexString("-afbbfae3cd0aff2714a1de7022b0021d");
1501         assert(cast(long) view == -0x14a1de7022b0021d);
1502         assert(cast(int) view == -0x22b0021d);
1503     }
1504 
1505     static if (W.sizeof == size_t.sizeof)
1506     version(mir_bignum_test)
1507     @safe pure
1508     unittest
1509     {
1510         auto view = BigIntView!ubyte.fromHexString!(char, true)("-afbb_fae3_cd0a_ff27_14a1_de70_22b0_021d");
1511         assert(cast(long) view == -0x14a1de7022b0021d);
1512         assert(cast(int) view == -0x22b0021d);
1513     }
1514 
1515     static if (W.sizeof == size_t.sizeof)
1516     version(mir_bignum_test)
1517     @safe pure
1518     unittest
1519     {
1520         auto view = BigIntView!size_t.fromBinaryString!(char, true)("-10101111101110111111101011100011110011010000101011111111001001110001010010100001110111100111000000100010101100000000001000011101");
1521         assert(cast(long) view == -0x14a1de7022b0021d);
1522         assert(cast(int) view == -0x22b0021d);
1523     }
1524 
1525     static if (W.sizeof == size_t.sizeof)
1526     version(mir_bignum_test)
1527     @safe pure
1528     unittest
1529     {
1530         auto view = BigIntView!size_t.fromBinaryString!(char, true)("-1010_1111_1011_1011_1111_1010_1110_0011_1100_1101_0000_1010_1111_1111_0010_0111_0001_0100_1010_0001_1101_1110_0111_0000_0010_0010_1011_0000_0000_0010_0001_1101");
1531         assert(cast(long) view == -0x14a1de7022b0021d);
1532         assert(cast(int) view == -0x22b0021d);
1533     }
1534 
1535     static if (W.sizeof == size_t.sizeof)
1536     version(mir_bignum_test)
1537     @safe pure
1538     unittest
1539     {
1540         auto view = BigIntView!ushort.fromBinaryString!(char, true)("-10101111101110111111101011100011110011010000101011111111001001110001010010100001110111100111000000100010101100000000001000011101");
1541         assert(cast(long) view == -0x14a1de7022b0021d);
1542         assert(cast(int) view == -0x22b0021d);
1543     }
1544 
1545     static if (W.sizeof == size_t.sizeof)
1546     version(mir_bignum_test)
1547     @safe pure
1548     unittest
1549     {
1550         auto view = BigIntView!ushort.fromBinaryString!(char, true)("-1010_1111_1011_1011_1111_1010_1110_0011_1100_1101_0000_1010_1111_1111_0010_0111_0001_0100_1010_0001_1101_1110_0111_0000_0010_0010_1011_0000_0000_0010_0001_1101");
1551         assert(cast(long) view == -0x14a1de7022b0021d);
1552         assert(cast(int) view == -0x22b0021d);
1553     }
1554 
1555     static if (W.sizeof == size_t.sizeof)
1556     version(mir_bignum_test)
1557     @safe pure
1558     unittest
1559     {
1560         auto view = BigIntView!ubyte.fromBinaryString!(char, true)("-10101111101110111111101011100011110011010000101011111111001001110001010010100001110111100111000000100010101100000000001000011101");
1561         assert(cast(long) view == -0x14a1de7022b0021d);
1562         assert(cast(int) view == -0x22b0021d);
1563     }
1564 
1565     static if (W.sizeof == size_t.sizeof)
1566     version(mir_bignum_test_llv)
1567     @safe pure
1568     unittest
1569     {
1570         auto view = BigIntView!ubyte.fromBinaryString!(char, true)("-1010_1111_1011_1011_1111_1010_1110_0011_1100_1101_0000_1010_1111_1111_0010_0111_0001_0100_1010_0001_1101_1110_0111_0000_0010_0010_1011_0000_0000_0010_0001_1101");
1571         assert(cast(long) view == -0x14a1de7022b0021d);
1572         assert(cast(int) view == -0x22b0021d);
1573     }
1574 
1575     /++
1576     +/
1577     T opCast(T : Fp!coefficientSize, uint coefficientSize)() const scope
1578     {
1579         auto ret = unsigned.opCast!(Fp!coefficientSize);
1580         ret.sign = sign;
1581         return ret;
1582     }
1583 
1584     ///
1585     BigIntView!V opCast(T : BigIntView!V, V)() return scope
1586         if (V.sizeof <= W.sizeof)
1587     {
1588         return typeof(return)(this.unsigned.opCast!(BigUIntView!V), sign);
1589     }
1590 
1591     ///
1592     BigIntView!V opCast(T : BigIntView!V, V)() const return scope
1593         if (V.sizeof <= W.sizeof)
1594     {
1595         return typeof(return)(this.unsigned.opCast!(BigUIntView!V), sign);
1596     }
1597 
1598     ///
1599     BigIntView!(const W) lightConst()() return scope
1600         const @safe pure nothrow @nogc @property
1601     {
1602         return typeof(return)(unsigned.lightConst, sign);
1603     }
1604 
1605     ///ditto
1606     alias lightConst this;
1607 
1608     /++
1609     +/
1610     sizediff_t opCmp(BigIntView!(const W) rhs) 
1611         const @safe pure nothrow @nogc scope
1612     {
1613         import mir.algorithm.iteration: cmp;
1614         if (auto s = rhs.sign - this.sign)
1615         {
1616             if (this.unsigned.coefficients.length && rhs.unsigned.coefficients.length)
1617                 return s;
1618         }
1619         auto d = this.unsigned.opCmp(rhs.unsigned);
1620         return sign ? -d : d;
1621     }
1622 
1623     ///
1624     bool opEquals(BigIntView!(const W) rhs)
1625         const @safe pure nothrow @nogc scope
1626     {
1627         return (this.sign == rhs.sign || unsigned.coefficients.length == 0) && this.unsigned == rhs.unsigned;
1628     }
1629 
1630     /++
1631     Returns: true if the integer and equals to `rhs`.
1632     +/
1633     bool opEquals(long rhs)
1634         @safe pure nothrow @nogc const scope
1635     {
1636         if (rhs == 0 && unsigned.coefficients.length == 0)
1637             return true;
1638         bool sign = rhs < 0;
1639         ulong urhs = sign ? -rhs : rhs;
1640         return sign == this.sign && unsigned == urhs;
1641     }
1642 
1643     /++
1644     +/
1645     BigIntView topMostSignificantPart(size_t length)
1646     {
1647         return BigIntView(unsigned.topMostSignificantPart(length), sign);
1648     }
1649 
1650     /++
1651     +/
1652     BigIntView topLeastSignificantPart(size_t length)
1653     {
1654         return BigIntView(unsigned.topLeastSignificantPart(length), sign);
1655     }
1656 
1657     static if (isMutable!W && W.sizeof >= 4)
1658     /++
1659     Performs `bool overflow = big +(-)= big` operatrion.
1660     Params:
1661         rhs = value to add with non-empty coefficients
1662         overflow = (overflow) initial iteration overflow
1663     Precondition: non-empty coefficients length of greater or equal to the `rhs` coefficients length.
1664     Returns:
1665         true in case of unsigned overflow
1666     +/
1667     bool opOpAssign(string op)(scope BigIntView!(const W) rhs, bool overflow = false)
1668     @safe pure nothrow @nogc
1669         if (op == "+" || op == "-")
1670     {
1671         assert(rhs.coefficients.length > 0);
1672         import mir.conv;
1673         debug assert(this.coefficients.length >= rhs.coefficients.length, this.coefficients.length.to!string ~ " " ~ rhs.coefficients.length.to!string);
1674         enum sum = op == "+";
1675         // pos += pos
1676         // neg += neg
1677         // neg -= pos
1678         // pos -= neg
1679         if ((sign == rhs.sign) == sum)
1680             return unsigned.opOpAssign!"+"(rhs.unsigned, overflow);
1681         // pos -= pos
1682         // pos += neg
1683         // neg += pos
1684         // neg -= neg
1685         if (unsigned.opOpAssign!"-"(rhs.unsigned, overflow))
1686         {
1687             sign = !sign;
1688             unsigned.twoComplementInPlace;
1689         }
1690         return false;
1691     }
1692 
1693     static if (isMutable!W && W.sizeof >= 4)
1694     /// ditto
1695     bool opOpAssign(string op)(scope BigUIntView!(const W) rhs, bool overflow = false)
1696     @safe pure nothrow @nogc
1697         if (op == "+" || op == "-")
1698     {
1699         return opOpAssign!op(rhs.signed, overflow);
1700     }
1701 
1702     static if (isMutable!W && W.sizeof >= 4)
1703     /++
1704     Performs `bool overflow = big +(-)= scalar` operatrion.
1705     Precondition: non-empty coefficients
1706     Params:
1707         rhs = value to add
1708     Returns:
1709         true in case of unsigned overflow
1710     +/
1711     bool opOpAssign(string op, T)(const T rhs)
1712         @safe pure nothrow @nogc
1713         if ((op == "+" || op == "-") && is(T == Signed!W))
1714     {
1715         assert(this.coefficients.length > 0);
1716         enum sum = op == "+";
1717         // pos += pos
1718         // neg += neg
1719         // neg -= pos
1720         // pos -= neg
1721         auto urhs = cast(W) (rhs < 0 ? -rhs : rhs);
1722         if ((sign == (rhs < 0)) == sum)
1723             return unsigned.opOpAssign!"+"(urhs);
1724         // pos -= pos
1725         // pos += neg
1726         // neg += pos
1727         // neg -= neg
1728         if (unsigned.opOpAssign!"-"(urhs))
1729         {
1730             sign = !sign;
1731             unsigned.twoComplementInPlace;
1732         }
1733         return false;
1734     }
1735 
1736     static if (isMutable!W && W.sizeof >= 4)
1737     /// ditto
1738     bool opOpAssign(string op, T)(const T rhs)
1739         @safe pure nothrow @nogc
1740         if ((op == "+" || op == "-") && is(T == W))
1741     {
1742         assert(this.coefficients.length > 0);
1743         enum sum = op == "+";
1744         // pos += pos
1745         // neg -= pos
1746         if ((sign == false) == sum)
1747             return unsigned.opOpAssign!"+"(rhs);
1748         // pos -= pos
1749         // neg += pos
1750         if (unsigned.opOpAssign!"-"(rhs))
1751         {
1752             sign = !sign;
1753             unsigned.twoComplementInPlace;
1754         }
1755         return false;
1756     }
1757 
1758     static if (isMutable!W && W.sizeof >= 4)
1759     /++
1760     Performs `W overflow = (big += overflow) *= scalar` operatrion.
1761     Precondition: non-empty coefficients
1762     Params:
1763         rhs = unsigned value to multiply by
1764         overflow = initial overflow
1765     Returns:
1766         unsigned overflow value
1767     +/
1768     W opOpAssign(string op : "*")(W rhs, W overflow = 0u)
1769         @safe pure nothrow @nogc
1770     {
1771         return unsigned.opOpAssign!op(rhs, overflow);
1772     }
1773 
1774     /++
1775     Returns: the same intger view with inversed sign
1776     +/
1777     BigIntView opUnary(string op : "-")()
1778     {
1779         return BigIntView(unsigned, !sign);
1780     }
1781 
1782     /++
1783     Returns: a slice of coefficients starting from the least significant.
1784     +/
1785     auto coefficients()
1786         @safe pure nothrow @nogc @property
1787     {
1788         return unsigned.coefficients;
1789     }
1790 
1791     /++
1792     Returns: a slice of coefficients starting from the most significant.
1793     +/
1794     auto mostSignificantFirst()
1795         @safe pure nothrow @nogc @property
1796     {
1797         return unsigned.mostSignificantFirst;
1798     }
1799 
1800     /++
1801     Strips zero most significant coefficients.
1802     Strips most significant zero coefficients.
1803     Sets sign to zero if no coefficients were left.
1804     +/
1805     BigIntView normalized() return scope
1806     {
1807         auto number = this;
1808         number.unsigned = number.unsigned.normalized;
1809         number.sign = number.coefficients.length == 0 ? false : number.sign;
1810         return number;
1811     }
1812 
1813     ///ditto
1814     BigIntView!(const W) normalized() const return scope
1815     {
1816         return lightConst.normalized;
1817     }
1818 }
1819 
1820 ///
1821 version(mir_bignum_test)
1822 @safe pure
1823 unittest
1824 {
1825     import mir.bignum.fixed: UInt;
1826     import mir.bignum.fp: Fp;
1827 
1828     auto fp = cast(Fp!128) BigIntView!size_t.fromHexString("-afbbfae3cd0aff2714a1de7022b0029d");
1829     assert(fp.sign);
1830     assert(fp.exponent == 0);
1831     assert(fp.coefficient == UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"));
1832 }
1833 
1834 ///
1835 version(mir_bignum_test)
1836 @safe pure
1837 unittest
1838 {
1839     auto a = cast(double) BigIntView!size_t.fromHexString("-afbbfae3cd0aff2714a1de7022b0029d");
1840     assert(a == -0xa.fbbfae3cd0bp+124);
1841 }
1842 
1843 ///
1844 version(mir_bignum_test)
1845 @safe pure
1846 unittest
1847 {
1848     auto a = cast(double) BigIntView!size_t.fromBinaryString("-10101111101110111111101011100011110011010000101011111111001001110001010010100001110111100111000000100010101100000000001010011101");
1849     assert(a == -0xa.fbbfae3cd0bp+124);
1850 }
1851 
1852 ///
1853 version(mir_bignum_test)
1854 @safe pure
1855 unittest
1856 {
1857     auto a = cast(double) BigIntView!size_t.fromHexString!(char, true)("-afbb_fae3_cd0a_ff27_14a1_de70_22b0_029d");
1858     assert(a == -0xa.fbbfae3cd0bp+124);
1859 }
1860 
1861 ///
1862 version(mir_bignum_test)
1863 @safe pure
1864 unittest
1865 {
1866     auto a = cast(double) BigIntView!size_t.fromBinaryString!(char, true)("-1010_1111_1011_1011_1111_1010_1110_0011_1100_1101_0000_1010_1111_1111_0010_0111_0001_0100_1010_0001_1101_1110_0111_0000_0010_0010_1011_0000_0000_0010_1001_1101");
1867     assert(a == -0xa.fbbfae3cd0bp+124);
1868 }
1869 
1870 version(mir_bignum_test_llv)
1871 @safe pure
1872 unittest
1873 {
1874     import mir.bignum.fixed: UInt;
1875     import mir.bignum.fp: Fp;
1876 
1877     auto fp = cast(Fp!128) BigIntView!size_t.fromHexString!(char, true)("-afbb_fae3_cd0a_ff27_14a1_de70_22b0_029d");
1878     assert(fp.sign);
1879     assert(fp.exponent == 0);
1880     assert(fp.coefficient == UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"));
1881 }
1882 
1883 ///
1884 version(mir_bignum_test_llv)
1885 @safe pure nothrow
1886 unittest
1887 {
1888     import std.traits;
1889     alias AliasSeq(T...) = T;
1890 
1891     foreach (T; AliasSeq!(ubyte, ushort, uint, ulong))
1892     {
1893         T[3] lhsData = [1, T.max-1, 0];
1894         T[3] rhsData = [T.max, T.max, 0];
1895 
1896         auto lhs = BigIntView!T(lhsData).normalized;
1897 
1898         ///  bool overflow = bigUInt op= scalar
1899         assert(lhs.coefficients == [1, T.max-1]);
1900         assert(lhs.mostSignificantFirst == [T.max-1, 1]);
1901 
1902         static if (T.sizeof >= 4)
1903         {
1904 
1905             assert((lhs += T.max) == false);
1906             assert(lhs.coefficients == [0, T.max]);
1907             assert((lhs += T.max) == false);
1908             assert((lhs += T.max) == true); // overflow bit
1909             assert(lhs.coefficients == [T.max-1, 0]);
1910             assert((lhs -= T(1)) == false);
1911             assert(lhs.coefficients == [T.max-2, 0]);
1912             assert((lhs -= T.max) == false);
1913             assert(lhs.coefficients == [2, 0]);
1914             assert(lhs.sign);
1915             assert((lhs -= Signed!T(-4)) == false);
1916             assert(lhs.coefficients == [2, 0]);
1917             assert(lhs.sign == false);
1918             assert((lhs += Signed!T.max) == false);
1919             assert(lhs.coefficients == [Signed!T.max + 2, 0]);
1920 
1921             ///  bool overflow = bigUInt op= bigUInt/bigInt
1922             lhs = BigIntView!T(lhsData);
1923             auto rhs = BigUIntView!T(rhsData).normalized;
1924             assert(lhs.coefficients == [Signed!T.max + 2, 0, 0]);
1925             assert(rhs.coefficients == [T.max, T.max]);
1926             assert((lhs += rhs) == false);
1927             assert(lhs.coefficients == [Signed!T.max + 1, 0, 1]);
1928             assert((lhs -= rhs) == false);
1929             assert(lhs.coefficients == [Signed!T.max + 2, 0, 0]);
1930             assert((lhs += -rhs) == false);
1931             assert(lhs.sign);
1932             assert(lhs.coefficients == [T.max - (Signed!T.max + 2), T.max, 0]);
1933             assert(lhs.sign);
1934             assert((lhs -= -rhs) == false);
1935             assert(lhs.coefficients == [Signed!T.max + 2, 0, 0]);
1936             assert(lhs.sign == false);
1937         }
1938     }
1939 }
1940 
1941 ///
1942 version(mir_bignum_test_llv)
1943 unittest
1944 {
1945     import mir.bignum.fixed: UInt;
1946     import mir.bignum.low_level_view: BigUIntView;
1947     auto bigView = BigUIntView!size_t.fromHexString("55a325ad18b2a77120d870d987d5237473790532acab45da44bc07c92c92babf0b5e2e2c7771cd472ae5d7acdb159a56fbf74f851a058ae341f69d1eb750d7e3");
1948     auto fixed = UInt!256.fromHexString("55e5669576d31726f4a9b58a90159de5923adc6c762ebd3c4ba518d495229072");
1949     auto overflow = bigView *= fixed;
1950     assert(overflow == UInt!256.fromHexString("1cbbe8c42dc21f936e4ce5b2f52ac404439857f174084012fcd1b71fdec2a398"));
1951     assert(bigView == BigUIntView!size_t.fromHexString("c73fd2b26f2514c103c324943b6c90a05d2732118d5f0099c36a69a8051bb0573adc825b5c9295896c70280faa4c4d369df8e92f82bfffafe078b52ae695d316"));
1952 
1953 }
1954 
1955 ///
1956 version(mir_bignum_test_llv)
1957 unittest
1958 {
1959     import mir.bignum.fixed: UInt;
1960     import mir.bignum.low_level_view: BigUIntView;
1961     auto bigView2 = BigUIntView!size_t.fromHexString("55a325ad18b2a77120d870d987d5237473790532acab45da44bc07c92c92babf0b5e2e2c7771cd472ae5d7acdb159a56fbf74f851a058ae341f69d1eb750d7e3");
1962     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");
1963     auto fixed = UInt!256.fromHexString!(true)("55e5_6695_76d3_1726_f4a9_b58a_9015_9de5_923a_dc6c_762e_bd3c_4ba5_18d4_9522_9072");
1964     auto overflow = bigView *= fixed;
1965     assert(overflow == UInt!256.fromHexString("1cbbe8c42dc21f936e4ce5b2f52ac404439857f174084012fcd1b71fdec2a398"));
1966     assert(bigView == BigUIntView!size_t.fromHexString("c73fd2b26f2514c103c324943b6c90a05d2732118d5f0099c36a69a8051bb0573adc825b5c9295896c70280faa4c4d369df8e92f82bfffafe078b52ae695d316"));
1967 }
1968 
1969 /++
1970 +/
1971 struct DecimalView(W)
1972     if (is(Unqual!W == size_t))
1973 {
1974     import mir.parse: DecimalExponentKey;
1975 
1976     ///
1977     bool sign;
1978     ///
1979     long exponent;
1980     ///
1981     BigUIntView!W coefficient;
1982 
1983 @safe:
1984 
1985     static if (is(W == size_t))
1986     /++
1987     Returns: false in case of overflow or incorrect string.
1988     Precondition: non-empty coefficients
1989     +/
1990     bool fromStringImpl(C,
1991         bool allowSpecialValues = true,
1992         bool allowDotOnBounds = true,
1993         bool allowDExponent = true,
1994         bool allowStartingPlus = true,
1995         bool allowUnderscores = true,
1996         bool allowLeadingZeros = true,
1997         bool allowExponent = true,
1998         bool checkEmpty = true,
1999         )
2000         (scope const(C)[] str, out DecimalExponentKey key, int exponentShift = 0)
2001         scope @trusted pure @nogc nothrow
2002         if (isSomeChar!C)
2003         in (coefficient.length)
2004     {
2005         pragma(inline, false);
2006 
2007         import mir.utility: _expect;
2008 
2009         BigUIntView!W work;
2010 
2011         bool mullAdd(W rhs, W overflow)
2012         {
2013             import mir.stdio;
2014             // debug dump(rhs, overflow, work);
2015             if ((overflow = work.opOpAssign!"*"(rhs, overflow)) != 0)
2016             {
2017                 if (_expect(work.coefficients.length < coefficient.coefficients.length, true))
2018                 {
2019                     work = coefficient.topLeastSignificantPart(work.coefficients.length + 1);
2020                     work.coefficients[$ - 1] = overflow;
2021                 // debug dump(overflow, work);
2022                     return false;
2023                 }
2024                 return true;
2025             }
2026                 // debug dump(overflow, work);
2027             return false;
2028         }
2029 
2030         import mir.bignum.internal.parse: decimalFromStringImpl;
2031         alias impl = decimalFromStringImpl!(mullAdd, W);
2032         alias specification = impl!(C,
2033             allowSpecialValues,
2034             allowDotOnBounds,
2035             allowDExponent,
2036             allowStartingPlus,
2037             allowUnderscores,
2038             allowLeadingZeros,
2039             allowExponent,
2040             checkEmpty,
2041         );
2042         exponent += exponentShift;
2043         auto ret = specification(str, key, exponent, sign);
2044         switch (key)
2045         {
2046             case DecimalExponentKey.infinity:
2047                 exponent = exponent.max;
2048                 coefficient = coefficient.topLeastSignificantPart(0);
2049                 return ret;
2050             case DecimalExponentKey.nan:
2051                 exponent = exponent.max;
2052                 coefficient = coefficient.topLeastSignificantPart(1);
2053                 coefficient.coefficients[0] = 1;
2054                 return ret;
2055             default:
2056                 coefficient = work;
2057                 return ret;
2058         }
2059     }
2060 
2061     ///
2062     DecimalView!(const W) lightConst()() return scope
2063         const @safe pure nothrow @nogc @property
2064     {
2065         return typeof(return)(sign, exponent, coefficient.lightConst);
2066     }
2067     ///ditto
2068     alias lightConst this;
2069 
2070     /++
2071     +/
2072     BigIntView!W signedCoefficient()
2073     {
2074         return typeof(return)(coefficient, sign);
2075     }
2076 
2077     static if (W.sizeof >= size_t.sizeof)
2078     /++
2079     Mir parsing supports up-to quadruple precision. The conversion error is 0 ULP for normal numbers. 
2080     Subnormal numbers with an exponent greater than or equal to -512 have upper error bound equal to 1 ULP.
2081     +/
2082     T opCast(T, bool wordNormalized = false)() const scope
2083         if (isFloatingPoint!T && isMutable!T)
2084     {
2085         import mir.bignum.internal.dec2float;
2086 
2087         auto coeff = coefficient.lightConst;
2088 
2089         static if (!wordNormalized)
2090             coeff = coeff.normalized;
2091         auto ret = exponent != exponent.max ?
2092             coeff.coefficients.decimalTo!T(exponent) :
2093             coeff.length ? T.nan : T.infinity;
2094         if (sign)
2095             ret = -ret;
2096         return ret;
2097     }
2098 }
2099 
2100 version(none)
2101 ///
2102 unittest
2103 {
2104     {
2105         auto view = DecimalView!ulong(false, -8, BigUIntView!ulong.fromHexString("BEBC2000000011E1A3"));
2106         auto coeff = (cast(BigUIntView!uint)view.coefficient).lightConst;
2107         assert (algoM!double(0.0, coeff, cast(int)view.exponent) == 3.518437208883201171875E+013);
2108     }
2109 
2110     // TBD: triggers underflow
2111     // {
2112     //     auto view = DecimalView!ulong(false, 0, BigUIntView!ulong.fromHexString("88BF4748507FB9900ADB624CCFF8D78897DC900FB0460327D4D86D327219"));
2113     //     auto coeff = (cast(BigUIntView!uint)view.coefficient).lightConst;
2114     //     debug {
2115     //         import std.stdio;
2116     //         writefln("%s", algoM!float(0.0, coeff, cast(int)view.exponent));
2117     //         writefln("%s", algoM!double(0.0, coeff, cast(int)view.exponent));
2118     //     }
2119     //     assert (algoM!float(0.0, coeff, cast(int)view.exponent) == float.infinity);
2120     //     assert (algoM!double(0.0, coeff, cast(int)view.exponent) == 0x1.117e8e90a0ff7p+239);
2121     // }
2122 
2123     {
2124         auto view = DecimalView!ulong(false, -324, BigUIntView!ulong.fromHexString("4F0CEDC95A718E"));
2125         auto coeff = (cast(BigUIntView!uint)view.coefficient).lightConst;
2126         assert (algoM!float(0.0, coeff, cast(int)view.exponent) == 0);
2127         assert (algoM!double(0.0, coeff, cast(int)view.exponent) == 2.2250738585072014e-308);
2128     }
2129 }
2130 
2131 ///
2132 version(mir_bignum_test_llv)
2133 unittest
2134 {
2135     import mir.test;
2136 
2137     auto view = DecimalView!size_t(false, -8, BigUIntView!size_t.fromHexString("BEBC2000000011E1A3"));
2138 
2139     should(cast(float)view) == 3.518437208883201171875E+013f;
2140     should(cast(double)view) == 3.518437208883201171875E+013;
2141     static if (real.mant_dig >= 64)
2142         should(cast(real)view) == 3.518437208883201171875E+013L;
2143 
2144     view = DecimalView!size_t(true, -169, BigUIntView!size_t.fromHexString("5A174AEDA65CC"));
2145     should(cast(float)view) == -0;
2146     should(cast(double)view) == -0x1.1p-511;
2147     static if (real.mant_dig >= 64)
2148         should(cast(real)view) == -0x8.80000000000019fp-514L;
2149 
2150     view = DecimalView!size_t(true, 293, BigUIntView!size_t.fromHexString("36496F6C4ED38"));
2151     should(cast(float)view) == -float.infinity;
2152     should(cast(double)view) == -9.55024478104888e+307;
2153     static if (real.mant_dig >= 64)
2154         should(cast(real)view) == -9.55024478104888e+307L;
2155 
2156     view = DecimalView!size_t(false, 0, BigUIntView!size_t.fromHexString("1"));
2157     should(cast(float)view) == 1;
2158     should(cast(double)view) == 1;
2159     static if (real.mant_dig >= 64)
2160         should(cast(real)view) == 1L;
2161 
2162     view = DecimalView!size_t(false, -5, BigUIntView!size_t.fromHexString("3"));
2163     should(cast(float)view) == 3e-5f;
2164     should(cast(double)view) == 3e-5;
2165     static if (real.mant_dig >= 64)
2166         should(cast(real)view) == 3e-5L;
2167 
2168     view = DecimalView!size_t(false, -1, BigUIntView!size_t.fromHexString("1"));
2169     should(cast(float)view) == 0.1f;
2170     should(cast(double)view) == 0.1;
2171     static if (real.mant_dig >= 64)
2172         should(cast(real)view) == 0.1L;
2173 
2174     view = DecimalView!size_t(false, 0, BigUIntView!size_t.fromHexString("3039"));
2175     should(cast(float)view) == 12345.0f;
2176     should(cast(double)view) == 12345.0;
2177     static if (real.mant_dig >= 64)
2178         should(cast(real)view) == 12345.0L;
2179 
2180     view = DecimalView!size_t(false, -7, BigUIntView!size_t.fromHexString("98967F"));
2181     should(cast(float)view) == 0.9999999f;
2182     should(cast(double)view) == 0.9999999;
2183     static if (real.mant_dig >= 64)
2184         should(cast(real)view) == 0.9999999L;
2185 
2186     view = DecimalView!size_t(false, -324, BigUIntView!size_t.fromHexString("4F0CEDC95A718E"));
2187     should(cast(float)view) == 0;
2188     should(cast(double)view) == 2.2250738585072014e-308;
2189     static if (real.mant_dig >= 64)
2190         should(cast(real)view) == 2.2250738585072014e-308L;
2191 
2192     view = DecimalView!size_t(false, 0, BigUIntView!size_t.fromHexString("1FFFFFFFFFFFFFFFD"));
2193     should(cast(float)view) == 36893488147419103229f;
2194     should(cast(double)view) == 36893488147419103229.0;
2195     static if (real.mant_dig >= 64)
2196         should(cast(real)view) == 0x1FFFFFFFFFFFFFFFDp0L;
2197 
2198     view = DecimalView!size_t(false, -33, BigUIntView!size_t.fromHexString("65"));
2199     should(cast(float)view) == 101e-33f;
2200     should(cast(double)view) == 101e-33;
2201     static if (real.mant_dig >= 64)
2202         should(cast(real)view) == 101e-33L;
2203 
2204     view = DecimalView!size_t(false, 23, BigUIntView!size_t.fromHexString("1"));
2205     should(cast(float)view) == 1e23f;
2206     should(cast(double)view) == 1e23;
2207     static if (real.mant_dig >= 64)
2208         should(cast(real)view) == 1e23L;
2209 
2210     view = DecimalView!size_t(false, 23, BigUIntView!size_t.fromHexString("81B"));
2211     should(cast(float)view) == 2075e23f;
2212     should(cast(double)view) == 0xaba3d58a1f1a98p+32;
2213     static if (real.mant_dig >= 64)
2214         should(cast(real)view) == 0xaba3d58a1f1a9cp+32L;
2215 
2216     view = DecimalView!size_t(false, -23, BigUIntView!size_t.fromHexString("2209"));
2217     should(cast(float)view) == 8713e-23f;
2218     should(cast(double)view) == 0x1.9b75b4e7de2b9p-64;
2219     static if (real.mant_dig >= 64)
2220         should(cast(real)view) == 0xc.dbada73ef15c401p-67L;
2221 
2222     view = DecimalView!size_t(false, 300, BigUIntView!size_t.fromHexString("1"));
2223     should(cast(float)view) == float.infinity;
2224     should(cast(double)view) == 0x1.7e43c8800759cp+996;
2225     static if (real.mant_dig >= 64)
2226         should(cast(real)view) == 0xb.f21e44003acdd2dp+993L;
2227 
2228     view = DecimalView!size_t(false, 245, BigUIntView!size_t.fromHexString("B3A73CEB227"));
2229     should(cast(float)view) == float.infinity;
2230     should(cast(double)view) == 0x1.48e3735333cb6p+857;
2231     static if (real.mant_dig >= 64)
2232         should(cast(real)view) == 0xa.471b9a999e5b01ep+854L;
2233 
2234     view = DecimalView!size_t(false, 0, BigUIntView!size_t.fromHexString("88BF4748507FB9900ADB624CCFF8D78897DC900FB0460327D4D86D327219"));
2235     should(cast(float)view) == float.infinity;
2236     should(cast(double)view) == 0x1.117e8e90a0ff7p+239;
2237     static if (real.mant_dig >= 64)
2238         should(cast(real)view) == 0x8.8bf4748507fb99p+236L;
2239 
2240     view = DecimalView!size_t(false, -324, BigUIntView!size_t.fromHexString("5"));
2241     should(cast(float)view) == 0;
2242     should(cast(double)view) == 0x0.0000000000001p-1022;
2243     static if (real.mant_dig >= 64)
2244         should(cast(real)view) == 0x8.18995ce7aa0e1b2p-1077L;
2245 
2246     view = DecimalView!size_t(false, -324, BigUIntView!size_t.fromHexString("5B"));
2247     should(cast(float)view) == 0;
2248     should(cast(double)view) == 0x0.0000000000012p-1022;
2249     static if (real.mant_dig >= 64)
2250         should(cast(real)view) == 0x9.3594d9adeb09a55p-1073L;
2251 
2252     view = DecimalView!size_t(false, -322, BigUIntView!size_t.fromHexString("1"));
2253     should(cast(float)view) == 0;
2254     should(cast(double)view) == 0x0.0000000000014p-1022;
2255     static if (real.mant_dig >= 64)
2256         should(cast(real)view) == 0xa.1ebfb4219491a1fp-1073L;
2257 
2258     view = DecimalView!size_t(false, -320, BigUIntView!size_t.fromHexString("CA1CCB"));
2259     should(cast(float)view) == 0;
2260     should(cast(double)view) == 0x0.000063df832d9p-1022;
2261     static if (real.mant_dig >= 64)
2262         should(cast(real)view) == 0xc.7bf065b215888c7p-1043L;
2263 
2264     view = DecimalView!size_t(false, -319, BigUIntView!size_t.fromHexString("33CE7943FB"));
2265     should(cast(float)view) == 0;
2266     should(cast(double)view) == 0x1.000000000162p-1022;
2267     static if (real.mant_dig >= 64)
2268         should(cast(real)view) == 0x8.000000000b103b6p-1025L;
2269 
2270     view = DecimalView!size_t(false, -309, BigUIntView!size_t.fromHexString("15"));
2271     should(cast(float)view) == 0;
2272     should(cast(double)view) == 0x0.f19c2629ccf53p-1022;
2273     static if (real.mant_dig >= 64)
2274         should(cast(real)view) == 0xf.19c2629ccf52fc4p-1026L;
2275 
2276     view = DecimalView!size_t(false, -340, BigUIntView!size_t.fromHexString("AF87023B9BF0EE"));
2277     should(cast(float)view) == 0;
2278     should(cast(double)view) == 0x0.0000000000001p-1022;
2279     static if (real.mant_dig >= 64)
2280         should(cast(real)view) == 0xf.fffffffffffff64p-1078L;
2281 
2282     view = DecimalView!size_t(false, 400, BigUIntView!size_t.fromHexString("1"));
2283     should(cast(float)view) == float.infinity;
2284     should(cast(double)view) == double.infinity;
2285     static if (real.mant_dig >= 64)
2286         should(cast(real)view) == 0xd.a763fc8cb9ff9e6p+1325L;
2287 
2288     view = DecimalView!size_t(false, 309, BigUIntView!size_t.fromHexString("1"));
2289     should(cast(float)view) == float.infinity;
2290     should(cast(double)view) == double.infinity;
2291     static if (real.mant_dig >= 64)
2292         should(cast(real)view) == 0xb.201833b35d63f73p+1023L;
2293 
2294     view = DecimalView!size_t(false, 308, BigUIntView!size_t.fromHexString("2"));
2295     should(cast(float)view) == float.infinity;
2296     should(cast(double)view) == double.infinity;
2297     static if (real.mant_dig >= 64)
2298         should(cast(real)view) == 0x8.e679c2f5e44ff8fp+1021L;
2299 
2300     view = DecimalView!size_t(false, 308, BigUIntView!size_t.fromHexString("2"));
2301     should(cast(float)view) == float.infinity;
2302     should(cast(double)view) == double.infinity;
2303     static if (real.mant_dig >= 64)
2304         should(cast(real)view) == 0x8.e679c2f5e44ff8fp+1021L;
2305 
2306     view = DecimalView!size_t(false, 295, BigUIntView!size_t.fromHexString("1059949B7090"));
2307     should(cast(float)view) == float.infinity;
2308     should(cast(double)view) == double.infinity;
2309     static if (real.mant_dig >= 64)
2310         should(cast(real)view) == 0x8.00000000006955ap+1021L;
2311 
2312     view = DecimalView!size_t(false, 0, BigUIntView!size_t.fromHexString("0"));
2313     should(cast(float)view) == 0;
2314     should(cast(double)view) == 0;
2315     static if (real.mant_dig >= 64)
2316         should(cast(real)view) == 0L;
2317 
2318     view = view;
2319     should(cast(float)view) == 0;
2320     should(cast(double)view) == 0;
2321     static if (real.mant_dig >= 64)
2322         should(cast(real)view) == 0L;
2323 
2324     view = DecimalView!size_t(false, -325, BigUIntView!size_t.fromHexString("1"));
2325     should(cast(float)view) == 0;
2326     should(cast(double)view) == 0;
2327     static if (real.mant_dig >= 64)
2328         should(cast(real)view) == 0xa.5ced43b7e3e9188p-1083L;
2329 
2330     view = DecimalView!size_t(false, -326, BigUIntView!size_t.fromHexString("1"));
2331     should(cast(float)view) == 0;
2332     should(cast(double)view) == 0;
2333     static if (real.mant_dig >= 64)
2334         should(cast(real)view) == 0x8.4a57695fe98746dp-1086L;
2335 
2336     view = DecimalView!size_t(false, -500, BigUIntView!size_t.fromHexString("1"));
2337     should(cast(float)view) == 0;
2338     should(cast(double)view) == 0;
2339     static if (real.mant_dig >= 64)
2340         should(cast(real)view) == 0x8.33ada2003db9a8p-1664L;
2341 
2342     view = DecimalView!size_t(false, -1000, BigUIntView!size_t.fromHexString("1"));
2343     should(cast(float)view) == 0;
2344     should(cast(double)view) == 0;
2345     static if (real.mant_dig >= 64)
2346         should(cast(real)view) == 0x8.68a9188a89e1467p-3325L;
2347 
2348     view = DecimalView!size_t(false, -4999, BigUIntView!size_t.fromHexString("1"));
2349     should(cast(float)view) == 0;
2350     should(cast(double)view) == 0;
2351     static if (real.mant_dig >= 64)
2352         should(cast(real)view) == 0L;
2353 
2354     view = DecimalView!size_t(false, -10000, BigUIntView!size_t.fromHexString("1"));
2355     should(cast(float)view) == 0;
2356     should(cast(double)view) == 0;
2357     static if (real.mant_dig >= 64)
2358         should(cast(real)view) == 0L;
2359 
2360     view = DecimalView!size_t(false, -4969, BigUIntView!size_t.fromHexString("329659A941466C6B"));
2361     should(cast(float)view) == 0;
2362     should(cast(double)view) == 0;
2363     static if (real.mant_dig >= 64)
2364         should(cast(real)view) == real.min_normal * real.epsilon;
2365 
2366     view = DecimalView!size_t(false, -15, BigUIntView!size_t.fromHexString("525DB0200FFAB"));
2367     should(cast(float)view) == 1.448997445238699f;
2368     should(cast(double)view) == 0x1.72f17f1f49aadp+0;
2369     static if (real.mant_dig >= 64)
2370         should(cast(real)view) == 0xb.978bf8fa4d56cp-3L;
2371 
2372     view = DecimalView!size_t(false, -15, BigUIntView!size_t.fromHexString("525DB0200FFAB"));
2373     should(cast(float)view) == 1.448997445238699f;
2374     should(cast(double)view) == 0x1.72f17f1f49aadp+0;
2375     static if (real.mant_dig >= 64)
2376         should(cast(real)view) == 0xb.978bf8fa4d56cp-3L;
2377 
2378     view = DecimalView!size_t(false, -325, BigUIntView!size_t.fromHexString("1"));
2379     should(cast(float)view) == 0;
2380     should(cast(double)view) == 0;
2381     static if (real.mant_dig >= 64)
2382         should(cast(real)view) == 0xa.5ced43b7e3e9188p-1083L;
2383 
2384     view = DecimalView!size_t(false, -326, BigUIntView!size_t.fromHexString("1"));
2385     should(cast(float)view) == 0;
2386     should(cast(double)view) == 0;
2387     static if (real.mant_dig >= 64)
2388         should(cast(real)view) == 0x8.4a57695fe98746dp-1086L;
2389 
2390     view = DecimalView!size_t(false, 0, BigUIntView!size_t.fromHexString("1"));
2391     should(cast(float)view) == 1;
2392     should(cast(double)view) == 0x1p+0;
2393     static if (real.mant_dig >= 64)
2394         should(cast(real)view) == 0x8p-3L;
2395 
2396     view = DecimalView!size_t(false, -5, BigUIntView!size_t.fromHexString("3"));
2397     should(cast(float)view) == 3e-5f;
2398     should(cast(double)view) == 0x1.f75104d551d69p-16;
2399     static if (real.mant_dig >= 64)
2400         should(cast(real)view) == 0xf.ba8826aa8eb4635p-19L;
2401 
2402     view = DecimalView!size_t(false, -1, BigUIntView!size_t.fromHexString("1"));
2403     should(cast(float)view) == 0.1f;
2404     should(cast(double)view) == 0x1.999999999999ap-4;
2405     static if (real.mant_dig >= 64)
2406         should(cast(real)view) == 0xc.ccccccccccccccdp-7L;
2407 
2408     view = DecimalView!size_t(false, -7, BigUIntView!size_t.fromHexString("98967F"));
2409     should(cast(float)view) == 0.9999999f;
2410     should(cast(double)view) == 0x1.fffffca501acbp-1;
2411     static if (real.mant_dig >= 64)
2412         should(cast(real)view) == 0xf.ffffe5280d65435p-4L;
2413 }
2414 
2415 /++
2416 +/
2417 struct BinaryView(W)
2418 {
2419     ///
2420     bool sign;
2421     ///
2422     long exponent;
2423     ///
2424     BigUIntView!W coefficient;
2425 
2426 @safe:
2427 
2428     ///
2429     DecimalView!(const W) lightConst()() return scope
2430         const @safe pure nothrow @nogc @property
2431     {
2432         return typeof(return)(sign, exponent, coefficient.lightConst);
2433     }
2434     ///ditto
2435     alias lightConst this;
2436 
2437     /++
2438     +/
2439     BigIntView!W signedCoefficient()
2440     {
2441         return typeof(return)(sign, coefficients);
2442     }
2443 }