blob: fc992f824224099e9c0d830dba2e1b652a84d1ef [file] [log] [blame]
// Written in the D programming language
/**
* Implements a signed 128 bit integer type.
*
Author: Walter Bright
Copyright: Copyright (c) 2022, D Language Foundation
License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0)
Source: $(PHOBOSSRC std/int128.d)
*/
module std.int128;
private import core.int128;
/***********************************
* 128 bit signed integer type.
*/
public struct Int128
{
@safe pure nothrow @nogc:
Cent data; /// core.int128.Cent
/****************
* Construct an `Int128` from a `long` value.
* The upper 64 bits are formed by sign extension.
* Params:
* lo = signed lower 64 bits
*/
this(long lo)
{
data.lo = lo;
data.hi = lo < 0 ? ~0L : 0;
}
/****************
* Construct an `Int128` from a `ulong` value.
* The upper 64 bits are set to zero.
* Params:
* lo = unsigned lower 64 bits
*/
this(ulong lo)
{
data.lo = lo;
data.hi = 0;
}
/****************
* Construct an `Int128` from a `long` value.
* Params:
* hi = upper 64 bits
* lo = lower 64 bits
*/
this(long hi, long lo)
{
data.hi = hi;
data.lo = lo;
}
/********************
* Construct an `Int128` from a `Cent`.
* Params:
* data = Cent data
*/
this(Cent data)
{
this.data = data;
}
/********************
* Returns: hash value for Int128
*/
size_t toHash() const
{
return cast(size_t)((data.lo & 0xFFFF_FFFF) + (data.hi & 0xFFFF_FFFF) + (data.lo >> 32) + (data.hi >> 32));
}
/************************
* Compare for equality
* Params: lo = signed value to compare with
* Returns: true if Int128 equals value
*/
bool opEquals(long lo) const
{
return data.lo == lo && data.hi == (lo >> 63);
}
/************************
* Compare for equality
* Params: lo = unsigned value to compare with
* Returns: true if Int128 equals value
*/
bool opEquals(ulong lo) const
{
return data.hi == 0 && data.lo == lo;
}
/************************
* Compare for equality
* Params: op2 = value to compare with
* Returns: true if Int128 equals value
*/
bool opEquals(Int128 op2) const
{
return data.hi == op2.data.hi && data.lo == op2.data.lo;
}
/** Support unary arithmentic operator +
* Params: op = "+"
* Returns: lvalue of result
*/
Int128 opUnary(string op)() const
if (op == "+")
{
return this;
}
/** Support unary arithmentic operator - ~
* Params: op = "-", "~"
* Returns: lvalue of result
*/
Int128 opUnary(string op)() const
if (op == "-" || op == "~")
{
static if (op == "-")
return Int128(neg(this.data));
else static if (op == "~")
return Int128(com(this.data));
}
/** Support unary arithmentic operator ++ --
* Params: op = "++", "--"
* Returns: lvalue of result
*/
Int128 opUnary(string op)()
if (op == "++" || op == "--")
{
static if (op == "++")
this.data = inc(this.data);
else static if (op == "--")
this.data = dec(this.data);
else
static assert(0, op);
return this;
}
/** Support casting to a bool
* Params: T = bool
* Returns: boolean result
*/
bool opCast(T : bool)() const
{
return tst(this.data);
}
/** Support binary arithmetic operators + - * / % & | ^ << >> >>>
* Params:
* op = one of the arithmetic binary operators
* op2 = second operand
* Returns: value after the operation is applied
*/
Int128 opBinary(string op)(Int128 op2) const
if (op == "+" || op == "-" ||
op == "*" || op == "/" || op == "%" ||
op == "&" || op == "|" || op == "^")
{
static if (op == "+")
return Int128(add(this.data, op2.data));
else static if (op == "-")
return Int128(sub(this.data, op2.data));
else static if (op == "*")
return Int128(mul(this.data, op2.data));
else static if (op == "/")
return Int128(div(this.data, op2.data));
else static if (op == "%")
{
Cent modulus;
divmod(this.data, op2.data, modulus);
return Int128(modulus);
}
else static if (op == "&")
return Int128(and(this.data, op2.data));
else static if (op == "|")
return Int128(or(this.data, op2.data));
else static if (op == "^")
return Int128(xor(this.data, op2.data));
else
static assert(0, "wrong op value");
}
/// ditto
Int128 opBinary(string op)(long op2) const
if (op == "+" || op == "-" ||
op == "*" || op == "/" || op == "%" ||
op == "&" || op == "|" || op == "^")
{
return mixin("this " ~ op ~ " Int128(0, op2)");
}
/// ditto
Int128 opBinaryRight(string op)(long op2) const
if (op == "+" || op == "-" ||
op == "*" || op == "/" || op == "%" ||
op == "&" || op == "|" || op == "^")
{
mixin("return Int128(0, op2) " ~ op ~ " this;");
}
/// ditto
Int128 opBinary(string op)(long op2) const
if (op == "<<")
{
return Int128(shl(this.data, cast(uint) op2));
}
/// ditto
Int128 opBinary(string op)(long op2) const
if (op == ">>")
{
return Int128(sar(this.data, cast(uint) op2));
}
/// ditto
Int128 opBinary(string op)(long op2) const
if (op == ">>>")
{
return Int128(shr(this.data, cast(uint) op2));
}
/** arithmetic assignment operators += -= *= /= %= &= |= ^= <<= >>= >>>=
* Params: op = one of +, -, etc.
* op2 = second operand
* Returns: lvalue of updated left operand
*/
ref Int128 opOpAssign(string op)(Int128 op2)
if (op == "+" || op == "-" ||
op == "*" || op == "/" || op == "%" ||
op == "&" || op == "|" || op == "^" ||
op == "<<" || op == ">>" || op == ">>>")
{
mixin("this = this " ~ op ~ " op2;");
return this;
}
/// ditto
ref Int128 opOpAssign(string op)(long op2)
if (op == "+" || op == "-" ||
op == "*" || op == "/" || op == "%" ||
op == "&" || op == "|" || op == "^" ||
op == "<<" || op == ">>" || op == ">>>")
{
mixin("this = this " ~ op ~ " op2;");
return this;
}
/** support signed arithmentic comparison operators < <= > >=
* Params: op2 = right hand operand
* Returns: -1 for less than, 0 for equals, 1 for greater than
*/
int opCmp(Int128 op2) const
{
return this == op2 ? 0 : gt(this.data, op2.data) * 2 - 1;
}
/** support signed arithmentic comparison operators < <= > >=
* Params: op2 = right hand operand
* Returns: -1 for less than, 0 for equals, 1 for greater than
*/
int opCmp(long op2) const
{
return opCmp(Int128(0, op2));
}
enum min = Int128(long.min, 0); /// minimum value
enum max = Int128(long.max, ulong.max); /// maximum value
}
/********************************************* Tests ************************************/
version (unittest)
{
import core.stdc.stdio;
@trusted void print(Int128 c)
{
printf("%lld, %lld\n", c.data.hi, c.data.lo);
}
@trusted void printx(Int128 c)
{
printf("%llx, %llx\n", c.data.hi, c.data.lo);
}
}
/// Int128 tests
@safe pure nothrow @nogc
unittest
{
Int128 c = Int128(5, 6);
assert(c == c);
assert(c == +c);
assert(c == - -c);
assert(~c == Int128(~5, ~6));
++c;
assert(c == Int128(5, 7));
assert(--c == Int128(5, 6));
assert(!!c);
assert(!Int128());
assert(c + Int128(10, 20) == Int128(15, 26));
assert(c - Int128(1, 2) == Int128(4, 4));
assert(c * Int128(100, 2) == Int128(610, 12));
assert(c / Int128(3, 2) == Int128(0, 1));
assert(c % Int128(3, 2) == Int128(2, 4));
assert((c & Int128(3, 2)) == Int128(1, 2));
assert((c | Int128(3, 2)) == Int128(7, 6));
assert((c ^ Int128(3, 2)) == Int128(6, 4));
assert(c + 15 == Int128(5, 21));
assert(c - 15 == Int128(4, -9));
assert(c * 15 == Int128(75, 90));
assert(c / 15 == Int128(0, 6148914691236517205));
assert(c % 15 == Int128(0, 11));
assert((c & 15) == Int128(0, 6));
assert((c | 15) == Int128(5, 15));
assert((c ^ 15) == Int128(5, 9));
assert(15 + c == Int128(5, 21));
assert(15 - c == Int128(-5, 9));
assert(15 * c == Int128(75, 90));
assert(15 / c == Int128(0, 0));
assert(15 % c == Int128(0, 15));
assert((15 & c) == Int128(0, 6));
assert((15 | c) == Int128(5, 15));
assert((15 ^ c) == Int128(5, 9));
assert(c << 1 == Int128(10, 12));
assert(-c >> 1 == Int128(-3, 9223372036854775805));
assert(-c >>> 1 == Int128(9223372036854775805, 9223372036854775805));
assert((c += 1) == Int128(5, 7));
assert((c -= 1) == Int128(5, 6));
assert((c += Int128(0, 1)) == Int128(5, 7));
assert((c -= Int128(0, 1)) == Int128(5, 6));
assert((c *= 2) == Int128(10, 12));
assert((c /= 2) == Int128(5, 6));
assert((c %= 2) == Int128());
c += Int128(5, 6);
assert((c *= Int128(10, 20)) == Int128(160, 120));
assert((c /= Int128(10, 20)) == Int128(0, 15));
c += Int128(72, 0);
assert((c %= Int128(10, 20)) == Int128(1, -125));
assert((c &= Int128(3, 20)) == Int128(1, 0));
assert((c |= Int128(8, 2)) == Int128(9, 2));
assert((c ^= Int128(8, 2)) == Int128(1, 0));
c |= Int128(10, 5);
assert((c <<= 1) == Int128(11 * 2, 5 * 2));
assert((c >>>= 1) == Int128(11, 5));
c = Int128(long.min, long.min);
assert((c >>= 1) == Int128(long.min >> 1, cast(ulong) long.min >> 1));
assert(-Int128.min == Int128.min);
assert(Int128.max + 1 == Int128.min);
c = Int128(5, 6);
assert(c < Int128(6, 5));
assert(c > 10);
c = Int128(-1UL);
assert(c == -1UL);
c = Int128(-1L);
assert(c == -1L);
}