@stringsync/vexml
Version:
MusicXML to Vexflow
141 lines (140 loc) • 5.45 kB
JavaScript
"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;