Skip to content

Commit

Permalink
Rely on Fraction throwing behaviour to deal with unsafe fractions
Browse files Browse the repository at this point in the history
  • Loading branch information
frostburn committed Dec 21, 2023
1 parent 7736927 commit c04505f
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 39 deletions.
13 changes: 13 additions & 0 deletions src/__tests__/scale.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,19 @@ describe('Scale', () => {
expect(scale.size).toBe(14);
});

it('can generate rank 2 (unsafe fractions)', () => {
const generator = new Interval(
ExtendedMonzo.fromFraction('11/7', 5),
'ratio'
);
const octave = new Interval(ExtendedMonzo.fromFraction(2, 5), 'ratio');
const scale = Scale.fromRank2(generator, octave, 100, 0);
// Expect not to raise.
for (let i = 0; i < 100; ++i) {
scale.getName(i);
}
});

it('can generate harmonic series segment', () => {
const scale = Scale.fromHarmonicSeries(4, 8, 4);
expect(
Expand Down
49 changes: 22 additions & 27 deletions src/interval.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {ExtendedMonzo} from './monzo';
import {fractionToString, isSafeFraction} from './utils';
import {fractionToString} from './utils';
import {Fraction} from 'xen-dev-utils';

/** Interval formatting options. */
Expand Down Expand Up @@ -502,18 +502,19 @@ export class Interval {
maybeFraction.cents = 0;

if (maybeFraction.isFractional()) {
const fraction = maybeFraction.toFraction();
if (isSafeFraction(fraction)) {
try {
const fraction = maybeFraction.toFraction();
return (
fractionToString(
fraction,
options.preferredNumerator,
options.preferredDenominator
) + this.centsString(true)
);
}
if (options.forbidMonzo) {
return cents();
} catch {
if (options.forbidMonzo) {
return cents();
}
}
}
}
Expand All @@ -523,18 +524,19 @@ export class Interval {
maybeEt.cents = 0;

if (maybeEt.isEqualTemperament()) {
const [fractionOfEquave, equave] = maybeEt.toEqualTemperament();
if (isSafeFraction(fractionOfEquave) && isSafeFraction(equave)) {
try {
maybeEt.toEqualTemperament();
const et = new Interval(
maybeEt,
'equal temperament',
'dummy',
this.options
);
return et.equalTemperamentString() + this.centsString(true);
}
if (options.forbidMonzo) {
return cents();
} catch {
if (options.forbidMonzo) {
return cents();
}
}
}
}
Expand All @@ -547,22 +549,15 @@ export class Interval {
if (options.forbidComposite) {
return cents();
}
if (isSafeFraction(this.monzo.residual)) {
if (this.type === 'monzo') {
console.warn('Failed to represent monzo. Displaying residue.');
}
return (
this.monzoString() +
' + ' +
fractionToString(
this.monzo.residual,
options.preferredNumerator,
options.preferredDenominator
)
);
} else {
return cents();
}
return (
this.monzoString() +
' + ' +
fractionToString(
this.monzo.residual,
options.preferredNumerator,
options.preferredDenominator
)
);
}
if (options.forbidComposite) {
return cents();
Expand Down
1 change: 1 addition & 0 deletions src/scale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ export class Scale {
period = period.mergeOptions(options);
if (
period.type === 'ratio' &&
period.monzo.isFractional() &&
period.monzo.toFraction().mod(genEquave).equals(0)
) {
period.type = 'equal temperament';
Expand Down
12 changes: 0 additions & 12 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,5 @@
import {Fraction} from 'xen-dev-utils';

/**
* Check if the fraction exactly representable as a `Number`.
* @param fraction Input fraction.
* @returns `true` if both numerator, numerator + 1, denominator and denominator + 1 are exactly representable.
*/
export function isSafeFraction(fraction: Fraction) {
return (
fraction.n <= Number.MAX_SAFE_INTEGER &&
fraction.d <= Number.MAX_SAFE_INTEGER
);
}

/**
* Convert fraction to a string.
* @param fraction Input fraction.
Expand Down

0 comments on commit c04505f

Please sign in to comment.