-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
628a58e
commit 248ad49
Showing
3 changed files
with
148 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
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 { | ||
return SafeMathI128.add(value, SafeMathI128.ONE); | ||
} | ||
|
||
/** | ||
* Decrement an i128 by 1 with underflow check. | ||
*/ | ||
public static dec(value: i128): i128 { | ||
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; | ||
} | ||
} |