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