1 /// Arbitrary size integer implementation (must be 0<x<=64)
2 module tern.integer;
3 
4 import tern.meta : isSame;
5 import std.conv;
6 import std.traits;
7 
8 /// Represents the shorthand for an integer
9 public alias utri = UInt!24;
10 /// ditto
11 public alias tri = Int!24;
12 /// ditto
13 public alias upent = UInt!40;
14 /// ditto
15 public alias pent = Int!40;
16 /// ditto
17 public alias usex = UInt!48;
18 /// ditto
19 public alias sex = Int!48;
20 /// ditto
21 public alias uhept = UInt!56;
22 /// ditto
23 public alias hept = Int!56;
24 
25 /// Represents the shorthand for a non-promoting integer
26 public alias npubyte = UInt!8;
27 /// ditto
28 public alias npbyte = Int!8;
29 /// ditto
30 public alias npushort = UInt!16;
31 /// ditto
32 public alias npshort = Int!16;
33 /// ditto
34 public alias nputri = UInt!24;
35 /// ditto
36 public alias nptri = Int!24;
37 /// ditto
38 public alias npuint = UInt!32;
39 /// ditto
40 public alias npint = Int!32;
41 /// ditto
42 public alias npupent = UInt!40;
43 /// ditto
44 public alias nppent = Int!40;
45 /// ditto
46 public alias npusex = UInt!48;
47 /// ditto
48 public alias npsex = Int!48;
49 /// ditto
50 public alias npuhept = UInt!56;
51 /// ditto
52 public alias nphept = Int!56;
53 /// ditto
54 public alias npulong = UInt!64;
55 /// ditto
56 public alias nplong = Int!64;
57 
58 /// Represents an arbitrary unsigned integer of `SIZE`.
59 public struct UInt(size_t SIZE)
60     if (SIZE <= 64 && SIZE >= 8 && SIZE % 8 == 0)
61 {
62 private:
63 final:
64     enum size = SIZE / 8;
65     ubyte[size] data;
66 
67 public:
68     enum max = 2 ^^ cast(ulong)SIZE - 1;
69     enum min = 0;
70 
71     string toString() const
72     {
73         return (cast(ulong)this).to!string;
74     }
75 
76 @nogc:
77     this(T)(T val)
78         if (isIntegral!T)
79     {
80         this = val;
81     }
82 
83     T opCast(T)() const
84     {
85         ubyte[T.sizeof] data;
86         static if (T.sizeof < size)
87             data[0..T.sizeof] = this.data[0..T.sizeof];
88         else
89             data[0..size] = this.data[0..size];
90         return *cast(T*)&data;
91     }
92 
93     auto opAssign(A)(A ahs)
94     {
95         ulong val = cast(ulong)ahs;
96         data[0..size] = (cast(ubyte*)&val)[0..size];  
97         return this;
98     }
99 
100     auto opBinary(string op, R)(const R rhs) const
101     {
102         return mixin("UInt!SIZE(cast(ulong)this "~op~" cast(ulong)rhs)");
103     }
104 
105     auto opBinaryRight(string op, L)(const L lhs) const
106     {
107         return mixin("UInt!SIZE(cast(ulong)rhs "~op~" cast(ulong)this)");
108     }
109 
110     auto opUnary(string op)()
111     {
112         return mixin("UInt!SIZE("~op~"cast(ulong)this)");
113     }
114 
115     auto opOpAssign(string op, A)(A ahs)
116     {
117         this = mixin("cast(A)this "~op~" cast(ulong)ahs");
118         return this;
119     }
120 
121     bool opEquals(A)(const A ahs) const
122     {
123         static if (!isSame!(Unqual!A, Unqual!(typeof(this))))
124             return cast(A)this == ahs;
125         else
126         {
127             static if (isSigned!A)
128                 return cast(long)this == ahs;
129             else
130                 return cast(ulong)this == ahs;
131         }
132     }
133 
134     int opCmp(A)(const A ahs) const
135     {
136         return cast(int)(cast(A)this - ahs);
137     }
138 }
139 
140 /// Represents an arbitrary signed integer of `SIZE`.
141 public struct Int(size_t SIZE)
142     if (SIZE <= 64 && SIZE >= 8 && SIZE % 8 == 0)
143 {
144 private:
145 final:
146     enum size = SIZE / 8;
147     ubyte[size] data;
148 
149 public:
150     enum max = 2UL ^^ (cast(ulong)SIZE - 1) - 1;
151     enum min = -(2L ^^ (cast(long)SIZE - 1) - 1);
152     
153     string toString() const
154     {
155         return (cast(long)this).to!string;
156     }
157 
158 @nogc:
159     this(T)(T val)
160         if (isIntegral!T)
161     {
162         this = val;
163     }
164 
165     T opCast(T)() const
166     {
167         ubyte[T.sizeof] data;
168         static if (T.sizeof < size)
169             data[0..T.sizeof] = this.data[0..T.sizeof];
170         else
171             data[0..size] = this.data[0..size];
172 
173         static if (isSigned!T)
174         {
175             if (this.data[$-1] & 128)
176                 return cast(T)(-(2L ^^ cast(long)SIZE - (*cast(T*)&data)));
177         }
178         static if (T.sizeof == size)
179             data[$-1] &= ~128;
180         return *cast(T*)&data;
181     }
182 
183     auto opAssign(A)(A ahs)
184     {
185         long val = cast(long)ahs;
186         data[0..size] = (cast(ubyte*)&val)[0..size];
187         data[$-1] >>= 1;
188         static if (isSigned!A)
189         if (val < 0)
190             data[$-1] |= 128;  
191         return this;
192     }
193 
194     auto opBinary(string op, R)(const R rhs) const
195     {
196         return mixin("Int!SIZE(cast(ulong)this "~op~" cast(ulong)rhs)");
197     }
198 
199     auto opBinaryRight(string op, L)(const L lhs) const
200     {
201         return mixin("Int!SIZE(cast(ulong)rhs "~op~" cast(ulong)this)");
202     }
203 
204     auto opUnary(string op)()
205     {
206         return mixin("Int!SIZE("~op~"cast(long)this)");
207     }
208 
209     auto opOpAssign(string op, A)(A ahs)
210     {
211         this = mixin("cast(A)this "~op~" ahs");
212         return this;
213     }
214 
215     bool opEquals(A)(const A ahs) const
216     {
217         static if (!isSame!(Unqual!A, Unqual!(typeof(this))))
218             return cast(A)this == ahs;
219         else
220         {
221             static if (isSigned!A)
222                 return cast(long)this == ahs;
223             else
224                 return cast(ulong)this == ahs;
225         }
226     }
227 
228     int opCmp(A)(const A ahs) const
229     {
230         return cast(int)(cast(A)this - ahs);
231     }
232 }