Simplify and document code
This commit is contained in:
parent
21098de5d6
commit
e6e3a172a5
217
index.html
217
index.html
|
@ -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>
|
||||||
|
|
Loading…
Reference in New Issue