The OpenD Programming Language

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