1 // Written in the D programming language
2 /**
3 * Implements a signed 128 bit integer type.
4 *
5 Author: Walter Bright
6 Copyright: Copyright (c) 2022, D Language Foundation
7 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0)
8 Source: $(PHOBOSSRC std/int128.d)
9 */
10 module std.int128;
11
12 private import core.int128;
13
14
15 /***********************************
16 * 128 bit signed integer type.
17 */
18
19 public struct Int128
20 {
21 @safe pure nothrow @nogc:
22
23 Cent data; /// core.int128.Cent
24
25 /****************
26 * Construct an `Int128` from a `long` value.
27 * The upper 64 bits are formed by sign extension.
28 * Params:
29 * lo = signed lower 64 bits
30 */
thisInt12831 this(long lo)
32 {
33 data.lo = lo;
34 data.hi = lo < 0 ? ~0L : 0;
35 }
36
37 /****************
38 * Construct an `Int128` from a `ulong` value.
39 * The upper 64 bits are set to zero.
40 * Params:
41 * lo = unsigned lower 64 bits
42 */
thisInt12843 this(ulong lo)
44 {
45 data.lo = lo;
46 data.hi = 0;
47 }
48
49 /****************
50 * Construct an `Int128` from a `long` value.
51 * Params:
52 * hi = upper 64 bits
53 * lo = lower 64 bits
54 */
thisInt12855 this(long hi, long lo)
56 {
57 data.hi = hi;
58 data.lo = lo;
59 }
60
61 /********************
62 * Construct an `Int128` from a `Cent`.
63 * Params:
64 * data = Cent data
65 */
thisInt12866 this(Cent data)
67 {
68 this.data = data;
69 }
70
71 /********************
72 * Returns: hash value for Int128
73 */
toHashInt12874 size_t toHash() const
75 {
76 return cast(size_t)((data.lo & 0xFFFF_FFFF) + (data.hi & 0xFFFF_FFFF) + (data.lo >> 32) + (data.hi >> 32));
77 }
78
79 /************************
80 * Compare for equality
81 * Params: lo = signed value to compare with
82 * Returns: true if Int128 equals value
83 */
opEqualsInt12884 bool opEquals(long lo) const
85 {
86 return data.lo == lo && data.hi == (lo >> 63);
87 }
88
89 /************************
90 * Compare for equality
91 * Params: lo = unsigned value to compare with
92 * Returns: true if Int128 equals value
93 */
opEqualsInt12894 bool opEquals(ulong lo) const
95 {
96 return data.hi == 0 && data.lo == lo;
97 }
98
99 /************************
100 * Compare for equality
101 * Params: op2 = value to compare with
102 * Returns: true if Int128 equals value
103 */
opEqualsInt128104 bool opEquals(Int128 op2) const
105 {
106 return data.hi == op2.data.hi && data.lo == op2.data.lo;
107 }
108
109 /** Support unary arithmentic operator +
110 * Params: op = "+"
111 * Returns: lvalue of result
112 */
113 Int128 opUnary(string op)() const
114 if (op == "+")
115 {
116 return this;
117 }
118
119 /** Support unary arithmentic operator - ~
120 * Params: op = "-", "~"
121 * Returns: lvalue of result
122 */
123 Int128 opUnary(string op)() const
124 if (op == "-" || op == "~")
125 {
126 static if (op == "-")
127 return Int128(neg(this.data));
128 else static if (op == "~")
129 return Int128(com(this.data));
130 }
131
132 /** Support unary arithmentic operator ++ --
133 * Params: op = "++", "--"
134 * Returns: lvalue of result
135 */
136 Int128 opUnary(string op)()
137 if (op == "++" || op == "--")
138 {
139 static if (op == "++")
140 this.data = inc(this.data);
141 else static if (op == "--")
142 this.data = dec(this.data);
143 else
144 static assert(0, op);
145 return this;
146 }
147
148 /** Support casting to a bool
149 * Params: T = bool
150 * Returns: boolean result
151 */
152 bool opCast(T : bool)() const
153 {
154 return tst(this.data);
155 }
156
157 /** Support binary arithmetic operators + - * / % & | ^ << >> >>>
158 * Params:
159 * op = one of the arithmetic binary operators
160 * op2 = second operand
161 * Returns: value after the operation is applied
162 */
163 Int128 opBinary(string op)(Int128 op2) const
164 if (op == "+" || op == "-" ||
165 op == "*" || op == "/" || op == "%" ||
166 op == "&" || op == "|" || op == "^")
167 {
168 static if (op == "+")
169 return Int128(add(this.data, op2.data));
170 else static if (op == "-")
171 return Int128(sub(this.data, op2.data));
172 else static if (op == "*")
173 return Int128(mul(this.data, op2.data));
174 else static if (op == "/")
175 return Int128(div(this.data, op2.data));
176 else static if (op == "%")
177 {
178 Cent modulus;
179 divmod(this.data, op2.data, modulus);
180 return Int128(modulus);
181 }
182 else static if (op == "&")
183 return Int128(and(this.data, op2.data));
184 else static if (op == "|")
185 return Int128(or(this.data, op2.data));
186 else static if (op == "^")
187 return Int128(xor(this.data, op2.data));
188 else
189 static assert(0, "wrong op value");
190 }
191
192 /// ditto
193 Int128 opBinary(string op)(long op2) const
194 if (op == "+" || op == "-" ||
195 op == "*" || op == "/" || op == "%" ||
196 op == "&" || op == "|" || op == "^")
197 {
198 return mixin("this " ~ op ~ " Int128(0, op2)");
199 }
200
201 /// ditto
202 Int128 opBinaryRight(string op)(long op2) const
203 if (op == "+" || op == "-" ||
204 op == "*" || op == "/" || op == "%" ||
205 op == "&" || op == "|" || op == "^")
206 {
207 mixin("return Int128(0, op2) " ~ op ~ " this;");
208 }
209
210 /// ditto
211 Int128 opBinary(string op)(long op2) const
212 if (op == "<<")
213 {
214 return Int128(shl(this.data, cast(uint) op2));
215 }
216
217 /// ditto
218 Int128 opBinary(string op)(long op2) const
219 if (op == ">>")
220 {
221 return Int128(sar(this.data, cast(uint) op2));
222 }
223
224 /// ditto
225 Int128 opBinary(string op)(long op2) const
226 if (op == ">>>")
227 {
228 return Int128(shr(this.data, cast(uint) op2));
229 }
230
231 /** arithmetic assignment operators += -= *= /= %= &= |= ^= <<= >>= >>>=
232 * Params: op = one of +, -, etc.
233 * op2 = second operand
234 * Returns: lvalue of updated left operand
235 */
236 ref Int128 opOpAssign(string op)(Int128 op2)
237 if (op == "+" || op == "-" ||
238 op == "*" || op == "/" || op == "%" ||
239 op == "&" || op == "|" || op == "^" ||
240 op == "<<" || op == ">>" || op == ">>>")
241 {
242 mixin("this = this " ~ op ~ " op2;");
243 return this;
244 }
245
246 /// ditto
247 ref Int128 opOpAssign(string op)(long op2)
248 if (op == "+" || op == "-" ||
249 op == "*" || op == "/" || op == "%" ||
250 op == "&" || op == "|" || op == "^" ||
251 op == "<<" || op == ">>" || op == ">>>")
252 {
253 mixin("this = this " ~ op ~ " op2;");
254 return this;
255 }
256
257 /** support signed arithmentic comparison operators < <= > >=
258 * Params: op2 = right hand operand
259 * Returns: -1 for less than, 0 for equals, 1 for greater than
260 */
opCmpInt128261 int opCmp(Int128 op2) const
262 {
263 return this == op2 ? 0 : gt(this.data, op2.data) * 2 - 1;
264 }
265
266 /** support signed arithmentic comparison operators < <= > >=
267 * Params: op2 = right hand operand
268 * Returns: -1 for less than, 0 for equals, 1 for greater than
269 */
opCmpInt128270 int opCmp(long op2) const
271 {
272 return opCmp(Int128(0, op2));
273 }
274
275 enum min = Int128(long.min, 0); /// minimum value
276 enum max = Int128(long.max, ulong.max); /// maximum value
277 }
278
279 /********************************************* Tests ************************************/
280
version(unittest)281 version (unittest)
282 {
283 import core.stdc.stdio;
284
285 @trusted void print(Int128 c)
286 {
287 printf("%lld, %lld\n", c.data.hi, c.data.lo);
288 }
289
290 @trusted void printx(Int128 c)
291 {
292 printf("%llx, %llx\n", c.data.hi, c.data.lo);
293 }
294 }
295
296 /// Int128 tests
297 @safe pure nothrow @nogc
298 unittest
299 {
300 Int128 c = Int128(5, 6);
301 assert(c == c);
302 assert(c == +c);
303 assert(c == - -c);
304 assert(~c == Int128(~5, ~6));
305 ++c;
306 assert(c == Int128(5, 7));
307 assert(--c == Int128(5, 6));
308 assert(!!c);
309 assert(!Int128());
310
311 assert(c + Int128(10, 20) == Int128(15, 26));
312 assert(c - Int128(1, 2) == Int128(4, 4));
313 assert(c * Int128(100, 2) == Int128(610, 12));
314 assert(c / Int128(3, 2) == Int128(0, 1));
315 assert(c % Int128(3, 2) == Int128(2, 4));
316 assert((c & Int128(3, 2)) == Int128(1, 2));
317 assert((c | Int128(3, 2)) == Int128(7, 6));
318 assert((c ^ Int128(3, 2)) == Int128(6, 4));
319
320 assert(c + 15 == Int128(5, 21));
321 assert(c - 15 == Int128(4, -9));
322 assert(c * 15 == Int128(75, 90));
323 assert(c / 15 == Int128(0, 6148914691236517205));
324 assert(c % 15 == Int128(0, 11));
325 assert((c & 15) == Int128(0, 6));
326 assert((c | 15) == Int128(5, 15));
327 assert((c ^ 15) == Int128(5, 9));
328
329 assert(15 + c == Int128(5, 21));
330 assert(15 - c == Int128(-5, 9));
331 assert(15 * c == Int128(75, 90));
332 assert(15 / c == Int128(0, 0));
333 assert(15 % c == Int128(0, 15));
334 assert((15 & c) == Int128(0, 6));
335 assert((15 | c) == Int128(5, 15));
336 assert((15 ^ c) == Int128(5, 9));
337
338 assert(c << 1 == Int128(10, 12));
339 assert(-c >> 1 == Int128(-3, 9223372036854775805));
340 assert(-c >>> 1 == Int128(9223372036854775805, 9223372036854775805));
341
342 assert((c += 1) == Int128(5, 7));
343 assert((c -= 1) == Int128(5, 6));
344 assert((c += Int128(0, 1)) == Int128(5, 7));
345 assert((c -= Int128(0, 1)) == Int128(5, 6));
346 assert((c *= 2) == Int128(10, 12));
347 assert((c /= 2) == Int128(5, 6));
348 assert((c %= 2) == Int128());
349 c += Int128(5, 6);
350 assert((c *= Int128(10, 20)) == Int128(160, 120));
351 assert((c /= Int128(10, 20)) == Int128(0, 15));
352 c += Int128(72, 0);
353 assert((c %= Int128(10, 20)) == Int128(1, -125));
354 assert((c &= Int128(3, 20)) == Int128(1, 0));
355 assert((c |= Int128(8, 2)) == Int128(9, 2));
356 assert((c ^= Int128(8, 2)) == Int128(1, 0));
357 c |= Int128(10, 5);
358 assert((c <<= 1) == Int128(11 * 2, 5 * 2));
359 assert((c >>>= 1) == Int128(11, 5));
360 c = Int128(long.min, long.min);
361 assert((c >>= 1) == Int128(long.min >> 1, cast(ulong) long.min >> 1));
362
363 assert(-Int128.min == Int128.min);
364 assert(Int128.max + 1 == Int128.min);
365
366 c = Int128(5, 6);
367 assert(c < Int128(6, 5));
368 assert(c > 10);
369
370 c = Int128(-1UL);
371 assert(c == -1UL);
372 c = Int128(-1L);
373 assert(c == -1L);
374 }
375