Skip to content

Commit

Permalink
Tweak residual handling for zero
Browse files Browse the repository at this point in the history
Add the option to return the prime limit as an ordinal
  • Loading branch information
frostburn committed Nov 17, 2023
1 parent ef629b4 commit a7fb2e7
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 9 deletions.
16 changes: 13 additions & 3 deletions src/__tests__/monzo.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,16 @@ describe('Fraction to monzo converter', () => {
});

it('throws for zero', () => {
expect(() => toMonzoAndResidual(0, 1)).toThrow();
const [monzo, residual] = toMonzoAndResidual(0, 1);
expect(residual.equals(0)).toBeTruthy();
expect(monzo).toHaveLength(1);
expect(new Fraction(2).pow(monzo[0]).mul(residual).equals(0)).toBeTruthy();
});

it('throws for zero (no vector part)', () => {
expect(() => toMonzoAndResidual(0, 0)).toThrow();
const [monzo, residual] = toMonzoAndResidual(0, 0);
expect(residual.equals(0)).toBeTruthy();
expect(monzo).toHaveLength(0);
});
});

Expand All @@ -106,12 +111,16 @@ describe('Prime limit calculator', () => {
expect(primeLimit(45)).toBe(5);
});

it('knows that the prime limit of 21 has ordinal #4', () => {
expect(primeLimit(21, true)).toBe(4);
});

it('knows that the prime limit of 11859211/11859210 is 19', () => {
expect(primeLimit('11859211/11859210')).toBe(19);
});

it('returns infinity when going beyond the given limit', () => {
expect(primeLimit(123456789, 97)).toBe(Infinity);
expect(primeLimit(123456789, false, 97)).toBe(Infinity);
});

it('stays within the given limit', () => {
Expand All @@ -120,6 +129,7 @@ describe('Prime limit calculator', () => {
Math.ceil(Math.random() * 10000),
Math.ceil(Math.random() * 10000)
),
false,
97
);
if (limit < Infinity) {
Expand Down
20 changes: 14 additions & 6 deletions src/monzo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ export function toMonzoAndResidual(
const denominator = n.d;

if (!n.n) {
throw new Error('Cannot convert zero to monzo');
return [Array(numberOfComponents).fill(0), new Fraction(0)];
}

let nProbe = 1;
Expand Down Expand Up @@ -261,19 +261,27 @@ export function monzoToFraction(monzo: Iterable<number>) {
/**
* Calculate the prime limit of an integer or a fraction.
* @param n Integer or fraction to calculate prime limit for.
* @param asOrdinal Return the limit as an ordinal instead of a prime. (1 is #0, 2 is #1, 3 is #2, 5 is #3, etc.)
* @param maxLimit Maximum prime limit to consider.
* @returns The largest prime in the factorization of the input. `Infinity` if above the maximum limit. `NaN` if not applicable.
*/
export function primeLimit(n: FractionValue, maxLimit = 7919): number {
export function primeLimit(
n: FractionValue,
asOrdinal = false,
maxLimit = 7919
): number {
if (typeof n !== 'number') {
n = new Fraction(n);
return Math.max(primeLimit(n.n, maxLimit), primeLimit(n.d, maxLimit));
return Math.max(
primeLimit(n.n, asOrdinal, maxLimit),
primeLimit(n.d, asOrdinal, maxLimit)
);
}
if (n < 1 || Math.round(n) !== n) {
return NaN;
}
if (n === 1) {
return 1;
return asOrdinal ? 0 : 1;
}

// Accumulate increasingly complex factors into the probe
Expand All @@ -285,7 +293,7 @@ export function primeLimit(n: FractionValue, maxLimit = 7919): number {
// Bit-magic for small 2-limit
probe = (n ^ (n - 1)) & n;
if (n === probe) {
return 2;
return asOrdinal ? 1 : 2;
}
limitIndex = 1;
}
Expand All @@ -300,7 +308,7 @@ export function primeLimit(n: FractionValue, maxLimit = 7919): number {
return Infinity;
}
} else if (n === probe) {
return PRIMES[limitIndex];
return asOrdinal ? limitIndex + 1 : PRIMES[limitIndex];
}
}
}

0 comments on commit a7fb2e7

Please sign in to comment.