UNPKG

moment-of-symmetry

Version:

Moment of Symmetry (MOS) musical scale generation and analysis for Javascript

667 lines 28.1 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __exportStar = (this && this.__exportStar) || function(m, exports) { for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.generatorRanges = exports.allForEdo = exports.anyForEdo = exports.makeEdoMap = exports.daughterMos = exports.mosScaleInfo = exports.parentMos = exports.splitMosPattern = exports.modeInfo = exports.mosModes = exports.mosWithDaughter = exports.mosWithParent = exports.mos = exports.stepString = exports.brightGeneratorMonzo = exports.euclid = void 0; const hardness_1 = require("./hardness"); const names_1 = require("./names"); const helpers_1 = require("./helpers"); const xen_dev_utils_1 = require("xen-dev-utils"); __exportStar(require("./hardness"), exports); __exportStar(require("./names"), exports); __exportStar(require("./generator-ratio"), exports); __exportStar(require("./info"), exports); __exportStar(require("./notation"), exports); /** * Produce an array of booleans that is mixed as evenly as possible. * @param numberOfTrue Number of true elements * @param numberOfFalse Number of false elements * @returns The array of evenly mixed booleans */ function euclid(numberOfTrue, numberOfFalse) { return (0, helpers_1.bjorklund)(numberOfTrue, numberOfFalse, true, false); } exports.euclid = euclid; /** * Obtain the bright generator of the MOS scale expressed as multipliers of the size of the large and small steps. * @param numberOfLargeSteps Number of large steps in the MOS pattern. * @param numberOfSmallSteps Number of small steps in the MOS pattern. * @returns An array of [number of large steps in the bright generator, number of small steps in the bright generator]. */ function brightGeneratorMonzo(numberOfLargeSteps, numberOfSmallSteps) { const numPeriods = (0, xen_dev_utils_1.gcd)(numberOfLargeSteps, numberOfSmallSteps); return [ ...(0, helpers_1.mosGeneratorMonzo)(numberOfLargeSteps / numPeriods, numberOfSmallSteps / numPeriods), ]; } exports.brightGeneratorMonzo = brightGeneratorMonzo; function getDown(options, period, numPeriods) { let down = 0; if (options.up !== undefined) { down = period * numPeriods - numPeriods - options.up; if (options.down !== undefined && down !== options.down) { throw new Error('Incompatible up and down with the scale size'); } } else if (options.down !== undefined) { down = options.down; } if (down < 0) { throw new Error('Down must not be negative'); } if (down >= period * numPeriods) { throw new Error('Up must not be negative'); } if (down % numPeriods !== 0) { throw new Error('Up/down must be divisible by the number of periods'); } return down; } /** * Obtain an abstract string like 'LLsLLLs' corresponding to the mode specified. * @param numberOfLargeSteps Number of large steps in the MOS pattern. * @param numberOfSmallSteps Number of small steps in the MOS pattern. * @param options Options for brightness of the scale. * @returns String with the given number of 'L' and 's' characters in the specified mode. */ function stepString(numberOfLargeSteps, numberOfSmallSteps, options) { if (!numberOfLargeSteps) { return 's'.repeat(numberOfSmallSteps); } if (!numberOfSmallSteps) { return 'L'.repeat(numberOfLargeSteps); } options !== null && options !== void 0 ? options : (options = {}); const brightest = (0, helpers_1.bjorklundStr)(numberOfLargeSteps, numberOfSmallSteps); let mode = brightest; const modes = []; while (true) { modes.push(mode); mode = mode.slice(1) + mode[0]; if (mode === brightest) { break; } } // Lexicographic order corresponds to brightness. modes.sort(); const numPeriods = (0, xen_dev_utils_1.gcd)(numberOfLargeSteps, numberOfSmallSteps); const period = (numberOfLargeSteps + numberOfSmallSteps) / numPeriods; const brightGeneratorsDown = getDown(options, period, numPeriods); return modes[brightGeneratorsDown / numPeriods]; } exports.stepString = stepString; /** * Generate MOS pattern as a subset of an EDO. * @param numberOfLargeSteps Number of large steps in the MOS pattern. * @param numberOfSmallSteps Number of small steps in the MOS pattern. * @param options Options for sizes of the steps and brightness of the scale. * @returns An array of integers representing the EDO subset. The 0 degree is not included, but the final degree representing the size of the EDO is. */ function mos(numberOfLargeSteps, numberOfSmallSteps, options) { var _a, _b; const abstract = stepString(numberOfLargeSteps, numberOfSmallSteps, options); const sizeOfLargeStep = (_a = options === null || options === void 0 ? void 0 : options.sizeOfLargeStep) !== null && _a !== void 0 ? _a : 2; const sizeOfSmallStep = (_b = options === null || options === void 0 ? void 0 : options.sizeOfSmallStep) !== null && _b !== void 0 ? _b : 1; let step = 0; const result = []; for (const character of abstract) { if (character === 'L') { step += sizeOfLargeStep; } else { step += sizeOfSmallStep; } result.push(step); } return result; } exports.mos = mos; /** * Generate MOS pattern as a subset of an EDO with parent MOS relationship indicated. * @param numberOfLargeSteps Number of large steps in the MOS pattern. * @param numberOfSmallSteps Number of small steps in the MOS pattern. * @param options Options for sizes of the steps, brightness of the scale and flat/sharp relationship. * @returns A map of integers representing the EDO subset to booleans indicating if the scale degree belongs to the parent MOS or not. * The 0 degree is not included, but the final degree representing the size of the EDO is. */ function mosWithParent(numberOfLargeSteps, numberOfSmallSteps, options) { var _a, _b; options !== null && options !== void 0 ? options : (options = {}); const numPeriods = (0, xen_dev_utils_1.gcd)(numberOfLargeSteps, numberOfSmallSteps); const period = (numberOfLargeSteps + numberOfSmallSteps) / numPeriods; const sizeOfLargeStep = (_a = options.sizeOfLargeStep) !== null && _a !== void 0 ? _a : 2; const sizeOfSmallStep = (_b = options.sizeOfSmallStep) !== null && _b !== void 0 ? _b : 1; const brightGeneratorsDown = getDown(options, period, numPeriods); const l = numberOfLargeSteps / numPeriods; const s = numberOfSmallSteps / numPeriods; const d = brightGeneratorsDown / numPeriods; const p = l * sizeOfLargeStep + s * sizeOfSmallStep; const gMonzo = (0, helpers_1.mosGeneratorMonzo)(l, s); const g = gMonzo[0] * sizeOfLargeStep + gMonzo[1] * sizeOfSmallStep; const parentPeriod = Math.max(l, s); const base = new Map(); for (let i = 0; i < period; ++i) { let isParent; if (options.accidentals === 'flat') { isParent = period - i <= parentPeriod; } else { isParent = i < parentPeriod; } base.set((0, xen_dev_utils_1.mmod)((i - d) * g, p), isParent); } const edoDegrees = [...base.keys()].sort((a, b) => a - b); let result = new Map(); for (let i = 0; i < numPeriods; ++i) { edoDegrees.forEach(degree => { result = result.set(degree + i * p, base.get(degree)); }); } const rootIsParent = result.get(0); result.delete(0); result.set(numPeriods * p, rootIsParent); return result; } exports.mosWithParent = mosWithParent; /** * Generate the daughter MOS pattern as a subset of an EDO with parent MOS relationship indicated. * @param numberOfLargeSteps Number of large steps in the parent MOS. * @param numberOfSmallSteps Number of small steps in the parent MOS. * @param options Options for sizes of the steps, brightness of the scale and flat/sharp relationship. * @returns A map of integers representing the EDO subset to booleans indicating if the scale degree belongs to the parent MOS or not. * The 0 degree is not included, but the final degree representing the size of the EDO is. */ function mosWithDaughter(numberOfLargeSteps, numberOfSmallSteps, options) { var _a, _b, _c; options !== null && options !== void 0 ? options : (options = {}); const numPeriods = (0, xen_dev_utils_1.gcd)(numberOfLargeSteps, numberOfSmallSteps); const period = (numberOfLargeSteps + numberOfSmallSteps) / numPeriods; const sizeOfLargeStep = (_a = options.sizeOfLargeStep) !== null && _a !== void 0 ? _a : 2; const sizeOfSmallStep = (_b = options.sizeOfSmallStep) !== null && _b !== void 0 ? _b : 1; const brightGeneratorsDown = getDown(options, period, numPeriods); const l = numberOfLargeSteps / numPeriods; const s = numberOfSmallSteps / numPeriods; const d = brightGeneratorsDown / numPeriods; const p = l * sizeOfLargeStep + s * sizeOfSmallStep; const gMonzo = (0, helpers_1.mosGeneratorMonzo)(l, s); const g = gMonzo[0] * sizeOfLargeStep + gMonzo[1] * sizeOfSmallStep; const daughterPeriod = 2 * l + s; const base = new Map(); for (let i = 0; i < period; ++i) { base.set((0, xen_dev_utils_1.mmod)((i - d) * g, p), 'parent'); } const accs = (_c = options.accidentals) !== null && _c !== void 0 ? _c : 'sharp'; if (accs === 'flat' || (accs === 'both' && sizeOfLargeStep > 2)) { for (let i = period - daughterPeriod; i < 0; ++i) { base.set((0, xen_dev_utils_1.mmod)((i - d) * g, p), 'flat'); } } if (accs === 'sharp' || accs === 'both') { const acc = sizeOfLargeStep === 2 ? 'both' : 'sharp'; for (let i = period; i < daughterPeriod; ++i) { base.set((0, xen_dev_utils_1.mmod)((i - d) * g, p), acc); } } const edoDegrees = [...base.keys()].sort((a, b) => a - b); let result = new Map(); for (let i = 0; i < numPeriods; ++i) { edoDegrees.forEach(degree => { result = result.set(degree + i * p, base.get(degree)); }); } const rootIsParent = result.get(0); result.delete(0); result.set(numPeriods * p, rootIsParent); return result; } exports.mosWithDaughter = mosWithDaughter; /** * Information about the modes of a MOS scale. * @param numberOfLargeSteps Number of large steps in the MOS pattern. * @param numberOfSmallSteps Number of small steps in the MOS pattern. * @param extraNames If true adds extra mode names in parenthesis such as Ionian (Major). * @returns An array of mode information. */ function mosModes(numberOfLargeSteps, numberOfSmallSteps, extraNames = false) { const numberOfPeriods = (0, xen_dev_utils_1.gcd)(numberOfLargeSteps, numberOfSmallSteps); const period = (numberOfLargeSteps + numberOfSmallSteps) / numberOfPeriods; const l = numberOfLargeSteps / numberOfPeriods; const s = numberOfSmallSteps / numberOfPeriods; const p = l * 2 + s; const gMonzo = (0, helpers_1.mosGeneratorMonzo)(l, s); const g = gMonzo[0] * 2 + gMonzo[1]; const result = []; for (let u = 0; u < period; ++u) { const base = []; for (let i = 0; i < period; ++i) { base.push((0, xen_dev_utils_1.mmod)((u - i) * g, p)); } base.sort((a, b) => a - b); let scale = base; for (let i = 1; i < numberOfPeriods; ++i) { scale = scale.concat(base.map(s => s + i * p)); } scale.push(numberOfPeriods * p); let pattern = ''; for (let i = 1; i < scale.length; ++i) { if (scale[i] - scale[i - 1] === 2) { pattern += 'L'; } else { pattern += 's'; } } const modeName_ = (0, names_1.modeName)(pattern, extraNames); let udp = `${u * numberOfPeriods}|${(period - 1 - u) * numberOfPeriods}`; if (numberOfPeriods > 1) { udp += `(${numberOfPeriods})`; } result.push({ period, numberOfPeriods, udp, mode: pattern, modeName: modeName_, }); } return result; } exports.mosModes = mosModes; /** * Information about a mode of a MOS scale. * @param numberOfLargeSteps Number of large steps in the MOS pattern. * @param numberOfSmallSteps Number of small steps in the MOS pattern. * @param options Options for brightness of the scale and for adding extra names like Ionian (Major). * @returns An array of mode information. */ function modeInfo(numberOfLargeSteps, numberOfSmallSteps, options) { options !== null && options !== void 0 ? options : (options = {}); const numberOfPeriods = (0, xen_dev_utils_1.gcd)(numberOfLargeSteps, numberOfSmallSteps); const period = (numberOfLargeSteps + numberOfSmallSteps) / numberOfPeriods; const brightGeneratorsDown = getDown(options, period, numberOfPeriods); const scale = mos(numberOfLargeSteps, numberOfSmallSteps, options); scale.unshift(0); let pattern = ''; for (let i = 1; i < scale.length; ++i) { if (scale[i] - scale[i - 1] === 2) { pattern += 'L'; } else { pattern += 's'; } } const modeName_ = (0, names_1.modeName)(pattern, options.extraNames); const brightGeneratorsUp = (period - 1) * numberOfPeriods - brightGeneratorsDown; let udp = `${brightGeneratorsUp}|${brightGeneratorsDown}`; if (numberOfPeriods > 1) { udp += `(${numberOfPeriods})`; } return { period, numberOfPeriods, udp, mode: pattern, modeName: modeName_, }; } exports.modeInfo = modeInfo; /** * Split a string like "5L 2s" into [5, 2]. * @param mosPattern MOS pattern such as "5L 2s". * @returns A pair of intergers representing the number of large and small steps. */ function splitMosPattern(mosPattern) { const [l, s] = mosPattern.split('L'); const numberOfLargeSteps = parseInt(l.trim(), 10); const numberOfSmallSteps = parseInt(s.split('s')[0].trim(), 10); return [numberOfLargeSteps, numberOfSmallSteps]; } exports.splitMosPattern = splitMosPattern; function parentMos(patternOrLarge, numberOfSmallSteps) { let numberOfLargeSteps; if (typeof patternOrLarge === 'string') { [numberOfLargeSteps, numberOfSmallSteps] = splitMosPattern(patternOrLarge); } else { numberOfLargeSteps = patternOrLarge; if (typeof numberOfSmallSteps !== 'number') { throw new Error('Number of small steps must be given'); } } // Calculate the parent's size. const size = Math.max(numberOfLargeSteps, numberOfSmallSteps); numberOfLargeSteps = Math.min(numberOfLargeSteps, numberOfSmallSteps); numberOfSmallSteps = size - numberOfLargeSteps; const mosPattern = `${numberOfLargeSteps}L ${numberOfSmallSteps}s`; const info = { size, numberOfLargeSteps, numberOfSmallSteps, mosPattern, }; Object.assign(info, (0, names_1.tamnamsInfo)(mosPattern)); return info; } exports.parentMos = parentMos; function mosScaleInfo(numberOfLargeSteps, numberOfSmallSteps, sizeOfLargeStep = 2, sizeOfSmallStep = 1) { const mosPattern = `${numberOfLargeSteps}L ${numberOfSmallSteps}s`; const numberOfPeriods = (0, xen_dev_utils_1.gcd)(numberOfLargeSteps, numberOfSmallSteps); const edo = numberOfLargeSteps * sizeOfLargeStep + numberOfSmallSteps * sizeOfSmallStep; const period = edo / numberOfPeriods; const periodMonzo = [ numberOfLargeSteps / numberOfPeriods, numberOfSmallSteps / numberOfPeriods, ]; const brightGeneratorMonzo = (0, helpers_1.mosGeneratorMonzo)(...periodMonzo); const brightGenerator = (0, xen_dev_utils_1.dot)(brightGeneratorMonzo, [ sizeOfLargeStep, sizeOfSmallStep, ]); const info = { numberOfLargeSteps, numberOfSmallSteps, sizeOfLargeStep, sizeOfSmallStep, edo, numberOfPeriods, period, brightGenerator, darkGenerator: period - brightGenerator, periodMonzo, brightGeneratorMonzo, mosPattern, hardness: (0, hardness_1.getHardness)(sizeOfLargeStep, sizeOfSmallStep), }; Object.assign(info, (0, names_1.tamnamsInfo)(mosPattern)); return info; } exports.mosScaleInfo = mosScaleInfo; function daughterMos(numberOfLargeSteps, numberOfSmallSteps, sizeOfLargeStep, sizeOfSmallStep) { const size = numberOfLargeSteps + numberOfSmallSteps; if (sizeOfLargeStep >= 2 * sizeOfSmallStep) { numberOfSmallSteps = size; sizeOfLargeStep -= sizeOfSmallStep; } else { numberOfSmallSteps = numberOfLargeSteps; numberOfLargeSteps = size; const temp = sizeOfSmallStep; sizeOfSmallStep = sizeOfLargeStep - sizeOfSmallStep; sizeOfLargeStep = temp; } return mosScaleInfo(numberOfLargeSteps, numberOfSmallSteps, sizeOfLargeStep, sizeOfSmallStep); } exports.daughterMos = daughterMos; // One entry in the EDO map for each hardness class const STEP_SIZES = [ [2, 1], // basic [3, 2], // soft [3, 1], // hard [4, 3], // supersoft [4, 1], // superhard [5, 3], // semisoft [5, 2], // semihard [5, 4], // ultrasoft [5, 1], // ultrahard [7, 5], // parasoft [7, 4], // minisoft [7, 3], // minihard [7, 2], // parahard [8, 5], // quasisoft [8, 3], // quasihard ]; /** * Construct a mapping from EDO size to supported MOS scales. * @param maxSize Maximum size of the MOS patterns to include. * @returns A mapping from EDO size to an array of information about the supported MOS scales. */ function makeEdoMap(maxSize = 12) { const result = new Map(); STEP_SIZES.forEach(([sizeOfLargeStep, sizeOfSmallStep]) => { const hardness = (0, hardness_1.getHardness)(sizeOfLargeStep, sizeOfSmallStep); for (let size = 2; size <= maxSize; ++size) { for (let numberOfLargeSteps = 1; numberOfLargeSteps < size; ++numberOfLargeSteps) { const numberOfSmallSteps = size - numberOfLargeSteps; const mosPattern = `${numberOfLargeSteps}L ${numberOfSmallSteps}s`; const edo = numberOfLargeSteps * sizeOfLargeStep + numberOfSmallSteps * sizeOfSmallStep; const info = { mosPattern, numberOfLargeSteps, numberOfSmallSteps, sizeOfLargeStep, sizeOfSmallStep, hardness, }; Object.assign(info, (0, names_1.tamnamsInfo)(mosPattern)); const infos = result.get(edo) || []; infos.push(info); result.set(edo, infos); } } }); return result; } exports.makeEdoMap = makeEdoMap; const STEP_COUNTS = [ [5, 2], // diatonic [4, 3], // smitonic [3, 4], // mosh [2, 5], // antidiatonic [3, 5], // sensoid [5, 3], // oneirotonic [6, 2], // echinoid [2, 6], // antiechinoid [4, 2], // lemon [2, 4], // antilemon [5, 1], // machinoid [2, 3], // pentic [3, 2], // antipentic [1, 4], // machinoid (subset) [1, 3], // manic [1, 2], // happy [2, 1], // grumpy [1, 1], // trivial ]; /** * Find a MOS scale supported by the given EDO. * @param edo Size of the EDO. * @returns Information about the supported MOS scale. */ function anyForEdo(edo) { if (edo <= 1) { throw new Error('Minimum size is 2'); } if (edo === 2) { return { mosPattern: '1L 1s', numberOfLargeSteps: 1, numberOfSmallSteps: 1, sizeOfLargeStep: 1, sizeOfSmallStep: 1, edo, numberOfPeriods: 1, period: edo, brightGenerator: 1, darkGenerator: 1, periodMonzo: [1, 1], brightGeneratorMonzo: [1, 0], hardness: 'equalized', name: 'trivial', subset: false, }; } for (let i = 0; i < STEP_COUNTS.length; ++i) { const [numberOfLargeSteps, numberOfSmallSteps] = STEP_COUNTS[i]; let sizeOfLargeStep = 2; while (true) { const largePart = sizeOfLargeStep * numberOfLargeSteps; const smallPart = edo - largePart; if (smallPart <= 0) { break; } if (smallPart % numberOfSmallSteps === 0) { const sizeOfSmallStep = smallPart / numberOfSmallSteps; if (sizeOfLargeStep <= 3 * sizeOfSmallStep && 3 * sizeOfSmallStep <= 2 * sizeOfLargeStep) { const mosPattern = `${numberOfLargeSteps}L ${numberOfSmallSteps}s`; const hardness = (0, hardness_1.getHardness)(sizeOfLargeStep, sizeOfSmallStep); const numberOfPeriods = (0, xen_dev_utils_1.gcd)(numberOfLargeSteps, numberOfSmallSteps); const period = edo / numberOfPeriods; const periodMonzo = [ numberOfLargeSteps / numberOfPeriods, numberOfSmallSteps / numberOfPeriods, ]; const brightGeneratorMonzo = (0, helpers_1.mosGeneratorMonzo)(...periodMonzo); const brightGenerator = (0, xen_dev_utils_1.dot)(brightGeneratorMonzo, [ sizeOfLargeStep, sizeOfSmallStep, ]); const info = { mosPattern, numberOfLargeSteps, numberOfSmallSteps, sizeOfLargeStep, sizeOfSmallStep, edo, numberOfPeriods, period, brightGenerator, darkGenerator: period - brightGenerator, periodMonzo, brightGeneratorMonzo, hardness, }; Object.assign(info, (0, names_1.tamnamsInfo)(mosPattern)); return info; } } sizeOfLargeStep++; } } throw new Error(`Failed to find MOS pattern for ${edo}`); } exports.anyForEdo = anyForEdo; /** * Find all MOS scales supported by the given EDO within the given constraints. * @param edo Size of the EDO. * @param minSize Minimum size of a MOS scale in the result. * @param maxSize Maximum size of a MOS scale in the result. * @param maxHardness Maximum hardness of the step ratio L/s. * @returns Array of information about the supported MOS scales. */ function allForEdo(edo, minSize = 2, maxSize, maxHardness) { if (maxSize === undefined) { maxSize = edo; } if (minSize < 2) { throw new Error('Minimum size must be at least 2'); } if (maxSize > edo) { throw new Error(`Maximum size must be smaller or equal to edo (${edo})`); } const result = []; for (let numberOfLargeSteps = 1; numberOfLargeSteps < maxSize; ++numberOfLargeSteps) { for (let numberOfSmallSteps = Math.max(1, minSize - numberOfLargeSteps); numberOfSmallSteps <= maxSize - numberOfLargeSteps; numberOfSmallSteps++) { for (const hardness of (0, xen_dev_utils_1.fareyInterior)(edo - numberOfSmallSteps)) { const { n: sizeOfSmallStep, d: sizeOfLargeStep } = hardness; if (maxHardness && sizeOfLargeStep > sizeOfSmallStep * maxHardness) { continue; } if (numberOfLargeSteps * sizeOfLargeStep + numberOfSmallSteps * sizeOfSmallStep === edo) { const mosPattern = `${numberOfLargeSteps}L ${numberOfSmallSteps}s`; const hardness = (0, hardness_1.getHardness)(sizeOfLargeStep, sizeOfSmallStep); const numberOfPeriods = (0, xen_dev_utils_1.gcd)(numberOfLargeSteps, numberOfSmallSteps); const period = edo / numberOfPeriods; const periodMonzo = [ numberOfLargeSteps / numberOfPeriods, numberOfSmallSteps / numberOfPeriods, ]; const brightGeneratorMonzo = (0, helpers_1.mosGeneratorMonzo)(...periodMonzo); const brightGenerator = (0, xen_dev_utils_1.dot)(brightGeneratorMonzo, [ sizeOfLargeStep, sizeOfSmallStep, ]); const info = { mosPattern, numberOfLargeSteps, numberOfSmallSteps, sizeOfLargeStep, sizeOfSmallStep, hardness, edo, numberOfPeriods, period, brightGenerator, darkGenerator: period - brightGenerator, periodMonzo, brightGeneratorMonzo, }; Object.assign(info, (0, names_1.tamnamsInfo)(mosPattern)); result.push(info); } } } } return result; } exports.allForEdo = allForEdo; /** * Find the ranges of all (equally tempered) fractions of the equave that span MOS scales. * @param size Size of the scales to consider. * @param includeMultiPeriods Include scales that split the equave into multiple periods. * @returns Information about the ranges of generator that span MOS. Ranges are grouped by period and otherwise sorted in ascending order. */ function generatorRanges(size, includeMultiPeriods = false) { const result = []; for (let numberOfLargeSteps = 1; numberOfLargeSteps < size; numberOfLargeSteps++) { const numberOfSmallSteps = size - numberOfLargeSteps; const numPeriods = (0, xen_dev_utils_1.gcd)(numberOfLargeSteps, numberOfSmallSteps); if (!includeMultiPeriods && numPeriods !== 1) { continue; } const period = new xen_dev_utils_1.Fraction(1, numPeriods); const monzo = (0, helpers_1.mosGeneratorMonzo)(numberOfLargeSteps / numPeriods, numberOfSmallSteps / numPeriods); // Collapsed endpoint let lowerBound = new xen_dev_utils_1.Fraction(monzo[0], numberOfLargeSteps); // Equalized endpoint let upperBound = new xen_dev_utils_1.Fraction(monzo[0] + monzo[1], size); if (lowerBound.compare(upperBound) > 0) { [lowerBound, upperBound] = [upperBound, lowerBound]; } result.push({ period, lowerBound, upperBound, numberOfLargeSteps, numberOfSmallSteps, bright: true, }); result.push({ period, lowerBound: period.sub(upperBound), upperBound: period.sub(lowerBound), numberOfLargeSteps, numberOfSmallSteps, bright: false, }); } result.sort((a, b) => a.period.compare(b.period) || a.lowerBound.compare(b.lowerBound)); return result; } exports.generatorRanges = generatorRanges; //# sourceMappingURL=index.js.map