Simplify and document code

This commit is contained in:
Florine W. Dekker 2020-03-14 12:20:16 +01:00
parent 21098de5d6
commit e6e3a172a5
Signed by: FWDekker
GPG Key ID: B1B567AF58D6EE0F
1 changed files with 111 additions and 106 deletions

View File

@ -64,7 +64,7 @@
<a href="https://git.fwdekker.com/FWDekker/simplify-fractions/src/branch/master/LICENSE">MIT License</a>. <a href="https://git.fwdekker.com/FWDekker/simplify-fractions/src/branch/master/LICENSE">MIT License</a>.
Source code available on <a href="https://git.fwdekker.com/FWDekker/simplify-fractions/">git</a>. Source code available on <a href="https://git.fwdekker.com/FWDekker/simplify-fractions/">git</a>.
<div style="float: right;">v1.0.11</div> <div style="float: right;">v1.0.12</div>
</section> </section>
</footer> </footer>
</main> </main>
@ -75,22 +75,35 @@
integrity="sha256-nvJJv9wWKEm88qvoQl9ekL2J+k/RWIsaSScxxlsrv8k=" crossorigin="anonymous"></script> integrity="sha256-nvJJv9wWKEm88qvoQl9ekL2J+k/RWIsaSScxxlsrv8k=" crossorigin="anonymous"></script>
<script src="https://static.fwdekker.com/js/common.js" crossorigin="anonymous"></script> <script src="https://static.fwdekker.com/js/common.js" crossorigin="anonymous"></script>
<script> <script>
/**
* A fraction that can be simplified.
*/
class Fraction { class Fraction {
constructor(numerator, denominator) { constructor(numerator, denominator) {
if (!isInt(numerator) || !isInt(denominator)) if (!isInt(numerator) || !isInt(denominator))
throw new Error("Numerator and denominator must be integer-like."); throw new Error("Numerator and denominator must be integer-like.");
this.sign = xor(numerator < 0, denominator < 0) ? -1 : 1; this.sign = numerator < 0 !== denominator < 0 ? -1 : 1;
this.numerator = Math.abs(+numerator); this.numerator = Math.abs(+numerator);
this.denominator = Math.abs(+denominator); 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
*/
simplify() { simplify() {
const common = gcd(this.numerator, this.denominator); const common = gcd(this.numerator, this.denominator);
return new Fraction(this.sign * this.numerator / common, this.denominator / common); return new Fraction(this.sign * this.numerator / common, this.denominator / common);
} }
/**
* Returns the MathJax string representation of this fraction.
*
* @returns {string} the MathJax string representation of this fraction
*/
toString() { toString() {
let frac = `\\frac{${this.numerator}}{${this.denominator}}`; let frac = `\\frac{${this.numerator}}{${this.denominator}}`;
if (this.sign === -1) if (this.sign === -1)
@ -99,6 +112,12 @@
return frac; return frac;
} }
/**
* Returns the MathJax string representation of this fraction, or of the numerator if the denominator is 1.
*
* @returns {string} the MathJax string representation of this fraction, or of the numerator if the denominator
* is 1.
*/
toReducedString() { toReducedString() {
if (this.numerator === 0) if (this.numerator === 0)
return "0"; return "0";
@ -116,125 +135,111 @@
} }
} }
const xor = (a, b) => (a || b) && !(a && b);
// noinspection EqualityComparisonWithCoercionJS // 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); const isInt = n => n == parseInt(n);
const factorize = n => { /**
if (!isInt(n)) * Returns the greatest common divisor of `a` and `b`.
return undefined; *
* @param a {number} the first operand
n = +n; * @param b {number} the second operand
* @returns {number} the greatest common divisor of `a` and `b`
if (n <= 0) */
return undefined;
if (n === 1)
return [1];
const factors = [];
while (n % 2 === 0) {
factors.push(2);
n /= 2;
}
while (n % 3 === 0) {
factors.push(3);
n /= 3;
}
for (let i = 5; i <= n; i += ((i - 1) % 6 === 0) ? 4 : 2) {
while (n % i === 0) {
factors.push(i);
n /= i;
}
}
return factors;
};
const defactorize = factors => {
let accumulator = 1;
for (let i = 0; i < factors.length; i++)
accumulator *= factors[i];
return accumulator;
};
const gcd = (a, b) => { const gcd = (a, b) => {
const aFactors = factorize(a); if (b > a) {
const bFactors = factorize(b); const temp = a;
a = b;
b = temp;
}
const overlap = []; while (true) {
if (b === 0) return a;
a %= b;
for (let aIndex = 0; aIndex < aFactors.length; aIndex++) { if (a === 0) return b;
const bIndex = bFactors.indexOf(aFactors[aIndex]); b %= a;
if (bIndex >= 0) { }
overlap.push(aFactors[aIndex]); };
bFactors.splice(bIndex, 1);
doAfterLoad(() => {
const numeratorInput = $("#numerator");
const denominatorInput = $("#denominator");
const submitButton = $("#submit");
const outputField = $("#out");
/**
* Returns `undefined` if the inputs are valid, or a tuple consisting of the invalid element and an explanation
* of its invalidity otherwise.
*
* @returns {(HTMLElement|string)[]|undefined} `undefined` if the inputs are valid, or a tuple consisting of the
* invalid element and an explanation of its invalidity otherwise
*/
const validateInputs = () => {
let numerator = numeratorInput.value;
let denominator = denominatorInput.value;
if (numerator === "")
return [numeratorInput, "Numerator must not be empty."];
if (denominator === "")
return [denominatorInput, "Denominator must not be empty."];
if (!isInt(numerator))
return [numeratorInput, "Numerator must be an integer."];
if (!isInt(denominator))
return [denominatorInput, "Denominator must be an integer."];
if (+denominator === 0)
return [denominatorInput, "Denominator must not be 0."];
return undefined;
};
/**
* Reads the inputs and tries to output the simplified fraction.
*
* Errors are handled and displayed where appropriate.
*/
const outputSimplifiedFraction = () => {
const validationInfo = validateInputs();
if (validationInfo !== undefined) {
validationInfo[0].select();
outputField.innerText = validationInfo[1];
return;
} }
}
return defactorize(overlap); const fraction = new Fraction(numeratorInput.value, denominatorInput.value);
}; outputField.innerText = "$$" + fraction.toString() + " = " + fraction.simplify().toReducedString() + "$$";
MathJax.Hub.Queue(["Typeset", MathJax.Hub]);
};
const numeratorInput = $("#numerator"); submitButton.onclick = () => outputSimplifiedFraction();
const denominatorInput = $("#denominator");
const submitButton = $("#submit");
const outputField = $("#out");
const validateInputs = () => { numeratorInput.addEventListener("keydown", event => {
let numerator = numeratorInput.value; if (event.key === "Enter") {
let denominator = denominatorInput.value; if (denominatorInput.value === "") {
denominatorInput.select();
} else {
outputSimplifiedFraction();
numeratorInput.select();
}
}
});
if (numerator === "") denominatorInput.addEventListener("keydown", event => {
return [numeratorInput, "Numerator must not be empty."]; if (event.key === "Enter") {
if (denominator === "")
return [denominatorInput, "Denominator must not be empty."];
if (!isInt(numerator))
return [numeratorInput, "Numerator must be an integer."];
if (!isInt(denominator))
return [denominatorInput, "Denominator must be an integer."];
if (+denominator === 0)
return [denominatorInput, "Denominator must not be 0."];
return undefined;
};
const outputSimplifiedFraction = () => {
const validationInfo = validateInputs();
if (validationInfo !== undefined) {
validationInfo[0].select();
outputField.innerText = validationInfo[1];
return;
}
const fraction = new Fraction(numeratorInput.value, denominatorInput.value);
outputField.innerText = "$$" + fraction.toString() + " = " + fraction.simplify().toReducedString() + "$$";
MathJax.Hub.Queue(["Typeset", MathJax.Hub]);
};
submitButton.onclick = () => outputSimplifiedFraction();
numeratorInput.addEventListener("keydown", event => {
if (event.key === "Enter") {
if (denominatorInput.value === "") {
denominatorInput.select();
} else {
outputSimplifiedFraction(); outputSimplifiedFraction();
numeratorInput.select(); denominatorInput.select();
} }
} });
});
denominatorInput.addEventListener("keydown", event => {
if (event.key === "Enter") {
outputSimplifiedFraction();
denominatorInput.select();
}
}); });
</script> </script>
</body> </body>