simplify-fractions/index.html

239 lines
7.7 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="author" content="Felix W. Dekker" />
<meta name="application-name" content="Dice probabilities" />
<meta name="description" content="Calculates the probability of throwing a value given a combination of dice." />
<meta name="theme-color" content="#0033cc" />
<title>Simplify fractions | FWDekker</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css"
integrity="sha256-l85OmPOjvil/SOvVt3HnSSjzF1TUMyT9eV0c2BzEGzU=" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/milligram/1.3.0/milligram.min.css"
integrity="sha256-Ro/wP8uUi8LR71kwIdilf78atpu8bTEwrK5ZotZo+Zc=" crossorigin="anonymous" />
<style>
body {
margin-top: 50px;
margin-bottom: 50px;
}
</style>
</head>
<body>
<main class="wrapper">
<!-- Header -->
<header class="header">
<div class="container">
<h1>Simplify fractions</h1>
<blockquote>
<p><em>Simplify a fraction to eliminate common factors.</em></p>
</blockquote>
</div>
</header>
<!-- Input -->
<section class="container">
<form>
<fieldset>
<label for="numerator">Numerator</label>
<input type="number" id="numerator" min="-2147483647" max="2147483647" autofocus />
<label for="denominator">Denominator</label>
<input type="number" id="denominator" min="-2147483647" max="2147483647" />
<button type="button" id="submit">Calculator</button>
</fieldset>
</form>
</section>
<!-- Output -->
<section class="container">
<span id="out"></span>
</section>
<!-- Footer -->
<footer class="footer">
<section class="container">
Made by <a href="https://fwdekker.com/">Felix W. Dekker</a>. Licensed under the <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>.
</section>
</footer>
</main>
<!-- Scripts -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-MML-AM_CHTML"
integrity="sha256-nvJJv9wWKEm88qvoQl9ekL2J+k/RWIsaSScxxlsrv8k=" crossorigin="anonymous"></script>
<script>
class Fraction {
constructor(numerator, denominator) {
if (!isInt(numerator) || !isInt(denominator))
throw new Error("Numerator and denominator must be integer-like.");
this.sign = xor(numerator < 0, denominator < 0) ? -1 : 1;
this.numerator = Math.abs(+numerator);
this.denominator = Math.abs(+denominator);
}
simplify() {
const common = gcd(this.numerator, this.denominator);
return new Fraction(this.sign * this.numerator / common, this.denominator / common);
}
toString() {
let frac = `\\frac{${this.numerator}}{${this.denominator}}`;
if (this.sign === -1)
frac = `-${frac}`;
return frac;
}
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;
}
}
const xor = (a, b) => (a || b) && !(a && b);
// noinspection EqualityComparisonWithCoercionJS
const isInt = n => n == parseInt(n);
const factorize = n => {
if (!isInt(n))
return undefined;
n = +n;
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 aFactors = factorize(a);
const bFactors = factorize(b);
const overlap = [];
for (let aIndex = 0; aIndex < aFactors.length; aIndex++) {
const bIndex = bFactors.indexOf(aFactors[aIndex]);
if (bIndex >= 0) {
overlap.push(aFactors[aIndex]);
bFactors.splice(bIndex, 1);
}
}
return defactorize(overlap);
};
const $ = query => document.querySelector(query);
const numeratorInput = $("#numerator");
const denominatorInput = $("#denominator");
const submitButton = $("#submit");
const outputField = $("#out");
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;
};
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();
numeratorInput.select();
}
}
});
denominatorInput.addEventListener("keydown", event => {
if (event.key === "Enter") {
outputSimplifiedFraction();
denominatorInput.select();
}
});
</script>
</body>
</html>