1 /**
2 Copyright: Copyright Thomas Stuart Bockman 2015
3 License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>.
4 Authors: Thomas Stuart Bockman
5 */
6 
7 module checkedint.internal;
8 import checkedint.flags;
9 
10 import future.bitop, core.checkedint, std.algorithm, std.conv, future.traits;
11 static import std.math;
12 
13 @safe:
14 
15 private:
16 
17 template trueMax(N)
18     if(isScalarType!N)
19 {
20     static if(isSomeChar!N)
21         enum trueMax = ~cast(N)0;
22     else
23         enum trueMax = N.max;
24 }
25 
26 package:
27 
28 template NumFromScal(N)
29     if(isScalarType!N)
30 {
31     static if(isNumeric!N)
32         alias NumFromScal = N;
33     else
34     static if(isSomeChar!N)
35         alias NumFromScal = IntFromChar!N;
36     else //if(isBoolean!N)
37         alias NumFromScal = ubyte;
38 }
39 
40 /+pragma(inline, true) {+/
41     // nothrow alternative to std.conv.to() using IntFlag
42     T toImpl(T, bool throws, S)(const S value)
43         if(isScalarType!T && isScalarType!S)
44     {
45         static if(throws)
46             return to!T(value);
47         else {
48             static if(! isFloatingPoint!T) {
49                 static if(isFloatingPoint!S) {
50                     if(value >= T.min) {
51                         if(value > trueMax!T)
52                             IntFlag.posOver.raise!throws();
53                     } else
54                         (value.isNaN? IntFlag.undef : IntFlag.negOver).raise!throws();
55                 } else {
56                     static if(cast(long)S.min < cast(long)T.min) {
57                         if(value < cast(S)T.min)
58                             IntFlag.negOver.raise!throws();
59                     }
60                     static if(cast(ulong)trueMax!S > cast(ulong)trueMax!T) {
61                         if(value > cast(S)trueMax!T)
62                             IntFlag.posOver.raise!throws();
63                     }
64                 }
65             }
66             return cast(T)value;
67         }
68     }
69 
70     int bsrImpl(bool throws, N)(const N num)
71         if(isFixedPoint!N)
72     {
73         if(num == 0)
74             IntFlag.undef.raise!throws();
75 
76         static assert(N.sizeof <= ulong.sizeof);
77         alias WN = Select!(N.sizeof > size_t.sizeof, ulong, size_t);
78 
79         return bsr(cast(WN)num);
80     }
81     int bsfImpl(bool throws, N)(const N num)
82         if(isFixedPoint!N)
83     {
84         if(num == 0)
85             IntFlag.undef.raise!throws();
86 
87         static assert(N.sizeof <= ulong.sizeof);
88         alias WN = Select!(N.sizeof > size_t.sizeof, ulong, size_t);
89 
90         return bsf(cast(WN)num);
91     }
92 
93     auto byPow2Impl(string op, N, M)(const N coef, const M exp) pure nothrow @nogc
94         if(op.among!("*", "/") && ((isFloatingPoint!N && isNumeric!M) || (isNumeric!N && isFloatingPoint!M)))
95     {
96         enum wantPrec = max(precision!N, precision!M);
97         alias R =
98             Select!(wantPrec <= precision!float, float,
99             Select!(wantPrec <= precision!double, double, real));
100 
101         static if(isFloatingPoint!M) {
102             R ret = void;
103             if(coef == 0 && std.math.isFinite(exp))
104                 ret = 0;
105             else {
106                 R wexp = cast(R)exp;
107                 static if(op == "/")
108                     wexp = -wexp;
109 
110                 ret = cast(R)coef * std.math.exp2(wexp);
111             }
112 
113             return ret;
114         } else {
115             int wexp =
116                 (exp > int.max)? int.max :
117                 (cast(long)exp < -int.max)? -int.max : cast(int)exp;
118             static if(op == "/")
119                 wexp = -wexp;
120 
121             return std.math.ldexp(cast(R)coef, wexp);
122         }
123     }
124     Promoted!N byPow2Impl(string op, bool throws, N, M)(const N coef, const M exp)
125         if(op.among!("*", "/") && isIntegral!N && isIntegral!M)
126     {
127         alias R = typeof(return);
128 
129         const rc = cast(R)coef;
130         const negE = exp < 0;
131         const shR = (op == "*")? negE : !negE;
132         const absE = cast(Unsigned!M)(negE?
133             -exp :
134              exp);
135 
136         R ret = void;
137         R back = void;
138         enum shLim = 8 * N.sizeof;
139         if(absE >= shLim) {
140             if(shR)
141                 return 0;
142             else {
143                 ret = 0;
144                 back = 0;
145                 goto LcheckL;
146             }
147         }
148 
149         if(shR) {
150             // ">>" rounds as floor(), but we want trunc() like "/"
151             ret = (rc < 0)?
152                 -(-rc >>> absE) :
153                 rc >>> absE;
154         } else {
155             ret  = rc  << absE;
156             back = ret >> absE;
157 
158         LcheckL:
159             if(back != rc)
160                 IntFlag.over.raise!throws();
161         }
162 
163         return ret;
164     }
165 /+}+/
166 
167 
168 //pragma(inline, false) // Minimize template bloat by using a common pow() implementation
169 B powImpl(B, E)(const B base, const E exp, ref IntFlag flag)
170     if((is(B == int) || is(B == uint) || is(B == long) || is(B == ulong)) &&
171         (is(E == long) || is(E == ulong)))
172 {
173     static if(isSigned!B) {
174         alias cmul = muls;
175         const smallB = (1 >= base && base >= -1);
176     } else {
177         alias cmul = mulu;
178         const smallB = (base <= 1);
179     }
180 
181     if(smallB) {
182         if(base == 0) {
183             static if(isSigned!E) {
184                 if(exp < 0)
185                     flag = IntFlag.div0;
186             }
187 
188             return (exp == 0);
189         }
190 
191         return (exp & 0x1)? base : 1;
192     }
193     if(exp <= 0)
194         return (exp == 0);
195 
196     B ret = 1;
197     if(exp <= precision!B) {
198         B b = base;
199         int e = cast(int)exp;
200         if(e & 0x1)
201             ret = b;
202         e >>>= 1;
203 
204         bool over = false;
205         while(e != 0) {
206             b = cmul(b, b, over);
207             if(e & 0x1)
208                 ret = cmul(ret, b, over);
209 
210             e >>>= 1;
211         }
212 
213         if(!over)
214             return ret;
215     }
216 
217     flag = (base < 0 && (exp & 0x1))?
218         IntFlag.negOver :
219         IntFlag.posOver;
220     return ret;
221 }