166 lines
4.7 KiB
JavaScript
166 lines
4.7 KiB
JavaScript
// noinspection JSUnresolvedVariable
|
|
const {$, doAfterLoad} = window.fwdekker;
|
|
// noinspection JSUnresolvedVariable
|
|
const {clearFormValidity, showInputInvalid} = window.fwdekker.validation;
|
|
|
|
import katex from "katex";
|
|
import "katex/dist/katex.min.css";
|
|
|
|
|
|
// noinspection EqualityComparisonWithCoercionJS
|
|
/**
|
|
* Returns `true` if and only if `n` is an integer.
|
|
*
|
|
* @param n {*} the value to check for integerness
|
|
* @returns {boolean} `true` if and only if `n` is an integer
|
|
*/
|
|
const isInt = n => n == parseInt(n);
|
|
|
|
/**
|
|
* Returns the greatest common divisor of `a` and `b`.
|
|
*
|
|
* @param a {number} the first operand
|
|
* @param b {number} the second operand
|
|
* @returns {number} the greatest common divisor of `a` and `b`
|
|
*/
|
|
const gcd = (a, b) => {
|
|
if (b > a) {
|
|
const temp = a;
|
|
a = b;
|
|
b = temp;
|
|
}
|
|
|
|
while (true) {
|
|
if (b === 0) return a;
|
|
a %= b;
|
|
|
|
if (a === 0) return b;
|
|
b %= a;
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* A fraction that can be simplified.
|
|
*/
|
|
class Fraction {
|
|
constructor(numerator, denominator) {
|
|
if (!isInt(numerator) || !isInt(denominator))
|
|
throw new Error("Numerator and denominator must be integer-like.");
|
|
|
|
this.sign = numerator < 0 !== denominator < 0 ? -1 : 1;
|
|
this.numerator = Math.abs(+numerator);
|
|
this.denominator = Math.abs(+denominator);
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns a new fraction such that the gcd of the numerator and denominator is 1.
|
|
*
|
|
* @returns {Fraction} a new fraction such that the gcd of the numerator and denominator is 1
|
|
*/
|
|
simplifyGcd() {
|
|
const common = gcd(this.numerator, this.denominator);
|
|
return new Fraction(this.sign * this.numerator / common, this.denominator / common);
|
|
}
|
|
|
|
/**
|
|
* Returns the LaTeX string representation of this fraction.
|
|
*
|
|
* Unlike `#toReducedString`, this method never simplifies the output to an integer.
|
|
*
|
|
* @returns {string} the LaTeX string representation of this fraction
|
|
*/
|
|
toString() {
|
|
let frac = `\\frac{${this.numerator}}{${this.denominator}}`;
|
|
if (this.sign === -1)
|
|
frac = `-${frac}`;
|
|
|
|
return frac;
|
|
}
|
|
|
|
/**
|
|
* Returns the LaTeX string representation of this fraction, or of the numerator if the denominator is 1.
|
|
*
|
|
* @returns {string} the string representation of the reduced form of this fraction
|
|
*/
|
|
toReducedString() {
|
|
if (this.numerator === 0)
|
|
return "0";
|
|
|
|
let frac;
|
|
if (this.denominator === 1)
|
|
frac = `${this.numerator}`;
|
|
else
|
|
frac = `\\frac{${this.numerator}}{${this.denominator}}`;
|
|
|
|
if (this.sign === -1)
|
|
frac = `-${frac}`;
|
|
|
|
return frac;
|
|
}
|
|
}
|
|
|
|
|
|
doAfterLoad(() => {
|
|
const numeratorInput = $("#numerator");
|
|
const denominatorInput = $("#denominator");
|
|
const outputField = $("#out");
|
|
|
|
|
|
/**
|
|
* Returns `true` if and only if both inputs are valid, showing the validity results to the user immediately.
|
|
*
|
|
* @param numerator {string} the numerator value
|
|
* @param denominator {string} the denominator value
|
|
* @returns {boolean} `true` if and only if both inputs are valid
|
|
*/
|
|
const validateInputs = (numerator, denominator) => {
|
|
clearFormValidity($("#inputs"));
|
|
let isValid = true;
|
|
|
|
if (numerator.trim() === "") {
|
|
isValid = false;
|
|
} else if (!isInt(numerator)) {
|
|
isValid = false;
|
|
showInputInvalid(numeratorInput, "Enter an integer.");
|
|
}
|
|
|
|
if (denominator.trim() === "") {
|
|
isValid = false;
|
|
} else if (!isInt(denominator)) {
|
|
isValid = false;
|
|
showInputInvalid(denominatorInput, "Enter an integer.");
|
|
} else if (+denominator === 0) {
|
|
isValid = false;
|
|
showInputInvalid(denominatorInput, "Enter a number other than zero.");
|
|
}
|
|
|
|
return isValid;
|
|
};
|
|
|
|
/**
|
|
* Reads the inputs and tries to output the simplified fraction.
|
|
*/
|
|
const outputSimplifiedFraction = () => {
|
|
let numerator = numeratorInput.value;
|
|
let denominator = denominatorInput.value;
|
|
|
|
if (!validateInputs(numerator, denominator)) return;
|
|
|
|
const fraction = new Fraction(numeratorInput.value, denominatorInput.value);
|
|
outputField.innerHTML = katex.renderToString(
|
|
fraction.toString() + " = " + fraction.simplifyGcd().toReducedString(),
|
|
{
|
|
displayMode: true,
|
|
throwOnError: false
|
|
}
|
|
);
|
|
};
|
|
|
|
|
|
numeratorInput.addEventListener("input", () => outputSimplifiedFraction());
|
|
denominatorInput.addEventListener("input", () => outputSimplifiedFraction());
|
|
outputSimplifiedFraction();
|
|
});
|