Skip to content

Commit

Permalink
Swaps: fix safe math negative number support, add comparison functions (
Browse files Browse the repository at this point in the history
#5776)

* Fix negative number support, add number comparison functions

* Add Worklet suffix to safe math functions
  • Loading branch information
christianbaroni authored May 27, 2024
1 parent 3bf7fae commit 0889e09
Show file tree
Hide file tree
Showing 2 changed files with 270 additions and 103 deletions.
199 changes: 139 additions & 60 deletions src/__swaps__/safe-math/SafeMath.ts
Original file line number Diff line number Diff line change
@@ -1,142 +1,221 @@
// Utility function to remove the decimal point and keep track of the number of decimal places
const removeDecimal = (num: string): [bigint, number] => {
const removeDecimalWorklet = (num: string): [bigint, number] => {
'worklet';
const parts = num.split('.');
const decimalPlaces = parts.length === 2 ? parts[1].length : 0;
const bigIntNum = BigInt(parts.join(''));
return [bigIntNum, decimalPlaces];
};

const isNumberString = (value: string): boolean => {
return /^\d+(\.\d+)?$/.test(value);
const isNumberStringWorklet = (value: string): boolean => {
'worklet';
return /^-?\d+(\.\d+)?$/.test(value);
};

const isZero = (value: string): boolean => {
const isZeroWorklet = (value: string): boolean => {
'worklet';
if (parseFloat(value) === 0) {
return true;
}
return false;
};

// Utility function to scale the number up to 20 decimal places
const scaleUp = (bigIntNum: bigint, decimalPlaces: number): bigint => {
const scaleFactor = BigInt(10) ** (BigInt(20) - BigInt(decimalPlaces));
return bigIntNum * scaleFactor;
const scaleUpWorklet = (bigIntNum: bigint, decimalPlaces: number): bigint => {
'worklet';
const scaleFactor = BigInt(10) ** BigInt(20);
return (bigIntNum * scaleFactor) / BigInt(10) ** BigInt(decimalPlaces);
};

// Utility function to format the result with 20 decimal places and remove trailing zeros
const formatResult = (result: bigint): string => {
const resultStr = result.toString().padStart(21, '0'); // 20 decimal places + at least 1 integer place
const formatResultWorklet = (result: bigint): string => {
'worklet';
const isNegative = result < 0;
const absResult = isNegative ? -result : result;
const resultStr = absResult.toString().padStart(21, '0'); // 20 decimal places + at least 1 integer place
const integerPart = resultStr.slice(0, -20) || '0';
let fractionalPart = resultStr.slice(-20);
fractionalPart = fractionalPart.replace(/0+$/, ''); // Remove trailing zeros
return fractionalPart.length > 0 ? `${integerPart}.${fractionalPart}` : integerPart;
const formattedResult = fractionalPart.length > 0 ? `${integerPart}.${fractionalPart}` : integerPart;
return isNegative ? `-${formattedResult}` : formattedResult;
};

// Sum function
export function sum(num1: string, num2: string): string {
if (!isNumberString(num1) || !isNumberString(num2)) {
export function sumWorklet(num1: string, num2: string): string {
'worklet';
if (!isNumberStringWorklet(num1) || !isNumberStringWorklet(num2)) {
throw new Error('Arguments must be a numeric string');
}
if (isZero(num1)) {
if (isZeroWorklet(num1)) {
return num2;
}

if (isZero(num2)) {
if (isZeroWorklet(num2)) {
return num1;
}
const [bigInt1, decimalPlaces1] = removeDecimal(num1);
const [bigInt2, decimalPlaces2] = removeDecimal(num2);
const scaledBigInt1 = scaleUp(bigInt1, decimalPlaces1);
const scaledBigInt2 = scaleUp(bigInt2, decimalPlaces2);
const [bigInt1, decimalPlaces1] = removeDecimalWorklet(num1);
const [bigInt2, decimalPlaces2] = removeDecimalWorklet(num2);
const scaledBigInt1 = scaleUpWorklet(bigInt1, decimalPlaces1);
const scaledBigInt2 = scaleUpWorklet(bigInt2, decimalPlaces2);
const result = scaledBigInt1 + scaledBigInt2;
return formatResult(result);
return formatResultWorklet(result);
}

// Subtract function
export function sub(num1: string, num2: string): string {
if (!isNumberString(num1) || !isNumberString(num2)) {
export function subWorklet(num1: string, num2: string): string {
'worklet';
if (!isNumberStringWorklet(num1) || !isNumberStringWorklet(num2)) {
throw new Error('Arguments must be a numeric string');
}

if (isZero(num2)) {
if (isZeroWorklet(num2)) {
return num1;
}

const [bigInt1, decimalPlaces1] = removeDecimal(num1);
const [bigInt2, decimalPlaces2] = removeDecimal(num2);
const scaledBigInt1 = scaleUp(bigInt1, decimalPlaces1);
const scaledBigInt2 = scaleUp(bigInt2, decimalPlaces2);
const [bigInt1, decimalPlaces1] = removeDecimalWorklet(num1);
const [bigInt2, decimalPlaces2] = removeDecimalWorklet(num2);
const scaledBigInt1 = scaleUpWorklet(bigInt1, decimalPlaces1);
const scaledBigInt2 = scaleUpWorklet(bigInt2, decimalPlaces2);
const result = scaledBigInt1 - scaledBigInt2;
return formatResult(result);
return formatResultWorklet(result);
}

// Multiply function
export function mul(num1: string, num2: string): string {
if (!isNumberString(num1) || !isNumberString(num2)) {
export function mulWorklet(num1: string, num2: string): string {
'worklet';
if (!isNumberStringWorklet(num1) || !isNumberStringWorklet(num2)) {
throw new Error('Arguments must be a numeric string');
}
if (isZero(num1) || isZero(num2)) {
if (isZeroWorklet(num1) || isZeroWorklet(num2)) {
return '0';
}
const [bigInt1, decimalPlaces1] = removeDecimal(num1);
const [bigInt2, decimalPlaces2] = removeDecimal(num2);
const scaledBigInt1 = scaleUp(bigInt1, decimalPlaces1);
const scaledBigInt2 = scaleUp(bigInt2, decimalPlaces2);
const [bigInt1, decimalPlaces1] = removeDecimalWorklet(num1);
const [bigInt2, decimalPlaces2] = removeDecimalWorklet(num2);
const scaledBigInt1 = scaleUpWorklet(bigInt1, decimalPlaces1);
const scaledBigInt2 = scaleUpWorklet(bigInt2, decimalPlaces2);
const result = (scaledBigInt1 * scaledBigInt2) / BigInt(10) ** BigInt(20);
return formatResult(result);
return formatResultWorklet(result);
}

// Divide function
export function div(num1: string, num2: string): string {
if (!isNumberString(num1) || !isNumberString(num2)) {
export function divWorklet(num1: string, num2: string): string {
'worklet';
if (!isNumberStringWorklet(num1) || !isNumberStringWorklet(num2)) {
throw new Error('Arguments must be a numeric string');
}
if (isZero(num2)) {
if (isZeroWorklet(num2)) {
throw new Error('Division by zero');
}
if (isZero(num1)) {
if (isZeroWorklet(num1)) {
return '0';
}
const [bigInt1, decimalPlaces1] = removeDecimal(num1);
const [bigInt2, decimalPlaces2] = removeDecimal(num2);
const scaledBigInt1 = scaleUp(bigInt1, decimalPlaces1);
const scaledBigInt2 = scaleUp(bigInt2, decimalPlaces2);
const [bigInt1, decimalPlaces1] = removeDecimalWorklet(num1);
const [bigInt2, decimalPlaces2] = removeDecimalWorklet(num2);
const scaledBigInt1 = scaleUpWorklet(bigInt1, decimalPlaces1);
const scaledBigInt2 = scaleUpWorklet(bigInt2, decimalPlaces2);
const result = (scaledBigInt1 * BigInt(10) ** BigInt(20)) / scaledBigInt2;
return formatResult(result);
return formatResultWorklet(result);
}

// Modulus function
export function mod(num1: string, num2: string): string {
if (!isNumberString(num1) || !isNumberString(num2)) {
export function modWorklet(num1: string, num2: string): string {
'worklet';
if (!isNumberStringWorklet(num1) || !isNumberStringWorklet(num2)) {
throw new Error('Arguments must be a numeric string');
}
if (isZero(num2)) {
if (isZeroWorklet(num2)) {
throw new Error('Division by zero');
}
if (isZero(num1)) {
if (isZeroWorklet(num1)) {
return '0';
}
const [bigInt1, decimalPlaces1] = removeDecimal(num1);
const [bigInt2, decimalPlaces2] = removeDecimal(num2);
const scaledBigInt1 = scaleUp(bigInt1, decimalPlaces1);
const scaledBigInt2 = scaleUp(bigInt2, decimalPlaces2);
const [bigInt1, decimalPlaces1] = removeDecimalWorklet(num1);
const [bigInt2, decimalPlaces2] = removeDecimalWorklet(num2);
const scaledBigInt1 = scaleUpWorklet(bigInt1, decimalPlaces1);
const scaledBigInt2 = scaleUpWorklet(bigInt2, decimalPlaces2);
const result = scaledBigInt1 % scaledBigInt2;
return formatResult(result);
return formatResultWorklet(result);
}

// Power function
export function pow(base: string, exponent: string): string {
if (!isNumberString(base) || !isNumberString(exponent)) {
export function powWorklet(base: string, exponent: string): string {
'worklet';
if (!isNumberStringWorklet(base) || !isNumberStringWorklet(exponent)) {
throw new Error('Arguments must be a numeric string');
}
if (isZero(base)) {
if (isZeroWorklet(base)) {
return '0';
}
if (isZero(exponent)) {
if (isZeroWorklet(exponent)) {
return '1';
}
const [bigIntBase, decimalPlaces] = removeDecimal(base);
const scaledBigIntBase = scaleUp(bigIntBase, decimalPlaces);
const [bigIntBase, decimalPlaces] = removeDecimalWorklet(base);
const scaledBigIntBase = scaleUpWorklet(bigIntBase, decimalPlaces);
const result = scaledBigIntBase ** BigInt(exponent) / BigInt(10) ** BigInt(20);
return formatResult(result);
return formatResultWorklet(result);
}

// Equality function
export function equalWorklet(num1: string, num2: string): boolean {
'worklet';
if (!isNumberStringWorklet(num1) || !isNumberStringWorklet(num2)) {
throw new Error('Arguments must be a numeric string');
}
const [bigInt1, decimalPlaces1] = removeDecimalWorklet(num1);
const [bigInt2, decimalPlaces2] = removeDecimalWorklet(num2);
const scaledBigInt1 = scaleUpWorklet(bigInt1, decimalPlaces1);
const scaledBigInt2 = scaleUpWorklet(bigInt2, decimalPlaces2);
return scaledBigInt1 === scaledBigInt2;
}

// Greater than function
export function greaterThanWorklet(num1: string, num2: string): boolean {
'worklet';
if (!isNumberStringWorklet(num1) || !isNumberStringWorklet(num2)) {
throw new Error('Arguments must be a numeric string');
}
const [bigInt1, decimalPlaces1] = removeDecimalWorklet(num1);
const [bigInt2, decimalPlaces2] = removeDecimalWorklet(num2);
const scaledBigInt1 = scaleUpWorklet(bigInt1, decimalPlaces1);
const scaledBigInt2 = scaleUpWorklet(bigInt2, decimalPlaces2);
return scaledBigInt1 > scaledBigInt2;
}

// Greater than or equal to function
export function greaterThanOrEqualToWorklet(num1: string, num2: string): boolean {
'worklet';
if (!isNumberStringWorklet(num1) || !isNumberStringWorklet(num2)) {
throw new Error('Arguments must be a numeric string');
}
const [bigInt1, decimalPlaces1] = removeDecimalWorklet(num1);
const [bigInt2, decimalPlaces2] = removeDecimalWorklet(num2);
const scaledBigInt1 = scaleUpWorklet(bigInt1, decimalPlaces1);
const scaledBigInt2 = scaleUpWorklet(bigInt2, decimalPlaces2);
return scaledBigInt1 >= scaledBigInt2;
}

// Less than function
export function lessThanWorklet(num1: string, num2: string): boolean {
'worklet';
if (!isNumberStringWorklet(num1) || !isNumberStringWorklet(num2)) {
throw new Error('Arguments must be a numeric string');
}
const [bigInt1, decimalPlaces1] = removeDecimalWorklet(num1);
const [bigInt2, decimalPlaces2] = removeDecimalWorklet(num2);
const scaledBigInt1 = scaleUpWorklet(bigInt1, decimalPlaces1);
const scaledBigInt2 = scaleUpWorklet(bigInt2, decimalPlaces2);
return scaledBigInt1 < scaledBigInt2;
}

// Less than or equal to function
export function lessThanOrEqualToWorklet(num1: string, num2: string): boolean {
'worklet';
if (!isNumberStringWorklet(num1) || !isNumberStringWorklet(num2)) {
throw new Error('Arguments must be a numeric string');
}
const [bigInt1, decimalPlaces1] = removeDecimalWorklet(num1);
const [bigInt2, decimalPlaces2] = removeDecimalWorklet(num2);
const scaledBigInt1 = scaleUpWorklet(bigInt1, decimalPlaces1);
const scaledBigInt2 = scaleUpWorklet(bigInt2, decimalPlaces2);
return scaledBigInt1 <= scaledBigInt2;
}
Loading

0 comments on commit 0889e09

Please sign in to comment.