@btc-vision/btc-runtime
Version:
Bitcoin Smart Contract Runtime
155 lines (129 loc) • 4.8 kB
text/typescript
import { i128 } from '@btc-vision/as-bignum/assembly';
export class SafeMathI128 {
public static readonly ZERO: i128 = i128.fromI32(0);
public static readonly ONE: i128 = i128.fromI32(1);
public static readonly NEG_ONE: i128 = i128.fromI32(-1);
public static readonly MIN: i128 = i128.Min;
public static readonly MAX: i128 = i128.Max;
/**
* Safe addition for i128.
* Throws if (a + b) overflows or underflows the signed 128-bit range.
*/
public static add(a: i128, b: i128): i128 {
let c = i128.add(a, b);
// Overflow check for 2's complement:
// If a and b have the same sign, but c differs, overflow occurred.
// We can detect sign mismatch using ((a ^ c) & (b ^ c)) < 0
// (i.e., the sign bit is set in that expression).
if (((a ^ c) & (b ^ c)).isNeg()) {
throw new Error('SafeMathI128: addition overflow');
}
return c;
}
/**
* Safe subtraction for i128.
* Throws if (a - b) overflows or underflows the signed 128-bit range.
*/
public static sub(a: i128, b: i128): i128 {
let c = i128.sub(a, b);
// Subtraction is (a + (-b)). We can do a direct check like:
// If (a ^ b) & (a ^ c) has sign bit set => overflow.
if (((a ^ b) & (a ^ c)).isNeg()) {
throw new Error('SafeMathI128: subtraction overflow');
}
return c;
}
/*public static mul(a: i128, b: i128): i128 {
// Quick check: if either is ZERO, product is ZERO => no overflow
if (a == SafeMathI128.ZERO || b == SafeMathI128.ZERO) {
return SafeMathI128.ZERO;
}
let c = i128.mul(a, b);
// Check overflow: c / b should be exactly a (if b != 0).
// Also watch for the i128 edge case: MIN * -1 => possible overflow if not representable.
// We'll rely on the division check:
if (b != SafeMathI128.ZERO) {
let divCheck = i128.div(c, b);
if (divCheck != a) {
throw new Error('SafeMathI128: multiplication overflow');
}
}
return c;
}*/
/*public static div(a: i128, b: i128): i128 {
if (b == SafeMathI128.ZERO) {
throw new Error('SafeMathI128: division by zero');
}
// Check i128 edge case: MIN / -1 => possible overflow if no corresponding positive.
if (a == SafeMathI128.MIN && b == SafeMathI128.NEG_ONE) {
throw new Error('SafeMathI128: division overflow (MIN / -1)');
}
return i128.div(a, b);
}*/
/*public static mod(a: i128, b: i128): i128 {
if (b == SafeMathI128.ZERO) {
throw new Error('SafeMathI128: modulo by zero');
}
// Similar edge case as division:
if (a == SafeMathI128.MIN && b == SafeMathI128.NEG_ONE) {
// Some implementations might treat MIN % -1 == 0,
// but if the library doesn't, you may handle it similarly to division.
// We'll assume we throw to be safe:
throw new Error('SafeMathI128: modulo overflow (MIN % -1)');
}
// Use i128.rem, i128.mod, or the operator as appropriate.
return i128.rem(a, b);
}*/
/**
* Increment an i128 by 1 with overflow check.
*/
public static inc(value: i128): i128 {
if (value == SafeMathI128.MAX) {
throw new Error('SafeMathI128: inc overflow');
}
return SafeMathI128.add(value, SafeMathI128.ONE);
}
/**
* Decrement an i128 by 1 with underflow check.
*/
public static dec(value: i128): i128 {
if (value == SafeMathI128.MIN) {
throw new Error('SafeMathI128: dec underflow');
}
return SafeMathI128.sub(value, SafeMathI128.ONE);
}
/**
* Return the absolute value of x, throwing if x == MIN (since |MIN| might not be representable).
*/
public static abs(x: i128): i128 {
if (x.isNeg()) {
// If x == MIN, -x can overflow.
if (x == SafeMathI128.MIN) {
throw new Error('SafeMathI128: abs overflow on MIN');
}
return x.neg();
}
return x;
}
/**
* Return the negation of x, throwing if x == MIN.
*/
public static neg(x: i128): i128 {
if (x == SafeMathI128.MIN) {
throw new Error('SafeMathI128: neg overflow on MIN');
}
return x.neg();
}
/**
* Returns the smaller of two i128s.
*/
public static min(a: i128, b: i128): i128 {
return i128.lt(a, b) ? a : b;
}
/**
* Returns the larger of two i128s.
*/
public static max(a: i128, b: i128): i128 {
return i128.gt(a, b) ? a : b;
}
}