UNPKG

@stringsync/vexml

Version:

MusicXML to Vexflow

141 lines (140 loc) 5.45 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Fraction = void 0; /** * Represents a mathematical fraction with a numerator and a denominator. * * The `Fraction` class provides methods for basic arithmetic operations such as addition, subtraction, multiplication, * and division, as well as utility methods for comparing, simplifying, and converting fractions. */ class Fraction { numerator; denominator; constructor(numerator, denominator = 1) { this.numerator = numerator; this.denominator = denominator; if (this.denominator === 0) { throw new Error('denominator cannot be zero'); } if (!Number.isInteger(this.numerator)) { throw new Error('numerator must be an integer, try Fraction.fromDecimal instead'); } if (!Number.isInteger(this.denominator)) { throw new Error('denominator must be an integer, try Fraction.fromDecimal instead'); } } /** Creates a 0 fraction. */ static zero() { return new Fraction(0); } /** Creates the maximum fraction. */ static max() { return new Fraction(Number.MAX_SAFE_INTEGER); } /** * Creates a fraction from a decimal number. * * NOTE: Integers work too. */ static fromDecimal(decimal) { const len = (decimal.toString().split('.')[1] || '').length; const denominator = 10 ** len; return new Fraction(decimal * denominator, denominator).simplify(); } /** Creates a fraction from a mixed fraction. */ static fromMixed(mixed) { const denominator = mixed.remainder.denominator; return new Fraction(mixed.whole * denominator, denominator).add(mixed.remainder); } /** Creates a fraction from something that appears to be a fraction. */ static fromFractionLike(fractionLike) { return new Fraction(fractionLike.numerator, fractionLike.denominator); } /** Returns the sum of the fractions. */ static sum(...fractions) { return fractions.reduce((acc, f) => acc.add(f), Fraction.zero()); } /** Returns the decimal of the fraction. */ toDecimal() { return this.numerator / this.denominator; } /** Returns the fraction in mixed form. */ toMixed() { const whole = Math.floor(this.numerator / this.denominator); const remainder = new Fraction(this.numerator % this.denominator, this.denominator); return { whole, remainder }; } /** Returns the sign of the fraction. */ sign() { const decimal = this.toDecimal(); if (decimal === 0) { return '/'; } else if (decimal > 0) { return '+'; } else { return '-'; } } /** Returns whether the other fraction is equal to this fraction. */ isEquivalent(value) { return this.numerator * value.denominator === value.numerator * this.denominator; } /** Returns whether the other fraction has the exact same numerator and denominator. */ isEqual(value) { return this.numerator === value.numerator && this.denominator === value.denominator; } /** Returns whether this fraction is less than the other. */ isLessThan(value) { return this.numerator * value.denominator < value.numerator * this.denominator; } /** Returns whether this fraction is less than or _equivalent_ to the other. */ isLessThanOrEqualTo(value) { return this.isLessThan(value) || this.isEquivalent(value); } /** Returns whether this fraction is greater than the other. */ isGreaterThan(value) { return this.numerator * value.denominator > value.numerator * this.denominator; } /** Returns whether this fraction is greater than or _equivalent_ to the other. */ isGreaterThanOrEqualTo(value) { return this.isGreaterThan(value) || this.isEquivalent(value); } /** Reduces the numerator and denominator to its lowest common factor. */ simplify() { const gcd = this.gcd(this.numerator, this.denominator); return new Fraction(this.numerator / gcd, this.denominator / gcd); } /** Returns the sum as a new fraction. */ add(value) { const commonDenominator = this.denominator * value.denominator; const newNumerator = this.numerator * value.denominator + value.numerator * this.denominator; return new Fraction(newNumerator, commonDenominator); } /** Returns the difference as a new fraction. */ subtract(value) { return this.add(new Fraction(-value.numerator, value.denominator)); } /** Returns the product as a new fraction. */ multiply(value) { return new Fraction(this.numerator * value.numerator, this.denominator * value.denominator); } /** Returns the quotient as a new fraction. */ divide(value) { return this.multiply(value.reciprocate()); } /** Returns the reciprocal a new fraction. */ reciprocate() { return new Fraction(this.denominator, this.numerator); } /** Returns a fraction-like POJO representing the fraction. */ toFractionLike() { return { numerator: this.numerator, denominator: this.denominator }; } /** Returns the greatest common denominator. */ gcd(a, b) { return b ? this.gcd(b, a % b) : a; } } exports.Fraction = Fraction;