1 /**
2 Checked integer arithmetic operations, functions, and types with improved handling of errors and corner cases compared
3 to the basic integral types.
4 
5 $(B Note:) Normally this module should not be imported directly. Instead, import one of
6 $(LINK2 ./throws.html, `checkedint.throws`), $(LINK2 ./asserts.html, `checkedint.asserts`), or
7 $(LINK2 ./noex.html, `checkedint.noex`), depending on which error signalling policy you want to use. (See below.)
8 
9 Copyright: Copyright Thomas Stuart Bockman 2015
10 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
11 Authors: Thomas Stuart Bockman
12 
13 $(BIG $(B Problems solved by `checkedint`)) $(BR)
14 As in many other programming languages (C, C++, Java, etc.) D's basic integral types (such as `int` or `ulong`) are
15 surprisingly difficult to use correctly in the general case, due to variuos departures from the behaviour of ideal
16 mathematical integers:
17 $(UL
18     $(LI Overflow silently wraps around: `assert(uint.max + 1 == 0);`)
19     $(LI Mixed signed/unsigned comparisons often give the wrong result: `assert(-1 > 1u);`)
20     $(LI Mixed signed/unsigned arithmetic operations can also give the wrong result.)
21     $(LI Integer division by zero crashes the program with a mis-named and uncatchable `Floating Point Exception`
22         (FPE).)
23     $(LI `int.min / -1` and `int.min % -1` may also crash with an FPE, even though the latter should simply yield `0`.)
24     $(LI If `x` is any integer value, and `y` is any negative integer value, `x ^^ y` will crash with an FPE.)
25     $(LI No bounds checking is done when casting from one integer type to another.)
26     $(LI The result of the bitshift operations (`<<`, `>>`, `>>>`) is formally undefined if the shift size is less than
27         zero or greater than `(8 * N.sizeof) - 1`.)
28 )
29 The `checkedint` package offers solutions to all of these issues and more.
30 
31 $(BIG $(B `SafeInt` versus `SmartInt`)) $(BR)
32 Two different approaches are available:
33 $(UL
34     $(LI $(LINK2 ./package.html#SmartInt, `SmartInt`) and $(LINK2 ./package.html#smartOp, `smartOp`) strive to actually
35         give the mathematically correct answer whenever possible, rather than just signaling an error.)
36     $(LI $(LINK2 ./package.html#SafeInt, `SafeInt`) and $(LINK2 ./package.html#safeOp, `safeOp`) strive to match the
37         behaviour of the basic integral types exactly, $(B except) that where the behaviour of the basic type is
38         wrong, or very unintuitive, an error is signaled instead.)
39 )
40 There is no meaningful performance difference between `SafeInt` and `SmartInt`. For general use, choose `SmartInt` to
41 simplify your code and maximize the range of inputs it accepts.
42 
43 `SafeInt` is intended mainly as a debugging tool, to help identify problems in code that must also work correctly with
44 the basic integral types. The $(LINK2 ./package.html#DebugInt, `DebugInt`) `template` `alias` makes it simple to use
45 of `SafeInt` in debug builds, and raw basic types in release builds.
46 
47 $(TABLE
48     $(TR $(TD)                $(TH `int` (basic type)) $(TH `SafeInt!int`)          $(TH `SmartInt!int`))
49     $(TR $(TH `int.max + 1`)  $(TD `0`)              $(TD `raise(IntFlag.over)`)    $(TD `raise(IntFlag.over)`))
50     $(TR $(TH `-1 > 1u`)      $(TD `true`)           $(TD compile-time error)       $(TD `false`))
51     $(TR $(TH `-1 - 2u`)      $(TD `4294967293`)     $(TD compile-time error)       $(TD `-3`))
52     $(TR $(TH `1 / 0`)        $(TD crash by FPE)     $(TD `raise(IntFlag.div0)`)    $(TD `raise(IntFlag.div0)`))
53     $(TR $(TH `int.min % -1`) $(TD crash by FPE)     $(TD `raise(IntFlag.posOver)`) $(TD `0`))
54     $(TR $(TH `-1 ^^ -7`)     $(TD crash by FPE)     $(TD `raise(IntFlag.undef)`)   $(TD `-1`))
55     $(TR $(TH `cast(uint)-1`) $(TD `4294967295`)     $(TD compile-time error)       $(TD `raise(IntFlag.negOver)`))
56     $(TR $(TH `-1 >> 100`)    $(TD undefined) $(TD `raise(IntFlag.undef)`)    $(TD `-1`))
57 )
58 
59 $(BIG $(B Error Signaling)) $(BR)
60 Some types of problem are signaled by a compile-time error, others at runtime. Runtime signaling is done through
61 $(LINK2 ./flags.html, `checkedint.flags`). Three different runtime signalling policies are available:
62 $(UL
63     $(LI With `IntFlagPolicy.throws`, a $(LINK2 ./flags.html#CheckedIntException, `CheckedIntException`) is thrown.
64         These are normal exceptions; not FPEs. As such, they can be caught and include a stack trace.)
65     $(LI With `IntFlagPolicy.asserts`, an assertion failure will be triggered. This policy is compatible with
66         `pure nothrow @nogc` code, but will crash the program in the event of a runtime integer math error.)
67     $(LI Alternatively, `IntFlagPolicy.noex` can be selected so that a thread-local flag is set when an operation fails.
68         This allows `checkedint` to be used from `nothrow` and `@nogc` (but not `pure`) code without crashing the
69         program, but requires the API user to manually insert checks of `IntFlags.local`.)
70 )
71 In normal code, there is no performance penalty for allowing `checkedint` to `throw`. Doing so is highly recommended
72 because this makes it easier to use correctly, and yields more precise error messages when something goes wrong.
73 
74 $(BIG $(B Generic Code)) $(BR)
75 The $(LINK2 ./traits.html, `checkedint.traits`) module provides `checkedint`-aware versions of various numerical type
76 traits from `std.traits`, such as `Signed`, `isSigned` and `isIntegral`. This allows writing generic algorithms that
77 work with any of `SmartInt`, `SafeInt`, and the built-in numeric types such as `uint` and `long`.
78 
79 Also of note is the $(LINK2 ./package.html#idx, `idx()`) function, which concisely and safely casts from any integral type
80 (built-in, `SmartInt`, or `SafeInt`) to either `size_t` or `ptrdiff_t` for easy array indexing.
81 
82 $(BIG $(B Performance)) $(BR)
83 Replacing all basic integer types with `SmartInt` or `SafeInt` will slow down exectuion somewhat. How much depends on
84 many factors, but for most code following a few simple rules should keep the penalty low:
85 $(OL
86     $(LI Build with $(B $(RED `--inline`)) and $(B `-O`) (DMD) or $(B `-O3`) (GDC and LDC). This by itself can improve
87         the performance of `checkedint` by around $(B 1,000%).)
88     $(LI With GDC or LDC, the performance hit in code that is bottlenecked by integer math will probably be between 30%
89         and 100%. The performance hit may be considerably larger with DMD, due to the weakness of the inliner.)
90     $(LI `checkedint` can't slow down code where it's not used! If you really need more speed, try switching to
91         $(LINK2 ./package.html#DebugInt, `DebugInt`) for the hottest code in your program (like inner loops) before
92         giving up on `checkedint` entirely.)
93 )
94 The above guidelines should be more than sufficient for most programs. But, here are some micro-optimization tips as
95 well, if you need them:
96 $(UL
97     $(LI Always use $(LINK2 ./package.html#smartOp.mulPow2, `mulPow2()`),
98         $(LINK2 ./package.html#smartOp.divPow2, `divPow2()`), and $(LINK2 ./package.html#smartOp.modPow2, `modPow2()`)
99         whenever they can naturally express your intent - they're faster than a regular `/`, `%`, or `pow()`.)
100     $(LI Unsigned types are a little bit faster than signed types, assuming negative values aren't needed.)
101     $(LI Although they are perfectly safe with `checkedint`, mixed signed/unsigned operations are a little bit slower
102         than same-signedness ones.)
103     $(LI The assignment operators (`++` or `+=`, for example) should never be slower than the equivalent two operation
104         sequence, and are sometimes a little bit faster.)
105 )
106 **/
107 module checkedint;
108 import checkedint.flags;
109 
110 import future.bitop, core.checkedint, std.algorithm, std.format, future.traits0, std.typecons;
111 static import std.math;
112 static if (__VERSION__ >= 2068)
113 {
114     version(GNU) { static assert(false); }
115     import std.meta;
116 }
117 else
118 {
119     import std.typetuple;
120     private alias AliasSeq = TypeTuple;
121 }
122 
123 /+pragma(inline, true)
124 {+/
125 // smart /////////////////////////////////////////////////
126     /**
127     Wrapper for any basic integral type `N` that uses the checked operations from
128     $(LINK2 ./package.html#smartOp, `smartOp`) and bounds checks assignments with
129     $(LINK2 ./package.html#to, `checkedint.to()`).
130 
131     $(UL
132         $(LI `policy` controls the error signalling policy (see `checkedint.flags`).)
133         $(LI `bitOps` may be set to `No.bitOps` if desired, to turn bitwise operations on this type into a
134             compile-time error.)
135     )
136     **/
137     struct SmartInt(N, IntFlagPolicy _policy, Flag!"bitOps" bitOps = Yes.bitOps)
138         if (isIntegral!N && isUnqual!N)
139     {
140         /// The error signalling policy used by this `SmartInt` type.
141         enum IntFlagPolicy policy = _policy;
142 
143         static if (bitOps)
144         {
145             /**
146             The basic integral value of this `SmartInt`. Accessing this directly may be useful for:
147             $(UL
148                 $(LI Intentionally doing modular (unchecked) arithmetic, or)
149                 $(LI Interacting with APIs that are not `checkedint` aware.)
150             )
151             **/
152             N bscal;
153             ///
154             unittest
155             {
156                 import checkedint.throws : SmartInt; // use IntFlagPolicy.throws
157 
158                 SmartInt!uint n;
159                 static assert(is(typeof(n.bscal) == uint));
160 
161                 n = 7;
162                 assert(n.bscal == 7);
163 
164                 n.bscal -= 8;
165                 assert(n == uint.max);
166             }
167 
168             /// Get a view of this `SmartInt` that allows bitwise operations.
169             @property ref inout(SmartInt!(N, policy, Yes.bitOps)) bits() /+return+/ inout pure @safe nothrow @nogc
170             {
171                 return this;
172             }
173             ///
174             unittest
175             {
176                 import checkedint.throws : SmartInt; // use IntFlagPolicy.throws
177 
178                 SmartInt!(int, No.bitOps) n = 1;
179                 static assert(!__traits(compiles, n << 2));
180                 assert(n.bits << 2 == 4);
181             }
182         }
183         else
184         {
185             @property ref inout(N) bscal() /+return+/ inout pure @safe nothrow @nogc
186             {
187                 return bits.bscal;
188             }
189             SmartInt!(N, policy, Yes.bitOps) bits;
190         }
191 
192         static if (__VERSION__ >= 2067)
193         {
194             version(GNU) { static assert(false); }
195 
196             /// The most negative possible value of this `SmartInt` type.
197             enum SmartInt!(N, policy, bitOps) min = typeof(this)(trueMin!N);
198             ///
199             unittest
200             {
201                 import checkedint.throws : SmartInt; // use IntFlagPolicy.throws
202 
203                 assert(SmartInt!(int).min == int.min);
204                 assert(SmartInt!(uint).min == uint.min);
205             }
206 
207             /// The most positive possible value of this `SmartInt` type.
208             enum SmartInt!(N, policy, bitOps) max = typeof(this)(trueMax!N);
209             ///
210             unittest
211             {
212                 import checkedint.throws : SmartInt; // use IntFlagPolicy.throws;
213 
214                 assert(SmartInt!(int).max == int.max);
215                 assert(SmartInt!(uint).max == uint.max);
216             }
217         }
218         else
219         {
220             static @property auto min() pure @safe nothrow @nogc
221             {
222                 return typeof(this)(trueMin!N);
223             }
224             static @property auto max() pure @safe nothrow @nogc
225             {
226                 return typeof(this)(trueMax!N);
227             }
228         }
229 
230         // Construction, assignment, and casting /////////////////////////////////////////////////
231         /**
232         Assign the value of `that` to this `SmartInt` instance.
233 
234         $(LINK2 ./package.html#to, `checkedint.to()`) is used to verify `that >= N.min && that <= N.max`. If not, an
235         $(LINK2 ./flags.html, `IntFlag`) will be raised.
236         **/
237         this(M)(const M that) @safe
238             if (isCheckedInt!M || isScalarType!M)
239         {
240             this.bscal = to!(N, policy)(that);
241         }
242         /// ditto
243         ref typeof(this) opAssign(M)(const M that) @safe
244             if (isCheckedInt!M || isScalarType!M)
245         {
246             this.bscal = to!(N, policy)(that);
247             return this;
248         }
249         ///
250         unittest
251         {
252             import checkedint.noex : SmartInt; // use IntFlagPolicy.noex
253 
254             // Any basic scalar or checkedint *type* is accepted...
255             SmartInt!int n = 0;
256             n = cast(ulong)0;
257             n = cast(dchar)0;
258             n = cast(byte)0;
259             n = cast(real)0;
260             assert(!IntFlags.local);
261 
262             // ...but not any *value*.
263             n = uint.max;
264             n = long.min;
265             n = real.nan;
266             assert(IntFlags.local.clear() == (IntFlag.posOver | IntFlag.negOver | IntFlag.undef));
267         }
268 
269         /**
270         Convert this value to floating-point. This always succeeds, although some loss of precision may
271         occur if M.sizeof <= N.sizeof.
272         **/
273         M opCast(M)() const pure @safe nothrow @nogc
274             if (isFloatingPoint!M)
275         {
276             return cast(M)bscal;
277         }
278         ///
279         unittest
280         {
281             import checkedint.throws : SmartInt; // use IntFlagPolicy.throws
282 
283             SmartInt!int n = 92;
284             auto f = cast(double)n;
285             static assert(is(typeof(f) == double));
286             assert(f == 92.0);
287         }
288 
289         /// `this != 0`
290         M opCast(M)() const pure @safe nothrow @nogc
291             if (is(M == bool))
292         {
293             return bscal != 0;
294         }
295         ///
296         unittest
297         {
298             import checkedint.throws : SmartInt; // use IntFlagPolicy.throws
299 
300             SmartInt!int n = -315;
301             assert( cast(bool)n);
302 
303             n = 0;
304             assert(!cast(bool)n);
305         }
306 
307         /**
308         Convert this value to type `M` using $(LINK2 ./package.html#to, `checkedint.to()`) for bounds checking. An
309         $(LINK2 ./flags.html, `IntFlag`) will be raised if `M` cannot represent the current value of this `SmartInt`.
310         **/
311         M opCast(M)() const @safe
312             if (isCheckedInt!M || isIntegral!M || isSomeChar!M)
313         {
314             return to!(M, policy)(bscal);
315         }
316         ///
317         unittest
318         {
319             import checkedint.noex : SmartInt; // use IntFlagPolicy.noex
320 
321             SmartInt!ulong n = 52;
322             auto a = cast(int)n;
323             static assert(is(typeof(a) == int));
324             assert(!IntFlags.local);
325             assert(a == 52);
326 
327             auto m = SmartInt!long(-1).mulPow2(n);
328             auto b = cast(wchar)m;
329             static assert(is(typeof(b) == wchar));
330             assert(IntFlags.local.clear() == IntFlag.negOver);
331         }
332 
333         /**
334         Convert this value to a type suitable for indexing an array:
335         $(UL
336             $(LI If `N` is signed, a `ptrdiff_t` is returned.)
337             $(LI If `N` is unsigned, a `size_t` is returned.)
338         )
339         $(LINK2 ./package.html#to, `checkedint.to()`) is used for bounds checking.
340         **/
341         @property Select!(isSigned!N, ptrdiff_t, size_t) idx() const @safe
342         {
343             return to!(typeof(return), policy)(bscal);
344         }
345         ///
346         unittest
347         {
348             import checkedint.throws : SmartInt; // use IntFlagPolicy.throws
349 
350             char[3] arr = ['a', 'b', 'c'];
351             SmartInt!long n = 1;
352 
353             // On 32-bit, `long` cannot be used directly for array indexing,
354             static if (size_t.sizeof < long.sizeof)
355                 static assert(!__traits(compiles, arr[n]));
356             // but idx can be used to concisely and safely cast to ptrdiff_t:
357             assert(arr[n.idx] == 'b');
358 
359             // The conversion is bounds checked:
360             static if (size_t.sizeof < long.sizeof)
361             {
362                 n = long.min;
363                 try
364                 {
365                     arr[n.idx] = '?';
366                 }
367                 catch (CheckedIntException e)
368                 {
369                     assert(e.intFlags == IntFlag.negOver);
370                 }
371             }
372         }
373 
374         /// Get a simple hashcode for this value.
375         size_t toHash() const pure @safe nothrow @nogc
376         {
377             static if (N.sizeof > size_t.sizeof)
378             {
379                 static assert(N.sizeof == (2 * size_t.sizeof));
380                 return cast(size_t)bscal ^ cast(size_t)(bscal >>> 32);
381             }
382             else
383                 return cast(size_t)bscal;
384         }
385 
386         /// Get a `string` representation of this value.
387         string toString() const @safe
388         {
389             return to!(string, IntFlagPolicy.noex)(bscal);
390         }
391         /// ditto
392         void toString(Writer, Char = char)(Writer sink, FormatSpec!Char fmt = (FormatSpec!Char).init) const
393         {
394             formatValue(sink, bscal, fmt);
395         }
396         ///
397         unittest
398         {
399             import checkedint.throws : smartInt; // use IntFlagPolicy.throws
400             assert(smartInt(-753).toString() == "-753");
401         }
402 
403         // Comparison /////////////////////////////////////////////////
404         /// Returns `true` if this value is mathematically precisely equal to `right`.
405         bool opEquals(M)(const M right) const pure @safe nothrow @nogc
406             if (isCheckedInt!M || isScalarType!M)
407         {
408             return smartOp!(policy).cmp!"=="(this.bscal, right.bscal);
409         }
410         /**
411         Perform a mathematically correct comparison to `right`.
412 
413         Returns: $(UL
414             $(LI `-1` if this value is less than `right`.)
415             $(LI ` 0` if this value is precisely equal to `right`.)
416             $(LI ` 1` if this value is greater than `right`.)
417             $(LI `float.nan` if `right` is a floating-point `nan` value.)
418         )
419         **/
420         auto opCmp(M)(const M right) const pure @safe nothrow @nogc
421             if (isFloatingPoint!M)
422         {
423             return
424                 (bscal <  right)? -1 :
425                 (bscal >  right)?  1 :
426                 (bscal == right)?  0 : float.nan;
427         }
428         /// ditto
429         int opCmp(M)(const M right) const pure @safe nothrow @nogc
430             if (isCheckedInt!M || isScalarType!M)
431         {
432             return smartOp!(policy).cmp(this.bscal, right.bscal);
433         }
434 
435         // Unary /////////////////////////////////////////////////
436         /// See $(LINK2 ./package.html#smartOp, `smartOp`).
437         typeof(this) opUnary(string op)() const pure @safe nothrow @nogc
438             if (op == "~")
439         {
440             static assert(bitOps,
441                 "Bitwise operations are disabled.");
442 
443             return typeof(return)(smartOp!(policy).unary!op(bscal));
444         }
445         /// ditto
446         SmartInt!(Signed!N, policy, bitOps) opUnary(string op)() const @safe
447             if (op == "+" || op == "-")
448         {
449             return typeof(return)(smartOp!(policy).unary!op(bscal));
450         }
451         /// ditto
452         ref typeof(this) opUnary(string op)() /+return+/ @safe
453             if (op.among!("++", "--"))
454         {
455             smartOp!(policy).unary!op(bscal);
456             return this;
457         }
458 
459         /// ditto
460         SmartInt!(Unsigned!N, policy, bitOps) abs() const pure @safe nothrow @nogc
461         {
462             return typeof(return)(smartOp!(policy).abs(bscal));
463         }
464 
465         /// Count the number of set bits using `core.bitop.popcnt()`.
466         SmartInt!(int, policy, bitOps) popcnt()() const pure @safe nothrow @nogc
467         {
468             static assert(bitOps, "Bitwise operations are disabled.");
469 
470             import future.bitop : stdPC = popcnt;
471             return typeof(return)(stdPC(bscal));
472         }
473 
474         /// See $(LINK2 ./package.html#smartOp, `smartOp`).
475         SmartInt!(ubyte, policy, bitOps) bsf()() const @safe
476         {
477             static assert(bitOps, "Bitwise operations are disabled.");
478 
479             return typeof(return)(smartOp!(policy).bsf(bscal));
480         }
481         /// ditto
482         SmartInt!(ubyte, policy, bitOps) bsr()() const @safe
483         {
484             static assert(bitOps, "Bitwise operations are disabled. Consider using ilogb() instead?");
485 
486             return typeof(return)(smartOp!(policy).bsr(bscal));
487         }
488 
489         /// ditto
490         SmartInt!(ubyte, policy, bitOps) ilogb() const @safe
491         {
492             return typeof(return)(smartOp!(policy).ilogb(bscal));
493         }
494 
495         // Binary /////////////////////////////////////////////////
496         /// ditto
497         auto opBinaryRight(string op, M)(const M left) const pure @safe nothrow @nogc
498             if (isFloatingPoint!M)
499         {
500             return smartOp!(policy).binary!op(left, bscal);
501         }
502         /// ditto
503         auto opBinary(string op, M)(const M right) const pure @safe nothrow @nogc
504             if (isFloatingPoint!M)
505         {
506             return smartOp!(policy).binary!op(bscal, right);
507         }
508         /// ditto
509         auto opBinaryRight(string op, M)(const M left) const @safe
510             if (isSafeInt!M || isFixedPoint!M)
511         {
512             enum mixPolicy = .max(policy, intFlagPolicyOf!M);
513             enum mixBitOps = bitOps && hasBitOps!M;
514             static assert(mixBitOps || !op.among!("<<", ">>", ">>>", "&", "|", "^"),
515                 "Bitwise operations are disabled. Consider using mulPow2(), divPow2(), or modPow2() instead?");
516 
517             const wret = smartOp!(mixPolicy).binary!op(left.bscal, this.bscal);
518             return SmartInt!(typeof(wret), mixPolicy, mixBitOps)(wret);
519         }
520         /// ditto
521         auto opBinary(string op, M)(const M right) const @safe
522             if (isCheckedInt!M || isFixedPoint!M)
523         {
524             enum mixPolicy = .max(policy, intFlagPolicyOf!M);
525             enum mixBitOps = bitOps && hasBitOps!M;
526             static assert(mixBitOps || !op.among!("<<", ">>", ">>>", "&", "|", "^"),
527                 "Bitwise operations are disabled. Consider using mulPow2(), divPow2(), or modPow2() instead?");
528 
529             const wret = smartOp!(mixPolicy).binary!op(this.bscal, right.bscal);
530             return SmartInt!(typeof(wret), mixPolicy, mixBitOps)(wret);
531         }
532         /// ditto
533         ref typeof(this) opOpAssign(string op, M)(const M right) /+return+/ @safe
534             if (isCheckedInt!M || isFixedPoint!M)
535         {
536             static assert((bitOps && hasBitOps!M) || !op.among!("<<", ">>", ">>>", "&", "|", "^"),
537                 "Bitwise operations are disabled. Consider using mulPow2(), divPow2(), or modPow2() instead?");
538 
539             smartOp!(.max(policy, intFlagPolicyOf!M)).binary!(op ~ "=")(this.bscal, right.bscal);
540             return this;
541         }
542 
543         /// ditto
544         auto mulPow2(M)(const M exp) const pure @safe nothrow @nogc
545             if (isFloatingPoint!M)
546         {
547             return smartOp!(policy).mulPow2(bscal, exp);
548         }
549         /// ditto
550         auto mulPow2(M)(const M exp) const @safe
551             if (isCheckedInt!M || isFixedPoint!M)
552         {
553             enum mixPolicy = .max(policy, intFlagPolicyOf!M);
554             const wret = smartOp!(mixPolicy).mulPow2(this.bscal, exp.bscal);
555             return SmartInt!(typeof(wret), mixPolicy, bitOps && hasBitOps!M)(wret);
556         }
557         /// ditto
558         auto divPow2(M)(const M exp) const pure @safe nothrow @nogc
559             if (isFloatingPoint!M)
560         {
561             return smartOp!(policy).divPow2(bscal, exp);
562         }
563         /// ditto
564         auto divPow2(M)(const M exp) const @safe
565             if (isCheckedInt!M || isFixedPoint!M)
566         {
567             enum mixPolicy = .max(policy, intFlagPolicyOf!M);
568             const wret = smartOp!(mixPolicy).divPow2(this.bscal, exp.bscal);
569             return SmartInt!(typeof(wret), mixPolicy, bitOps && hasBitOps!M)(wret);
570         }
571         /// ditto
572         auto modPow2(M)(const M exp) const pure @safe nothrow @nogc
573             if (isFloatingPoint!M)
574         {
575             return smartOp!(policy).modPow2(bscal, exp);
576         }
577         /// ditto
578         auto modPow2(M)(const M exp) const @safe
579             if (isCheckedInt!M || isFixedPoint!M)
580         {
581             enum mixPolicy = .max(policy, intFlagPolicyOf!M);
582             const wret = smartOp!(mixPolicy).modPow2(this.bscal, exp.bscal);
583             return SmartInt!(typeof(wret), mixPolicy, bitOps && hasBitOps!M)(wret);
584         }
585 
586         /// Raise `this` to the `exp` power using `std.math.pow()`.
587         auto pow(M)(const M exp) const pure @safe nothrow @nogc
588             if (isFloatingPoint!M)
589         {
590             return std.math.pow(bscal, exp);
591         }
592         /// See $(LINK2 ./package.html#smartOp, `smartOp`).
593         auto pow(M)(const M exp) const @safe
594             if (isCheckedInt!M || isFixedPoint!M)
595         {
596             enum mixPolicy = .max(policy, intFlagPolicyOf!M);
597             const wret = smartOp!(mixPolicy).pow(this.bscal, exp.bscal);
598             return SmartInt!(typeof(wret), mixPolicy, bitOps && hasBitOps!M)(wret);
599         }
600     }
601     /// ditto
602     template SmartInt(N, IntFlagPolicy policy, Flag!"bitOps" bitOps = Yes.bitOps)
603         if ((isIntegral!N && !isUnqual!N) || isCheckedInt!N)
604     {
605         alias SmartInt = SmartInt!(BasicScalar!N, policy, bitOps);
606     }
607     ///
608     unittest
609     {
610         // Mixing standard signed and unsigned types is dangerous, but...
611         int ba = -1;
612         uint bb = 0;
613         assert(ba > bb);
614 
615         auto bc = ba + bb;
616         assert(is(typeof(bc) == uint));
617         assert(bc == 4294967295u);
618 
619         // ...with SmartInt, mixed signed/unsigned operations "just work":
620         import checkedint.throws : SmartInt; // use IntFlagPolicy.throws
621 
622         SmartInt!int ma = -1;
623         SmartInt!uint mb = 0;
624         assert(ma < mb);
625 
626         auto mc = ma + mb;
627         assert(is(typeof(mc) == SmartInt!int));
628         assert(mc != 4294967295u);
629         assert(mc == -1);
630     }
631     ///
632     unittest
633     {
634         // When IntFlagPolicy.throws is used, failed SmartInt operations will throw a CheckedIntException.
635         import checkedint.throws : SmartInt;
636 
637         SmartInt!uint ma = 1;
638         SmartInt!uint mb = 0;
639 
640         bool overflow = false;
641         try
642         {
643             SmartInt!uint mc = mb - ma;
644             assert(false);
645         }
646         catch (CheckedIntException e)
647         {
648             assert(e.intFlags == IntFlag.negOver);
649             overflow = true;
650         }
651         assert(overflow);
652 
653         bool div0 = false;
654         try
655         {
656             // With standard integers, this would crash the program with an unrecoverable FPE...
657             SmartInt!uint mc = ma / mb;
658             assert(false);
659         }
660         catch (CheckedIntException e)
661         {
662             // ...but with SmartInt, it just throws a normal Exception.
663             assert(e.intFlags == IntFlag.div0);
664             div0 = true;
665         }
666         assert(div0);
667     }
668     ///
669     unittest
670     {
671         // When IntFlagPolicy.noex is used, failed SmartInt operations set one or more bits in IntFlags.local.
672         import checkedint.noex : SmartInt;
673 
674         SmartInt!uint ma = 1;
675         SmartInt!uint mb = 0;
676         SmartInt!uint mc;
677 
678         mc = mb - ma;
679         assert(IntFlags.local == IntFlag.negOver);
680 
681         // With standard integers, this would crash the program with an unrecoverable FPE...
682         mc = ma / mb;
683         // ...but with SmartInt, it just sets a bit in IntFlags.local.
684         assert(IntFlags.local & IntFlag.div0);
685 
686         // Each flag will remain set until cleared:
687         assert(IntFlags.local.clear() == (IntFlag.negOver | IntFlag.div0));
688         assert(!IntFlags.local);
689     }
690 
691     private template SmartInt(N, IntFlagPolicy policy, bool bitOps)
692         if (isIntegral!N)
693     {
694         alias SmartInt = SmartInt!(
695             Unqual!N,
696             policy,
697             cast(Flag!"bitOps")bitOps);
698     }
699 
700     /// Get the value of `num` as a `SmartInt!N`. The integral type `N` can be infered from the argument.
701     SmartInt!(N, policy, bitOps) smartInt(IntFlagPolicy policy, Flag!"bitOps" bitOps = Yes.bitOps, N)(N num) @safe
702         if (isIntegral!N || isCheckedInt!N)
703     {
704         return typeof(return)(num.bscal);
705     }
706     ///
707     unittest
708     {
709         import checkedint.throws : smartInt, SmartInt; // use IntFlagPolicy.throws
710 
711         auto a = smartInt(55uL);
712         static assert(is(typeof(a) == SmartInt!ulong));
713         assert(a == 55);
714     }
715 
716     /**
717     Implements various integer math operations with error checking.
718 
719     `smartOp` strives to give the mathematically correct result, with integer-style rounding, for all inputs. Only
720     if the correct result is undefined or not representable by the return type is an error signalled, using
721     `checkedint.flags`.
722 
723     The error-signalling policy may be selected using the `policy` template parameter.
724     **/
725     template smartOp(IntFlagPolicy policy)
726     {
727         // NOTE: ddoc only scans the first branch of a static if
728         static if (policy == IntFlagPolicy.none)
729         {
730             // No need to redundantly instantiate members which don't depend on `policy`.
731 
732             private void cmpTypeCheck(N, M)() pure nothrow @nogc
733             {
734                 static assert(isBoolean!N == isBoolean!M,
735                     "The intent of a direct comparison of " ~
736                     N.stringof ~ " with " ~ M.stringof ~
737                     " is unclear. Add an explicit cast."
738                 );
739             }
740 
741             /**
742             Compare `left` and `right` using `op`.
743             $(UL
744                 $(LI Unlike the standard integer comparison operator, this function correctly handles negative
745                     values in signed/unsigned comparisons.)
746                 $(LI Like the standard operator, comparisons involving any floating-point `nan` value always return
747                     `false`.)
748             ) $(BR)
749             Direct comparisons between boolean values and numeric ones are forbidden. Make your intention explicit:
750             $(UL
751                 $(LI `numeric == cast(N)boolean`)
752                 $(LI `(numeric != 0) == boolean`)
753             )
754             **/
755             bool cmp(string op, N, M)(const N left, const M right) pure @safe nothrow @nogc
756                 if (isScalarType!N && isScalarType!M)
757             {
758                 cmpTypeCheck!(N, M)();
759 
760                 static if (isSigned!N != isSigned!M)
761                 {
762                     static if (isSigned!N)
763                     {
764                         if (left < 0)
765                             return mixin("-1 " ~ op ~ " 0");
766                     }
767                     else
768                     {
769                         if (right < 0)
770                             return mixin("0 " ~ op ~ " -1");
771                     }
772                 }
773 
774                 return mixin("left " ~ op ~ " right");
775             }
776             ///
777             unittest
778             {
779                 import checkedint.noex : smartOp; // smartOp.cmp() never throws
780 
781                 assert(uint.max == -1);
782                 assert( smartOp.cmp!"!="(uint.max, -1));
783                 assert(-3156 > 300u);
784                 assert( smartOp.cmp!"<"(-3156, 300u));
785 
786                 assert(!smartOp.cmp!"<"(1, real.nan));
787                 assert(!smartOp.cmp!"<"(real.nan, 1));
788             }
789 
790             /**
791             Defines a total order on all basic scalar values, using the same rules as `std.math.cmp()`.
792 
793             $(UL
794                 $(LI Mixed signed/unsigned comparisons return the mathematically correct result.)
795                 $(LI If neither `left` nor `right` is floating-point, this function is faster than
796                     `std.math.cmp()`.)
797                 $(LI If either `left` or `right` $(I is) floating-point, this function forwards to
798                     `std.math.cmp()`.)
799             ) $(BR)
800             Direct comparisons between boolean values and numeric ones are forbidden. Make your intention explicit:
801             $(UL
802                 $(LI `numeric == cast(N)boolean`)
803                 $(LI `(numeric != 0) == boolean`)
804             )
805             **/
806             int cmp(N, M)(const N left, const M right) pure @safe nothrow @nogc
807                 if (isScalarType!N && isScalarType!M)
808             {
809                 cmpTypeCheck!(N, M)();
810 
811                 static if (isFloatingPoint!N || isFloatingPoint!M)
812                 {
813                     import future.math : stdCmp = cmp;
814                     return stdCmp(left, right);
815                 }
816                 else
817                 {
818                     static if (isSigned!N != isSigned!M)
819                     {
820                         static if (isSigned!N)
821                         {
822                             if (left < 0)
823                                 return -1;
824                         }
825                         else
826                         {
827                             if (right < 0)
828                                 return  1;
829                         }
830                     }
831 
832                     return (left < right)? -1 : (right < left);
833                 }
834             }
835             ///
836             unittest
837             {
838                 import checkedint.noex : smartOp; // smartOp.cmp() never throws
839 
840                 assert(smartOp.cmp(325.0, 325u) == 0);
841                 assert(smartOp.cmp(uint.max, -1) == 1);
842                 assert(smartOp.cmp(-3156, 300u) == -1);
843             }
844 
845             /// Get the absolute value of `num`. Because the return type is always unsigned, overflow is not possible.
846             Unsigned!N abs(N)(const N num) pure @safe nothrow @nogc
847                 if (isIntegral!N)
848             {
849                 static if (!isSigned!N)
850                     return num;
851                 else
852                     return cast(typeof(return))(num < 0?
853                         -num : // -num doesn't need to be checked for overflow
854                          num);
855             }
856             /// ditto
857             IntFromChar!N abs(N)(const N num) pure @safe nothrow @nogc
858                 if (isSomeChar!N)
859             {
860                 return num;
861             }
862             ///
863             unittest
864             {
865                 import checkedint.noex : smartOp; // smartOp.abs() never throws
866 
867                 assert(smartOp.abs(int.min) == std.math.pow(2.0, 31));
868                 assert(smartOp.abs(-25) == 25u);
869                 assert(smartOp.abs(745u) == 745u);
870             }
871 
872             private template Result(N, string op, M)
873                 if (isNumeric!N && isNumeric!M)
874             {
875             private:
876                 enum reqFloat = isFloatingPoint!N || isFloatingPoint!M;
877                 enum precN = precision!N, precM = precision!M;
878                 enum precStd = reqFloat? precision!float : precision!uint;
879                 enum smallSub = (op == "-") && precN < precision!int && precM < precision!int;
880 
881                 enum reqSign = reqFloat ||
882                     (op.among!("+", "-", "*" , "/") && (isSigned!N || isSigned!M || smallSub)) ||
883                     (op.among!("%", "^^", "<<", ">>", ">>>") && isSigned!N) ||
884                     (op.among!("&", "|", "^") && (isSigned!N && isSigned!M));
885 
886                 enum reqPrec = reqFloat? max(precStd, precN, precM) :
887                     op.among!("+", "-", "*")? max(precStd, precN, precM) - 1 :
888                     op == "/"? (isSigned!M? max(precStd, precN) - 1 : precN) :
889                     op == "%"? min(precision!N, precision!M) :
890                     op == "^^"? max(precStd - 1, precN) :
891                     op.among!("<<", ">>", ">>>")? precN :
892                   /+op.among!("&", "|", "^")?+/ max(precN, precM);
893 
894             public:
895                 alias Result = Select!(reqFloat,
896                     Select!(reqPrec <= double.mant_dig || double.mant_dig >= real.mant_dig,
897                         Select!(reqPrec <= float.mant_dig, float, double),
898                         real),
899                     Select!(reqSign,
900                         Select!(reqPrec <= 15,
901                             Select!(reqPrec <= 7, byte, short),
902                             Select!(reqPrec <= 31, int, long)),
903                         Select!(reqPrec <= 16,
904                             Select!(reqPrec <= 8, ubyte, ushort),
905                             Select!(reqPrec <= 32, uint, ulong))));
906             }
907             private template Result(N, string op, M)
908                 if (isScalarType!N && isScalarType!M &&
909                     (!isNumeric!N || !isNumeric!M))
910             {
911                 alias Result = Result!(NumFromScal!N, op, NumFromScal!M);
912             }
913         }
914         else
915         {
916             alias cmp = smartOp!(IntFlagPolicy.none).cmp;
917             alias abs = smartOp!(IntFlagPolicy.none).abs;
918             private alias Result = smartOp!(IntFlagPolicy.none).Result;
919         }
920 
921         /**
922         Perform the unary (single-argument) integer operation specified by `op`.
923 
924         Key differences from the standard unary operators:
925         $(UL
926             $(LI `-` and `+` always return a signed value.)
927             $(LI `-` is checked for overflow, because `-int.min` is greater than `int.max`.)
928             $(LI `++` and `--` are checked for overflow.)
929         ) $(BR)
930         Note that like the standard operators, `++` and `--` take the operand by `ref` and overwrite its value with
931         the result.
932         **/
933         N unary(string op, N)(const N num) pure @safe nothrow @nogc
934             if (isIntegral!N && op == "~")
935         {
936             return ~num;
937         }
938         /// ditto
939         IntFromChar!N unary(string op, N)(const N num) pure @safe nothrow @nogc
940             if (isSomeChar!N && op == "~")
941         {
942             return ~num;
943         }
944         /// ditto
945         Signed!(Promoted!N) unary(string op, N)(const N num) @safe 
946             if (isFixedPoint!N && op.among!("-", "+"))
947         {
948             alias R = typeof(return);
949             alias UR = Unsigned!R;
950 
951             static if (op == "-")
952             {
953                 static if (isSigned!N)
954                 {
955                     if (num < -trueMax!R)
956                         IntFlag.posOver.raise!policy();
957                 }
958                 else
959                 {
960                     if (num > cast(UR)trueMin!R)
961                         IntFlag.negOver.raise!policy();
962                 }
963 
964                 return -cast(R)num;
965             }
966             else
967             {
968                 static if (!isSigned!N)
969                 {
970                     if (num > trueMax!R)
971                         IntFlag.posOver.raise!policy();
972                 }
973 
974                 return num;
975             }
976         }
977         /// ditto
978         /+ref+/ N unary(string op, N)(/+return+/ ref N num) @safe
979             if (isIntegral!N && op.among!("++", "--"))
980         {
981             static if (op == "++")
982             {
983                 if (num >= trueMax!N)
984                     IntFlag.posOver.raise!policy();
985 
986                 return ++num;
987             }
988             else static if (op == "--")
989             {
990                 if (num <= trueMin!N)
991                     IntFlag.negOver.raise!policy();
992 
993                 return --num;
994             }
995             else
996                 static assert(false);
997         }
998         ///
999         unittest
1000         {
1001             import checkedint.noex : smartOp; // use IntFlagPolicy.noex
1002 
1003             assert(smartOp.unary!"~"(0u) == uint.max);
1004 
1005             auto a = smartOp.unary!"-"(20uL);
1006             static assert(is(typeof(a) == long));
1007             assert(a == -20);
1008 
1009             auto b = smartOp.unary!"+"(uint.max);
1010             static assert(is(typeof(b) == int));
1011             assert(IntFlags.local.clear() == IntFlag.posOver);
1012 
1013             uint c = 1u;
1014             assert(smartOp.unary!"--"(c) == 0u);
1015             assert(c == 0u);
1016             smartOp.unary!"--"(c);
1017             assert(IntFlags.local.clear() == IntFlag.negOver);
1018 
1019             int d = 7;
1020             assert(smartOp.unary!"++"(d) == 8);
1021             assert(d == 8);
1022         }
1023 
1024         /// `core.bitop.bsf` without the undefined behaviour. `smartOp.bsf(0)` will raise `IntFlag.undef`.
1025         ubyte bsf(N)(const N num) @safe
1026             if (isFixedPoint!N)
1027         {
1028             return cast(ubyte) bsfImpl!policy(num);
1029         }
1030         ///
1031         unittest
1032         {
1033             import checkedint.noex : smartOp;
1034 
1035             assert(smartOp.bsf(20) == 2);
1036 
1037             smartOp.bsf(0);
1038             assert(IntFlags.local.clear() == IntFlag.undef);
1039         }
1040 
1041         /// `core.bitop.bsr` without the undefined behaviour. `smartOp.bsr(0)` will raise `IntFlag.undef`.
1042         ubyte bsr(N)(const N num) @safe
1043             if (isFixedPoint!N)
1044         {
1045             return cast(ubyte) bsrImpl!policy(num);
1046         }
1047         ///
1048         unittest
1049         {
1050             import checkedint.noex : smartOp;
1051 
1052             assert(smartOp.bsr( 20) ==  4);
1053             assert(smartOp.bsr(-20) == 31);
1054 
1055             smartOp.bsr(0);
1056             assert(IntFlags.local.clear() == IntFlag.undef);
1057         }
1058 
1059         /**
1060         Get the base 2 logarithm of `abs(num)`, rounded down to the nearest integer.
1061 
1062         `smartOp.ilogb(0)` will raise `IntFlag.undef`.
1063         **/
1064         ubyte ilogb(N)(const N num) @safe
1065             if (isFixedPoint!N)
1066         {
1067             return cast(ubyte) bsrImpl!policy(abs(num));
1068         }
1069         ///
1070         unittest
1071         {
1072             import checkedint.noex : smartOp;
1073 
1074             assert(smartOp.ilogb(20) == 4);
1075             assert(smartOp.ilogb(-20) == 4);
1076 
1077             smartOp.ilogb(0);
1078             assert(IntFlags.local.clear() == IntFlag.undef);
1079         }
1080 
1081         private auto binaryImpl(string op, N, M)(const N left, const M right) @safe
1082             if (isIntegral!N && isIntegral!M)
1083         {
1084             enum wop = (op[$-1] == '=')? op[0 .. $-1] : op;
1085             alias
1086                 UN = Unsigned!N,
1087                 UM = Unsigned!M,
1088                  W = Result!(N, wop, M),
1089                  R = Select!(wop == op, W, N);
1090 
1091             static if (wop.among!("+", "-", "*"))
1092             {
1093                 enum safePrec = (wop == "*")?
1094                     precision!N + precision!M :
1095                     max(precision!N, precision!M) + 1;
1096                 enum safeR = precision!R >= safePrec;
1097 
1098                 static if (safeR)
1099                     return mixin("cast(R)left " ~ wop ~ " cast(R)right");
1100                 else
1101                 {
1102                     enum safeW = precision!W >= safePrec;
1103                     enum matched = (isSigned!N == isSigned!M);
1104                     enum oX = staticIndexOf!(wop, "+", "-", "*") << 1;
1105                     alias cops = AliasSeq!(addu, adds, subu, subs, mulu, muls);
1106 
1107                     static if (safeW || matched || wop == "*")
1108                     {
1109                         bool over;
1110                         static if (safeW)
1111                             const wR = mixin("cast(W)left " ~ wop ~ " cast(W)right");
1112                         else
1113                         {
1114                             static if (matched)
1115                             {
1116                                 alias cop = cops[oX + isSigned!W];
1117                                 const wR = cop(left, right, over);
1118                             }
1119                             else
1120                             {
1121                                 // integer multiplication is commutative
1122                                 static if (isSigned!N)
1123                                     W sa = left, ub = right;
1124                                 else
1125                                     W ub = left, sa = right;
1126 
1127                                 static if (isSigned!R)
1128                                 {
1129                                     W wR = muls(sa, ub, over);
1130                                     if (ub < 0)
1131                                         over = (sa != 0) && (ub != trueMin!W || sa != -1);
1132                                 }
1133                                 else
1134                                 {
1135                                     over = (sa < 0) && (ub != 0);
1136                                     const wR = mulu(sa, ub, over);
1137                                 }
1138                             }
1139                         }
1140 
1141                         alias WR = typeof(wR);
1142                         static if (isSigned!WR && trueMin!WR < trueMin!R)
1143                         {
1144                             if (wR < trueMin!R)
1145                                 over = true;
1146                         }
1147                         static if (trueMax!WR > trueMax!R)
1148                         {
1149                             if (wR > trueMax!R)
1150                                 over = true;
1151                         }
1152                     }
1153                     else
1154                     {
1155                         alias UW = Unsigned!W;
1156                         alias WR = Select!(isSigned!R, W, UW);
1157                         alias cop = cops[oX];
1158 
1159                         bool hiBit = false;
1160                         const wR = cast(WR) cop(cast(UW)left, cast(UW)right, hiBit);
1161                         const bool wSign = (Select!(isSigned!N, left, right) < 0) ^ hiBit;
1162 
1163                         static if (isSigned!WR)
1164                         {
1165                             static if (trueMax!WR > trueMax!R)
1166                             {
1167                                 const over = (wR < 0)?
1168                                     !wSign || (wR < trueMin!R) :
1169                                      wSign || (wR > trueMax!R);
1170                             }
1171                             else
1172                                 const over = (wR < 0) != wSign;
1173                         }
1174                         else
1175                         {
1176                             static if (trueMax!WR > trueMax!R)
1177                                 const over = wSign || (wR > trueMax!R);
1178                             else
1179                                 alias over = wSign;
1180                         }
1181                     }
1182 
1183                     if (over)
1184                         IntFlag.over.raise!policy();
1185                     return cast(R) wR;
1186                 }
1187             }
1188             else static if (wop == "/")
1189             {
1190                 static if (!isSigned!N && !isSigned!M)
1191                 {
1192                     R ret = void;
1193                     if (right == 0)
1194                     {
1195                         IntFlag.div0.raise!policy();
1196                         ret = 0;
1197                     }
1198                     else
1199                         ret = cast(R)(left / right);
1200 
1201                     return ret;
1202                 }
1203                 else
1204                 {
1205                     alias P = Select!(precision!N <= 32 && precision!M <= 32, uint, ulong);
1206 
1207                     IntFlag flag;
1208                     R ret = void;
1209                     if (right == 0)
1210                     {
1211                         flag = IntFlag.div0;
1212                         ret = 0;
1213                     }
1214                     else
1215                     {
1216                         static if (isSigned!N && isSigned!M)
1217                         {
1218                             if (left == trueMin!R && right == -1)
1219                             {
1220                                 flag = IntFlag.posOver;
1221                                 ret = 0;
1222                             }
1223                             else
1224                                 ret = cast(R)(left / right);
1225                         }
1226                         else
1227                         {
1228                             alias UR = Unsigned!R;
1229 
1230                             P wL = void;
1231                             P wG = void;
1232                             static if (isSigned!N)
1233                             {
1234                                 const negR = left < 0;
1235                                 alias side = left;
1236                                 alias wS = wL;
1237                                 wG = cast(P)right;
1238                             }
1239                             else
1240                             {
1241                                 const negR = right < 0;
1242                                 wL = cast(P)left;
1243                                 alias side = right;
1244                                 alias wS = wG;
1245                             }
1246 
1247                             if (negR)
1248                             {
1249                                 wS = -cast(P)side;
1250                                 const P wR = wL / wG;
1251 
1252                                 if (wR > cast(UR)trueMin!R)
1253                                     flag = IntFlag.negOver;
1254                                 ret = -cast(R)wR;
1255                             }
1256                             else
1257                             {
1258                                 wS =  cast(P)side;
1259                                 const P wR = wL / wG;
1260 
1261                                 if (wR > cast(UR)trueMax!R)
1262                                     flag = IntFlag.posOver;
1263                                 ret =  cast(R)wR;
1264                             }
1265                         }
1266                     }
1267 
1268                     if (!flag.isNull)
1269                         flag.raise!policy();
1270                     return ret;
1271                 }
1272             }
1273             else static if (wop == "%")
1274             {
1275                 R ret = void;
1276                 static if (isSigned!M)
1277                     const wG = cast(UM)((right < 0)? -right : right);
1278                 else
1279                     const wG = right;
1280 
1281                 if (wG <= trueMax!N)
1282                 {
1283                     if (wG)
1284                         ret = cast(R)(left % cast(N)wG);
1285                     else
1286                     {
1287                         IntFlag.div0.raise!policy();
1288                         ret = 0;
1289                     }
1290                 }
1291                 else
1292                 {
1293                     static if (isSigned!N)
1294                     {
1295                         ret = (wG != cast(UN)trueMin!N || left != trueMin!N)?
1296                             cast(R)left :
1297                             cast(R)0;
1298                     }
1299                     else
1300                         ret = cast(R)left;
1301                 }
1302 
1303                 return ret;
1304             }
1305             else static if (wop.among!("<<", ">>", ">>>"))
1306             {
1307                 const negG = right < 0;
1308                 const shR = (wop == "<<")?
1309                      negG :
1310                     !negG;
1311 
1312                 R ret = void;
1313                 static if (wop == ">>>")
1314                     const wL = cast(UN)left;
1315                 else
1316                     alias wL = left;
1317                 const absG = negG?
1318                     -cast(UM)right :
1319                      cast(UM)right;
1320 
1321                 enum maxSh = precision!UN - 1;
1322                 if (absG <= maxSh)
1323                 {
1324                     const wG = cast(int)absG;
1325                     ret = cast(R)(shR?
1326                         wL >> wG :
1327                         wL << wG);
1328                 }
1329                 else
1330                 {
1331                     ret = cast(R)((isSigned!N && (wop != ">>>") && shR)?
1332                         (wL >> maxSh) :
1333                         0);
1334                 }
1335 
1336                 return ret;
1337             }
1338             else static if (wop.among!("&", "|", "^"))
1339                 return cast(R)mixin("left " ~ wop ~ " right");
1340             else
1341                 static assert(false);
1342         }
1343 
1344         /**
1345         Perform the binary (two-argument) integer operation specified by `op`.
1346 
1347         Key differences from the standard binary operators:
1348         $(UL
1349             $(LI `+`, `-`, `*`, `/`, and `%` return a signed type if the result could be negative, unless $(B both)
1350                 inputs are unsigned.)
1351             $(LI `+`, `-`, `*`, and `/` are checked for overflow.)
1352             $(LI `/` and `%` are checked for divide-by-zero, and will never generate an FPE.)
1353             $(LI `<<`, `>>`, and `>>>` are well-defined for all possible values of `right`. Large shifts return the
1354                 same result as shifting by `1` `right` times in a row. (But, much faster because no actual loop is
1355                 used.))
1356         ) $(BR)
1357         Note also:
1358         $(UL
1359             $(LI The shift operators are $(B not) checked for overflow and should not be used for
1360                 multiplication, division, or exponentiation. Instead, use `mulPow2()` and `divPow2()`, which internally
1361                 use the bitshifts for speed, but check for overflow and correctly handle negative values.)
1362             $(LI Likewise, `modPow2()` should be used for remainders instead of `&`.)
1363             $(LI `^^` and `^^=` will remain disabled in favour of `pow` until DMD issues 15288 and 15412 are fixed.)
1364         ) $(BR)
1365         Like the standard equiavlents, the assignment operators (`+=`, `-=`, `*=`, etc.) take `left` by `ref` and will
1366         overwrite it with the result of the operation.
1367         **/
1368         auto binary(string op, N, M)(const N left, const M right) @safe
1369             if (isFixedPoint!N && isFixedPoint!M &&
1370                 op.among!("+", "-", "*", "/", "%", "^^", "<<", ">>", ">>>", "&", "|", "^"))
1371         {
1372             static assert(op != "^^",
1373                 "pow() should be used instead of operator ^^ because of issue 15288.");
1374 
1375             return binaryImpl!(op, NumFromScal!N, NumFromScal!M)(left, right);
1376         }
1377         /// ditto
1378         /+ref+/ N binary(string op, N, M)(/+return+/ ref N left, const M right) @safe
1379             if (isIntegral!N && isFixedPoint!M && (op[$ - 1] == '='))
1380         {
1381             static assert(op != "^^=",
1382                 "pow() should be used instead of operator ^^= because of issue 15412.");
1383 
1384             left = binaryImpl!(op, NumFromScal!N, NumFromScal!M)(left, right);
1385             return left;
1386         }
1387         ///
1388         unittest
1389         {
1390             import checkedint.noex : smartOp; // use IntFlagPolicy.noex
1391 
1392             ulong a = 18_446_744_073_709_551_615uL;
1393             long b =      -6_744_073_709_551_615L;
1394             auto c = smartOp.binary!"+"(a, b);
1395             static assert(isSigned!(typeof(c)));
1396             assert(IntFlags.local.clear() == IntFlag.posOver);
1397 
1398             assert(smartOp.binary!"+="(a, b) == 18_440_000_000_000_000_000uL);
1399             assert(a == 18_440_000_000_000_000_000uL);
1400 
1401             uint d = 25u;
1402             int e = 32;
1403             auto f = smartOp.binary!"-"(d, e);
1404             static assert(isSigned!(typeof(f)));
1405             assert(f == -7);
1406 
1407             smartOp.binary!"-="(d, e);
1408             assert(IntFlags.local.clear() == IntFlag.negOver);
1409 
1410             uint g = 1u << 31;
1411             int h = -1;
1412             auto i = smartOp.binary!"*"(g, h);
1413             static assert(isSigned!(typeof(i)));
1414             assert(i == int.min);
1415 
1416             smartOp.binary!"*="(g, h);
1417             assert(IntFlags.local.clear() == IntFlag.negOver);
1418 
1419             long j = long.min;
1420             ulong k = 1uL << 63;
1421             auto m = smartOp.binary!"/"(j, k);
1422             static assert(isSigned!(typeof(m)));
1423             assert(m == -1);
1424 
1425             smartOp.binary!"/="(j, -1);
1426             assert(IntFlags.local.clear() == IntFlag.posOver);
1427 
1428             ushort n = 20u;
1429             ulong p = ulong.max;
1430             auto q = smartOp.binary!"%"(n, p);
1431             static assert(is(typeof(q) == ushort));
1432             assert(q == 20u);
1433 
1434             smartOp.binary!"%="(n, 0);
1435             assert(IntFlags.local.clear() == IntFlag.div0);
1436         }
1437         ///
1438         unittest
1439         {
1440             import checkedint.noex : smartOp; // use IntFlagPolicy.noex
1441 
1442             assert(smartOp.binary!"<<"(-0x80, -2) == -0x20);
1443             ubyte a = 0x3u;
1444             long b = long.max;
1445             auto c = smartOp.binary!"<<"(a, b);
1446             static assert(is(typeof(c) == ubyte));
1447             assert(c == 0u);
1448 
1449             assert(smartOp.binary!"<<="(a, 7) == 0x80u);
1450             assert(a == 0x80u);
1451 
1452             short d = -0xC;
1453             ubyte e = 5u;
1454             auto f = smartOp.binary!">>"(d, e);
1455             static assert(is(typeof(f) == short));
1456             assert(f == -0x1);
1457 
1458             assert(smartOp.binary!">>="(d, -8) == -0xC00);
1459             assert(d == -0xC00);
1460 
1461             int g = -0x80;
1462             ulong h = 2u;
1463             auto i = smartOp.binary!">>>"(g, h);
1464             static assert(is(typeof(i) == int));
1465             assert(i == 0x3FFF_FFE0);
1466 
1467             assert(smartOp.binary!">>>="(g, 32) == 0);
1468             assert(g == 0);
1469 
1470             ubyte j = 0x6Fu;
1471             short k = 0x4076;
1472             auto m = smartOp.binary!"&"(j, k);
1473             static assert(is(typeof(m) == ushort));
1474             assert(m == 0x66u);
1475 
1476             assert(smartOp.binary!"&="(j, k) == 0x66u);
1477             assert(j == 0x66u);
1478 
1479             byte n = 0x6F;
1480             ushort p = 0x4076u;
1481             auto q = smartOp.binary!"|"(n, p);
1482             static assert(is(typeof(q) == ushort));
1483             assert(q == 0x407Fu);
1484 
1485             assert(smartOp.binary!"|="(n, p) == 0x7F);
1486             assert(n == 0x7F);
1487 
1488             int r = 0x6F;
1489             int s = 0x4076;
1490             auto t = smartOp.binary!"^"(r, s);
1491             static assert(is(typeof(t) == int));
1492             assert(t == 0x4019);
1493 
1494             assert(smartOp.binary!"^="(r, s) == 0x4019);
1495             assert(r == 0x4019);
1496 
1497             assert(!IntFlags.local);
1498         }
1499 
1500         /**
1501         Equivalent to `left * pow(2, exp)`, but faster and works with a wider range of inputs. This is a safer
1502         alternative to `left << exp` that is still very fast.
1503 
1504         Note that (conceptually) rounding occurs $(I after) the `*`, meaning that `mulPow2(left, -exp)` is
1505         equivalent to `divPow2(left, exp)`.
1506         **/
1507         auto mulPow2(N, M)(const N left, const M exp) pure @safe nothrow @nogc
1508             if ((isFloatingPoint!N && isScalarType!M) || (isScalarType!N && isFloatingPoint!M))
1509         {
1510             return byPow2Impl!("*", NumFromScal!N, NumFromScal!M)(left, exp);
1511         }
1512         /// ditto
1513         auto mulPow2(N, M)(const N left, const M exp) @safe
1514             if (isFixedPoint!N && isFixedPoint!M)
1515         {
1516             return byPow2Impl!("*", policy, NumFromScal!N, NumFromScal!M)(left, exp);
1517         }
1518         ///
1519         unittest
1520         {
1521             import checkedint.noex : smartOp; // use IntFlagPolicy.noex
1522 
1523             assert(smartOp.mulPow2(-23, 5) == -736);
1524             smartOp.mulPow2(10_000_000, 10);
1525             assert(IntFlags.local.clear() == IntFlag.posOver);
1526 
1527             assert(smartOp.mulPow2(65536, -8) == 256);
1528             assert(smartOp.mulPow2(-100, -100) == 0);
1529         }
1530 
1531         /**
1532         Equivalent to `left / pow(2, exp)`, but faster and works with a wider range of inputs. This is a safer
1533         alternative to `left >> exp` that is still very fast.
1534 
1535         Note that (conceptually) rounding occurs $(I after) the `/`, meaning that `divPow2(left, -exp)` is
1536         equivalent to `mulPow2(left, exp)`.
1537         **/
1538         auto divPow2(N, M)(const N left, const M exp) pure @safe nothrow @nogc
1539             if ((isFloatingPoint!N && isScalarType!M) || (isScalarType!N && isFloatingPoint!M))
1540         {
1541             return byPow2Impl!("/", NumFromScal!N, NumFromScal!M)(left, exp);
1542         }
1543         /// ditto
1544         auto divPow2(N, M)(const N left, const M exp) @safe
1545             if (isFixedPoint!N && isFixedPoint!M)
1546         {
1547             return byPow2Impl!("/", policy, NumFromScal!N, NumFromScal!M)(left, exp);
1548         }
1549         ///
1550         unittest
1551         {
1552             import checkedint.noex : smartOp; // use IntFlagPolicy.noex
1553 
1554             assert(smartOp.divPow2(65536, 8) == 256);
1555             assert(smartOp.divPow2(-100, 100) == 0);
1556             assert(smartOp.divPow2(-23, -5) == -736);
1557 
1558             smartOp.divPow2(10_000_000, -10);
1559             assert(IntFlags.local.clear() == IntFlag.posOver);
1560         }
1561 
1562         /**
1563         Equivalent to `left % pow(2, exp)`, but faster and works with a wider range of inputs. This is a safer
1564         alternative to `left & ((1 << exp) - 1)` that is still very fast.
1565         **/
1566         auto modPow2(N, M)(const N left, const M exp) pure @safe nothrow @nogc
1567             if ((isFloatingPoint!N && isScalarType!M) || (isScalarType!N && isFloatingPoint!M))
1568         {
1569             return byPow2Impl!("%", NumFromScal!N, NumFromScal!M)(left, exp);
1570         }
1571         /// ditto
1572         auto modPow2(N, M)(const N left, const M exp) pure @safe nothrow @nogc
1573             if (isFixedPoint!N && isFixedPoint!M)
1574         {
1575             return byPow2Impl!("%", IntFlagPolicy.noex, NumFromScal!N, NumFromScal!M)(left, exp);
1576         }
1577         ///
1578         unittest
1579         {
1580             import checkedint.noex : smartOp; // use IntFlagPolicy.noex
1581 
1582             assert(smartOp.modPow2( 101,  1) ==  1);
1583             assert(smartOp.modPow2( 101,  3) ==  5);
1584             assert(smartOp.modPow2(-101,  3) == -5);
1585 
1586             assert(smartOp.modPow2(101, -2) ==  0);
1587             assert(smartOp.modPow2(101, 1_000) == 101);
1588         }
1589 
1590         /**
1591         Raise `base` to the `exp` power.
1592 
1593         Errors that may be signalled if neither input is floating-point:
1594         $(UL
1595             $(LI `IntFlag.posOver` or `IntFlag.negOver` if the absolute value of the result is too large to
1596                 represent with the return type.)
1597             $(LI `IntFlag.div0` if `base == 0` and `exp < 0`.)
1598         )
1599         **/
1600         auto pow(N, M)(const N base, const M exp) pure @safe nothrow @nogc
1601             if ((isFloatingPoint!N && isScalarType!M) || (isScalarType!N && isFloatingPoint!M))
1602         {
1603             alias R = Result!(N, "^^", M);
1604             static assert(is(typeof(return) == R));
1605             return std.math.pow(cast(R)base, exp);
1606         }
1607         /// ditto
1608         auto pow(N, M)(const N base, const M exp) @safe
1609             if (isFixedPoint!N && isFixedPoint!M)
1610         {
1611             alias R = Result!(N, "^^", M);
1612 
1613             const po = powImpl!(R, Select!(isSigned!M, long, ulong))(base, exp);
1614             static assert(is(typeof(po.num) == const(R)));
1615 
1616             if (!po.flag.isNull)
1617                 po.flag.raise!policy();
1618             return po.num;
1619         }
1620         ///
1621         unittest
1622         {
1623             import checkedint.noex : smartOp; // use IntFlagPolicy.noex
1624 
1625             assert(smartOp.pow(-10, 3) == -1_000);
1626             assert(smartOp.pow(16, 4uL) == 65536);
1627             assert(smartOp.pow(2, -1) == 0);
1628 
1629             smartOp.pow(-3, 27);
1630             assert(IntFlags.local.clear() == IntFlag.negOver);
1631             smartOp.pow(0, -5);
1632             assert(IntFlags.local.clear() == IntFlag.div0);
1633         }
1634     }
1635     private alias smartOp(bool throws) = smartOp!(cast(Flag!"throws")throws);
1636 
1637 // debug /////////////////////////////////////////////////
1638     /**
1639     `template` `alias` that evaluates to `SafeInt!(N, policy, bitOps)` in debug mode, and `N` in release mode. This
1640     way, you can use `SafeInt!N` to debug your integer logic in testing, but switch to basic `N` in release mode for
1641     maximum speed and the smallest binaries.
1642 
1643     While this may be very helpful for debuggin your algorithms, note that `DebugInt` is $(B not) a substitute
1644     for input validation in release mode. Unrecoverable FPEs or silent data-corrupting overflow can still occur in
1645     release mode if you get your algorithm wrong, or fail to add manual bounds checks in the right places.
1646 
1647     If performance is your only motivation for using `DebugInt` rather than
1648     $(LINK2 ./package.html#SmartInt, `SmartInt`), consider limiting `DebugInt` to only the hotest code paths - inner
1649     loops and the like. For most programs, this should provide nearly the same performance boost as using it
1650     everywhere, with far less loss of safety.
1651     **/
1652     template DebugInt(N, IntFlagPolicy policy, Flag!"bitOps" bitOps = Yes.bitOps)
1653         if (isIntegral!N || isCheckedInt!N)
1654     {
1655         version (Debug)
1656             alias DebugInt = SafeInt!(N, policy, bitOps);
1657         else
1658             alias DebugInt = Unqual!(BasicScalar!N);
1659     }
1660 
1661 // safe /////////////////////////////////////////////////
1662     /**
1663     Wrapper for any basic integral type `N` that uses the checked operations from `safeOp` and rejects attempts to
1664     directly assign values that cannot be proven to be within the range representable by `N`.
1665     ($(LINK2 ./package.html#to, `checkedint.to()`) can be used to safely assign values of incompatible types, with
1666     runtime bounds checking.)
1667 
1668     `SafeInt` is designed to be as interchangeable with `N` as possible, without compromising safety. The
1669     $(LINK2 ./package.html#DebugInt, `DebugInt`) `template` allows a variable to use `SafeInt` in debug mode to find
1670     bugs, and `N` directly in release mode for greater speed and a smaller binary.
1671 
1672     If you're not trying to write generic code that works with both `SafeInt!N` and `N`, you should probably be using
1673     $(LINK2 ./package.html#SmartInt, `SmartInt`) instead. It generates far fewer error messages; mostly it
1674     "just works".
1675 
1676     $(UL
1677         $(LI `policy` controls the error signalling policy (see `checkedint.flags`).)
1678         $(LI `bitOps` may be set to `No.bitOps` if desired, to turn bitwise operations on this type into a compile-time
1679             error.)
1680     )
1681     **/
1682     struct SafeInt(N, IntFlagPolicy _policy, Flag!"bitOps" bitOps = Yes.bitOps)
1683         if (isIntegral!N && isUnqual!N)
1684     {
1685         /// The error signalling policy used by this `SafeInt` type.
1686         enum IntFlagPolicy policy = _policy;
1687 
1688         static if (bitOps)
1689         {
1690             /**
1691             The basic integral value of this `SafeInt`. Accessing this directly may be useful for:
1692             $(UL
1693                 $(LI Intentionally doing modular (unchecked) arithmetic, or)
1694                 $(LI Interacting with APIs that are not `checkedint` aware.)
1695             )
1696             **/
1697             N bscal;
1698             ///
1699             unittest
1700             {
1701                 import checkedint.throws : SafeInt; // use IntFlagPolicy.throws
1702 
1703                 SafeInt!uint n;
1704                 static assert(is(typeof(n.bscal) == uint));
1705 
1706                 n = 7u;
1707                 assert(n.bscal == 7u);
1708 
1709                 n.bscal -= 8u;
1710                 assert(n == uint.max);
1711             }
1712 
1713             /// Get a view of this `SafeInt` that allows bitwise operations.
1714             @property ref inout(SafeInt!(N, policy, Yes.bitOps)) bits() /+return+/ inout pure @safe nothrow @nogc
1715             {
1716                 return this;
1717             }
1718             ///
1719             unittest
1720             {
1721                 import checkedint.throws : SafeInt; // use IntFlagPolicy.throws
1722 
1723                 SafeInt!(int, No.bitOps) n = 1;
1724                 static assert(!__traits(compiles, n << 2));
1725                 assert(n.bits << 2 == 4);
1726             }
1727         }
1728         else
1729         {
1730             @property ref inout(N) bscal() /+return+/ inout pure @safe nothrow @nogc
1731             {
1732                 return bits.bscal;
1733             }
1734             SafeInt!(N, policy, Yes.bitOps) bits;
1735         }
1736 
1737         static if (__VERSION__ >= 2067)
1738         {
1739             version(GNU) { static assert(false); }
1740 
1741             /// The most negative possible value of this `SafeInt` type.
1742             enum SafeInt!(N, policy, bitOps) min = typeof(this)(trueMin!N);
1743             ///
1744             unittest
1745             {
1746                 import checkedint.throws : SafeInt; // use IntFlagPolicy.throws
1747 
1748                 assert(SafeInt!(int).min == int.min);
1749                 assert(SafeInt!(uint).min == uint.min);
1750             }
1751 
1752             /// The most positive possible value of this `SafeInt` type.
1753             enum SafeInt!(N, policy, bitOps) max = typeof(this)(trueMax!N);
1754             ///
1755             unittest
1756             {
1757                 import checkedint.throws : SafeInt; // use IntFlagPolicy.throws;
1758 
1759                 assert(SafeInt!(int).max == int.max);
1760                 assert(SafeInt!(uint).max == uint.max);
1761             }
1762         }
1763         else
1764         {
1765             static @property auto min() pure @safe nothrow @nogc
1766             {
1767                 return typeof(this)(trueMin!N);
1768             }
1769             static @property auto max() pure @safe nothrow @nogc
1770             {
1771                 return typeof(this)(trueMax!N);
1772             }
1773         }
1774 
1775         // Construction, assignment, and casting /////////////////////////////////////////////////
1776         private void checkImplicit(M)() const @safe
1777             if (isCheckedInt!M || isScalarType!M)
1778         {
1779             alias MB = BasicScalar!M;
1780             static assert(trueMin!MB >= cast(real)N.min && MB.max <= cast(real)N.max,
1781                 "SafeInt does not support implicit conversions from " ~
1782                 MB.stringof ~ " to " ~ N.stringof ~
1783                 ", because they are unsafe when unchecked. Use the explicit checkedint.to()."
1784             );
1785         }
1786 
1787         /**
1788         Assign the value of `that` to this `SafeInt` instance.
1789 
1790         Trying to assign a value that cannot be proven at compile time to be representable by `N` is an error. Use
1791         $(LINK2 ./package.html#to, `checkedint.to()`) to safely convert `that` with runtime bounds checking, instead.
1792         **/
1793         this(M)(const M that) pure @safe nothrow @nogc
1794             if (isCheckedInt!M || isScalarType!M)
1795         {
1796             checkImplicit!M();
1797             this.bscal = that.bscal;
1798         }
1799         /// ditto
1800         ref typeof(this) opAssign(M)(const M that) /+return+/ pure @safe nothrow @nogc
1801             if (isCheckedInt!M || isScalarType!M)
1802         {
1803             checkImplicit!M();
1804             this.bscal = that.bscal;
1805             return this;
1806         }
1807         ///
1808         unittest
1809         {
1810             import checkedint.noex : SafeInt, to; // use IntFlagPolicy.noex
1811 
1812             // Only types that for which N can represent all values are accepted directly:
1813             SafeInt!int n = int.max;
1814             n = byte.max;
1815             n = wchar.max;
1816 
1817             // Values of a type that could be `< N.min` or `> N.max` are rejected at compile time:
1818             static assert(!__traits(compiles, n = long.max));
1819             static assert(!__traits(compiles, n = uint.max));
1820             static assert(!__traits(compiles, n = real.max));
1821 
1822             // Instead, use checkedint.to(), which does runtime bounds checking:
1823             n = to!int(315L);
1824             assert(n == 315);
1825 
1826             n = to!int(long.max);
1827             assert(IntFlags.local.clear() == IntFlag.posOver);
1828         }
1829 
1830         /**
1831         Convert this value to floating-point. This always succeeds, although some loss of precision may
1832         occur if M.sizeof <= N.sizeof.
1833         **/
1834         M opCast(M)() const pure @safe nothrow @nogc
1835             if (isFloatingPoint!M)
1836         {
1837             return cast(M)bscal;
1838         }
1839         ///
1840         unittest
1841         {
1842             import checkedint.throws : SafeInt; // use IntFlagPolicy.throws
1843 
1844             SafeInt!int n = 92;
1845             auto f = cast(double)n;
1846             static assert(is(typeof(f) == double));
1847             assert(f == 92.0);
1848         }
1849 
1850         /// `this != 0`
1851         M opCast(M)() const pure @safe nothrow @nogc
1852             if (is(M == bool))
1853         {
1854             return bscal != 0;
1855         }
1856         ///
1857         unittest
1858         {
1859             import checkedint.throws : SafeInt; // use IntFlagPolicy.throws
1860 
1861             SafeInt!int n = -315;
1862             assert( cast(bool)n);
1863 
1864             n = 0;
1865             assert(!cast(bool)n);
1866         }
1867 
1868         /**
1869         Convert this value to type `M` using $(LINK2 ./package.html#to, `checkedint.to()`) for bounds checking. An
1870         $(LINK2 ./flags.html, `IntFlag`) will be raised if `M` cannot represent the current value of this `SafeInt`.
1871         **/
1872         M opCast(M)() const @safe
1873             if (isCheckedInt!M || isIntegral!M || isSomeChar!M)
1874         {
1875             return to!(M, policy)(bscal);
1876         }
1877         ///
1878         unittest
1879         {
1880             import checkedint.noex : SafeInt; // use IntFlagPolicy.noex
1881 
1882             SafeInt!ulong n = 52uL;
1883             auto a = cast(int)n;
1884             static assert(is(typeof(a) == int));
1885             assert(!IntFlags.local);
1886             assert(a == 52);
1887 
1888             auto m = SafeInt!long(-1).mulPow2(n);
1889             auto b = cast(wchar)m;
1890             static assert(is(typeof(b) == wchar));
1891             assert(IntFlags.local.clear() == IntFlag.negOver);
1892         }
1893 
1894         /**
1895         Convert this value to a type suitable for indexing an array:
1896         $(UL
1897             $(LI If `N` is signed, a `ptrdiff_t` is returned.)
1898             $(LI If `N` is unsigned, a `size_t` is returned.)
1899         )
1900         $(LINK2 ./package.html#to, `checkedint.to()`) is used for bounds checking.
1901         **/
1902         @property Select!(isSigned!N, ptrdiff_t, size_t) idx() const @safe
1903         {
1904             return to!(typeof(return), policy)(bscal);
1905         }
1906         ///
1907         unittest
1908         {
1909             import checkedint.throws : SafeInt; // use IntFlagPolicy.throws
1910 
1911             char[3] arr = ['a', 'b', 'c'];
1912             SafeInt!long n = 1;
1913 
1914             // On 32-bit, `long` cannot be used directly for array indexing,
1915             static if (size_t.sizeof < long.sizeof)
1916                 static assert(!__traits(compiles, arr[n]));
1917             // but idx can be used to concisely and safely cast to ptrdiff_t:
1918             assert(arr[n.idx] == 'b');
1919 
1920             // The conversion is bounds checked:
1921             static if (size_t.sizeof < long.sizeof)
1922             {
1923                 n = long.min;
1924                 try
1925                 {
1926                     arr[n.idx] = '?';
1927                 }
1928                 catch (CheckedIntException e)
1929                 {
1930                     assert(e.intFlags == IntFlag.negOver);
1931                 }
1932             }
1933         }
1934 
1935         /// Get a simple hashcode for this value.
1936         size_t toHash() const pure @safe nothrow @nogc
1937         {
1938             static if (N.sizeof > size_t.sizeof)
1939             {
1940                 static assert(N.sizeof == (2 * size_t.sizeof));
1941                 return cast(size_t)bscal ^ cast(size_t)(bscal >>> 32);
1942             }
1943             else
1944                 return cast(size_t)bscal;
1945         }
1946 
1947         /// Get a `string` representation of this value.
1948         string toString() const @safe
1949         {
1950             return to!(string, IntFlagPolicy.noex)(bscal);
1951         }
1952         /// ditto
1953         void toString(Writer, Char = char)(Writer sink, FormatSpec!Char fmt = (FormatSpec!Char).init) const
1954         {
1955             formatValue(sink, bscal, fmt);
1956         }
1957         ///
1958         unittest
1959         {
1960             import checkedint.throws : safeInt; // use IntFlagPolicy.throws
1961             assert(safeInt(-753).toString() == "-753");
1962         }
1963 
1964         // Comparison /////////////////////////////////////////////////
1965         /// See $(LINK2 ./package.html#safeOp, `safeOp`).
1966         bool opEquals(M)(const M right) const pure @safe nothrow @nogc
1967             if (isSafeInt!M || isScalarType!M)
1968         {
1969             return safeOp!(policy).cmp!"=="(this.bscal, right.bscal);
1970         }
1971 
1972         /**
1973         Perform a floating-point comparison to `right`.
1974 
1975         Returns: $(UL
1976             $(LI `-1` if this value is less than `right`.)
1977             $(LI ` 0` if this value is equal to `right`.)
1978             $(LI ` 1` if this value is greater than `right`.)
1979             $(LI `float.nan` if `right` is a floating-point `nan` value.))
1980         **/
1981         auto opCmp(M)(const M right) const pure @safe nothrow @nogc
1982             if (isFloatingPoint!M)
1983         {
1984             return
1985                 (left <  right)? -1 :
1986                 (left >  right)?  1 :
1987                 (left == right)?  0 : float.nan;
1988         }
1989         /// See $(LINK2 ./package.html#safeOp, `safeOp`).
1990         int opCmp(M)(const M right) const pure @safe nothrow @nogc
1991             if (isSafeInt!M || isFixedPoint!M)
1992         {
1993             return
1994                 safeOp!(policy).cmp!"<"(this.bscal, right.bscal)? -1 :
1995                 safeOp!(policy).cmp!">"(this.bscal, right.bscal);
1996         }
1997 
1998         // Unary /////////////////////////////////////////////////
1999         /// ditto
2000         typeof(this) opUnary(string op)() const @safe
2001             if (op.among!("-", "+", "~"))
2002         {
2003             static assert(bitOps || (op != "~"),
2004                 "Bitwise operations are disabled.");
2005 
2006             return typeof(return)(safeOp!(policy).unary!op(bscal));
2007         }
2008         /// ditto
2009         ref typeof(this) opUnary(string op)() /+return+/ @safe
2010             if (op.among!("++", "--"))
2011         {
2012             safeOp!(policy).unary!op(bscal);
2013             return this;
2014         }
2015 
2016         /// ditto
2017         typeof(this) abs() const @safe
2018         {
2019             return typeof(return)(safeOp!(policy).abs(bscal));
2020         }
2021 
2022         /// Count the number of set bits using `core.bitop.popcnt()`.
2023         SafeInt!(int, policy, bitOps) popcnt()() const pure @safe nothrow @nogc
2024         {
2025             static assert(bitOps, "Bitwise operations are disabled.");
2026 
2027             import future.bitop : stdPC = popcnt;
2028             return typeof(return)(stdPC(bscal));
2029         }
2030 
2031         /// See $(LINK2 ./package.html#safeOp, `safeOp`).
2032         SafeInt!(int, policy, bitOps) bsf()() const @safe
2033         {
2034             static assert(bitOps, "Bitwise operations are disabled.");
2035 
2036             return typeof(return)(safeOp!(policy).bsf(bscal));
2037         }
2038         /// ditto
2039         SafeInt!(int, policy, bitOps) bsr()() const @safe
2040         {
2041             static assert(bitOps, "Bitwise operations are disabled. Consider using ilogb() instead?");
2042 
2043             return typeof(return)(safeOp!(policy).bsr(bscal));
2044         }
2045 
2046         /// ditto
2047         SafeInt!(int, policy, bitOps) ilogb() const @safe
2048         {
2049             return typeof(return)(safeOp!(policy).ilogb(bscal));
2050         }
2051 
2052         // Binary /////////////////////////////////////////////////
2053         /// Perform a floating-point math operation.
2054         M opBinaryRight(string op, M)(const M left) const pure @safe nothrow @nogc
2055             if (isFloatingPoint!M)
2056         {
2057             return mixin("left " ~ op ~ " bscal");
2058         }
2059         /// ditto
2060         M opBinary(string op, M)(const M right) const pure @safe nothrow @nogc
2061             if (isFloatingPoint!M)
2062         {
2063             return mixin("bscal " ~ op ~ " right");
2064         }
2065         /// See $(LINK2 ./package.html#safeOp, `safeOp`).
2066         SafeInt!(OpType!(M, op, N), policy, bitOps) opBinaryRight(string op, M)(const M left) const @safe
2067             if (isFixedPoint!M)
2068         {
2069             static assert(bitOps || !op.among!("<<", ">>", ">>>", "&", "|", "^"),
2070                 "Bitwise operations are disabled. Consider using mulPow2(), divPow2(), or modPow2() instead?");
2071 
2072             return typeof(return)(safeOp!(policy).binary!op(left, bscal));
2073         }
2074         /// ditto
2075         SafeInt!(OpType!(N, op, BasicScalar!M), .max(policy, intFlagPolicyOf!M), bitOps && hasBitOps!M) opBinary(string op, M)(const M right) const @safe
2076             if (isSafeInt!M || isFixedPoint!M)
2077         {
2078             static assert(bitOps && hasBitOps!M || !op.among!("<<", ">>", ">>>", "&", "|", "^"),
2079                 "Bitwise operations are disabled. Consider using mulPow2(), divPow2(), or modPow2() instead?");
2080 
2081             return typeof(return)(safeOp!(.max(policy, intFlagPolicyOf!M)).binary!op(this.bscal, right.bscal));
2082         }
2083         /// ditto
2084         ref typeof(this) opOpAssign(string op, M)(const M right) /+return+/ @safe
2085             if (isCheckedInt!M || isFixedPoint!M)
2086         {
2087             static assert((bitOps && hasBitOps!M) || !op.among!("<<", ">>", ">>>", "&", "|", "^"),
2088                 "Bitwise operations are disabled. Consider using mulPow2(), divPow2(), or modPow2() instead?");
2089             checkImplicit!(OpType!(N, op, BasicScalar!M))();
2090 
2091             safeOp!(.max(policy, intFlagPolicyOf!M)).binary!(op ~ "=")(this.bscal, right.bscal);
2092             return this;
2093         }
2094 
2095         /// ditto
2096         auto mulPow2(M)(const M exp) const pure @safe nothrow @nogc
2097             if (isFloatingPoint!M)
2098         {
2099             return safeOp!(policy).mulPow2(bscal, exp);
2100         }
2101         /// ditto
2102         auto mulPow2(M)(const M exp) const @safe
2103             if (isCheckedInt!M || isFixedPoint!M)
2104         {
2105             enum mixPolicy = .max(policy, intFlagPolicyOf!M);
2106             const wret = safeOp!(mixPolicy).mulPow2(this.bscal, exp.bscal);
2107             return SafeInt!(typeof(wret), mixPolicy, bitOps && hasBitOps!M)(wret);
2108         }
2109         /// ditto
2110         auto divPow2(M)(const M exp) const pure @safe nothrow @nogc
2111             if (isFloatingPoint!M)
2112         {
2113             return safeOp!(policy).divPow2(bscal, exp);
2114         }
2115         /// ditto
2116         auto divPow2(M)(const M exp) const @safe
2117             if (isCheckedInt!M || isFixedPoint!M)
2118         {
2119             enum mixPolicy = .max(policy, intFlagPolicyOf!M);
2120             const wret = safeOp!(mixPolicy).divPow2(this.bscal, exp.bscal);
2121             return SafeInt!(typeof(wret), mixPolicy, bitOps && hasBitOps!M)(wret);
2122         }
2123         /// ditto
2124         auto modPow2(M)(const M exp) const pure @safe nothrow @nogc
2125             if (isFloatingPoint!M)
2126         {
2127             return safeOp!(policy).modPow2(bscal, exp);
2128         }
2129         /// ditto
2130         auto modPow2(M)(const M exp) const @safe
2131             if (isCheckedInt!M || isFixedPoint!M)
2132         {
2133             enum mixPolicy = .max(policy, intFlagPolicyOf!M);
2134             const wret = safeOp!(mixPolicy).modPow2(this.bscal, exp.bscal);
2135             return SafeInt!(typeof(wret), mixPolicy, bitOps && hasBitOps!M)(wret);
2136         }
2137 
2138         /// Raise `this` to the `exp` power using `std.math.pow()`.
2139         M pow(M)(const M exp) const pure @safe nothrow @nogc
2140             if (isFloatingPoint!M)
2141         {
2142             return std.math.pow(bscal, exp);
2143         }
2144         /// See $(LINK2 ./package.html#safeOp, `safeOp`).
2145         SafeInt!(CallType!(std.math.pow, N, BasicScalar!M), .max(policy, intFlagPolicyOf!M), bitOps && hasBitOps!M) pow(M)(const M exp) const @safe
2146             if (isCheckedInt!M || isFixedPoint!M)
2147         {
2148             return typeof(return)(safeOp!(.max(policy, intFlagPolicyOf!M)).pow(this.bscal, exp.bscal));
2149         }
2150     }
2151     /// ditto
2152     template SafeInt(N, IntFlagPolicy policy, Flag!"bitOps" bitOps = Yes.bitOps)
2153         if ((isIntegral!N && !isUnqual!N) || isCheckedInt!N)
2154     {
2155         alias SafeInt = SafeInt!(BasicScalar!N, policy, bitOps);
2156     }
2157     ///
2158     unittest
2159     {
2160         // Mixing standard signed and unsigned types is dangerous...
2161         int ba = -1;
2162         uint bb = 0;
2163         assert(ba > bb);
2164 
2165         auto bc = ba + bb;
2166         assert(is(typeof(bc) == uint));
2167         assert(bc == 4294967295u);
2168 
2169         // ...that's why SafeInt won't let you do it.
2170         import checkedint.throws : SafeInt, to; // use IntFlagPolicy.throws
2171 
2172         SafeInt!int sa = -1;
2173         SafeInt!uint sb = 0u;
2174         static assert(!__traits(compiles, sa < sb));
2175         static assert(!__traits(compiles, sa + sb));
2176 
2177         // Instead, use checkedint.to() to safely convert to a common type...
2178         auto sbi = to!(SafeInt!int)(sb);
2179         assert(sa < sbi);
2180         auto sc = sa + sbi;
2181         assert(sc == -1);
2182         // (...or just switch to SmartInt.)
2183     }
2184     ///
2185     unittest
2186     {
2187         // When IntFlagPolicy.throws is set, SafeInt operations that fail at runtime will throw a CheckedIntException.
2188         import checkedint.throws : SafeInt;
2189 
2190         SafeInt!uint sa = 1u;
2191         SafeInt!uint sb = 0u;
2192 
2193         bool overflow = false;
2194         try
2195         {
2196             SafeInt!uint sc = sb - sa;
2197             assert(false);
2198         }
2199         catch (CheckedIntException e)
2200         {
2201             assert(e.intFlags == IntFlag.negOver);
2202             overflow = true;
2203         }
2204         assert(overflow);
2205 
2206         bool div0 = false;
2207         try
2208         {
2209             // With standard integers, this would crash the program with an unrecoverable FPE...
2210             SafeInt!uint sc = sa / sb;
2211             assert(false);
2212         }
2213         catch (CheckedIntException e)
2214         {
2215             // ...but with SafeInt, it just throws a normal Exception.
2216             assert(e.intFlags == IntFlag.div0);
2217             div0 = true;
2218         }
2219         assert(div0);
2220     }
2221     ///
2222     unittest
2223     {
2224         // When IntFlagPolicy.noex is set, SafeInt operations that fail at runtime set one or more bits in IntFlags.local.
2225         import checkedint.noex : SafeInt;
2226 
2227         SafeInt!uint sa = 1u;
2228         SafeInt!uint sb = 0u;
2229         SafeInt!uint sc;
2230 
2231         sc = sb - sa;
2232         assert(IntFlags.local == IntFlag.negOver);
2233 
2234         // With standard integers, this would crash the program with an unrecoverable FPE...
2235         sc = sa / sb;
2236         // ...but with SmartInt, it just sets a bit in IntFlags.local.
2237         assert(IntFlags.local & IntFlag.div0);
2238 
2239         // Each flag will remain set until cleared:
2240         assert(IntFlags.local.clear() == (IntFlag.negOver | IntFlag.div0));
2241         assert(!IntFlags.local);
2242     }
2243 
2244     private template SafeInt(N, IntFlagPolicy policy, bool bitOps)
2245         if (isIntegral!N)
2246     {
2247         alias SafeInt = SafeInt!(
2248             Unqual!N,
2249             policy,
2250             cast(Flag!"bitOps")bitOps);
2251     }
2252 
2253     /// Get the value of `num` as a `SafeInt!N`. The integral type `N` can be infered from the argument.
2254     SafeInt!(N, policy, bitOps) safeInt(IntFlagPolicy policy, Flag!"bitOps" bitOps = Yes.bitOps, N)(N num) @safe
2255         if (isIntegral!N || isCheckedInt!N)
2256     {
2257         return typeof(return)(num.bscal);
2258     }
2259     ///
2260     unittest
2261     {
2262         import checkedint.throws : safeInt, SafeInt; // use IntFlagPolicy.throws
2263 
2264         auto a = safeInt(55uL);
2265         static assert(is(typeof(a) == SafeInt!ulong));
2266         assert(a == 55u);
2267     }
2268 
2269     /**
2270     Implements various integer math operations with error checking.
2271 
2272     `safeOp` strives to mimic the standard integer math operations in every way, except:
2273     $(UL
2274         $(LI If the operation is generally untrustworthy - for example, signed/unsigned comparisons - a compile-time error
2275             is generated. The message will usually suggest a workaround.)
2276         $(LI At runtime, if the result is mathematically incorrect an appropriate $(LINK2 ./flags.html, `IntFlag`)
2277             will be raised.)
2278     )
2279     The runtime error-signalling policy may be selected using the `policy` template parameter.
2280     **/
2281     template safeOp(IntFlagPolicy policy)
2282     {
2283         // NOTE: ddoc only scans the first branch of a static if
2284         static if (policy == IntFlagPolicy.none)
2285         {
2286         // No need to redundantly instantiate members which don't depend on `policy`.
2287 
2288             private void cmpTypeCheck(N, M)() pure @safe nothrow @nogc
2289             {
2290                 static assert(isBoolean!N == isBoolean!M,
2291                     "The intent of a direct comparison of " ~
2292                     N.stringof ~ " with " ~ M.stringof ~
2293                     " is unclear. Add an explicit cast."
2294                 );
2295 
2296                 alias OT = OpType!(N, "+", M);
2297                 static assert(isFloatingPoint!OT || isSigned!OT || !(isSigned!N || isSigned!M),
2298                     "The standard signed/unsigned comparisons of " ~ N.stringof ~ " to " ~ M.stringof ~
2299                     " are unsafe. Use an explicit cast, or switch to smartOp/SmartInt."
2300                 );
2301             }
2302 
2303             /**
2304             Compare `left` and `right` using `op`.
2305 
2306             Unsafe signed/unsigned comparisons will trigger a compile-time error. Possible solutions include:
2307             $(UL
2308                 $(LI Should the inputs really have different signedness? Changing the type of one to match the other
2309                     is the simplest solution.)
2310                 $(LI Consider using `smartOp.cmp()`, instead, as it can safely do signed/unsigned comparisons.)
2311                 $(LI Alternately, $(LINK2 ./package.html#to, `checkedint.to()`) can be used to safely convert the
2312                     type of one input, with runtime bounds checking.)
2313             ) $(BR)
2314             Direct comparisons between boolean values and numeric ones are also forbidden. Make your intention explicit:
2315             $(UL
2316                 $(LI `numeric == cast(N)boolean`)
2317                 $(LI `(numeric != 0) == boolean`)
2318             )
2319             **/
2320             bool cmp(string op, N, M)(const N left, const M right) pure @safe nothrow @nogc
2321                 if (isScalarType!N && isScalarType!M)
2322             {
2323                 cmpTypeCheck!(N, M)();
2324                 return mixin("left " ~ op ~ " right");
2325             }
2326             ///
2327             unittest
2328             {
2329                 import checkedint.noex : safeOp; // safeOp.cmp() never throws
2330 
2331                 assert(safeOp.cmp!"=="(int.max, 0x7FFF_FFFF));
2332                 assert(safeOp.cmp!"!="(uint.min, 5u));
2333                 assert(safeOp.cmp!"<="(int.min, 0));
2334 
2335                 static assert(!__traits(compiles, safeOp.cmp!"=="(uint.max, -1)));
2336                 static assert(!__traits(compiles, safeOp.cmp!">"(-1, 1u)));
2337             }
2338         }
2339         else
2340             alias cmp = safeOp!(IntFlagPolicy.none).cmp;
2341 
2342         /**
2343         Perform the unary (single-argument) integer operation specified by `op`.
2344 
2345         Trying to negate `-` an unsigned value will generate a compile-time error, because mathematically, the result should
2346         always be negative (except for -0), but the unsigned return type cannot represent this.
2347 
2348         `++` and `--` are checked for overflow at runtime, and will raise `IntFlag.posOver` or `IntFlag.negOver` if needed.
2349         **/
2350         N unary(string op, N)(const N num) @safe
2351             if ((isIntegral!N) && op.among!("-", "+", "~"))
2352         {
2353             static assert(isSigned!N || op != "-",
2354                 "The standard unary - operation for " ~ N.stringof ~
2355                 " is unsafe. Use an explicit cast to a signed type, or switch to smartOp/SmartInt."
2356             );
2357 
2358             static if (op == "-")
2359             {
2360                 static if (is(N == int) || is(N == long))
2361                 {
2362                     bool over = false;
2363                     const N ret = negs(num, over);
2364                 }
2365                 else
2366                 {
2367                     const over = (num <= trueMin!N);
2368                     const N ret = -num;
2369                 }
2370 
2371                 if (over)
2372                     IntFlag.posOver.raise!policy();
2373 
2374                 return ret;
2375             }
2376             else
2377                 return mixin(op ~ "num");
2378         }
2379         /// ditto
2380         /+ref+/ N unary(string op, N)(/+return+/ ref N num) @safe
2381             if ((isIntegral!N) && op.among!("++", "--"))
2382         {
2383             static if (op == "++")
2384             {
2385                 if (num >= trueMax!N)
2386                     IntFlag.posOver.raise!policy();
2387             }
2388             else static if (op == "--")
2389             {
2390                 if (num <= trueMin!N)
2391                     IntFlag.negOver.raise!policy();
2392             }
2393 
2394             return mixin(op ~ "num");
2395         }
2396         ///
2397         unittest
2398         {
2399             import checkedint.noex : safeOp; // use IntFlagPolicy.noex
2400 
2401             assert(safeOp.unary!"~"(0u) == uint.max);
2402 
2403             assert(safeOp.unary!"-"(20L) == -20L);
2404             static assert(!__traits(compiles, safeOp.unary!"-"(20uL)));
2405             safeOp.unary!"-"(long.min);
2406             assert(IntFlags.local.clear() == IntFlag.posOver);
2407 
2408             auto a = safeOp.unary!"+"(uint.max);
2409             static assert(is(typeof(a) == uint));
2410             assert(a == uint.max);
2411 
2412             uint b = 1u;
2413             assert(safeOp.unary!"--"(b) == 0u);
2414             assert(b == 0u);
2415             safeOp.unary!"--"(b);
2416             assert(IntFlags.local.clear() == IntFlag.negOver);
2417 
2418             int c = 7;
2419             assert(safeOp.unary!"++"(c) == 8);
2420             assert(c == 8);
2421         }
2422 
2423         /**
2424         Get the absolute value of `num`.
2425 
2426         `IntFlag.posOver` is raised if `N` is signed and `num == N.min`.
2427         **/
2428         N abs(N)(const N num) @safe
2429             if (isIntegral!N || isBoolean!N)
2430         {
2431             static if (isSigned!N)
2432             {
2433                 if (num < 0)
2434                     return unary!"-"(num);
2435             }
2436             return num;
2437         }
2438         ///
2439         unittest
2440         {
2441             import checkedint.noex : safeOp; // use IntFlagPolicy.noex
2442 
2443             assert(safeOp.abs(-25) == 25);
2444             assert(safeOp.abs(745u) == 745u);
2445 
2446             safeOp.abs(int.min);
2447             assert(IntFlags.local.clear() == IntFlag.posOver);
2448         }
2449 
2450         /// `core.bitop.bsf` without the undefined behaviour. `safeOp.bsf(0)` will raise `IntFlag.undef`.
2451         int bsf(N)(const N num) @safe
2452             if (isFixedPoint!N)
2453         {
2454             return bsfImpl!policy(num);
2455         }
2456         ///
2457         unittest
2458         {
2459             import checkedint.noex : safeOp; // use IntFlagPolicy.noex
2460 
2461             assert(safeOp.bsf(20) == 2);
2462 
2463             safeOp.bsf(0);
2464             assert(IntFlags.local.clear() == IntFlag.undef);
2465         }
2466 
2467         /// `core.bitop.bsr` without the undefined behaviour. `safeOp.bsr(0)` will raise `IntFlag.undef`.
2468         int bsr(N)(const N num) @safe
2469             if (isFixedPoint!N)
2470         {
2471             return bsrImpl!policy(num);
2472         }
2473         ///
2474         unittest
2475         {
2476             import checkedint.noex : safeOp; // use IntFlagPolicy.noex
2477 
2478             assert(safeOp.bsr( 20) ==  4);
2479             assert(safeOp.bsr(-20) == 31);
2480 
2481             safeOp.bsr(0);
2482             assert(IntFlags.local.clear() == IntFlag.undef);
2483         }
2484 
2485         /**
2486         Get the base 2 logarithm of `abs(num)`, rounded down to the nearest integer.
2487 
2488         `safeOp.ilogb(0)` will raise `IntFlag.undef`.
2489         **/
2490         int ilogb(N)(const N num) @safe
2491             if (isFixedPoint!N)
2492         {
2493             static if (isSigned!N)
2494                 const absN = cast(Unsigned!N) (num < 0? -num : num);
2495             else
2496                 alias absN = num;
2497 
2498             return bsrImpl!policy(absN);
2499         }
2500         ///
2501         unittest
2502         {
2503             import checkedint.noex : safeOp; // use IntFlagPolicy.noex
2504 
2505             assert(safeOp.ilogb( 20) == 4);
2506             assert(safeOp.ilogb(-20) == 4);
2507 
2508             safeOp.ilogb(0);
2509             assert(IntFlags.local.clear() == IntFlag.undef);
2510         }
2511 
2512         private auto binaryImpl(string op, N, M)(const N left, const M right) @safe
2513             if (isFixedPoint!N && isFixedPoint!M)
2514         {
2515             enum wop = (op[$-1] == '=')? op[0 .. $-1] : op;
2516             alias P = OpType!(N, wop, M);
2517             alias R = Select!(wop == op, P, N);
2518 
2519             static if (wop.among!("+", "-", "*"))
2520             {
2521                 enum isPromSafe = !(isSigned!N || isSigned!M) || (isSigned!P && isSigned!R);
2522                 enum needCOp = (wop == "*")?
2523                     (precision!N + precision!M) > precision!P :
2524                     (max(precision!N, precision!M) + 1) > precision!P;
2525 
2526                 bool over = false;
2527                 static if (needCOp)
2528                 {
2529                     enum cx = (staticIndexOf!(wop, "+", "-", "*") << 1) + isSigned!P;
2530                     alias cop = AliasSeq!(addu, adds, subu, subs, mulu, muls)[cx];
2531 
2532                     const pR = cop(cast(P)left, cast(P)right, over);
2533                 }
2534                 else
2535                     const pR = mixin("left " ~ wop ~ " right");
2536 
2537                 static if (isSigned!P && trueMin!P < trueMin!R)
2538                 {
2539                     if (pR < trueMin!R)
2540                         over = true;
2541                 }
2542                 static if (trueMax!P > trueMax!R)
2543                 {
2544                     if (pR > trueMax!R)
2545                         over = true;
2546                 }
2547 
2548                 if (over)
2549                     IntFlag.over.raise!policy();
2550                 return cast(R)pR;
2551             }
2552             else static if (wop.among!("/", "%"))
2553             {
2554                 enum isPromSafe = !(isSigned!N || isSigned!M) ||
2555                     (isSigned!P && (wop == "%"? (isSigned!R || !isSigned!N) : isSigned!R));
2556 
2557                 const div0 = (right == 0);
2558                 static if (isSigned!N && isSigned!M)
2559                     const posOver = (left == trueMin!R) && (right == -1);
2560                 else
2561                     enum posOver = false;
2562 
2563                 R ret = void;
2564                 if (div0 || posOver)
2565                 {
2566                     (posOver? IntFlag.posOver : IntFlag.div0).raise!policy();
2567                     ret = 0; // Prevent unrecoverable FPE
2568                 }
2569                 else
2570                     ret = cast(R)mixin("left " ~ wop ~ " right");
2571 
2572                 return ret;
2573             }
2574             else static if (wop.among!("<<", ">>", ">>>"))
2575             {
2576                 enum isPromSafe = !isSigned!N || isSigned!R || (op == ">>>");
2577 
2578                 enum invalidSh = ~cast(M)(8 * P.sizeof - 1);
2579                 if (right & invalidSh)
2580                     IntFlag.undef.raise!policy();
2581 
2582                 return cast(R) mixin("cast(P)left " ~ wop ~ " right");
2583             }
2584             else static if (wop.among!("&", "|", "^"))
2585             {
2586                 enum isPromSafe = true;
2587 
2588                 return cast(R)mixin("left " ~ wop ~ " right");
2589             }
2590             else
2591                 static assert(false);
2592 
2593             static assert(isPromSafe,
2594                 "The standard " ~ N.stringof ~ " " ~ op ~ " " ~ M.stringof ~
2595                 " operation is unsafe, due to a signed/unsigned mismatch. " ~
2596                 "Use an explicit cast, or switch to smartOp/SmartInt."
2597             );
2598         }
2599 
2600         /**
2601         Perform the binary (two-argument) integer operation specified by `op`.
2602         $(UL
2603             $(LI Unsafe signed/unsigned operations will generate a compile-time error.)
2604             $(LI `+`, `-`, `*`, `/`, and `%` are checked for overflow at runtime.)
2605             $(LI `/` and `%` are also checked for divide-by-zero.)
2606             $(LI `<<`, `>>`, and `>>>` are checked to verify that `right >= 0` and `right < (8 * typeof(left).sizeof)`.
2607                 Otherwise, `IntFlag.undef` is raised.)
2608         ) $(BR)
2609         Note also:
2610         $(UL
2611             $(LI The shift operators are $(B not) checked for overflow and should not be used for multiplication,
2612                 division, or exponentiation. Instead, use `mulPow2()` and `divPow2()`, which internally use the bitshifts for speed,
2613                 but check for overflow and correctly handle negative values.)
2614             $(LI Likewise, `modPow2()` should be used for remainders instead of `&`.)
2615             $(LI `^^` and `^^=` will remain disabled in favour of `pow` until DMD issues 15288 and 15412 are fixed.)
2616         ) $(BR)
2617         Like the standard equiavlents, the assignment operators (`+=`, `-=`, `*=`, etc.) take `left` by `ref` and will overwrite
2618         it with the result of the operation.
2619         **/
2620         OpType!(N, op, M) binary(string op, N, M)(const N left, const M right) @safe
2621             if (isFixedPoint!N && isFixedPoint!M &&
2622                 op.among!("+", "-", "*", "/", "%", "^^", "<<", ">>", ">>>", "&", "|", "^"))
2623         {
2624             static assert(op != "^^",
2625                 "pow() should be used instead of operator ^^ because of issue 15288.");
2626 
2627             return binaryImpl!op(left, right);
2628         }
2629         /// ditto
2630         /+ref+/ N binary(string op, N, M)(/+return+/ ref N left, const M right) @safe
2631             if (isIntegral!N && isFixedPoint!M && (op[$ - 1] == '='))
2632         {
2633             static assert(op != "^^=",
2634                 "pow() should be used instead of operator ^^= because of issue 15412.");
2635 
2636             left = binaryImpl!op(left, right);
2637             return left;
2638         }
2639         ///
2640         unittest
2641         {
2642             import checkedint.noex : safeOp; // use IntFlagPolicy.noex
2643 
2644             assert(safeOp.binary!"+"(17, -5) == 12);
2645             static assert(!__traits(compiles, safeOp.binary!"+"(-1, 1u)));
2646 
2647             ulong a = 18_446_744_073_709_551_615uL;
2648             safeOp.binary!"+="(a, 1u);
2649             assert(IntFlags.local.clear() == IntFlag.posOver);
2650 
2651             assert(safeOp.binary!"-"(17u, 5u) == 12u);
2652             safeOp.binary!"-"(5u, 17u);
2653             assert(IntFlags.local.clear() == IntFlag.negOver);
2654 
2655             ulong b = 123_456_789_987_654_321uL;
2656             static assert(!__traits(compiles, safeOp.binary!"-="(b, 987_654_321)));
2657             assert(safeOp.binary!"-="(b, 987_654_321u) == 123_456_789_000_000_000uL);
2658             assert(b == 123_456_789_000_000_000uL);
2659 
2660             assert(safeOp.binary!"*"(-1 << 30, 2) == int.min);
2661             safeOp.binary!"*"(1 << 30, 2);
2662             assert(IntFlags.local.clear() == IntFlag.negOver);
2663 
2664             uint c = 1u << 18;
2665             assert(safeOp.binary!"*="(c, 1u << 4) == 1u << 22);
2666             assert(c == 1u << 22);
2667 
2668             assert(safeOp.binary!"/"(22, 11) == 2);
2669             assert(!__traits(compiles, safeOp.binary!"/"(-22, 11u)));
2670             safeOp.binary!"/"(0, 0);
2671             assert(IntFlags.local.clear() == IntFlag.div0);
2672 
2673             long j = long.min;
2674             safeOp.binary!"/="(j, -1);
2675             assert(IntFlags.local.clear() == IntFlag.posOver);
2676 
2677             assert(safeOp.binary!"%"(20u, 7u) == 6u);
2678             static assert(!__traits(compiles, safeOp.binary!"%"(20u, -7)));
2679             safeOp.binary!"%"(20u, 0u);
2680             assert(IntFlags.local.clear() == IntFlag.div0);
2681 
2682             short n = 75;
2683             assert(safeOp.binary!"%="(n, -10) == 5);
2684             assert(n == 5);
2685         }
2686         ///
2687         unittest
2688         {
2689             import checkedint.noex : safeOp; // use IntFlagPolicy.noex
2690 
2691             assert(safeOp.binary!"<<"(-0x80,  2) == -0x200);
2692             safeOp.binary!"<<"(-0x80, -2);
2693             assert(IntFlags.local.clear() == IntFlag.undef);
2694 
2695             ubyte a = 0x3u;
2696             safeOp.binary!"<<="(a, 7);
2697             assert(a == 0x80u);
2698 
2699             assert(safeOp.binary!">>"(-0xC, 5u) == -0x1);
2700             safeOp.binary!">>"(-0xC, long.max);
2701             assert(IntFlags.local.clear() == IntFlag.undef);
2702 
2703             short b = 0x700;
2704             assert(safeOp.binary!">>="(b, 8) == 0x7);
2705             assert(b == 0x7);
2706 
2707             assert(safeOp.binary!">>>"(-0x80, 2u) == 0x3FFF_FFE0);
2708             safeOp.binary!">>>"(-0x80, 32);
2709             assert(IntFlags.local.clear() == IntFlag.undef);
2710 
2711             int c = 0xFE_DCBA;
2712             assert(safeOp.binary!">>>="(c, 12) == 0xFED);
2713             assert(c == 0xFED);
2714 
2715             assert(safeOp.binary!"&"(0x6Fu, 0x4076)  == 0x66u);
2716 
2717             ubyte d = 0x6Fu;
2718             assert(safeOp.binary!"&="(d, 0x4076) == 0x66u);
2719             assert(d == 0x66u);
2720 
2721             assert(safeOp.binary!"|"(0x6F, 0x4076u) == 0x407Fu);
2722 
2723             byte e = 0x6F;
2724             assert(safeOp.binary!"|="(e, 0x4076u) == 0x7F);
2725             assert(e == 0x7F);
2726 
2727             assert(safeOp.binary!"^"(0x6F, 0x4076) == 0x4019);
2728 
2729             int f = 0x6F;
2730             assert(safeOp.binary!"^="(f, 0x4076) == 0x4019);
2731             assert(f == 0x4019);
2732 
2733             assert(!IntFlags.local);
2734         }
2735 
2736         /**
2737         Equivalent to `left * pow(2, exp)`, but faster and works with a wider range of inputs. This is a safer alternative to
2738         `left << exp` that is still very fast.
2739 
2740         Note that (conceptually) rounding occurs $(I after) the `*`, meaning that `mulPow2(left, -exp)` is equivalent to
2741         `divPow2(left, exp)`.
2742         **/
2743         auto mulPow2(N, M)(const N left, const M exp) pure @safe nothrow @nogc
2744             if ((isFloatingPoint!N && isScalarType!M) || (isScalarType!N && isFloatingPoint!M))
2745         {
2746             return byPow2Impl!("*", NumFromScal!N, NumFromScal!M)(left, exp);
2747         }
2748         /// ditto
2749         auto mulPow2(N, M)(const N left, const M exp) @safe
2750             if (isFixedPoint!N && isFixedPoint!M)
2751         {
2752             return byPow2Impl!("*", policy, NumFromScal!N, NumFromScal!M)(left, exp);
2753         }
2754         ///
2755         unittest
2756         {
2757             import checkedint.noex : safeOp; // use IntFlagPolicy.noex
2758 
2759             assert(safeOp.mulPow2(-23, 5) == -736);
2760             safeOp.mulPow2(10_000_000, 10);
2761             assert(IntFlags.local.clear() == IntFlag.posOver);
2762 
2763             assert(safeOp.mulPow2(65536, -8) == 256);
2764             assert(safeOp.mulPow2(-100, -100) == 0);
2765         }
2766 
2767         /**
2768         Equivalent to `left / pow(2, exp)`, but faster and works with a wider range of inputs. This is a safer alternative to
2769         `left >> exp` that is still very fast.
2770 
2771         Note that (conceptually) rounding occurs $(I after) the `/`, meaning that `divPow2(left, -exp)` is equivalent to
2772         `mulPow2(left, exp)`.
2773         **/
2774         auto divPow2(N, M)(const N left, const M exp) pure @safe nothrow @nogc
2775             if ((isFloatingPoint!N && isScalarType!M) || (isScalarType!N && isFloatingPoint!M))
2776         {
2777             return byPow2Impl!("/", NumFromScal!N, NumFromScal!M)(left, exp);
2778         }
2779         /// ditto
2780         auto divPow2(N, M)(const N left, const M exp) @safe
2781             if (isFixedPoint!N && isFixedPoint!M)
2782         {
2783             return byPow2Impl!("/", policy, NumFromScal!N, NumFromScal!M)(left, exp);
2784         }
2785         ///
2786         unittest
2787         {
2788             import checkedint.noex : safeOp; // use IntFlagPolicy.noex
2789 
2790             assert(safeOp.divPow2(65536, 8) == 256);
2791             assert(safeOp.divPow2(-100, 100) == 0);
2792             assert(safeOp.divPow2(-23, -5) == -736);
2793 
2794             safeOp.divPow2(10_000_000, -10);
2795             assert(IntFlags.local.clear() == IntFlag.posOver);
2796         }
2797 
2798         /**
2799         Equivalent to `left % pow(2, exp)`, but faster and works with a wider range of inputs. This is a safer alternative to
2800         `left & ((1 << exp) - 1)` that is still very fast.
2801         **/
2802         auto modPow2(N, M)(const N left, const M exp) pure @safe nothrow @nogc
2803             if ((isFloatingPoint!N && isScalarType!M) || (isScalarType!N && isFloatingPoint!M))
2804         {
2805             return byPow2Impl!("%", NumFromScal!N, NumFromScal!M)(left, exp);
2806         }
2807         /// ditto
2808         auto modPow2(N, M)(const N left, const M exp) pure @safe nothrow @nogc
2809             if (isFixedPoint!N && isFixedPoint!M)
2810         {
2811             return byPow2Impl!("%", IntFlagPolicy.noex, NumFromScal!N, NumFromScal!M)(left, exp);
2812         }
2813         ///
2814         unittest
2815         {
2816             import checkedint.noex : safeOp; // use IntFlagPolicy.noex
2817 
2818             assert(safeOp.modPow2( 101,  1) ==  1);
2819             assert(safeOp.modPow2( 101,  3) ==  5);
2820             assert(safeOp.modPow2(-101,  3) == -5);
2821 
2822             assert(safeOp.modPow2(101, -2) ==  0);
2823             assert(safeOp.modPow2(101, 1_000) == 101);
2824         }
2825 
2826         /**
2827         Raise `base` to the `exp` power.
2828 
2829         Errors that may be signalled if neither input is floating-point:
2830         $(UL
2831             $(LI `IntFlag.posOver` or `IntFlag.negOver` if the absolute value of the result is too large to
2832                 represent with the return type.)
2833             $(LI `exp < 0`, `IntFlag.undef` is raised because `std.math.pow` would trigger an FPE given the same input.)
2834         )
2835         **/
2836         CallType!(std.math.pow, N, M) pow(N, M)(const N base, const M exp) @safe
2837             if (isFixedPoint!N && isFixedPoint!M)
2838         {
2839             alias R = typeof(return);
2840             static assert(!isSigned!N || isSigned!R,
2841                 "std.math.pow(" ~ N.stringof ~ ", " ~ M.stringof ~
2842                 ") is unsafe, due to a signed/unsigned mismatch. Use an explicit cast, or switch to smartOp/SmartInt."
2843             );
2844 
2845             auto po = powImpl!(R, Select!(isSigned!M, long, ulong))(base, exp);
2846             static assert(is(typeof(po.num) == R));
2847             if (exp < 0)
2848                 po.flag = IntFlag.undef;
2849 
2850             if (!po.flag.isNull)
2851                 po.flag.raise!policy();
2852             return po.num;
2853         }
2854         ///
2855         unittest
2856         {
2857             import checkedint.noex : safeOp; // use IntFlagPolicy.noex
2858 
2859             assert(safeOp.pow(-10, 3) == -1_000);
2860             static assert(!__traits(compiles, safeOp.pow(16, 4uL)));
2861             safeOp.pow(2, -1);
2862             assert(IntFlags.local.clear() == IntFlag.undef);
2863 
2864             safeOp.pow(-3, 27);
2865             assert(IntFlags.local.clear() == IntFlag.negOver);
2866             safeOp.pow(0, -5);
2867             assert(IntFlags.local.clear() == IntFlag.undef);
2868         }
2869     }
2870     private alias safeOp(bool throws) = safeOp!(cast(Flag!"throws")throws);
2871 
2872 // conv /////////////////////////////////////////////////
2873     /**
2874     A wrapper for `std.conv.to()` which uses `checkedint.flags` for error signaling when converting between any combination
2875     of basic scalar types and `checkedint` types. With an appropriate `policy`, this allows
2876     $(LINK2 ./package.html#to, `checkedint.to()`) to be used for numeric conversions in `pure nothrow` code, unlike
2877     `std.conv.to()`.
2878 
2879     Conversions involving any other type are simply forwarded to `std.conv.to()`, with no runtime overhead.
2880     **/
2881     template to(T, IntFlagPolicy policy)
2882     {
2883         private enum useFlags(S) = (isCheckedInt!T || isScalarType!T) && (isCheckedInt!S || isScalarType!S);
2884         private enum reqAttrs =
2885             ((policy == IntFlagPolicy.noex || policy == IntFlagPolicy.asserts)? " nothrow" : "") ~
2886             ((policy == IntFlagPolicy.asserts || policy == IntFlagPolicy.throws)? " pure" : "");
2887 
2888         T to(S)(const S value) @safe
2889             if (useFlags!S)
2890         {
2891             static if (isCheckedInt!T || isCheckedInt!S)
2892                 return T(checkedint.to!(BasicScalar!T, policy)(value.bscal));
2893             else
2894             {
2895                 static if (policy != IntFlagPolicy.none && !isFloatingPoint!T)
2896                 {
2897                     static if (isFloatingPoint!S)
2898                     {
2899                         if (value >= trueMin!T)
2900                         {
2901                             if (value > trueMax!T)
2902                                 IntFlag.posOver.raise!policy();
2903                         }
2904                         else
2905                             (std.math.isNaN(value)? IntFlag.undef : IntFlag.negOver).raise!policy();
2906                     }
2907                     else
2908                     {
2909                         static if (cast(long)trueMin!S < cast(long)trueMin!T)
2910                         {
2911                             if (value < cast(S)trueMin!T)
2912                                 IntFlag.negOver.raise!policy();
2913                         }
2914                         static if (cast(ulong)trueMax!S > cast(ulong)trueMax!T)
2915                         {
2916                             if (value > cast(S)trueMax!T)
2917                                 IntFlag.posOver.raise!policy();
2918                         }
2919                     }
2920                 }
2921                 return cast(T)value;
2922             }
2923         }
2924 
2925         mixin(`
2926         T to(S)(S value)` ~ reqAttrs ~ `
2927             if (!useFlags!S)
2928         {
2929             import std.conv : impl = to;
2930             return impl!T(value);
2931         }`);
2932     }
2933     ///
2934     unittest
2935     {
2936         // Conversions involving only basic scalars or checkedint types use IntFlags for error signalling.
2937         import checkedint.noex : smartInt, SmartInt, smartOp, to; // use IntFlagPolicy.noex
2938 
2939         assert(to!int(smartInt(-421751L)) == -421751);
2940         assert(to!(SmartInt!ubyte)(100) == 100u);
2941 
2942         assert(is(typeof(to!int(50u)) == int));
2943         assert(to!int(50u) == 50);
2944         assert(!IntFlags.local);
2945 
2946         // If IntFlagPolicy.noex is set, failed conversions return garbage, but...
2947         assert(smartOp.cmp!"!="(to!int(uint.max), uint.max));
2948         // ...IntFlags.local can be checked to see if anything went wrong.
2949         assert(IntFlags.local.clear() == IntFlag.posOver);
2950     }
2951     ///
2952     unittest
2953     {
2954         // Everything else forwards to std.conv.to().
2955         assert(to!(string, IntFlagPolicy.throws)(55) == "55");
2956         assert(to!(real, IntFlagPolicy.throws)("3.141519e0") == 3.141519L);
2957 
2958         // Setting IntFlagPolicy.noex or .asserts will block std.conv.to(), unless the instantiation is nothrow.
2959         // Setting IntFlagPolicy.asserts or .throws will block std.conv.to(), unless the instantiation is pure.
2960         static assert(!__traits(compiles, to!(real, IntFlagPolicy.noex)("3.141519e0")));
2961     }
2962 
2963     @property {
2964         /**
2965         Get a view or copy of `num` as a basic scalar.
2966 
2967         Useful in generic code that handles both basic types, and `checkedint` types.
2968         **/
2969       /+ref inout(N) bscal(N)(return ref inout(N) num) @safe
2970             if (isScalarType!N)
2971         {
2972             return num;
2973         }
2974         /// ditto
2975         ref inout(N) bscal(N)(return ref inout(N) num) @safe
2976             if (isCheckedInt!N)
2977         {
2978             return num.bscal;
2979         }
2980         /// ditto +/
2981         N bscal(N)(const N num) @safe
2982             if (isScalarType!N)
2983         {
2984             return num;
2985         }
2986         /// ditto
2987         BasicScalar!N bscal(N)(const N num) @safe
2988             if (isCheckedInt!N)
2989         {
2990             return num.bscal;
2991         }
2992         ///
2993         unittest
2994         {
2995             import checkedint.throws : smartInt, SmartInt; // use IntFlagPolicy.throws
2996 
2997             assert(is(typeof(bscal(2u)) == uint));
2998             assert(is(typeof(bscal(SmartInt!int(2))) == int));
2999 
3000             assert(bscal(-3153) == -3153);
3001             assert(bscal(smartInt(75_000)) == 75_000);
3002         }
3003 
3004         /**
3005         Get a view or copy of `num` that supports bitwise operations.
3006 
3007         Useful in generic code that handles both basic types and `checkedint` types.
3008         **/
3009       /+ref inout(N) bits(N)(return ref inout(N) num) @safe
3010             if (isFixedPoint!N)
3011         {
3012             return num;
3013         }
3014         /// ditto
3015         ref inout(N) bits(N)(return ref inout(N) num) @safe
3016             if (isCheckedInt!N)
3017         {
3018             return num.bits;
3019         }
3020         /// ditto +/
3021         N bits(N)(const N num) @safe
3022             if (isFixedPoint!N)
3023         {
3024             return num;
3025         }
3026         /// ditto
3027         SmartInt!(BasicScalar!N, N.policy, Yes.bitOps) bits(N)(const N num) @safe
3028             if (isSmartInt!N)
3029         {
3030             return num.bits;
3031         }
3032         /// ditto
3033         SafeInt!(BasicScalar!N, N.policy, Yes.bitOps) bits(N)(const N num) @safe
3034             if (isSafeInt!N)
3035         {
3036             return num.bits;
3037         }
3038         ///
3039         unittest
3040         {
3041             import checkedint.throws : SmartInt; // use IntFlagPolicy.throws
3042 
3043             assert(is(typeof(bits(5)) == int));
3044 
3045             SmartInt!(int, No.bitOps) noBits = 5;
3046             assert(is(typeof(bits(noBits)) == SmartInt!(int, Yes.bitOps)));
3047 
3048             static assert(!__traits(compiles, noBits << 2));
3049             assert((bits(noBits) << 2) == 20);
3050         }
3051 
3052         /**
3053         Cast `num` to a basic type suitable for indexing an array.
3054 
3055         For signed types, `ptrdiff_t` is returned. For unsigned types, `size_t` is returned.
3056         **/
3057         Select!(isSigned!N, ptrdiff_t, size_t) idx(IntFlagPolicy policy, N)(const N num) @safe
3058             if (isScalarType!N || isCheckedInt!N)
3059         {
3060             return to!(typeof(return), policy)(num.bscal);
3061         }
3062         /// ditto
3063         Select!(isSigned!(BasicScalar!N), ptrdiff_t, size_t) idx(N)(const N num) @safe
3064             if (isCheckedInt!N)
3065         {
3066             return num.idx;
3067         }
3068         ///
3069         unittest
3070         {
3071             import checkedint.noex : idx, SmartInt, safeInt; // use IntFlagPolicy.noex
3072 
3073             assert(is(typeof(idx(cast(long)1)) == ptrdiff_t));
3074             assert(is(typeof(idx(cast(ubyte)1)) == size_t));
3075             assert(is(typeof(idx(SmartInt!ulong(1))) == size_t));
3076 
3077             assert(idx(17uL) == 17);
3078             assert(idx(-3) == -3);
3079             assert(idx(safeInt(cast(byte)100)) == 100);
3080 
3081             static if (size_t.sizeof == 4)
3082             {
3083                 idx(ulong.max);
3084                 assert(IntFlags.local.clear() == IntFlag.posOver);
3085 
3086                 idx(long.min);
3087                 assert(IntFlags.local.clear() == IntFlag.negOver);
3088             }
3089         }
3090     }
3091 /+}+/
3092 
3093 // traits /////////////////////////////////////////////////
3094 
3095 /// Evaluates to `true` if `T` is an instance of $(LINK2 ./package.html#SafeInt, `SafeInt`).
3096 enum isSafeInt(T) = isInstanceOf!(SafeInt, T);
3097 ///
3098 unittest
3099 {
3100     import checkedint.throws : SmartInt, SafeInt; // use IntFlagPolicy.throws
3101 
3102     assert( isSafeInt!(SafeInt!int));
3103 
3104     assert(!isSafeInt!int);
3105     assert(!isSafeInt!(SmartInt!int));
3106 }
3107 
3108 /// Evaluates to `true` if `T` is an instance of $(LINK2 ./package.html#SmartInt, `SmartInt`).
3109 enum isSmartInt(T) = isInstanceOf!(SmartInt, T);
3110 ///
3111 unittest
3112 {
3113     import checkedint.throws : SmartInt, SafeInt; // use IntFlagPolicy.throws
3114 
3115     assert( isSmartInt!(SmartInt!int));
3116 
3117     assert(!isSmartInt!int);
3118     assert(!isSmartInt!(SafeInt!int));
3119 }
3120 
3121 /**
3122 Evaluates to `true` if `T` is an instance of $(LINK2 ./package.html#SafeInt, `SafeInt`)
3123 or $(LINK2 ./package.html#SmartInt, `SmartInt`).
3124 **/
3125 enum isCheckedInt(T) = isSafeInt!T || isSmartInt!T;
3126 ///
3127 unittest
3128 {
3129     import checkedint.throws : SmartInt, SafeInt; // use IntFlagPolicy.throws
3130 
3131     assert( isCheckedInt!(SafeInt!int));
3132     assert( isCheckedInt!(SmartInt!int));
3133 
3134     assert(!isCheckedInt!int);
3135 }
3136 
3137 /**
3138 Evaluates to `true` if either:
3139 $(UL
3140     $(LI `isScalarType!T`, or)
3141     $(LI `isCheckedInt!T`)
3142 )
3143 $(B And) bitwise operators such as `<<` and `~` are available for `T`.
3144 **/
3145 template hasBitOps(T)
3146 {
3147     static if (isCheckedInt!T)
3148         enum hasBitOps = TemplateArgsOf!T[2];
3149     else
3150         enum hasBitOps = isFixedPoint!T;
3151 }
3152 ///
3153 unittest
3154 {
3155     import checkedint.throws : SmartInt, SafeInt; // use IntFlagPolicy.throws
3156 
3157     assert( hasBitOps!(SafeInt!(int, Yes.bitOps)));
3158     assert( hasBitOps!(SmartInt!(int, Yes.bitOps)));
3159     assert( hasBitOps!int);
3160     assert( hasBitOps!bool);
3161     assert( hasBitOps!dchar);
3162 
3163     assert(!hasBitOps!(SafeInt!(int, No.bitOps)));
3164     assert(!hasBitOps!(SmartInt!(int, No.bitOps)));
3165     assert(!hasBitOps!float);
3166 }
3167 
3168 /**
3169 Aliases to the basic scalar type associated with `T`, assuming either:
3170 $(UL
3171     $(LI `isScalarType!T`, or)
3172     $(LI `isCheckedInt!T`)
3173 )
3174 Otherwise, `BasicScalar` aliases to `void`.
3175 **/
3176 template BasicScalar(T)
3177 {
3178     static if (isScalarType!T)
3179         alias BasicScalar = Unqual!T;
3180     else static if (isCheckedInt!T)
3181         alias BasicScalar = TemplateArgsOf!T[0];
3182     else
3183         alias BasicScalar = void;
3184 }
3185 ///
3186 unittest
3187 {
3188     import checkedint.throws : SmartInt, SafeInt; // use IntFlagPolicy.throws
3189 
3190     assert(is(BasicScalar!(SafeInt!int) == int));
3191     assert(is(BasicScalar!(SmartInt!ushort) == ushort));
3192 
3193     assert(is(BasicScalar!int == int));
3194     assert(is(BasicScalar!(const shared real) == real));
3195 }
3196 
3197 // internal /////////////////////////////////////////////////
3198 private
3199 {
3200     enum N trueMin(N) = mostNegative!N;
3201     template trueMax(N)
3202         if (isScalarType!N)
3203     {
3204         static if (is(Unqual!N == dchar))
3205             enum N trueMax = ~cast(N)0;
3206         else
3207             enum N trueMax = N.max;
3208     }
3209 
3210     template NumFromScal(N)
3211         if (isScalarType!N)
3212     {
3213         static if (isNumeric!N)
3214             alias NumFromScal = N;
3215         else static if (isSomeChar!N)
3216             alias NumFromScal = IntFromChar!N;
3217         else //if (isBoolean!N)
3218             alias NumFromScal = ubyte;
3219     }
3220 
3221     /+pragma(inline, true)
3222     {+/
3223         int bsfImpl(IntFlagPolicy policy, N)(const N num) @safe
3224             if (isFixedPoint!N)
3225         {
3226             static if (isSigned!N)
3227                 return bsfImpl!(policy, Unsigned!N)(num);
3228             else
3229             {
3230                 static assert(N.sizeof <= ulong.sizeof);
3231 
3232                 int ret = void;
3233                 if (num == 0)
3234                 {
3235                     IntFlag.undef.raise!policy();
3236                     ret = int.min;
3237                 }
3238                 else
3239                     ret = bsf(num);
3240 
3241                 return ret;
3242             }
3243         }
3244         int bsrImpl(IntFlagPolicy policy, N)(const N num) @safe
3245             if (isFixedPoint!N)
3246         {
3247             static if (isSigned!N)
3248                 return bsrImpl!(policy, Unsigned!N)(num);
3249             else
3250             {
3251                 static assert(N.sizeof <= ulong.sizeof);
3252 
3253                 int ret = void;
3254                 if (num == 0)
3255                 {
3256                     IntFlag.undef.raise!policy();
3257                     ret = int.min;
3258                 }
3259                 else
3260                     ret = bsr(num);
3261 
3262                 return ret;
3263             }
3264         }
3265 
3266         auto byPow2Impl(string op, N, M)(const N left, const M exp) pure @safe nothrow @nogc
3267             if (op.among!("*", "/", "%") && ((isFloatingPoint!N && isNumeric!M) || (isNumeric!N && isFloatingPoint!M)))
3268         {
3269             import std.math : exp2, isFinite, frexp, ldexp;
3270 
3271             enum wantPrec = max(precision!N, precision!M);
3272             alias R =
3273                 Select!(wantPrec <= precision!float, float,
3274                 Select!(wantPrec <= precision!double, double, real));
3275 
3276             static if (isFloatingPoint!M)
3277             {
3278                 R ret = void;
3279 
3280                 static if (op.among!("*", "/"))
3281                 {
3282                     if (left == 0 && exp.isFinite)
3283                         ret = 0;
3284                     else
3285                     {
3286                         R wexp = cast(R)exp;
3287                         static if (op == "/")
3288                             wexp = -wexp;
3289 
3290                         ret = cast(R)left * exp2(wexp);
3291                     }
3292                 }
3293                 else
3294                 {
3295                     const p2 = exp2(cast(R)exp);
3296                     ret =
3297                         p2.isFinite? cast(R)left % p2 :
3298                         (p2 > 0)? cast(R)left :
3299                         (p2 < 0)? cast(R)0 :
3300                         R.nan;
3301                 }
3302 
3303                 return ret;
3304             }
3305             else
3306             {
3307                 static if (op.among!("*", "/"))
3308                 {
3309                     int wexp =
3310                         (exp > int.max)? int.max :
3311                         (cast(long)exp < -int.max)? -int.max : cast(int)exp;
3312                     static if (op == "/")
3313                         wexp = -wexp;
3314 
3315                     return ldexp(cast(R)left, wexp);
3316                 }
3317                 else
3318                 {
3319                     int expL;
3320                     real mantL = frexp(left, expL);
3321 
3322                     static if (!isSigned!M)
3323                         const retL = expL <= exp;
3324                     else
3325                         const retL = (expL < 0) || (expL <= exp);
3326 
3327                     R ret = void;
3328                     if (retL)
3329                         ret = left;
3330                     else
3331                     {
3332                         const expDiff = expL - exp;
3333                         ret = (expDiff > N.mant_dig)?
3334                             cast(R)0 :
3335                             left - ldexp(floor(ldexp(mantissa, expDiff)), expL - expDiff);
3336                     }
3337 
3338                     return ret;
3339                 }
3340             }
3341         }
3342         auto byPow2Impl(string op, IntFlagPolicy policy, N, M)(const N left, const M exp) @safe
3343             if (op.among!("*", "/", "%") && isIntegral!N && isIntegral!M)
3344         {
3345             alias R = Select!(op.among!("*", "/") != 0, Promoted!N, N);
3346             enum Unsigned!M maxSh = 8 * N.sizeof - 1;
3347 
3348             R ret = void;
3349             static if (op.among!("*", "/"))
3350             {
3351                 const rc = cast(R)left;
3352                 const negE = exp < 0;
3353                 const absE = cast(Unsigned!M)(negE?
3354                     -exp :
3355                      exp);
3356                 const bigSh = (absE > maxSh);
3357 
3358                 R back = void;
3359                 if ((op == "*")? negE : !negE)
3360                 {
3361                     if (bigSh)
3362                         ret = 0;
3363                     else
3364                     {
3365                         // ">>" rounds as floor(), but we want trunc() like "/"
3366                         ret = (rc < 0)?
3367                             -(-rc >>> absE) :
3368                             rc >>> absE;
3369                     }
3370                 }
3371                 else
3372                 {
3373                     if (bigSh)
3374                     {
3375                         ret = 0;
3376                         back = 0;
3377                     }
3378                     else
3379                     {
3380                         ret  = rc  << absE;
3381                         back = ret >> absE;
3382                     }
3383 
3384                     if (back != rc)
3385                         IntFlag.over.raise!policy();
3386                 }
3387             }
3388             else
3389             {
3390                 if (exp & ~maxSh)
3391                     ret = (exp < 0)? 0 : left;
3392                 else
3393                 {
3394                     const mask = ~(~cast(N)0 << exp);
3395                     ret = cast(R)(left < 0?
3396                         -(-left & mask) :
3397                          left & mask);
3398                 }
3399             }
3400 
3401             return ret;
3402         }
3403     /+}+/
3404 
3405     struct PowOut(B)
3406     {
3407         B num;
3408         IntFlag flag;
3409     }
3410 
3411     // Minimize template bloat by using a common pow() implementation
3412     PowOut!B powImpl(B, E)(const B base, const E exp) @safe
3413         if ((is(B == int) || is(B == uint) || is(B == long) || is(B == ulong)) &&
3414             (is(E == long) || is(E == ulong)))
3415     {
3416         static if (__VERSION__ >= 2068) pragma(inline, false);
3417         PowOut!B ret;
3418 
3419         static if (isSigned!B)
3420         {
3421             alias cmul = muls;
3422             const smallB = (1 >= base && base >= -1);
3423         }
3424         else
3425         {
3426             alias cmul = mulu;
3427             const smallB = (base <= 1);
3428         }
3429 
3430         if (smallB)
3431         {
3432             if (base == 0)
3433             {
3434                 static if (isSigned!E)
3435                 {
3436                     if (exp < 0)
3437                         ret.flag = IntFlag.div0;
3438                 }
3439 
3440                 ret.num = (exp == 0);
3441             }
3442             else
3443                 ret.num = (exp & 0x1)? base : 1;
3444 
3445             return ret;
3446         }
3447         if (exp <= 0)
3448         {
3449             ret.num = (exp == 0);
3450             return ret;
3451         }
3452 
3453         ret.num = 1;
3454         if (exp <= precision!B)
3455         {
3456             B b = base;
3457             int e = cast(int)exp;
3458             if (e & 0x1)
3459                 ret.num = b;
3460             e >>>= 1;
3461 
3462             bool over = false;
3463             while (e != 0)
3464             {
3465                 b = cmul(b, b, over);
3466                 if (e & 0x1)
3467                     ret.num = cmul(ret.num, b, over);
3468 
3469                 e >>>= 1;
3470             }
3471 
3472             if (!over)
3473                 return ret;
3474         }
3475 
3476         ret.flag = (base < 0 && (exp & 0x1))?
3477             IntFlag.negOver :
3478             IntFlag.posOver;
3479         return ret;
3480     }
3481 }