1 /**
2   Provide a 2^N-bit integer type.
3   Guaranteed to never allocate and expected binary layout
4   Recursive implementation with very slow division.
5 
6   Copied from https://raw.githubusercontent.com/d-gamedev-team/gfm/master/integers/gfm/integers/wideint.d
7 
8   <b>Supports all operations that builtin integers support.</b>
9 
10   TODO: Integrate representations and potential assembly optimizations from
11   https://github.com/ckormanyos/wide-integer
12 
13   See_Also: https://github.com/ckormanyos/wide-integer
14 
15   Bugs: it's not sure if the unsigned operand would take precedence in a comparison/division.
16 		  - a < b should be an unsigned comparison if at least one operand is unsigned
17 		  - a / b should be an unsigned division   if at least one operand is unsigned
18  */
19 module nxt.wideint;
20 
21 // version = format;				// Support std.format
22 
23 import std.traits, std.ascii;
24 
25 /// Signed integer of arbitary static precision `bits`.
26 /// Params:
27 ///	bits = number of bits, must be a power of 2.
28 alias SInt(uint bits) = Int!(true, bits);
29 
30 /// Unsigned integer of arbitary static precision `bits`.
31 /// Params:
32 ///	bits = number of bits, must be a power of 2.
33 alias UInt(uint bits) = Int!(false, bits);
34 
35 // Some predefined integers (any power of 2 greater than 128 would work)
36 
37 /// Use this template to get an arbitrary sized integer type.
38 private template Int(bool signed, uint bits)
39 if ((bits & (bits - 1)) == 0)
40 {
41 	// forward to native type for lower numbers of bits in order of most probable
42 	static if (bits == 64)
43 	{
44 		static if (signed)
45 			alias Int = long;
46 		else
47 			alias Int = ulong;
48 	}
49 	else static if (bits == 32)
50 	{
51 		static if (signed)
52 			alias Int = int;
53 		else
54 			alias Int = uint;
55 	}
56 	else static if (bits == 16)
57 	{
58 		static if (signed)
59 			alias Int = short;
60 		else
61 			alias Int = ushort;
62 	}
63 	else static if (bits == 8)
64 	{
65 		static if (signed)
66 			alias Int = byte;
67 		else
68 			alias Int = ubyte;
69 	}
70 	else
71 	{
72 		alias Int = IntImpl!(signed, bits);
73 	}
74 }
75 
76 private template Int(bool signed, uint bits)
77 if (!isPowerOf2(bits))
78 {
79 	static assert(0, "Integer bits " ~ bits.stringof ~ " is not a power of two.");
80 }
81 
82 private bool isPowerOf2(in uint x) pure @safe nothrow @nogc
83 {
84 	auto y = cast(typeof(x + 0u))x;
85 	return (y & -y) > (y - 1);
86 }
87 
88 version (unittest)
89 {
90 	static assert(isPowerOf2(2));
91 	static assert(!isPowerOf2(3));
92 	static assert(isPowerOf2(4));
93 	static assert(!isPowerOf2(5));
94 	static assert(!isPowerOf2(7));
95 	static assert(isPowerOf2(8));
96 }
97 
98 /// Recursive 2^n integer implementation.
99 private struct IntImpl(bool signed, uint bits)
100 {
101 	static assert(bits >= 128);
102 	private
103 	{
104 		alias Self = typeof(this);
105 		enum bool isSelf(T) = is(Unqual!T == typeof(this));
106 
107 		alias sub_int_t = Int!(true, bits/2);   // signed bits/2 integer
108 		alias sub_uint_t = Int!(false, bits/2); // unsigned bits/2 integer
109 
110 		alias sub_sub_int_t = Int!(true, bits/4);   // signed bits/4 integer
111 		alias sub_sub_uint_t = Int!(false, bits/4); // unsigned bits/4 integer
112 
113 		static if (signed)
114 			alias hi_t = sub_int_t; // hi_t has same signedness as the whole struct
115 		else
116 			alias hi_t = sub_uint_t;
117 
118 		alias low_t = sub_uint_t;   // low_t is always unsigned
119 
120 		enum _bits = bits, _signed = signed;
121 	}
122 
123 	/// Construct from a value.
124 	this(T)(T x) pure nothrow @nogc
125 	{
126 		opAssign!T(x);
127 	}
128 
129 	// Private functions used by the `literal` template.
130 	private static bool isValidDigitString(string digits)
131 	{
132 		import std.algorithm.searching : startsWith;
133 		import std.ascii : isDigit;
134 
135 		if (digits.startsWith("0x"))
136 		{
137 			foreach (const d; digits[2 .. $])
138 				if (!isHexDigit(d) && d != '_')
139 					return false;
140 		}
141 		else // decimal
142 		{
143 			static if (signed)
144 				if (digits.startsWith("-"))
145 					digits = digits[1 .. $];
146 			if (digits.length < 1)
147 				return false;   // at least 1 digit required
148 			foreach (const d; digits)
149 				if (!isDigit(d) && d != '_')
150 					return false;
151 		}
152 		return true;
153 	}
154 
155 	private static typeof(this) literalImpl(string digits)
156 	{
157 		import std.algorithm.searching : startsWith;
158 		import std.ascii : isDigit;
159 
160 		typeof(this) value = 0;
161 		if (digits.startsWith("0x"))
162 		{
163 			foreach (const d; digits[2 .. $])
164 			{
165 				if (d == '_')
166 					continue;
167 				value <<= 4;
168 				if (isDigit(d))
169 					value += d - '0';
170 				else
171 					value += 10 + toUpper(d) - 'A';
172 			}
173 		}
174 		else
175 		{
176 			static if (signed)
177 			{
178 				bool negative = false;
179 				if (digits.startsWith("-"))
180 				{
181 					negative = true;
182 					digits = digits[1 .. $];
183 				}
184 			}
185 			foreach (const d; digits)
186 			{
187 				if (d == '_')
188 					continue;
189 				value *= 10;
190 				value += d - '0';
191 			}
192 			static if (signed)
193 				if (negative)
194 					value = -value;
195 		}
196 		return value;
197 	}
198 
199 	/// Construct from compile-time digit string.
200 	///
201 	/// Both decimal and hex digit strings are supported.
202 	///
203 	/// Example:
204 	/// ----
205 	/// auto x = int128_t.literal!"20_000_000_000_000_000_001";
206 	/// assert((x >>> 1) == 0x8AC7_2304_89E8_0000);
207 	///
208 	/// auto y = int126.literal!"0x1_158E_4609_13D0_0001";
209 	/// assert(y == x);
210 	/// ----
211 	template literal(string digits)
212 	{
213 		static assert(isValidDigitString(digits),
214 					  "invalid digits in literal: " ~ digits);
215 		enum literal = literalImpl(digits);
216 	}
217 
218 	/// Assign with a smaller unsigned type.
219 	ref typeof(this) opAssign(T)(T n) pure nothrow @nogc if (isIntegral!T && isUnsigned!T)
220 	{
221 		hi = 0;
222 		lo = n;
223 		return this;
224 	}
225 
226 	/// Assign with a smaller signed type (sign is extended).
227 	ref typeof(this) opAssign(T)(T n) pure nothrow @nogc if (isIntegral!T && isSigned!T)
228 	{
229 		// shorter int always gets sign-extended,
230 		// regardless of the larger int being signed or not
231 		hi = (n < 0) ? cast(hi_t)(-1) : cast(hi_t)0;
232 
233 		// will also sign extend as well if needed
234 		lo = cast(sub_int_t)n;
235 		return this;
236 	}
237 
238 	/// Assign with a wide integer of the same size (sign is lost).
239 	ref typeof(this) opAssign(T)(T n) pure nothrow @nogc if (isWideIntInstantiation!T && T._bits == bits)
240 	{
241 		hi = n.hi;
242 		lo = n.lo;
243 		return this;
244 	}
245 
246 	/// Assign with a smaller wide integer (sign is extended accordingly).
247 	ref typeof(this) opAssign(T)(T n) pure nothrow @nogc if (isWideIntInstantiation!T && T._bits < bits)
248 	{
249 		static if (T._signed)
250 		{
251 			// shorter int always gets sign-extended,
252 			// regardless of the larger int being signed or not
253 			hi = cast(hi_t)((n < 0) ? -1 : 0);
254 
255 			// will also sign extend as well if needed
256 			lo = cast(sub_int_t)n;
257 			return this;
258 		}
259 		else
260 		{
261 			hi = 0;
262 			lo = n;
263 			return this;
264 		}
265 	}
266 
267 	/// Cast to a smaller integer type (truncation).
268 	T opCast(T)() pure const nothrow @nogc if (isIntegral!T) => cast(T)lo;
269 
270 	/// Cast to bool.
271 	T opCast(T)() pure const nothrow @nogc if (is(T == bool)) => this != 0;
272 
273 	/// Cast to wide integer of any size.
274 	T opCast(T)() pure const nothrow @nogc if (isWideIntInstantiation!T)
275 	{
276 		static if (T._bits < bits)
277 			return cast(T)lo;
278 		else
279 			return T(this);
280 	}
281 
282 	version (format)
283 	{
284 		import std.format : FormatSpec;
285 
286 		/// Converts to a string. Supports format specifiers %d, %s (both decimal)
287 		/// and %x (hex).
288 		void toString(Sink)(Sink sink, in FormatSpec!char fmt) const /+ TODO: something like is(Sink == scope void delegate(scope const(char)[]) @safe)) +/
289 		{
290 			if (fmt.spec == 'x')
291 				toStringHexadecimal(sink);
292 			else
293 				toStringDecimal(sink);
294 		}
295 	}
296 
297 	void toStringHexadecimal(Sink)(Sink sink) const /+ TODO: something like is(Sink == scope void delegate(scope const(char)[]) @safe)) +/
298 	{
299 		if (this == 0)
300 			return sink("0");
301 		enum maxDigits = bits / 4;
302 		char[maxDigits] buf;
303 		IntImpl tmp = this;
304 		size_t i;
305 		for (i = maxDigits-1; tmp != 0 && i < buf.length; i--)
306 		{
307 			buf[i] = hexDigits[cast(int)tmp & 0b00001111];
308 			tmp >>= 4;
309 		}
310 		assert(i+1 < buf.length);
311 		sink(buf[i+1 .. $]);
312 	}
313 
314 	void toStringDecimal(Sink)(Sink sink) const /+ TODO: something like is(Sink == scope void delegate(scope const(char)[]) @safe)) +/
315 	{
316 		if (this == 0)
317 			return sink("0");
318 
319 		// The maximum number of decimal digits is basically
320 		// ceil(log_10(2^^bits - 1)), which is slightly below
321 		// ceil(bits * log(2)/log(10)). The value 0.30103 is a slight
322 		// overestimate of log(2)/log(10), to be sure we never
323 		// underestimate. We add 1 to account for rounding up.
324 		enum maxDigits = cast(ulong)(0.30103 * bits) + 1;
325 		char[maxDigits] buf;
326 		size_t i;
327 		Self q = void, r = void;
328 
329 		IntImpl tmp = this;
330 		if (tmp < 0)
331 		{
332 			sink("-");
333 			tmp = -tmp;
334 		}
335 		for (i = maxDigits-1; tmp > 0; i--)
336 		{
337 			assert(i < buf.length);
338 			static if (signed)
339 				Internals!bits.signedDivide(tmp, Self.literal!"10", q, r);
340 			else
341 				Internals!bits.unsignedDivide(tmp, Self.literal!"10", q, r);
342 
343 			buf[i] = digits[cast(int)(r)];
344 			tmp = q;
345 		}
346 		assert(i+1 < buf.length);
347 		sink(buf[i+1 .. $]);
348 	}
349 
350 	typeof(this) opBinary(string op, T)(T o) pure const nothrow @nogc
351 	{
352 		typeof(return) r = this;
353 		typeof(return) y = o;
354 		return r.opOpAssign!(op)(y);
355 	}
356 
357 	ref typeof(this) opOpAssign(string op, T)(T y) pure nothrow @nogc if (!isSelf!T)
358 	{
359 		const(Self) o = y;
360 		return opOpAssign!(op)(o); /+ TODO: this can be optimized +/
361 	}
362 
363 	ref typeof(this) opOpAssign(string op, T)(T y) pure nothrow @nogc if (isSelf!T)
364 	{
365 		static if (op == "+")
366 		{
367 			hi += y.hi;
368 			if (lo + y.lo < lo) // deal with overflow
369 				++hi;
370 			lo += y.lo;
371 		}
372 		else static if (op == "-")
373 		{
374 			opOpAssign!"+"(-y);
375 		}
376 		else static if (op == "<<")
377 		{
378 			if (y >= bits)
379 			{
380 				hi = 0;
381 				lo = 0;
382 			}
383 			else if (y >= bits / 2)
384 			{
385 				hi = lo << (y.lo - bits / 2);
386 				lo = 0;
387 			}
388 			else if (y > 0)
389 			{
390 				hi = (lo >>> (-y.lo + bits / 2)) | (hi << y.lo);
391 				lo = lo << y.lo;
392 			}
393 		}
394 		else static if (op == ">>" || op == ">>>")
395 		{
396 			assert(y >= 0);
397 			static if (!signed || op == ">>>")
398 				immutable(sub_int_t) signFill = 0;
399 			else
400 				immutable(sub_int_t) signFill = cast(sub_int_t)(isNegative() ? -1 : 0);
401 
402 			if (y >= bits)
403 			{
404 				hi = signFill;
405 				lo = signFill;
406 			}
407 			else if (y >= bits/2)
408 			{
409 				lo = hi >> (y.lo - bits/2);
410 				hi = signFill;
411 			}
412 			else if (y > 0)
413 			{
414 				lo = (hi << (-y.lo + bits/2)) | (lo >> y.lo);
415 				hi = hi >> y.lo;
416 			}
417 		}
418 		else static if (op == "*")
419 		{
420 			const sub_sub_uint_t[4] a = toParts();
421 			const sub_sub_uint_t[4] b = y.toParts();
422 
423 			this = 0;
424 			foreach (const uint i; 0 .. 4)
425 				foreach (const uint j; 0 .. (4 - i))
426 					this += Self(cast(sub_uint_t)(a[i]) * b[j]) << ((bits/4) * (i + j));
427 		}
428 		else static if (op == "&")
429 		{
430 			hi &= y.hi;
431 			lo &= y.lo;
432 		}
433 		else static if (op == "|")
434 		{
435 			hi |= y.hi;
436 			lo |= y.lo;
437 		}
438 		else static if (op == "^")
439 		{
440 			hi ^= y.hi;
441 			lo ^= y.lo;
442 		}
443 		else static if (op == "/" || op == "%")
444 		{
445 			Self q = void, r = void;
446 			static if (signed)
447 				Internals!bits.signedDivide(this, y, q, r);
448 			else
449 				Internals!bits.unsignedDivide(this, y, q, r);
450 			static if (op == "/")
451 				this = q;
452 			else
453 				this = r;
454 		}
455 		else
456 		{
457 			static assert(false, "unsupported operation '" ~ op ~ "'");
458 		}
459 		return this;
460 	}
461 
462 	// const unary operations
463 	Self opUnary(string op)() pure const nothrow @nogc if (op == "+" || op == "-" || op == "~")
464 	{
465 		static if (op == "-")
466 		{
467 			Self r = this;
468 			r.not();
469 			r.increment();
470 			return r;
471 		}
472 		else static if (op == "+")
473 		   return this;
474 		else static if (op == "~")
475 		{
476 			Self r = this;
477 			r.not();
478 			return r;
479 		}
480 	}
481 
482 	// non-const unary operations
483 	Self opUnary(string op)() pure nothrow @nogc if (op == "++" || op == "--")
484 	{
485 		static if (op == "++")
486 			increment();
487 		else static if (op == "--")
488 			decrement();
489 		return this;
490 	}
491 
492 	bool opEquals(T)(in T y) pure const @nogc if (!isSelf!T) => this == Self(y);
493 	bool opEquals(T)(in T y) pure const @nogc if (isSelf!T) => lo == y.lo && y.hi == hi;
494 
495 	int opCmp(T)(in T y) pure const @nogc if (!isSelf!T) => opCmp(Self(y));
496 	int opCmp(T)(in T y) pure const @nogc if (isSelf!T)
497 	{
498 		if (hi < y.hi) return -1;
499 		if (hi > y.hi) return 1;
500 		if (lo < y.lo) return -1;
501 		if (lo > y.lo) return 1;
502 		return 0;
503 	}
504 
505 	// binary layout should be what is expected on this platform
506 	version (LittleEndian)
507 	{
508 		low_t lo;
509 		hi_t hi;
510 	}
511 	else
512 	{
513 		hi_t hi;
514 		low_t lo;
515 	}
516 
517 	private
518 	{
519 		static if (signed)
520 			bool isNegative() @safe pure nothrow const @nogc => signBit();
521 		else
522 			bool isNegative() @safe pure nothrow const @nogc => false;
523 
524 		void not() pure nothrow @safe @nogc
525 		{
526 			hi = ~hi;
527 			lo = ~lo;
528 		}
529 
530 		void increment() pure nothrow @safe @nogc
531 		{
532 			++lo;
533 			if (lo == 0) ++hi;
534 		}
535 
536 		void decrement() pure nothrow @safe @nogc
537 		{
538 			if (lo == 0) --hi;
539 			--lo;
540 		}
541 
542 		enum SIGN_SHIFT = bits / 2 - 1;
543 
544 		bool signBit() @safe pure const nothrow @nogc => ((hi >> SIGN_SHIFT) & 1) != 0;
545 
546 		sub_sub_uint_t[4] toParts() @safe pure const nothrow @nogc
547 		{
548 			sub_sub_uint_t[4] p = void;
549 			enum SHIFT = bits / 4;
550 			immutable lomask = cast(sub_uint_t)(cast(sub_sub_int_t)(-1));
551 			p[3] = cast(sub_sub_uint_t)(hi >> SHIFT);
552 			p[2] = cast(sub_sub_uint_t)(hi & lomask);
553 			p[1] = cast(sub_sub_uint_t)(lo >> SHIFT);
554 			p[0] = cast(sub_sub_uint_t)(lo & lomask);
555 			return p;
556 		}
557 	}
558 }
559 
560 template isWideIntInstantiation(U)
561 {
562 	private static void isWideInt(bool signed, uint bits)(IntImpl!(signed, bits) x)
563 	{
564 	}
565 
566 	enum bool isWideIntInstantiation = is(typeof(isWideInt(U.init)));
567 }
568 
569 public IntImpl!(signed, bits) abs(bool signed, uint bits)(IntImpl!(signed, bits) x) pure nothrow @nogc
570 	=> (x >= 0) ? x : -x;
571 
572 private struct Internals(uint bits)
573 {
574 	alias wint_t = IntImpl!(true, bits);
575 	alias uwint_t = IntImpl!(false, bits);
576 
577 	static void unsignedDivide(uwint_t dividend, uwint_t divisor, out uwint_t quotient, out uwint_t remainder) pure nothrow @nogc
578 	{
579 		assert(divisor != 0);
580 
581 		uwint_t rQuotient = 0;
582 		uwint_t cDividend = dividend;
583 
584 		while (divisor <= cDividend)
585 		{
586 			// find N so that (divisor << N) <= cDividend && cDividend < (divisor << (N + 1) )
587 
588 			uwint_t N = 0;
589 			uwint_t cDivisor = divisor;
590 			while (cDividend > cDivisor)
591 			{
592 				if (cDivisor.signBit())
593 					break;
594 
595 				if (cDividend < (cDivisor << 1))
596 					break;
597 
598 				cDivisor <<= 1;
599 				++N;
600 			}
601 			cDividend = cDividend - cDivisor;
602 			rQuotient += (uwint_t(1) << N);
603 		}
604 
605 		quotient = rQuotient;
606 		remainder = cDividend;
607 	}
608 
609 	static void signedDivide(wint_t dividend, wint_t divisor, out wint_t quotient, out wint_t remainder) pure nothrow @nogc
610 	{
611 		uwint_t q, r;
612 		unsignedDivide(uwint_t(abs(dividend)), uwint_t(abs(divisor)), q, r);
613 
614 		// remainder has same sign as the dividend
615 		if (dividend < 0)
616 			r = -r;
617 
618 		// negate the quotient if opposite signs
619 		if ((dividend >= 0) != (divisor >= 0))
620 			q = -q;
621 
622 		quotient = q;
623 		remainder = r;
624 
625 		assert(remainder == 0 || ((remainder < 0) == (dividend < 0)));
626 	}
627 }
628 
629 // Verify that toString is callable from pure / nothrow / @nogc code as long as
630 // the callback also has these attributes.
631 @safe unittest {
632 	int256 x = 123;
633 	x.toStringDecimal((scope const(char)[]) @safe {});
634 	x.toStringDecimal((scope const(char)[] x) @safe { assert(x == "123"); });
635 }
636 
637 version (format)
638 unittest {
639 	import std.format : format;
640 
641 	int128 x;
642 	x.hi = 1;
643 	x.lo = 0x158E_4609_13D0_0001;
644 	assert(format("%s", x) == "20000000000000000001");
645 	assert(format("%d", x) == "20000000000000000001");
646 	assert(format("%x", x) == "1158E460913D00001");
647 
648 	x.hi = 0xFFFF_FFFF_FFFF_FFFE;
649 	x.lo = 0xEA71_B9F6_EC2F_FFFF;
650 	assert(format("%d", x) == "-20000000000000000001");
651 	assert(format("%x", x) == "FFFFFFFFFFFFFFFEEA71B9F6EC2FFFFF");
652 
653 	x.hi = x.lo = 0;
654 	assert(format("%d", x) == "0");
655 
656 	x.hi = x.lo = 0xFFFF_FFFF_FFFF_FFFF;
657 	assert(format("%d", x) == "-1"); // array index boundary condition
658 }
659 
660 unittest {
661 	string testSigned(string op) @safe pure nothrow
662 	{
663 		return "assert(cast(ulong)(si" ~ op ~ "sj) == cast(ulong)(csi" ~ op ~ "csj));";
664 	}
665 	string testMixed(string op) @safe pure nothrow
666 	{
667 		return "assert(cast(ulong)(ui" ~ op ~ "sj) == cast(ulong)(cui" ~ op ~ "csj));"
668 		~ "assert(cast(ulong)(si" ~ op ~ "uj) == cast(ulong)(csi" ~ op ~ "cuj));";
669 	}
670 	string testUnsigned(string op) @safe pure nothrow
671 	{
672 		return "assert(cast(ulong)(ui" ~ op ~ "uj) == cast(ulong)(cui" ~ op ~ "cuj));";
673 	}
674 	string testAll(string op) @safe pure nothrow
675 	{
676 		return testSigned(op) ~ testMixed(op) ~ testUnsigned(op);
677 	}
678 	const long step = 164703072086692425;
679 	for (long si = long.min; si <= long.max - step; si += step)
680 	{
681 		for (long sj = long.min; sj <= long.max - step; sj += step)
682 		{
683 			const ulong ui = cast(ulong)si;
684 			const ulong uj = cast(ulong)sj;
685 			int128 csi = si;
686 			const uint128 cui = si;
687 			const int128 csj = sj;
688 			const uint128 cuj = sj;
689 			assert(csi == csi);
690 			assert(~~csi == csi);
691 			assert(-(-csi) == csi);
692 			assert(++csi == si + 1);
693 			assert(--csi == si);
694 
695 			mixin(testAll("+"));
696 			mixin(testAll("-"));
697 			mixin(testAll("*"));
698 			mixin(testAll("|"));
699 			mixin(testAll("&"));
700 			mixin(testAll("^"));
701 			if (sj != 0)
702 			{
703 				mixin(testSigned("/"));
704 				mixin(testSigned("%"));
705 				if (si >= 0 && sj >= 0)
706 				{
707 					// those operations are not supposed to be the same at
708 					// higher bitdepth: a sign-extended negative may yield higher dividend
709 					testMixed("/");
710 					testUnsigned("/");
711 					testMixed("%");
712 					testUnsigned("%");
713 				}
714 			}
715 		}
716 	}
717 }
718 
719 unittest {
720 	// Just a little over 2^64, so it actually needs int128.
721 	// Hex value should be 0x1_158E_4609_13D0_0001.
722 	enum x = int128.literal!"20_000_000_000_000_000_001";
723 	assert(x.hi == 0x1 && x.lo == 0x158E_4609_13D0_0001);
724 	assert((x >>> 1) == 0x8AC7_2304_89E8_0000);
725 
726 	enum y = int128.literal!"0x1_158E_4609_13D0_0001";
727 	enum z = int128.literal!"0x1_158e_4609_13d0_0001"; // case insensitivity
728 	assert(x == y && y == z && x == z);
729 }
730 
731 unittest {
732 	version (format) import std.format : format;
733 
734 	// Malformed literals that should be rejected
735 	assert(!__traits(compiles, int128.literal!""));
736 	assert(!__traits(compiles, int128.literal!"-"));
737 
738 	// Negative literals should be supported
739 	auto x = int128.literal!"-20000000000000000001";
740 	assert(x.hi == 0xFFFF_FFFF_FFFF_FFFE &&
741 		   x.lo == 0xEA71_B9F6_EC2F_FFFF);
742 	version (format) assert(format("%d", x) == "-20000000000000000001");
743 	version (format) assert(format("%x", x) == "FFFFFFFFFFFFFFFEEA71B9F6EC2FFFFF");
744 
745 	// Negative literals should not be supported for unsigned types
746 	assert(!__traits(compiles, uint128.literal!"-1"));
747 
748 	// Hex formatting tests
749 	x = 0;
750 	version (format) assert(format("%x", x) == "0");
751 	x = -1;
752 	version (format) assert(format("%x", x) == "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
753 }
754 
755 version (unittest)
756 {
757 	alias int128 = SInt!128;		// cent
758 	alias uint128 = UInt!128;		// ucent
759 	alias int256 = SInt!256;
760 	alias uint256 = UInt!256;
761 }