From a43f5a599247331746af6fd918a781a946c1beec Mon Sep 17 00:00:00 2001 From: Lumi Pakkanen Date: Thu, 25 Apr 2024 08:43:46 +0300 Subject: [PATCH] WIP: Implement a method for accurately measuring the sizes of small commas --- src/__tests__/index.spec.ts | 21 +++++++++++++++++++++ src/index.ts | 27 +++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/src/__tests__/index.spec.ts b/src/__tests__/index.spec.ts index 1ebb389..5f81f47 100644 --- a/src/__tests__/index.spec.ts +++ b/src/__tests__/index.spec.ts @@ -19,6 +19,7 @@ import { iteratedEuclid, norm, valueToCents, + monzoToCents, } from '../index'; describe('Array equality tester', () => { @@ -382,3 +383,23 @@ describe('Constant structure checker with a margin of equivalence', () => { expect(hasMarginConstantStructure([1199, 1200], 2)).toBe(false); }); }); + +describe('Monzo size measure', () => { + it('calculates the size of the perfect fourth accurately', () => { + expect(monzoToCents([2, -1])).toBeCloseTo(498.0449991346125, 12); + }); + + it('calculates the size of the neutrino accurately', () => { + expect(monzoToCents([1889, -2145, 138, 424])).toBeCloseTo( + 1.6361187484440885e-10, + 24 + ); + }); + + it('calculates the size of the demiquartervice comma accurately', () => { + expect(monzoToCents([-3, 2, -1, -1, 0, 0, -1, 0, 2])).toBeCloseTo( + 0.3636664332386927, + 14 + ); + }); +}); diff --git a/src/index.ts b/src/index.ts index 2e914f8..4f6f9ad 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,6 @@ import {Fraction, mmod} from './fraction'; +import {Monzo, monzoToBigInt} from './monzo'; +import {PRIME_CENTS} from './primes'; export * from './fraction'; export * from './primes'; @@ -449,3 +451,28 @@ export function hasMarginConstantStructure( } return true; } + +const NATS_TO_CENTS = 1200 / Math.LN2; +const IEEE_LIMIT = 2n ** 1024n; + +export function monzoToCents(monzo: Monzo) { + // TODO: dotPrecise + const result = dot(monzo, PRIME_CENTS); + if (Math.abs(result) > 10) { + return result; + } + for (const component of monzo) { + if (!Number.isInteger(component)) { + return result; + } + } + // TODO: monzoToBigNumeratorDenominator + const p = monzoToBigInt(monzo.map(c => Math.max(0, c))); + let q = monzoToBigInt(monzo.map(c => Math.max(0, -c))); + let n = p - q; + while (n > IEEE_LIMIT || q > IEEE_LIMIT) { + n >>= 1n; + q >>= 1n; + } + return Math.log1p(Number(n) / Number(q)) * NATS_TO_CENTS; +}