170 lines
4.2 KiB
TypeScript
170 lines
4.2 KiB
TypeScript
/**
|
|
* Exposes maths functions for prime numbers.
|
|
*/
|
|
export interface PrimeMath {
|
|
/**
|
|
* Checks a number for primality.
|
|
*
|
|
* @param n the number to check for primality
|
|
* @return `true` if and only if `n` is prime
|
|
*/
|
|
isPrime(n: number): boolean
|
|
|
|
/**
|
|
* Decomposes `n` into its prime factors.
|
|
*
|
|
* @param n the number to decompose
|
|
* @return the array of prime factors of `n`
|
|
*/
|
|
decompose(n: number): number[]
|
|
}
|
|
|
|
/**
|
|
* Simple implementation of `PrimeMath`.
|
|
*/
|
|
export class SimplePrimeMath implements PrimeMath {
|
|
/**
|
|
* The singleton instance.
|
|
*
|
|
* @private
|
|
*/
|
|
private static instance: SimplePrimeMath;
|
|
|
|
|
|
/**
|
|
* Constructs a new `SimplePrimeMath`.
|
|
*
|
|
* @private
|
|
*/
|
|
private constructor() {
|
|
// Do nothing
|
|
}
|
|
|
|
/**
|
|
* Returns the singleton instance.
|
|
*
|
|
* @return the singleton instance
|
|
*/
|
|
public static getInstance(): SimplePrimeMath {
|
|
if (!SimplePrimeMath.instance)
|
|
SimplePrimeMath.instance = new SimplePrimeMath();
|
|
|
|
return SimplePrimeMath.instance;
|
|
}
|
|
|
|
|
|
public isPrime(n: number): boolean {
|
|
if (!Number.isInteger(n)) throw Error("Cannot check non-integer number for primality.");
|
|
|
|
if (n < 2) return false;
|
|
if (n === 2) return true;
|
|
if (n % 2 === 0) return false;
|
|
|
|
const limit = Math.floor(Math.sqrt(n));
|
|
for (let i = 3; i <= limit; i += 2)
|
|
if (n % i === 0)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
public decompose(n: number): number[] {
|
|
if (!Number.isInteger(n)) throw Error("Cannot find prime decomposition of non-integer number.");
|
|
|
|
let factors: number[] = [];
|
|
|
|
let target = n;
|
|
for (let i = 2; target !== 1; i = i === 2 ? 3 : i + 2) {
|
|
while (target % i === 0) {
|
|
factors.push(i);
|
|
target = target / i;
|
|
}
|
|
}
|
|
|
|
return factors;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Implements `PrimeMath` such that all queries are cached so that subsequent queries are faster.
|
|
*/
|
|
export class CachedPrimeMath implements PrimeMath {
|
|
/**
|
|
* The singleton instance.
|
|
*
|
|
* @private
|
|
*/
|
|
private static instance: CachedPrimeMath;
|
|
/**
|
|
* The underlying `SimplePrimeMath` instance of which the answers are cached.
|
|
*
|
|
* @private
|
|
*/
|
|
private static simple: SimplePrimeMath = SimplePrimeMath.getInstance();
|
|
|
|
/**
|
|
* Cached outputs for `isPrime` invocations.
|
|
*
|
|
* @private
|
|
*/
|
|
private isPrimeCache: Map<number, boolean> = new Map();
|
|
/**
|
|
* Cached outputs for `decomposeCache` invocations.
|
|
*
|
|
* @private
|
|
*/
|
|
private decomposeCache: Map<number, number[]> = new Map();
|
|
|
|
|
|
/**
|
|
* Constructs a new instance.
|
|
*
|
|
* @private
|
|
*/
|
|
private constructor() {
|
|
// Do nothing
|
|
}
|
|
|
|
/**
|
|
* Returns the singleton instance.
|
|
*
|
|
* @return the singleton instance
|
|
*/
|
|
public static getInstance(): CachedPrimeMath {
|
|
if (!CachedPrimeMath.instance)
|
|
CachedPrimeMath.instance = new CachedPrimeMath();
|
|
|
|
return CachedPrimeMath.instance;
|
|
}
|
|
|
|
|
|
public isPrime(n: number): boolean {
|
|
return CachedPrimeMath.invokeCached(n, CachedPrimeMath.simple.isPrime, this.isPrimeCache);
|
|
}
|
|
|
|
public decompose(n: number): number[] {
|
|
return CachedPrimeMath.invokeCached(n, CachedPrimeMath.simple.decompose, this.decomposeCache);
|
|
}
|
|
|
|
|
|
/**
|
|
* Invokes `fun` given `input`, and stores its output in `cache`.
|
|
*
|
|
* If `fun(input)` has previously been invoked, then the previous output is returned immediately.
|
|
*
|
|
* @param input the input to pass to `fun`
|
|
* @param fun the function to invoke with `input`
|
|
* @param cache the cache to store the output of `fun` in, and to read previous outputs from
|
|
* @return the output of `fun(input)`, or the previously stored output if there is one
|
|
* @private
|
|
*/
|
|
private static invokeCached<IN, OUT>(input: IN, fun: (_: IN) => OUT, cache: Map<IN, OUT>): OUT {
|
|
if (cache.has(input))
|
|
return cache.get(input)!!;
|
|
|
|
const output = fun(input);
|
|
cache.set(input, output);
|
|
return output;
|
|
}
|
|
}
|