Skip to content

Commit

Permalink
Merge 442febe into 5a47b16
Browse files Browse the repository at this point in the history
  • Loading branch information
Jsoto22 committed Mar 28, 2024
2 parents 5a47b16 + 442febe commit 5c7073c
Show file tree
Hide file tree
Showing 4 changed files with 327 additions and 15 deletions.
38 changes: 35 additions & 3 deletions src/modulus.spec.ts
@@ -1,6 +1,6 @@
import { modulus } from "./modulus";
import { modulus, modulusE } from "./modulus";

describe("modulus", function () {
describe("modulus(remainder)", function () {
it("should modulus(7,4) = 3", function () {
expect(modulus(7, 4)).toBe("3");
});
Expand All @@ -10,7 +10,7 @@ describe("modulus", function () {
it("should modulus(7,-4) = 3", function () {
expect(modulus(7, -4)).toBe("3");
});
it("should modulus(-7,-4) = 3", function () {
it("should modulus(-7,-4) = -3", function () {
expect(modulus(-7, -4)).toBe("-3");
});
it("should modulus(-7,0) throw", function () {
Expand All @@ -31,3 +31,35 @@ describe("modulus", function () {
expect(() => modulus("7.5", "32")).toThrowError();
});
});

describe("modulus(Euclidean division)", function () {
it("should modulusE(7,4) = 3", function () {
expect(modulusE(7, 4)).toBe("3");
});
it("should modulusE(-7,4) = 1", function () {
expect(modulusE(-7, 4)).toBe("1");
});
it("should modulus(7,-4) = -1", function () {
expect(modulusE(7, -4)).toBe("-1");
});
it("should modulus(-7,-4) = -3", function () {
expect(modulusE(-7, -4)).toBe("-3");
});
it("should modulus(-7,0) throw", function () {
expect(() => modulusE(-7, 0)).toThrowError();
});

it("should modulusE(76457896543456, 77732) = 45352", function () {
expect(modulusE("76457896543456", "77732")).toBe("45352");
});

it("should modulusE(7.5, 3.2) to throw error", function () {
expect(() => modulusE("7.5", "3.2")).toThrowError();
});
it("should modulusE(75, 3.2) to throw error", function () {
expect(() => modulusE("75", "3.2")).toThrowError();
});
it("should modulusE(7.5, 32) to throw error", function () {
expect(() => modulusE("7.5", "32")).toThrowError();
});
});
83 changes: 71 additions & 12 deletions src/modulus.ts
@@ -1,10 +1,71 @@
import { divide } from './divide';
import { roundOff } from './round';
import { multiply } from './multiply';
import { subtract } from './subtract';
import { negate, subtract } from './subtract';
import { RoundingModes } from './roundingModes';
import { abs } from './abs';

export function modulus(dividend: number|string, divisor:number|string) {

// export function modulus(dividend: number | string, divisor: number | string) {
// if (divisor == 0) {
// throw new Error('Cannot divide by 0');
// }

// dividend = dividend.toString();
// divisor = divisor.toString();

// validate(dividend);
// validate(divisor);

// let sign = '';
// if (dividend[0] == '-') {
// sign = '-';
// dividend = dividend.substr(1);
// }
// if (divisor[0] == '-') {
// divisor = divisor.substr(1);
// }

// let result = subtract(dividend, multiply(divisor, roundOff(divide(dividend, divisor), 0, RoundingModes.FLOOR)));
// return sign + result;
// }

// function validate(oparand: string) {
// if (oparand.indexOf('.') != -1) { // oparand.includes('.') could also work here
// throw new Error('Modulus of non-integers not supported');
// }
// }


// For technical purposes, this is actually Remainder, and not Modulus (Euclidean division).
// Could seperate the Modulus equation into its own function,
// then use it within the Remainder function after proper negation.
// Proper neation only depends on the sign of the dividend, where the result takes the sign
// of the divident, and ignores the sign of the divisor. For this effect, the absolute values of
// each oparand is used, then the original sign of the divident dictates
// nagation of the result to negative or not.


// To ensure backwards compatibility, the new Modulus function could be named 'modulusE',
// where 'E' denotes 'Euclidean' in 'Euclidean division'.

// Sugested changes are bellow

export function modulusE(n: number | string, base: number | string = '1', percision: number | undefined = undefined) {
if (base == 0) {
throw new Error('Cannot divide by 0');
}

n = n.toString();
base = base.toString();

validate(n);
validate(base);

return subtract(n, multiply(base, roundOff(divide(n, base, percision), 0, RoundingModes.FLOOR)));
}

export function modulus(dividend: number | string, divisor: number | string, percision: number | undefined = undefined) {
if (divisor == 0) {
throw new Error('Cannot divide by 0');
}
Expand All @@ -15,21 +76,19 @@ export function modulus(dividend: number|string, divisor:number|string) {
validate(dividend);
validate(divisor);

let sign = '';
if(dividend[0] == '-'){
sign = '-';
dividend = dividend.substr(1);
}
if(divisor[0] == '-'){
divisor = divisor.substr(1);
let sign = false;
if (dividend[0] == '-') { // or dividend.includes('-')
sign = true;
}

let result = subtract(dividend, multiply(divisor, roundOff(divide(dividend, divisor), 0, RoundingModes.FLOOR)));
return sign+result;
const result = modulusE(abs(dividend), abs(divisor), percision);
return (sign) ? negate(result) : result;
}

function validate(oparand: string) {
if (oparand.indexOf('.') != -1) {
if (oparand.indexOf('.') != -1) { // or oparand.includes('.')
throw new Error('Modulus of non-integers not supported');
}
}


53 changes: 53 additions & 0 deletions src/pow.spec.ts
@@ -0,0 +1,53 @@
import { NonIntegerExponentError, intPow } from "./pow";

describe("intPow", function () {

it("should be defined", function () {
expect(intPow).toBeDefined();
});

it("should: 2^2 = 4", function () {
expect(intPow(2, 2)).toBe("4");
});

it("should: -2^2 = 4", function () {
expect(intPow(-2, 2)).toBe("4");
});

it("should: -2^3 = -8", function () {
expect(intPow(-2, 3)).toBe("-8");
});

describe('Negated', function () {

it("should: -(2^2) = -4", function () {
expect(intPow(2, 2, true)).toBe("-4");
});

it("should: -(-2^2) = -4", function () {
expect(intPow(2, 2, true)).toBe("-4");
});

it("should: -(-2^3) = 8", function () {
expect(intPow(-2, 3, true)).toBe("8");
});
})

describe('Special Cases', function () {
it("should: 2^0 = 1", function () {
expect(intPow(2, 0)).toBe("1");
});

it("should: -2^1 = 2", function () {
expect(intPow(2, 1)).toBe("2");
});
})

describe('Errors and Exceptions', function () {
it("should throw error", function () {
expect(()=>{intPow(2, 2.5)}).toThrowError();
});

})

});
168 changes: 168 additions & 0 deletions src/pow.ts
@@ -0,0 +1,168 @@
import { abs } from "./abs";
import { compareTo } from "./compareTo";
import { divide } from "./divide";
import { modulus } from "./modulus";
import { multiply } from "./multiply";
import { roundOff } from "./round";
import { RoundingModes } from "./roundingModes";
import { negate as negateFn, subtract } from "./subtract";

export type ExponentErrorOrException = {
message: string,
type: 'error' | 'exception',
}

export const NonIntegerExponentError: ExponentErrorOrException = {
message: `Exponent must be an integer.`,
type: 'error',
}

export const ComplexExponentException: ExponentErrorOrException = {
message: `Result is a Complex number with only an Imaginary component.`,
type: 'exception',
}




/**
* Calculates the power of a given base raised to an integer exponent
*
* @param base - Base number
* @param exponent - Exponent integer
* @param negate - If set to true, parameters will be evaluated as `-(x ^ n)`
*
* @returns The resulting power as a string
*
* @throws {NonIntegerExponentError} - If `exponent` is a non-integer number, this error is thrown.
*
* @example Basic usage:
* ```
* // Positive Base
* console.log(pow(2,2)) // Prints '4'
* // Negative Base
* console.log(pow(-2,2)) // Prints '4'
* // Negative Base where the result will be a negative number
* console.log(pow(-2,3)) // Prints '-8'
* ```
*
* @example Negation usage:
* ```
* // Positive Base
* console.log(pow(2, 2, true)) // Prints '-4'
* // Negative Base
* console.log(pow(-2, 2, true)) // Prints '-4'
* // Negative Base where the result will be a negative number
* console.log(pow(-2, 3, true)) // Prints '8'
* ```
*
* @example Special cases:
* ```
* // Exponent of 0
* console.log(pow(2, 0)) // Prints '1'
* // Exponent of 1
* console.log(pow(2, 1)) // Prints '2'
* ```
*/

// Integer Exponent Only Implementation

export function intPow(base: number | string, exponent: number | string, negate: boolean = false) {

exponent = exponent.toString();
base = base.toString();

try {
if (exponent.includes('.')) {
throw NonIntegerExponentError
}

// Special Handling of Complex numbers

// const imaginary = exponent < 0 && Number(remainder) > 0 && Number(remainder) < 1;

// if (imaginary) {
// throw ComplexExponentException
// }

} catch (errorOrException) {
errorOrException = <ExponentErrorOrException>errorOrException
switch (errorOrException.type) {
case 'error':
const error = Error(`${errorOrException.message}`)
console.error(error)
throw error
// case 'exception': // For Complex nunmbers
// console.error(`Exception(${errorOrException.severity}): ${errorOrException.message}`)
// return NaN // Todo: Break or continue
}
}

const reciprical = compareTo(exponent, '0') == -1;
const base10Percision = compareTo(base, '10') == 0 ? exponent.length : undefined;

let result = '1';

exponent = abs(exponent)

while (compareTo(exponent, '0') == 1) {
if (modulus(exponent, 2) == '1') { result = multiply(result, base) }
base = multiply(base, base);
exponent = roundOff(divide(exponent, 2), 0, RoundingModes.FLOOR);
}

result = (reciprical) ? divide(1, result, base10Percision) : result;
return (negate) ? negateFn(result) : result;
};

// Todo: Core Powers function
// Needs Nth-Root implementation for fractional powers

// export function pow(x: number, n: number, negate: boolean = false) {

// const reciprical = n < 0;
// const percision = x == 10 && n >= 1 ? Math.abs(n) : undefined

// const exp = abs(n);
// const floor = roundOff(exp, 0, RoundingModes.FLOOR);
// const remainder = subtract(exp, floor);
// const imaginary = x < 0 && Number(remainder) > 0 && Number(remainder) < 1;

// try {
// if (imaginary) {
// x = Math.abs(x);
// negate = true;
// throw `Complex Number Exception: Cannot calculate powers resulting in Imaginary Numbers. Base will be subsituted with it's absolute value, and result will be negated.`;
// }
// } catch (warning) {
// console.warn(warning);
// }

// const base = x;

// let result = x.toString();

// if (Number(remainder) > 0 && Number(remainder) < 1) {
// const factor = divide(1, remainder, 3);
// const root = nthRoot(x, Number(factor));

// if (Number(floor) > 0) {
// for (let i = 0; i < Number(floor) - 1; i++) {
// result = multiply(result, base);
// }
// } else {
// result = '1';
// }

// result = multiply(result, root);
// } else if (n == 0) {
// result = '1';
// } else {
// for (let i = 0; i < Number(exp) - 1; i++) {
// result = multiply(result, base);
// }
// }
// result = negate ? negateFn(result) : result;
// result = reciprical ? divide(1, result, percision) : result;
// return result;
// };

0 comments on commit 5c7073c

Please sign in to comment.