Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Swaps: fix safe math negative number support, add comparison functions #5776

Merged
merged 2 commits into from
May 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading