Index: node_modules/fraction.js/CHANGELOG.md
===================================================================
--- node_modules/fraction.js/CHANGELOG.md	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
+++ node_modules/fraction.js/CHANGELOG.md	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
@@ -0,0 +1,38 @@
+# CHANGELOG
+
+v5.2.2
+    - Improved documentation and removed unecessary check
+
+v5.2.1:
+  - 2bb7b05: Added negative sign check
+
+v5.2:
+  - 6f9d124: Implemented log and improved simplify
+  - b773e7a: Added named export to TS definition
+  - 70304f9: Fixed merge conflict
+  - 3b940d3: Implemented other comparing functions
+  - 10acdfc: Update README.md
+  - ba41d00: Update README.md
+  - 73ded97: Update README.md
+  - acabc39: Fixed param parsing
+
+v5.0.5:
+  - 2c9d4c2: Improved roundTo() and param parser
+
+v5.0.4:
+  - 39e61e7: Fixed bignum param passing
+
+v5.0.3:
+  - 7d9a3ec: Upgraded bundler for code quality
+
+v5.0.2:
+  - c64b1d6: fixed esm export
+
+v5.0.1:
+  - e440f9c: Fixed CJS export
+  - 9bbdd29: Fixed CJS export
+
+v5.0.0:
+  - ac7cd06: Fixed readme
+  - 33cc9e5: Added crude build
+  - 1adcc76: Release breaking v5.0. Fraction.js now builds on BigInt. The API stays the same as v4, except that the object attributes `n`, `d`, and `s`, are not Number but BigInt and may break code that directly accesses these attributes.
Index: node_modules/fraction.js/LICENSE
===================================================================
--- node_modules/fraction.js/LICENSE	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
+++ node_modules/fraction.js/LICENSE	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2025 Robert Eisele
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
Index: node_modules/fraction.js/README.md
===================================================================
--- node_modules/fraction.js/README.md	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
+++ node_modules/fraction.js/README.md	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
@@ -0,0 +1,520 @@
+# Fraction.js - ℚ in JavaScript
+
+[![NPM Package](https://img.shields.io/npm/v/fraction.js.svg?style=flat)](https://npmjs.org/package/fraction.js "View this project on npm")
+[![MIT license](http://img.shields.io/badge/license-MIT-brightgreen.svg)](http://opensource.org/licenses/MIT)
+
+Do you find the limitations of floating-point arithmetic frustrating, especially when rational and irrational numbers like π or √2 are stored within the same finite precision? This can lead to avoidable inaccuracies such as:
+
+```javascript
+1 / 98 * 98 // Results in 0.9999999999999999
+```
+
+For applications requiring higher precision or where working with fractions is preferable, consider incorporating *Fraction.js* into your project.
+
+The library effectively addresses precision issues, as demonstrated below:
+
+```javascript
+Fraction(1).div(98).mul(98) // Returns 1
+```
+
+*Fraction.js* uses a `BigInt` representation for both the numerator and denominator, ensuring minimal performance overhead while maximizing accuracy. Its design is optimized for precision, making it an ideal choice as a foundational library for other math tools, such as [Polynomial.js](https://github.com/rawify/Polynomial.js) and [Math.js](https://github.com/josdejong/mathjs).
+
+## Convert Decimal to Fraction
+
+One of the core features of *Fraction.js* is its ability to seamlessly convert decimal numbers into fractions.
+
+```javascript
+let x = new Fraction(1.88);
+let res = x.toFraction(true); // Returns "1 22/25" as a string
+```
+
+This is particularly useful when you need precise fraction representations instead of dealing with the limitations of floating-point arithmetic. What if you allow some error tolerance?
+
+```javascript
+let x = new Fraction(0.33333);
+let res = x.simplify(0.001) // Error < 0.001
+       .toFraction(); // Returns "1/3" as a string
+```
+
+## Precision
+
+As native `BigInt` support in JavaScript becomes more common, libraries like *Fraction.js* use it to handle calculations with higher precision. This improves the speed and accuracy of math operations with large numbers, providing a better solution for tasks that need more precision than floating-point numbers can offer.
+
+## Examples / Motivation
+
+A simple example of using *Fraction.js* might look like this:
+
+```javascript
+var f = new Fraction("9.4'31'"); // 9.4313131313131...
+f.mul([-4, 3]).mod("4.'8'"); // 4.88888888888888...
+```
+
+The result can then be displayed as:
+
+```javascript
+console.log(f.toFraction()); // -4154 / 1485
+```
+
+Additionally, you can access the internal attributes of the fraction, such as the sign (s), numerator (n), and denominator (d). Keep in mind that these values are stored as `BigInt`:
+
+```javascript
+Number(f.s) * Number(f.n) / Number(f.d) = -1 * 4154 / 1485 = -2.797306...
+```
+
+If you attempted to calculate this manually using floating-point arithmetic, you'd get something like:
+
+```javascript
+(9.4313131 * (-4 / 3)) % 4.888888 = -2.797308133...
+```
+
+While the result is reasonably close, it’s not as accurate as the fraction-based approach that *Fraction.js* provides, especially when dealing with repeating decimals or complex operations. This highlights the value of precision that the library brings.
+
+### Laplace Probability
+
+Here's a straightforward example of using *Fraction.js* to calculate probabilities. Let's determine the probability of rolling a specific outcome on a fair die:
+
+- **P({3})**: The probability of rolling a 3.
+- **P({1, 4})**: The probability of rolling either 1 or 4.
+- **P({2, 4, 6})**: The probability of rolling 2, 4, or 6.
+
+#### P({3}):
+
+```javascript
+var p = new Fraction([3].length, 6).toString(); // "0.1(6)"
+```
+
+#### P({1, 4}):
+
+```javascript
+var p = new Fraction([1, 4].length, 6).toString(); // "0.(3)"
+```
+
+#### P({2, 4, 6}):
+
+```javascript
+var p = new Fraction([2, 4, 6].length, 6).toString(); // "0.5"
+```
+
+### Convert degrees/minutes/seconds to precise rational representation:
+
+57+45/60+17/3600
+
+```javascript
+var deg = 57; // 57°
+var min = 45; // 45 Minutes
+var sec = 17; // 17 Seconds
+
+new Fraction(deg).add(min, 60).add(sec, 3600).toString() // -> 57.7547(2)
+```
+
+
+### Rational approximation of irrational numbers
+
+To approximate a number like *sqrt(5) - 2* with a numerator and denominator, you can reformat the equation as follows: *pow(n / d + 2, 2) = 5*.
+
+Then the following algorithm will generate the rational number besides the binary representation.
+
+```javascript
+var x = "/", s = "";
+
+var a = new Fraction(0),
+    b = new Fraction(1);
+for (var n = 0; n <= 10; n++) {
+
+  var c = a.add(b).div(2);
+
+  console.log(n + "\t" + a + "\t" + b + "\t" + c + "\t" + x);
+
+  if (c.add(2).pow(2).valueOf() < 5) {
+    a = c;
+    x = "1";
+  } else {
+    b = c;
+    x = "0";
+  }
+  s+= x;
+}
+console.log(s)
+```
+
+The result is
+
+```
+n   a[n]        b[n]        c[n]            x[n]
+0   0/1         1/1         1/2             /
+1   0/1         1/2         1/4             0
+2   0/1         1/4         1/8             0
+3   1/8         1/4         3/16            1
+4   3/16        1/4         7/32            1
+5   7/32        1/4         15/64           1
+6   15/64       1/4         31/128          1
+7   15/64       31/128      61/256          0
+8   15/64       61/256      121/512         0
+9   15/64       121/512     241/1024        0
+10  241/1024    121/512     483/2048        1
+```
+
+Thus the approximation after 11 iterations of the bisection method is *483 / 2048* and the binary representation is 0.00111100011 (see [WolframAlpha](http://www.wolframalpha.com/input/?i=sqrt%285%29-2+binary))
+
+I published another example on how to approximate PI with fraction.js on my [blog](https://raw.org/article/rational-numbers-in-javascript/) (Still not the best idea to approximate irrational numbers, but it illustrates the capabilities of Fraction.js perfectly).
+
+
+### Get the exact fractional part of a number
+
+```javascript
+var f = new Fraction("-6.(3416)");
+console.log(f.mod(1).abs().toFraction()); // = 3416/9999
+```
+
+### Mathematical correct modulo
+
+The behaviour on negative congruences is different to most modulo implementations in computer science. Even the *mod()* function of Fraction.js behaves in the typical way. To solve the problem of having the mathematical correct modulo with Fraction.js you could come up with this:
+
+```javascript
+var a = -1;
+var b = 10.99;
+
+console.log(new Fraction(a)
+  .mod(b)); // Not correct, usual Modulo
+
+console.log(new Fraction(a)
+  .mod(b).add(b).mod(b)); // Correct! Mathematical Modulo
+```
+
+fmod() imprecision circumvented
+---
+It turns out that Fraction.js outperforms almost any fmod() implementation, including JavaScript itself, [php.js](http://phpjs.org/functions/fmod/), C++, Python, Java and even Wolframalpha due to the fact that numbers like 0.05, 0.1, ... are infinite decimal in base 2.
+
+The equation *fmod(4.55, 0.05)* gives *0.04999999999999957*, wolframalpha says *1/20*. The correct answer should be **zero**, as 0.05 divides 4.55 without any remainder.
+
+
+## Parser
+
+Any function (see below) as well as the constructor of the *Fraction* class parses its input and reduce it to the smallest term.
+
+You can pass either Arrays, Objects, Integers, Doubles or Strings.
+
+### Arrays / Objects
+
+```javascript
+new Fraction(numerator, denominator);
+new Fraction([numerator, denominator]);
+new Fraction({n: numerator, d: denominator});
+```
+
+### Integers
+
+```javascript
+new Fraction(123);
+```
+
+### Doubles
+
+```javascript
+new Fraction(55.4);
+```
+
+**Note:** If you pass a double as it is, Fraction.js will perform a number analysis based on Farey Sequences. If you concern performance, cache Fraction.js objects and pass arrays/objects.
+
+The method is really precise, but too large exact numbers, like 1234567.9991829 will result in a wrong approximation. If you want to keep the number as it is, convert it to a string, as the string parser will not perform any further observations. If you have problems with the approximation, in the file `examples/approx.js` is a different approximation algorithm, which might work better in some more specific use-cases.
+
+
+### Strings
+
+```javascript
+new Fraction("123.45");
+new Fraction("123/45"); // A rational number represented as two decimals, separated by a slash
+new Fraction("123:45"); // A rational number represented as two decimals, separated by a colon
+new Fraction("4 123/45"); // A rational number represented as a whole number and a fraction
+new Fraction("123.'456'"); // Note the quotes, see below!
+new Fraction("123.(456)"); // Note the brackets, see below!
+new Fraction("123.45'6'"); // Note the quotes, see below!
+new Fraction("123.45(6)"); // Note the brackets, see below!
+```
+
+### Two arguments
+
+```javascript
+new Fraction(3, 2); // 3/2 = 1.5
+```
+
+### Repeating decimal places
+
+*Fraction.js* can easily handle repeating decimal places. For example *1/3* is *0.3333...*. There is only one repeating digit. As you can see in the examples above, you can pass a number like *1/3* as "0.'3'" or "0.(3)", which are synonym. There are no tests to parse something like 0.166666666 to 1/6! If you really want to handle this number, wrap around brackets on your own with the function below for example: 0.1(66666666)
+
+Assume you want to divide 123.32 / 33.6(567). [WolframAlpha](http://www.wolframalpha.com/input/?i=123.32+%2F+%2812453%2F370%29) states that you'll get a period of 1776 digits. *Fraction.js* comes to the same result. Give it a try:
+
+```javascript
+var f = new Fraction("123.32");
+console.log("Bam: " + f.div("33.6(567)"));
+```
+
+To automatically make a number like "0.123123123" to something more Fraction.js friendly like "0.(123)", I hacked this little brute force algorithm in a 10 minutes. Improvements are welcome...
+
+```javascript
+function formatDecimal(str) {
+
+  var comma, pre, offset, pad, times, repeat;
+
+  if (-1 === (comma = str.indexOf(".")))
+    return str;
+
+  pre = str.substr(0, comma + 1);
+  str = str.substr(comma + 1);
+
+  for (var i = 0; i < str.length; i++) {
+
+    offset = str.substr(0, i);
+
+    for (var j = 0; j < 5; j++) {
+
+      pad = str.substr(i, j + 1);
+
+      times = Math.ceil((str.length - offset.length) / pad.length);
+
+      repeat = new Array(times + 1).join(pad); // Silly String.repeat hack
+
+      if (0 === (offset + repeat).indexOf(str)) {
+        return pre + offset + "(" + pad + ")";
+      }
+    }
+  }
+  return null;
+}
+
+var f, x = formatDecimal("13.0123123123"); // = 13.0(123)
+if (x !== null) {
+  f = new Fraction(x);
+}
+```
+
+## Attributes
+
+
+The Fraction object allows direct access to the numerator, denominator and sign attributes. It is ensured that only the sign-attribute holds sign information so that a sign comparison is only necessary against this attribute.
+
+```javascript
+var f = new Fraction('-1/2');
+console.log(f.n); // Numerator: 1
+console.log(f.d); // Denominator: 2
+console.log(f.s); // Sign: -1
+```
+
+
+## Functions
+
+### Fraction abs()
+
+Returns the actual number without any sign information
+
+### Fraction neg()
+
+Returns the actual number with flipped sign in order to get the additive inverse
+
+### Fraction add(n)
+
+Returns the sum of the actual number and the parameter n
+
+### Fraction sub(n)
+
+Returns the difference of the actual number and the parameter n
+
+### Fraction mul(n)
+
+Returns the product of the actual number and the parameter n
+
+### Fraction div(n)
+
+Returns the quotient of the actual number and the parameter n
+
+### Fraction pow(exp)
+
+Returns the power of the actual number, raised to an possible rational exponent. If the result becomes non-rational the function returns `null`.
+
+### Fraction log(base)
+
+Returns the logarithm of the actual number to a given rational base. If the result becomes non-rational the function returns `null`.
+
+### Fraction mod(n)
+
+Returns the modulus (rest of the division) of the actual object and n (this % n). It's a much more precise [fmod()](#fmod-impreciseness-circumvented) if you like. Please note that *mod()* is just like the modulo operator of most programming languages. If you want a mathematical correct modulo, see [here](#mathematical-correct-modulo).
+
+### Fraction mod()
+
+Returns the modulus (rest of the division) of the actual object (numerator mod denominator)
+
+### Fraction gcd(n)
+
+Returns the fractional greatest common divisor
+
+### Fraction lcm(n)
+
+Returns the fractional least common multiple
+
+### Fraction ceil([places=0-16])
+
+Returns the ceiling of a rational number with Math.ceil
+
+### Fraction floor([places=0-16])
+
+Returns the floor of a rational number with Math.floor
+
+### Fraction round([places=0-16])
+
+Returns the rational number rounded with Math.round
+
+### Fraction roundTo(multiple)
+
+Rounds a fraction to the closest multiple of another fraction. 
+
+### Fraction inverse()
+
+Returns the multiplicative inverse of the actual number (n / d becomes d / n) in order to get the reciprocal
+
+### Fraction simplify([eps=0.001])
+
+Simplifies the rational number under a certain error threshold. Ex. `0.333` will be `1/3` with `eps=0.001`
+
+### boolean equals(n)
+
+Check if two rational numbers are equal
+
+### boolean lt(n)
+
+Check if this rational number is less than another
+
+### boolean lte(n)
+
+Check if this rational number is less than or equal another
+
+### boolean gt(n)
+
+Check if this rational number is greater than another
+
+### boolean gte(n)
+
+Check if this rational number is greater than or equal another
+
+### int compare(n)
+
+Compare two numbers.
+```
+result < 0: n is greater than actual number
+result > 0: n is smaller than actual number
+result = 0: n is equal to the actual number
+```
+
+### boolean divisible(n)
+
+Check if two numbers are divisible (n divides this)
+
+### double valueOf()
+
+Returns a decimal representation of the fraction
+
+### String toString([decimalPlaces=15])
+
+Generates an exact string representation of the given object. For repeating decimal places, digits within repeating cycles are enclosed in parentheses, e.g., `1/3 = "0.(3)"`. For other numbers, the string will include up to the specified `decimalPlaces` significant digits, including any trailing zeros if truncation occurs. For example, `1/2` will be represented as `"0.5"`, without additional trailing zeros.
+
+**Note:** Since both `valueOf()` and `toString()` are provided, `toString()` will only be invoked implicitly when the object is used in a string context. For instance, when using the plus operator like `"123" + new Fraction`, `valueOf()` will be called first, as JavaScript attempts to combine primitives before concatenating them, with the string type taking precedence. However, `alert(new Fraction)` or `String(new Fraction)` will behave as expected. To ensure specific behavior, explicitly call either `toString()` or `valueOf()`.
+
+### String toLatex(showMixed=false)
+
+Generates an exact LaTeX representation of the actual object. You can see a [live demo](https://raw.org/article/rational-numbers-in-javascript/) on my blog.
+
+The optional boolean parameter indicates if you want to show the a mixed fraction. "1 1/3" instead of "4/3"
+
+### String toFraction(showMixed=false)
+
+Gets a string representation of the fraction
+
+The optional boolean parameter indicates if you want to showa mixed fraction. "1 1/3" instead of "4/3"
+
+### Array toContinued()
+
+Gets an array of the fraction represented as a continued fraction. The first element always contains the whole part.
+
+```javascript
+var f = new Fraction('88/33');
+var c = f.toContinued(); // [2, 1, 2]
+```
+
+### Fraction clone()
+
+Creates a copy of the actual Fraction object
+
+
+## Exceptions
+
+If a really hard error occurs (parsing error, division by zero), *Fraction.js* throws exceptions! Please make sure you handle them correctly.
+
+
+## Installation
+
+You can install `Fraction.js` via npm:
+
+```bash
+npm install fraction.js
+```
+
+Or with yarn:
+
+```bash
+yarn add fraction.js
+```
+
+Alternatively, download or clone the repository:
+
+```bash
+git clone https://github.com/rawify/Fraction.js
+```
+
+## Usage
+
+Include the `fraction.min.js` file in your project:
+
+```html
+<script src="path/to/fraction.min.js"></script>
+<script>
+  var x = new Fraction("13/4");
+</script>
+```
+
+Or in a Node.js project:
+
+```javascript
+const Fraction = require('fraction.js');
+```
+
+or 
+
+```javascript
+import Fraction from 'fraction.js';
+```
+
+
+## Coding Style
+
+As every library I publish, Fraction.js is also built to be as small as possible after compressing it with Google Closure Compiler in advanced mode. Thus the coding style orientates a little on maxing-out the compression rate. Please make sure you keep this style if you plan to extend the library.
+
+## Building the library
+
+After cloning the Git repository run:
+
+```bash
+npm install
+npm run build
+```
+
+## Run a test
+
+Testing the source against the shipped test suite is as easy as
+
+```bash
+npm run test
+```
+
+## Copyright and Licensing
+
+Copyright (c) 2025, [Robert Eisele](https://raw.org/)
+Licensed under the MIT license.
Index: node_modules/fraction.js/dist/fraction.js
===================================================================
--- node_modules/fraction.js/dist/fraction.js	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
+++ node_modules/fraction.js/dist/fraction.js	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
@@ -0,0 +1,1045 @@
+'use strict';
+
+/**
+ *
+ * This class offers the possibility to calculate fractions.
+ * You can pass a fraction in different formats. Either as array, as double, as string or as an integer.
+ *
+ * Array/Object form
+ * [ 0 => <numerator>, 1 => <denominator> ]
+ * { n => <numerator>, d => <denominator> }
+ *
+ * Integer form
+ * - Single integer value as BigInt or Number
+ *
+ * Double form
+ * - Single double value as Number
+ *
+ * String form
+ * 123.456 - a simple double
+ * 123/456 - a string fraction
+ * 123.'456' - a double with repeating decimal places
+ * 123.(456) - synonym
+ * 123.45'6' - a double with repeating last place
+ * 123.45(6) - synonym
+ *
+ * Example:
+ * let f = new Fraction("9.4'31'");
+ * f.mul([-4, 3]).div(4.9);
+ *
+ */
+
+// Set Identity function to downgrade BigInt to Number if needed
+if (typeof BigInt === 'undefined') BigInt = function (n) { if (isNaN(n)) throw new Error(""); return n; };
+
+const C_ZERO = BigInt(0);
+const C_ONE = BigInt(1);
+const C_TWO = BigInt(2);
+const C_THREE = BigInt(3);
+const C_FIVE = BigInt(5);
+const C_TEN = BigInt(10);
+const MAX_INTEGER = BigInt(Number.MAX_SAFE_INTEGER);
+
+// Maximum search depth for cyclic rational numbers. 2000 should be more than enough.
+// Example: 1/7 = 0.(142857) has 6 repeating decimal places.
+// If MAX_CYCLE_LEN gets reduced, long cycles will not be detected and toString() only gets the first 10 digits
+const MAX_CYCLE_LEN = 2000;
+
+// Parsed data to avoid calling "new" all the time
+const P = {
+  "s": C_ONE,
+  "n": C_ZERO,
+  "d": C_ONE
+};
+
+function assign(n, s) {
+
+  try {
+    n = BigInt(n);
+  } catch (e) {
+    throw InvalidParameter();
+  }
+  return n * s;
+}
+
+function ifloor(x) {
+  return typeof x === 'bigint' ? x : Math.floor(x);
+}
+
+// Creates a new Fraction internally without the need of the bulky constructor
+function newFraction(n, d) {
+
+  if (d === C_ZERO) {
+    throw DivisionByZero();
+  }
+
+  const f = Object.create(Fraction.prototype);
+  f["s"] = n < C_ZERO ? -C_ONE : C_ONE;
+
+  n = n < C_ZERO ? -n : n;
+
+  const a = gcd(n, d);
+
+  f["n"] = n / a;
+  f["d"] = d / a;
+  return f;
+}
+
+const FACTORSTEPS = [C_TWO * C_TWO, C_TWO, C_TWO * C_TWO, C_TWO, C_TWO * C_TWO, C_TWO * C_THREE, C_TWO, C_TWO * C_THREE]; // repeats
+function factorize(n) {
+
+  const factors = Object.create(null);
+  if (n <= C_ONE) {
+    factors[n] = C_ONE;
+    return factors;
+  }
+
+  const add = (p) => { factors[p] = (factors[p] || C_ZERO) + C_ONE; };
+
+  while (n % C_TWO === C_ZERO) { add(C_TWO); n /= C_TWO; }
+  while (n % C_THREE === C_ZERO) { add(C_THREE); n /= C_THREE; }
+  while (n % C_FIVE === C_ZERO) { add(C_FIVE); n /= C_FIVE; }
+
+  // 30-wheel trial division: test only residues coprime to 2*3*5
+  // Residue step pattern after 5: 7,11,13,17,19,23,29,31, ...
+  for (let si = 0, p = C_TWO + C_FIVE; p * p <= n;) {
+    while (n % p === C_ZERO) { add(p); n /= p; }
+    p += FACTORSTEPS[si];
+    si = (si + 1) & 7; // fast modulo 8
+  }
+  if (n > C_ONE) add(n);
+  return factors;
+}
+
+const parse = function (p1, p2) {
+
+  let n = C_ZERO, d = C_ONE, s = C_ONE;
+
+  if (p1 === undefined || p1 === null) { // No argument
+    /* void */
+  } else if (p2 !== undefined) { // Two arguments
+
+    if (typeof p1 === "bigint") {
+      n = p1;
+    } else if (isNaN(p1)) {
+      throw InvalidParameter();
+    } else if (p1 % 1 !== 0) {
+      throw NonIntegerParameter();
+    } else {
+      n = BigInt(p1);
+    }
+
+    if (typeof p2 === "bigint") {
+      d = p2;
+    } else if (isNaN(p2)) {
+      throw InvalidParameter();
+    } else if (p2 % 1 !== 0) {
+      throw NonIntegerParameter();
+    } else {
+      d = BigInt(p2);
+    }
+
+    s = n * d;
+
+  } else if (typeof p1 === "object") {
+    if ("d" in p1 && "n" in p1) {
+      n = BigInt(p1["n"]);
+      d = BigInt(p1["d"]);
+      if ("s" in p1)
+        n *= BigInt(p1["s"]);
+    } else if (0 in p1) {
+      n = BigInt(p1[0]);
+      if (1 in p1)
+        d = BigInt(p1[1]);
+    } else if (typeof p1 === "bigint") {
+      n = p1;
+    } else {
+      throw InvalidParameter();
+    }
+    s = n * d;
+  } else if (typeof p1 === "number") {
+
+    if (isNaN(p1)) {
+      throw InvalidParameter();
+    }
+
+    if (p1 < 0) {
+      s = -C_ONE;
+      p1 = -p1;
+    }
+
+    if (p1 % 1 === 0) {
+      n = BigInt(p1);
+    } else {
+
+      let z = 1;
+
+      let A = 0, B = 1;
+      let C = 1, D = 1;
+
+      let N = 10000000;
+
+      if (p1 >= 1) {
+        z = 10 ** Math.floor(1 + Math.log10(p1));
+        p1 /= z;
+      }
+
+      // Using Farey Sequences
+
+      while (B <= N && D <= N) {
+        let M = (A + C) / (B + D);
+
+        if (p1 === M) {
+          if (B + D <= N) {
+            n = A + C;
+            d = B + D;
+          } else if (D > B) {
+            n = C;
+            d = D;
+          } else {
+            n = A;
+            d = B;
+          }
+          break;
+
+        } else {
+
+          if (p1 > M) {
+            A += C;
+            B += D;
+          } else {
+            C += A;
+            D += B;
+          }
+
+          if (B > N) {
+            n = C;
+            d = D;
+          } else {
+            n = A;
+            d = B;
+          }
+        }
+      }
+      n = BigInt(n) * BigInt(z);
+      d = BigInt(d);
+    }
+
+  } else if (typeof p1 === "string") {
+
+    let ndx = 0;
+
+    let v = C_ZERO, w = C_ZERO, x = C_ZERO, y = C_ONE, z = C_ONE;
+
+    let match = p1.replace(/_/g, '').match(/\d+|./g);
+
+    if (match === null)
+      throw InvalidParameter();
+
+    if (match[ndx] === '-') {// Check for minus sign at the beginning
+      s = -C_ONE;
+      ndx++;
+    } else if (match[ndx] === '+') {// Check for plus sign at the beginning
+      ndx++;
+    }
+
+    if (match.length === ndx + 1) { // Check if it's just a simple number "1234"
+      w = assign(match[ndx++], s);
+    } else if (match[ndx + 1] === '.' || match[ndx] === '.') { // Check if it's a decimal number
+
+      if (match[ndx] !== '.') { // Handle 0.5 and .5
+        v = assign(match[ndx++], s);
+      }
+      ndx++;
+
+      // Check for decimal places
+      if (ndx + 1 === match.length || match[ndx + 1] === '(' && match[ndx + 3] === ')' || match[ndx + 1] === "'" && match[ndx + 3] === "'") {
+        w = assign(match[ndx], s);
+        y = C_TEN ** BigInt(match[ndx].length);
+        ndx++;
+      }
+
+      // Check for repeating places
+      if (match[ndx] === '(' && match[ndx + 2] === ')' || match[ndx] === "'" && match[ndx + 2] === "'") {
+        x = assign(match[ndx + 1], s);
+        z = C_TEN ** BigInt(match[ndx + 1].length) - C_ONE;
+        ndx += 3;
+      }
+
+    } else if (match[ndx + 1] === '/' || match[ndx + 1] === ':') { // Check for a simple fraction "123/456" or "123:456"
+      w = assign(match[ndx], s);
+      y = assign(match[ndx + 2], C_ONE);
+      ndx += 3;
+    } else if (match[ndx + 3] === '/' && match[ndx + 1] === ' ') { // Check for a complex fraction "123 1/2"
+      v = assign(match[ndx], s);
+      w = assign(match[ndx + 2], s);
+      y = assign(match[ndx + 4], C_ONE);
+      ndx += 5;
+    }
+
+    if (match.length <= ndx) { // Check for more tokens on the stack
+      d = y * z;
+      s = /* void */
+        n = x + d * v + z * w;
+    } else {
+      throw InvalidParameter();
+    }
+
+  } else if (typeof p1 === "bigint") {
+    n = p1;
+    s = p1;
+    d = C_ONE;
+  } else {
+    throw InvalidParameter();
+  }
+
+  if (d === C_ZERO) {
+    throw DivisionByZero();
+  }
+
+  P["s"] = s < C_ZERO ? -C_ONE : C_ONE;
+  P["n"] = n < C_ZERO ? -n : n;
+  P["d"] = d < C_ZERO ? -d : d;
+};
+
+function modpow(b, e, m) {
+
+  let r = C_ONE;
+  for (; e > C_ZERO; b = (b * b) % m, e >>= C_ONE) {
+
+    if (e & C_ONE) {
+      r = (r * b) % m;
+    }
+  }
+  return r;
+}
+
+function cycleLen(n, d) {
+
+  for (; d % C_TWO === C_ZERO;
+    d /= C_TWO) {
+  }
+
+  for (; d % C_FIVE === C_ZERO;
+    d /= C_FIVE) {
+  }
+
+  if (d === C_ONE) // Catch non-cyclic numbers
+    return C_ZERO;
+
+  // If we would like to compute really large numbers quicker, we could make use of Fermat's little theorem:
+  // 10^(d-1) % d == 1
+  // However, we don't need such large numbers and MAX_CYCLE_LEN should be the capstone,
+  // as we want to translate the numbers to strings.
+
+  let rem = C_TEN % d;
+  let t = 1;
+
+  for (; rem !== C_ONE; t++) {
+    rem = rem * C_TEN % d;
+
+    if (t > MAX_CYCLE_LEN)
+      return C_ZERO; // Returning 0 here means that we don't print it as a cyclic number. It's likely that the answer is `d-1`
+  }
+  return BigInt(t);
+}
+
+function cycleStart(n, d, len) {
+
+  let rem1 = C_ONE;
+  let rem2 = modpow(C_TEN, len, d);
+
+  for (let t = 0; t < 300; t++) { // s < ~log10(Number.MAX_VALUE)
+    // Solve 10^s == 10^(s+t) (mod d)
+
+    if (rem1 === rem2)
+      return BigInt(t);
+
+    rem1 = rem1 * C_TEN % d;
+    rem2 = rem2 * C_TEN % d;
+  }
+  return 0;
+}
+
+function gcd(a, b) {
+
+  if (!a)
+    return b;
+  if (!b)
+    return a;
+
+  while (1) {
+    a %= b;
+    if (!a)
+      return b;
+    b %= a;
+    if (!b)
+      return a;
+  }
+}
+
+/**
+ * Module constructor
+ *
+ * @constructor
+ * @param {number|Fraction=} a
+ * @param {number=} b
+ */
+function Fraction(a, b) {
+
+  parse(a, b);
+
+  if (this instanceof Fraction) {
+    a = gcd(P["d"], P["n"]); // Abuse a
+    this["s"] = P["s"];
+    this["n"] = P["n"] / a;
+    this["d"] = P["d"] / a;
+  } else {
+    return newFraction(P['s'] * P['n'], P['d']);
+  }
+}
+
+const DivisionByZero = function () { return new Error("Division by Zero"); };
+const InvalidParameter = function () { return new Error("Invalid argument"); };
+const NonIntegerParameter = function () { return new Error("Parameters must be integer"); };
+
+Fraction.prototype = {
+
+  "s": C_ONE,
+  "n": C_ZERO,
+  "d": C_ONE,
+
+  /**
+   * Calculates the absolute value
+   *
+   * Ex: new Fraction(-4).abs() => 4
+   **/
+  "abs": function () {
+
+    return newFraction(this["n"], this["d"]);
+  },
+
+  /**
+   * Inverts the sign of the current fraction
+   *
+   * Ex: new Fraction(-4).neg() => 4
+   **/
+  "neg": function () {
+
+    return newFraction(-this["s"] * this["n"], this["d"]);
+  },
+
+  /**
+   * Adds two rational numbers
+   *
+   * Ex: new Fraction({n: 2, d: 3}).add("14.9") => 467 / 30
+   **/
+  "add": function (a, b) {
+
+    parse(a, b);
+    return newFraction(
+      this["s"] * this["n"] * P["d"] + P["s"] * this["d"] * P["n"],
+      this["d"] * P["d"]
+    );
+  },
+
+  /**
+   * Subtracts two rational numbers
+   *
+   * Ex: new Fraction({n: 2, d: 3}).add("14.9") => -427 / 30
+   **/
+  "sub": function (a, b) {
+
+    parse(a, b);
+    return newFraction(
+      this["s"] * this["n"] * P["d"] - P["s"] * this["d"] * P["n"],
+      this["d"] * P["d"]
+    );
+  },
+
+  /**
+   * Multiplies two rational numbers
+   *
+   * Ex: new Fraction("-17.(345)").mul(3) => 5776 / 111
+   **/
+  "mul": function (a, b) {
+
+    parse(a, b);
+    return newFraction(
+      this["s"] * P["s"] * this["n"] * P["n"],
+      this["d"] * P["d"]
+    );
+  },
+
+  /**
+   * Divides two rational numbers
+   *
+   * Ex: new Fraction("-17.(345)").inverse().div(3)
+   **/
+  "div": function (a, b) {
+
+    parse(a, b);
+    return newFraction(
+      this["s"] * P["s"] * this["n"] * P["d"],
+      this["d"] * P["n"]
+    );
+  },
+
+  /**
+   * Clones the actual object
+   *
+   * Ex: new Fraction("-17.(345)").clone()
+   **/
+  "clone": function () {
+    return newFraction(this['s'] * this['n'], this['d']);
+  },
+
+  /**
+   * Calculates the modulo of two rational numbers - a more precise fmod
+   *
+   * Ex: new Fraction('4.(3)').mod([7, 8]) => (13/3) % (7/8) = (5/6)
+   * Ex: new Fraction(20, 10).mod().equals(0) ? "is Integer"
+   **/
+  "mod": function (a, b) {
+
+    if (a === undefined) {
+      return newFraction(this["s"] * this["n"] % this["d"], C_ONE);
+    }
+
+    parse(a, b);
+    if (C_ZERO === P["n"] * this["d"]) {
+      throw DivisionByZero();
+    }
+
+    /**
+     * I derived the rational modulo similar to the modulo for integers
+     *
+     * https://raw.org/book/analysis/rational-numbers/
+     *
+     *    n1/d1 = (n2/d2) * q + r, where 0 ≤ r < n2/d2
+     * => d2 * n1 = n2 * d1 * q + d1 * d2 * r
+     * => r = (d2 * n1 - n2 * d1 * q) / (d1 * d2)
+     *      = (d2 * n1 - n2 * d1 * floor((d2 * n1) / (n2 * d1))) / (d1 * d2)
+     *      = ((d2 * n1) % (n2 * d1)) / (d1 * d2)
+     */
+    return newFraction(
+      this["s"] * (P["d"] * this["n"]) % (P["n"] * this["d"]),
+      P["d"] * this["d"]);
+  },
+
+  /**
+   * Calculates the fractional gcd of two rational numbers
+   *
+   * Ex: new Fraction(5,8).gcd(3,7) => 1/56
+   */
+  "gcd": function (a, b) {
+
+    parse(a, b);
+
+    // https://raw.org/book/analysis/rational-numbers/
+    // gcd(a / b, c / d) = gcd(a, c) / lcm(b, d)
+
+    return newFraction(gcd(P["n"], this["n"]) * gcd(P["d"], this["d"]), P["d"] * this["d"]);
+  },
+
+  /**
+   * Calculates the fractional lcm of two rational numbers
+   *
+   * Ex: new Fraction(5,8).lcm(3,7) => 15
+   */
+  "lcm": function (a, b) {
+
+    parse(a, b);
+
+    // https://raw.org/book/analysis/rational-numbers/
+    // lcm(a / b, c / d) = lcm(a, c) / gcd(b, d)
+
+    if (P["n"] === C_ZERO && this["n"] === C_ZERO) {
+      return newFraction(C_ZERO, C_ONE);
+    }
+    return newFraction(P["n"] * this["n"], gcd(P["n"], this["n"]) * gcd(P["d"], this["d"]));
+  },
+
+  /**
+   * Gets the inverse of the fraction, means numerator and denominator are exchanged
+   *
+   * Ex: new Fraction([-3, 4]).inverse() => -4 / 3
+   **/
+  "inverse": function () {
+    return newFraction(this["s"] * this["d"], this["n"]);
+  },
+
+  /**
+   * Calculates the fraction to some integer exponent
+   *
+   * Ex: new Fraction(-1,2).pow(-3) => -8
+   */
+  "pow": function (a, b) {
+
+    parse(a, b);
+
+    // Trivial case when exp is an integer
+
+    if (P['d'] === C_ONE) {
+
+      if (P['s'] < C_ZERO) {
+        return newFraction((this['s'] * this["d"]) ** P['n'], this["n"] ** P['n']);
+      } else {
+        return newFraction((this['s'] * this["n"]) ** P['n'], this["d"] ** P['n']);
+      }
+    }
+
+    // Negative roots become complex
+    //     (-a/b)^(c/d) = x
+    // ⇔ (-1)^(c/d) * (a/b)^(c/d) = x
+    // ⇔ (cos(pi) + i*sin(pi))^(c/d) * (a/b)^(c/d) = x
+    // ⇔ (cos(c*pi/d) + i*sin(c*pi/d)) * (a/b)^(c/d) = x       # DeMoivre's formula
+    // From which follows that only for c=0 the root is non-complex
+    if (this['s'] < C_ZERO) return null;
+
+    // Now prime factor n and d
+    let N = factorize(this['n']);
+    let D = factorize(this['d']);
+
+    // Exponentiate and take root for n and d individually
+    let n = C_ONE;
+    let d = C_ONE;
+    for (let k in N) {
+      if (k === '1') continue;
+      if (k === '0') {
+        n = C_ZERO;
+        break;
+      }
+      N[k] *= P['n'];
+
+      if (N[k] % P['d'] === C_ZERO) {
+        N[k] /= P['d'];
+      } else return null;
+      n *= BigInt(k) ** N[k];
+    }
+
+    for (let k in D) {
+      if (k === '1') continue;
+      D[k] *= P['n'];
+
+      if (D[k] % P['d'] === C_ZERO) {
+        D[k] /= P['d'];
+      } else return null;
+      d *= BigInt(k) ** D[k];
+    }
+
+    if (P['s'] < C_ZERO) {
+      return newFraction(d, n);
+    }
+    return newFraction(n, d);
+  },
+
+  /**
+   * Calculates the logarithm of a fraction to a given rational base
+   *
+   * Ex: new Fraction(27, 8).log(9, 4) => 3/2
+   */
+  "log": function (a, b) {
+
+    parse(a, b);
+
+    if (this['s'] <= C_ZERO || P['s'] <= C_ZERO) return null;
+
+    const allPrimes = Object.create(null);
+
+    const baseFactors = factorize(P['n']);
+    const T1 = factorize(P['d']);
+
+    const numberFactors = factorize(this['n']);
+    const T2 = factorize(this['d']);
+
+    for (const prime in T1) {
+      baseFactors[prime] = (baseFactors[prime] || C_ZERO) - T1[prime];
+    }
+    for (const prime in T2) {
+      numberFactors[prime] = (numberFactors[prime] || C_ZERO) - T2[prime];
+    }
+
+    for (const prime in baseFactors) {
+      if (prime === '1') continue;
+      allPrimes[prime] = true;
+    }
+    for (const prime in numberFactors) {
+      if (prime === '1') continue;
+      allPrimes[prime] = true;
+    }
+
+    let retN = null;
+    let retD = null;
+
+    // Iterate over all unique primes to determine if a consistent ratio exists
+    for (const prime in allPrimes) {
+
+      const baseExponent = baseFactors[prime] || C_ZERO;
+      const numberExponent = numberFactors[prime] || C_ZERO;
+
+      if (baseExponent === C_ZERO) {
+        if (numberExponent !== C_ZERO) {
+          return null; // Logarithm cannot be expressed as a rational number
+        }
+        continue; // Skip this prime since both exponents are zero
+      }
+
+      // Calculate the ratio of exponents for this prime
+      let curN = numberExponent;
+      let curD = baseExponent;
+
+      // Simplify the current ratio
+      const gcdValue = gcd(curN, curD);
+      curN /= gcdValue;
+      curD /= gcdValue;
+
+      // Check if this is the first ratio; otherwise, ensure ratios are consistent
+      if (retN === null && retD === null) {
+        retN = curN;
+        retD = curD;
+      } else if (curN * retD !== retN * curD) {
+        return null; // Ratios do not match, logarithm cannot be rational
+      }
+    }
+
+    return retN !== null && retD !== null
+      ? newFraction(retN, retD)
+      : null;
+  },
+
+  /**
+   * Check if two rational numbers are the same
+   *
+   * Ex: new Fraction(19.6).equals([98, 5]);
+   **/
+  "equals": function (a, b) {
+
+    parse(a, b);
+    return this["s"] * this["n"] * P["d"] === P["s"] * P["n"] * this["d"];
+  },
+
+  /**
+   * Check if this rational number is less than another
+   *
+   * Ex: new Fraction(19.6).lt([98, 5]);
+   **/
+  "lt": function (a, b) {
+
+    parse(a, b);
+    return this["s"] * this["n"] * P["d"] < P["s"] * P["n"] * this["d"];
+  },
+
+  /**
+   * Check if this rational number is less than or equal another
+   *
+   * Ex: new Fraction(19.6).lt([98, 5]);
+   **/
+  "lte": function (a, b) {
+
+    parse(a, b);
+    return this["s"] * this["n"] * P["d"] <= P["s"] * P["n"] * this["d"];
+  },
+
+  /**
+   * Check if this rational number is greater than another
+   *
+   * Ex: new Fraction(19.6).lt([98, 5]);
+   **/
+  "gt": function (a, b) {
+
+    parse(a, b);
+    return this["s"] * this["n"] * P["d"] > P["s"] * P["n"] * this["d"];
+  },
+
+  /**
+   * Check if this rational number is greater than or equal another
+   *
+   * Ex: new Fraction(19.6).lt([98, 5]);
+   **/
+  "gte": function (a, b) {
+
+    parse(a, b);
+    return this["s"] * this["n"] * P["d"] >= P["s"] * P["n"] * this["d"];
+  },
+
+  /**
+   * Compare two rational numbers
+   * < 0 iff this < that
+   * > 0 iff this > that
+   * = 0 iff this = that
+   *
+   * Ex: new Fraction(19.6).compare([98, 5]);
+   **/
+  "compare": function (a, b) {
+
+    parse(a, b);
+    let t = this["s"] * this["n"] * P["d"] - P["s"] * P["n"] * this["d"];
+
+    return (C_ZERO < t) - (t < C_ZERO);
+  },
+
+  /**
+   * Calculates the ceil of a rational number
+   *
+   * Ex: new Fraction('4.(3)').ceil() => (5 / 1)
+   **/
+  "ceil": function (places) {
+
+    places = C_TEN ** BigInt(places || 0);
+
+    return newFraction(ifloor(this["s"] * places * this["n"] / this["d"]) +
+      (places * this["n"] % this["d"] > C_ZERO && this["s"] >= C_ZERO ? C_ONE : C_ZERO),
+      places);
+  },
+
+  /**
+   * Calculates the floor of a rational number
+   *
+   * Ex: new Fraction('4.(3)').floor() => (4 / 1)
+   **/
+  "floor": function (places) {
+
+    places = C_TEN ** BigInt(places || 0);
+
+    return newFraction(ifloor(this["s"] * places * this["n"] / this["d"]) -
+      (places * this["n"] % this["d"] > C_ZERO && this["s"] < C_ZERO ? C_ONE : C_ZERO),
+      places);
+  },
+
+  /**
+   * Rounds a rational numbers
+   *
+   * Ex: new Fraction('4.(3)').round() => (4 / 1)
+   **/
+  "round": function (places) {
+
+    places = C_TEN ** BigInt(places || 0);
+
+    /* Derivation:
+
+    s >= 0:
+      round(n / d) = ifloor(n / d) + (n % d) / d >= 0.5 ? 1 : 0
+                   = ifloor(n / d) + 2(n % d) >= d ? 1 : 0
+    s < 0:
+      round(n / d) =-ifloor(n / d) - (n % d) / d > 0.5 ? 1 : 0
+                   =-ifloor(n / d) - 2(n % d) > d ? 1 : 0
+
+    =>:
+
+    round(s * n / d) = s * ifloor(n / d) + s * (C + 2(n % d) > d ? 1 : 0)
+        where C = s >= 0 ? 1 : 0, to fix the >= for the positve case.
+    */
+
+    return newFraction(ifloor(this["s"] * places * this["n"] / this["d"]) +
+      this["s"] * ((this["s"] >= C_ZERO ? C_ONE : C_ZERO) + C_TWO * (places * this["n"] % this["d"]) > this["d"] ? C_ONE : C_ZERO),
+      places);
+  },
+
+  /**
+    * Rounds a rational number to a multiple of another rational number
+    *
+    * Ex: new Fraction('0.9').roundTo("1/8") => 7 / 8
+    **/
+  "roundTo": function (a, b) {
+
+    /*
+    k * x/y ≤ a/b < (k+1) * x/y
+    ⇔ k ≤ a/b / (x/y) < (k+1)
+    ⇔ k = floor(a/b * y/x)
+    ⇔ k = floor((a * y) / (b * x))
+    */
+
+    parse(a, b);
+
+    const n = this['n'] * P['d'];
+    const d = this['d'] * P['n'];
+    const r = n % d;
+
+    // round(n / d) = ifloor(n / d) + 2(n % d) >= d ? 1 : 0
+    let k = ifloor(n / d);
+    if (r + r >= d) {
+      k++;
+    }
+    return newFraction(this['s'] * k * P['n'], P['d']);
+  },
+
+  /**
+   * Check if two rational numbers are divisible
+   *
+   * Ex: new Fraction(19.6).divisible(1.5);
+   */
+  "divisible": function (a, b) {
+
+    parse(a, b);
+    if (P['n'] === C_ZERO) return false;
+    return (this['n'] * P['d']) % (P['n'] * this['d']) === C_ZERO;
+  },
+
+  /**
+   * Returns a decimal representation of the fraction
+   *
+   * Ex: new Fraction("100.'91823'").valueOf() => 100.91823918239183
+   **/
+  'valueOf': function () {
+    //if (this['n'] <= MAX_INTEGER && this['d'] <= MAX_INTEGER) {
+    return Number(this['s'] * this['n']) / Number(this['d']);
+    //}
+  },
+
+  /**
+   * Creates a string representation of a fraction with all digits
+   *
+   * Ex: new Fraction("100.'91823'").toString() => "100.(91823)"
+   **/
+  'toString': function (dec = 15) {
+
+    let N = this["n"];
+    let D = this["d"];
+
+    let cycLen = cycleLen(N, D); // Cycle length
+    let cycOff = cycleStart(N, D, cycLen); // Cycle start
+
+    let str = this['s'] < C_ZERO ? "-" : "";
+
+    // Append integer part
+    str += ifloor(N / D);
+
+    N %= D;
+    N *= C_TEN;
+
+    if (N)
+      str += ".";
+
+    if (cycLen) {
+
+      for (let i = cycOff; i--;) {
+        str += ifloor(N / D);
+        N %= D;
+        N *= C_TEN;
+      }
+      str += "(";
+      for (let i = cycLen; i--;) {
+        str += ifloor(N / D);
+        N %= D;
+        N *= C_TEN;
+      }
+      str += ")";
+    } else {
+      for (let i = dec; N && i--;) {
+        str += ifloor(N / D);
+        N %= D;
+        N *= C_TEN;
+      }
+    }
+    return str;
+  },
+
+  /**
+   * Returns a string-fraction representation of a Fraction object
+   *
+   * Ex: new Fraction("1.'3'").toFraction() => "4 1/3"
+   **/
+  'toFraction': function (showMixed = false) {
+
+    let n = this["n"];
+    let d = this["d"];
+    let str = this['s'] < C_ZERO ? "-" : "";
+
+    if (d === C_ONE) {
+      str += n;
+    } else {
+      const whole = ifloor(n / d);
+      if (showMixed && whole > C_ZERO) {
+        str += whole;
+        str += " ";
+        n %= d;
+      }
+
+      str += n;
+      str += '/';
+      str += d;
+    }
+    return str;
+  },
+
+  /**
+   * Returns a latex representation of a Fraction object
+   *
+   * Ex: new Fraction("1.'3'").toLatex() => "\frac{4}{3}"
+   **/
+  'toLatex': function (showMixed = false) {
+
+    let n = this["n"];
+    let d = this["d"];
+    let str = this['s'] < C_ZERO ? "-" : "";
+
+    if (d === C_ONE) {
+      str += n;
+    } else {
+      const whole = ifloor(n / d);
+      if (showMixed && whole > C_ZERO) {
+        str += whole;
+        n %= d;
+      }
+
+      str += "\\frac{";
+      str += n;
+      str += '}{';
+      str += d;
+      str += '}';
+    }
+    return str;
+  },
+
+  /**
+   * Returns an array of continued fraction elements
+   *
+   * Ex: new Fraction("7/8").toContinued() => [0,1,7]
+   */
+  'toContinued': function () {
+
+    let a = this['n'];
+    let b = this['d'];
+    const res = [];
+
+    while (b) {
+      res.push(ifloor(a / b));
+      const t = a % b;
+      a = b;
+      b = t;
+    }
+    return res;
+  },
+
+  "simplify": function (eps = 1e-3) {
+
+    // Continued fractions give best approximations for a max denominator,
+    // generally outperforming mediants in denominator–accuracy trade-offs.
+    // Semiconvergents can further reduce the denominator within tolerance.
+
+    const ieps = BigInt(Math.ceil(1 / eps));
+
+    const thisABS = this['abs']();
+    const cont = thisABS['toContinued']();
+
+    for (let i = 1; i < cont.length; i++) {
+
+      let s = newFraction(cont[i - 1], C_ONE);
+      for (let k = i - 2; k >= 0; k--) {
+        s = s['inverse']()['add'](cont[k]);
+      }
+
+      let t = s['sub'](thisABS);
+      if (t['n'] * ieps < t['d']) { // More robust than Math.abs(t.valueOf()) < eps
+        return s['mul'](this['s']);
+      }
+    }
+    return this;
+  }
+};
+
+Object.defineProperty(Fraction, "__esModule", { 'value': true });
+Fraction['default'] = Fraction;
+Fraction['Fraction'] = Fraction;
+module['exports'] = Fraction;
Index: node_modules/fraction.js/dist/fraction.min.js
===================================================================
--- node_modules/fraction.js/dist/fraction.min.js	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
+++ node_modules/fraction.js/dist/fraction.min.js	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
@@ -0,0 +1,21 @@
+/*
+Fraction.js v5.3.4 8/22/2025
+https://raw.org/article/rational-numbers-in-javascript/
+
+Copyright (c) 2025, Robert Eisele (https://raw.org/)
+Licensed under the MIT license.
+*/
+'use strict';(function(F){function D(){return Error("Parameters must be integer")}function x(){return Error("Invalid argument")}function C(){return Error("Division by Zero")}function q(a,b){var d=g,c=h;let f=h;if(void 0!==a&&null!==a)if(void 0!==b){if("bigint"===typeof a)d=a;else{if(isNaN(a))throw x();if(0!==a%1)throw D();d=BigInt(a)}if("bigint"===typeof b)c=b;else{if(isNaN(b))throw x();if(0!==b%1)throw D();c=BigInt(b)}f=d*c}else if("object"===typeof a){if("d"in a&&"n"in a)d=BigInt(a.n),c=BigInt(a.d),
+"s"in a&&(d*=BigInt(a.s));else if(0 in a)d=BigInt(a[0]),1 in a&&(c=BigInt(a[1]));else if("bigint"===typeof a)d=a;else throw x();f=d*c}else if("number"===typeof a){if(isNaN(a))throw x();0>a&&(f=-h,a=-a);if(0===a%1)d=BigInt(a);else{b=1;var k=0,l=1,m=1;let r=1;1<=a&&(b=10**Math.floor(1+Math.log10(a)),a/=b);for(;1E7>=l&&1E7>=r;)if(c=(k+m)/(l+r),a===c){1E7>=l+r?(d=k+m,c=l+r):r>l?(d=m,c=r):(d=k,c=l);break}else a>c?(k+=m,l+=r):(m+=k,r+=l),1E7<l?(d=m,c=r):(d=k,c=l);d=BigInt(d)*BigInt(b);c=BigInt(c)}}else if("string"===
+typeof a){c=0;k=b=d=g;l=m=h;a=a.replace(/_/g,"").match(/\d+|./g);if(null===a)throw x();"-"===a[c]?(f=-h,c++):"+"===a[c]&&c++;if(a.length===c+1)b=w(a[c++],f);else if("."===a[c+1]||"."===a[c]){"."!==a[c]&&(d=w(a[c++],f));c++;if(c+1===a.length||"("===a[c+1]&&")"===a[c+3]||"'"===a[c+1]&&"'"===a[c+3])b=w(a[c],f),m=t**BigInt(a[c].length),c++;if("("===a[c]&&")"===a[c+2]||"'"===a[c]&&"'"===a[c+2])k=w(a[c+1],f),l=t**BigInt(a[c+1].length)-h,c+=3}else"/"===a[c+1]||":"===a[c+1]?(b=w(a[c],f),m=w(a[c+2],h),c+=
+3):"/"===a[c+3]&&" "===a[c+1]&&(d=w(a[c],f),b=w(a[c+2],f),m=w(a[c+4],h),c+=5);if(a.length<=c)c=m*l,f=d=k+c*d+l*b;else throw x();}else if("bigint"===typeof a)f=d=a,c=h;else throw x();if(c===g)throw C();e.s=f<g?-h:h;e.n=d<g?-d:d;e.d=c<g?-c:c}function w(a,b){try{a=BigInt(a)}catch(d){throw x();}return a*b}function u(a){return"bigint"===typeof a?a:Math.floor(a)}function n(a,b){if(b===g)throw C();const d=Object.create(v.prototype);d.s=a<g?-h:h;a=a<g?-a:a;const c=y(a,b);d.n=a/c;d.d=b/c;return d}function A(a){const b=
+Object.create(null);if(a<=h)return b[a]=h,b;for(;a%p===g;)b[p]=(b[p]||g)+h,a/=p;for(;a%B===g;)b[B]=(b[B]||g)+h,a/=B;for(;a%z===g;)b[z]=(b[z]||g)+h,a/=z;for(let d=0,c=p+z;c*c<=a;){for(;a%c===g;)b[c]=(b[c]||g)+h,a/=c;c+=G[d];d=d+1&7}a>h&&(b[a]=(b[a]||g)+h);return b}function y(a,b){if(!a)return b;if(!b)return a;for(;;){a%=b;if(!a)return b;b%=a;if(!b)return a}}function v(a,b){q(a,b);if(this instanceof v)a=y(e.d,e.n),this.s=e.s,this.n=e.n/a,this.d=e.d/a;else return n(e.s*e.n,e.d)}"undefined"===typeof BigInt&&
+(BigInt=function(a){if(isNaN(a))throw Error("");return a});const g=BigInt(0),h=BigInt(1),p=BigInt(2),B=BigInt(3),z=BigInt(5),t=BigInt(10),e={s:h,n:g,d:h},G=[p*p,p,p*p,p,p*p,p*B,p,p*B];v.prototype={s:h,n:g,d:h,abs:function(){return n(this.n,this.d)},neg:function(){return n(-this.s*this.n,this.d)},add:function(a,b){q(a,b);return n(this.s*this.n*e.d+e.s*this.d*e.n,this.d*e.d)},sub:function(a,b){q(a,b);return n(this.s*this.n*e.d-e.s*this.d*e.n,this.d*e.d)},mul:function(a,b){q(a,b);return n(this.s*e.s*
+this.n*e.n,this.d*e.d)},div:function(a,b){q(a,b);return n(this.s*e.s*this.n*e.d,this.d*e.n)},clone:function(){return n(this.s*this.n,this.d)},mod:function(a,b){if(void 0===a)return n(this.s*this.n%this.d,h);q(a,b);if(g===e.n*this.d)throw C();return n(this.s*e.d*this.n%(e.n*this.d),e.d*this.d)},gcd:function(a,b){q(a,b);return n(y(e.n,this.n)*y(e.d,this.d),e.d*this.d)},lcm:function(a,b){q(a,b);return e.n===g&&this.n===g?n(g,h):n(e.n*this.n,y(e.n,this.n)*y(e.d,this.d))},inverse:function(){return n(this.s*
+this.d,this.n)},pow:function(a,b){q(a,b);if(e.d===h)return e.s<g?n((this.s*this.d)**e.n,this.n**e.n):n((this.s*this.n)**e.n,this.d**e.n);if(this.s<g)return null;a=A(this.n);b=A(this.d);let d=h,c=h;for(let f in a)if("1"!==f){if("0"===f){d=g;break}a[f]*=e.n;if(a[f]%e.d===g)a[f]/=e.d;else return null;d*=BigInt(f)**a[f]}for(let f in b)if("1"!==f){b[f]*=e.n;if(b[f]%e.d===g)b[f]/=e.d;else return null;c*=BigInt(f)**b[f]}return e.s<g?n(c,d):n(d,c)},log:function(a,b){q(a,b);if(this.s<=g||e.s<=g)return null;
+var d=Object.create(null);a=A(e.n);const c=A(e.d);b=A(this.n);const f=A(this.d);for(var k in c)a[k]=(a[k]||g)-c[k];for(var l in f)b[l]=(b[l]||g)-f[l];for(var m in a)"1"!==m&&(d[m]=!0);for(var r in b)"1"!==r&&(d[r]=!0);l=k=null;for(const E in d)if(m=a[E]||g,d=b[E]||g,m===g){if(d!==g)return null}else if(r=y(d,m),d/=r,m/=r,null===k&&null===l)k=d,l=m;else if(d*l!==k*m)return null;return null!==k&&null!==l?n(k,l):null},equals:function(a,b){q(a,b);return this.s*this.n*e.d===e.s*e.n*this.d},lt:function(a,
+b){q(a,b);return this.s*this.n*e.d<e.s*e.n*this.d},lte:function(a,b){q(a,b);return this.s*this.n*e.d<=e.s*e.n*this.d},gt:function(a,b){q(a,b);return this.s*this.n*e.d>e.s*e.n*this.d},gte:function(a,b){q(a,b);return this.s*this.n*e.d>=e.s*e.n*this.d},compare:function(a,b){q(a,b);a=this.s*this.n*e.d-e.s*e.n*this.d;return(g<a)-(a<g)},ceil:function(a){a=t**BigInt(a||0);return n(u(this.s*a*this.n/this.d)+(a*this.n%this.d>g&&this.s>=g?h:g),a)},floor:function(a){a=t**BigInt(a||0);return n(u(this.s*a*this.n/
+this.d)-(a*this.n%this.d>g&&this.s<g?h:g),a)},round:function(a){a=t**BigInt(a||0);return n(u(this.s*a*this.n/this.d)+this.s*((this.s>=g?h:g)+a*this.n%this.d*p>this.d?h:g),a)},roundTo:function(a,b){q(a,b);var d=this.n*e.d;a=this.d*e.n;b=d%a;d=u(d/a);b+b>=a&&d++;return n(this.s*d*e.n,e.d)},divisible:function(a,b){q(a,b);return e.n===g?!1:this.n*e.d%(e.n*this.d)===g},valueOf:function(){return Number(this.s*this.n)/Number(this.d)},toString:function(a=15){let b=this.n,d=this.d;var c;a:{for(c=d;c%p===g;c/=
+p);for(;c%z===g;c/=z);if(c===h)c=g;else{for(var f=t%c,k=1;f!==h;k++)if(f=f*t%c,2E3<k){c=g;break a}c=BigInt(k)}}a:{f=h;k=t;var l=c;let m=h;for(;l>g;k=k*k%d,l>>=h)l&h&&(m=m*k%d);k=m;for(l=0;300>l;l++){if(f===k){f=BigInt(l);break a}f=f*t%d;k=k*t%d}f=0}k=f;f=this.s<g?"-":"";f+=u(b/d);(b=b%d*t)&&(f+=".");if(c){for(a=k;a--;)f+=u(b/d),b%=d,b*=t;f+="(";for(a=c;a--;)f+=u(b/d),b%=d,b*=t;f+=")"}else for(;b&&a--;)f+=u(b/d),b%=d,b*=t;return f},toFraction:function(a=!1){let b=this.n,d=this.d,c=this.s<g?"-":"";
+if(d===h)c+=b;else{const f=u(b/d);a&&f>g&&(c+=f,c+=" ",b%=d);c=c+b+"/"+d}return c},toLatex:function(a=!1){let b=this.n,d=this.d,c=this.s<g?"-":"";if(d===h)c+=b;else{const f=u(b/d);a&&f>g&&(c+=f,b%=d);c=c+"\\frac{"+b+"}{"+d;c+="}"}return c},toContinued:function(){let a=this.n,b=this.d;const d=[];for(;b;){d.push(u(a/b));const c=a%b;a=b;b=c}return d},simplify:function(a=.001){a=BigInt(Math.ceil(1/a));const b=this.abs(),d=b.toContinued();for(let f=1;f<d.length;f++){let k=n(d[f-1],h);for(var c=f-2;0<=
+c;c--)k=k.inverse().add(d[c]);c=k.sub(b);if(c.n*a<c.d)return k.mul(this.s)}return this}};"function"===typeof define&&define.amd?define([],function(){return v}):"object"===typeof exports?(Object.defineProperty(v,"__esModule",{value:!0}),v["default"]=v,v.Fraction=v,module.exports=v):F.Fraction=v})(this);
Index: node_modules/fraction.js/dist/fraction.mjs
===================================================================
--- node_modules/fraction.js/dist/fraction.mjs	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
+++ node_modules/fraction.js/dist/fraction.mjs	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
@@ -0,0 +1,1043 @@
+'use strict';
+
+/**
+ *
+ * This class offers the possibility to calculate fractions.
+ * You can pass a fraction in different formats. Either as array, as double, as string or as an integer.
+ *
+ * Array/Object form
+ * [ 0 => <numerator>, 1 => <denominator> ]
+ * { n => <numerator>, d => <denominator> }
+ *
+ * Integer form
+ * - Single integer value as BigInt or Number
+ *
+ * Double form
+ * - Single double value as Number
+ *
+ * String form
+ * 123.456 - a simple double
+ * 123/456 - a string fraction
+ * 123.'456' - a double with repeating decimal places
+ * 123.(456) - synonym
+ * 123.45'6' - a double with repeating last place
+ * 123.45(6) - synonym
+ *
+ * Example:
+ * let f = new Fraction("9.4'31'");
+ * f.mul([-4, 3]).div(4.9);
+ *
+ */
+
+// Set Identity function to downgrade BigInt to Number if needed
+if (typeof BigInt === 'undefined') BigInt = function (n) { if (isNaN(n)) throw new Error(""); return n; };
+
+const C_ZERO = BigInt(0);
+const C_ONE = BigInt(1);
+const C_TWO = BigInt(2);
+const C_THREE = BigInt(3);
+const C_FIVE = BigInt(5);
+const C_TEN = BigInt(10);
+const MAX_INTEGER = BigInt(Number.MAX_SAFE_INTEGER);
+
+// Maximum search depth for cyclic rational numbers. 2000 should be more than enough.
+// Example: 1/7 = 0.(142857) has 6 repeating decimal places.
+// If MAX_CYCLE_LEN gets reduced, long cycles will not be detected and toString() only gets the first 10 digits
+const MAX_CYCLE_LEN = 2000;
+
+// Parsed data to avoid calling "new" all the time
+const P = {
+  "s": C_ONE,
+  "n": C_ZERO,
+  "d": C_ONE
+};
+
+function assign(n, s) {
+
+  try {
+    n = BigInt(n);
+  } catch (e) {
+    throw InvalidParameter();
+  }
+  return n * s;
+}
+
+function ifloor(x) {
+  return typeof x === 'bigint' ? x : Math.floor(x);
+}
+
+// Creates a new Fraction internally without the need of the bulky constructor
+function newFraction(n, d) {
+
+  if (d === C_ZERO) {
+    throw DivisionByZero();
+  }
+
+  const f = Object.create(Fraction.prototype);
+  f["s"] = n < C_ZERO ? -C_ONE : C_ONE;
+
+  n = n < C_ZERO ? -n : n;
+
+  const a = gcd(n, d);
+
+  f["n"] = n / a;
+  f["d"] = d / a;
+  return f;
+}
+
+const FACTORSTEPS = [C_TWO * C_TWO, C_TWO, C_TWO * C_TWO, C_TWO, C_TWO * C_TWO, C_TWO * C_THREE, C_TWO, C_TWO * C_THREE]; // repeats
+function factorize(n) {
+
+  const factors = Object.create(null);
+  if (n <= C_ONE) {
+    factors[n] = C_ONE;
+    return factors;
+  }
+
+  const add = (p) => { factors[p] = (factors[p] || C_ZERO) + C_ONE; };
+
+  while (n % C_TWO === C_ZERO) { add(C_TWO); n /= C_TWO; }
+  while (n % C_THREE === C_ZERO) { add(C_THREE); n /= C_THREE; }
+  while (n % C_FIVE === C_ZERO) { add(C_FIVE); n /= C_FIVE; }
+
+  // 30-wheel trial division: test only residues coprime to 2*3*5
+  // Residue step pattern after 5: 7,11,13,17,19,23,29,31, ...
+  for (let si = 0, p = C_TWO + C_FIVE; p * p <= n;) {
+    while (n % p === C_ZERO) { add(p); n /= p; }
+    p += FACTORSTEPS[si];
+    si = (si + 1) & 7; // fast modulo 8
+  }
+  if (n > C_ONE) add(n);
+  return factors;
+}
+
+const parse = function (p1, p2) {
+
+  let n = C_ZERO, d = C_ONE, s = C_ONE;
+
+  if (p1 === undefined || p1 === null) { // No argument
+    /* void */
+  } else if (p2 !== undefined) { // Two arguments
+
+    if (typeof p1 === "bigint") {
+      n = p1;
+    } else if (isNaN(p1)) {
+      throw InvalidParameter();
+    } else if (p1 % 1 !== 0) {
+      throw NonIntegerParameter();
+    } else {
+      n = BigInt(p1);
+    }
+
+    if (typeof p2 === "bigint") {
+      d = p2;
+    } else if (isNaN(p2)) {
+      throw InvalidParameter();
+    } else if (p2 % 1 !== 0) {
+      throw NonIntegerParameter();
+    } else {
+      d = BigInt(p2);
+    }
+
+    s = n * d;
+
+  } else if (typeof p1 === "object") {
+    if ("d" in p1 && "n" in p1) {
+      n = BigInt(p1["n"]);
+      d = BigInt(p1["d"]);
+      if ("s" in p1)
+        n *= BigInt(p1["s"]);
+    } else if (0 in p1) {
+      n = BigInt(p1[0]);
+      if (1 in p1)
+        d = BigInt(p1[1]);
+    } else if (typeof p1 === "bigint") {
+      n = p1;
+    } else {
+      throw InvalidParameter();
+    }
+    s = n * d;
+  } else if (typeof p1 === "number") {
+
+    if (isNaN(p1)) {
+      throw InvalidParameter();
+    }
+
+    if (p1 < 0) {
+      s = -C_ONE;
+      p1 = -p1;
+    }
+
+    if (p1 % 1 === 0) {
+      n = BigInt(p1);
+    } else {
+
+      let z = 1;
+
+      let A = 0, B = 1;
+      let C = 1, D = 1;
+
+      let N = 10000000;
+
+      if (p1 >= 1) {
+        z = 10 ** Math.floor(1 + Math.log10(p1));
+        p1 /= z;
+      }
+
+      // Using Farey Sequences
+
+      while (B <= N && D <= N) {
+        let M = (A + C) / (B + D);
+
+        if (p1 === M) {
+          if (B + D <= N) {
+            n = A + C;
+            d = B + D;
+          } else if (D > B) {
+            n = C;
+            d = D;
+          } else {
+            n = A;
+            d = B;
+          }
+          break;
+
+        } else {
+
+          if (p1 > M) {
+            A += C;
+            B += D;
+          } else {
+            C += A;
+            D += B;
+          }
+
+          if (B > N) {
+            n = C;
+            d = D;
+          } else {
+            n = A;
+            d = B;
+          }
+        }
+      }
+      n = BigInt(n) * BigInt(z);
+      d = BigInt(d);
+    }
+
+  } else if (typeof p1 === "string") {
+
+    let ndx = 0;
+
+    let v = C_ZERO, w = C_ZERO, x = C_ZERO, y = C_ONE, z = C_ONE;
+
+    let match = p1.replace(/_/g, '').match(/\d+|./g);
+
+    if (match === null)
+      throw InvalidParameter();
+
+    if (match[ndx] === '-') {// Check for minus sign at the beginning
+      s = -C_ONE;
+      ndx++;
+    } else if (match[ndx] === '+') {// Check for plus sign at the beginning
+      ndx++;
+    }
+
+    if (match.length === ndx + 1) { // Check if it's just a simple number "1234"
+      w = assign(match[ndx++], s);
+    } else if (match[ndx + 1] === '.' || match[ndx] === '.') { // Check if it's a decimal number
+
+      if (match[ndx] !== '.') { // Handle 0.5 and .5
+        v = assign(match[ndx++], s);
+      }
+      ndx++;
+
+      // Check for decimal places
+      if (ndx + 1 === match.length || match[ndx + 1] === '(' && match[ndx + 3] === ')' || match[ndx + 1] === "'" && match[ndx + 3] === "'") {
+        w = assign(match[ndx], s);
+        y = C_TEN ** BigInt(match[ndx].length);
+        ndx++;
+      }
+
+      // Check for repeating places
+      if (match[ndx] === '(' && match[ndx + 2] === ')' || match[ndx] === "'" && match[ndx + 2] === "'") {
+        x = assign(match[ndx + 1], s);
+        z = C_TEN ** BigInt(match[ndx + 1].length) - C_ONE;
+        ndx += 3;
+      }
+
+    } else if (match[ndx + 1] === '/' || match[ndx + 1] === ':') { // Check for a simple fraction "123/456" or "123:456"
+      w = assign(match[ndx], s);
+      y = assign(match[ndx + 2], C_ONE);
+      ndx += 3;
+    } else if (match[ndx + 3] === '/' && match[ndx + 1] === ' ') { // Check for a complex fraction "123 1/2"
+      v = assign(match[ndx], s);
+      w = assign(match[ndx + 2], s);
+      y = assign(match[ndx + 4], C_ONE);
+      ndx += 5;
+    }
+
+    if (match.length <= ndx) { // Check for more tokens on the stack
+      d = y * z;
+      s = /* void */
+        n = x + d * v + z * w;
+    } else {
+      throw InvalidParameter();
+    }
+
+  } else if (typeof p1 === "bigint") {
+    n = p1;
+    s = p1;
+    d = C_ONE;
+  } else {
+    throw InvalidParameter();
+  }
+
+  if (d === C_ZERO) {
+    throw DivisionByZero();
+  }
+
+  P["s"] = s < C_ZERO ? -C_ONE : C_ONE;
+  P["n"] = n < C_ZERO ? -n : n;
+  P["d"] = d < C_ZERO ? -d : d;
+};
+
+function modpow(b, e, m) {
+
+  let r = C_ONE;
+  for (; e > C_ZERO; b = (b * b) % m, e >>= C_ONE) {
+
+    if (e & C_ONE) {
+      r = (r * b) % m;
+    }
+  }
+  return r;
+}
+
+function cycleLen(n, d) {
+
+  for (; d % C_TWO === C_ZERO;
+    d /= C_TWO) {
+  }
+
+  for (; d % C_FIVE === C_ZERO;
+    d /= C_FIVE) {
+  }
+
+  if (d === C_ONE) // Catch non-cyclic numbers
+    return C_ZERO;
+
+  // If we would like to compute really large numbers quicker, we could make use of Fermat's little theorem:
+  // 10^(d-1) % d == 1
+  // However, we don't need such large numbers and MAX_CYCLE_LEN should be the capstone,
+  // as we want to translate the numbers to strings.
+
+  let rem = C_TEN % d;
+  let t = 1;
+
+  for (; rem !== C_ONE; t++) {
+    rem = rem * C_TEN % d;
+
+    if (t > MAX_CYCLE_LEN)
+      return C_ZERO; // Returning 0 here means that we don't print it as a cyclic number. It's likely that the answer is `d-1`
+  }
+  return BigInt(t);
+}
+
+function cycleStart(n, d, len) {
+
+  let rem1 = C_ONE;
+  let rem2 = modpow(C_TEN, len, d);
+
+  for (let t = 0; t < 300; t++) { // s < ~log10(Number.MAX_VALUE)
+    // Solve 10^s == 10^(s+t) (mod d)
+
+    if (rem1 === rem2)
+      return BigInt(t);
+
+    rem1 = rem1 * C_TEN % d;
+    rem2 = rem2 * C_TEN % d;
+  }
+  return 0;
+}
+
+function gcd(a, b) {
+
+  if (!a)
+    return b;
+  if (!b)
+    return a;
+
+  while (1) {
+    a %= b;
+    if (!a)
+      return b;
+    b %= a;
+    if (!b)
+      return a;
+  }
+}
+
+/**
+ * Module constructor
+ *
+ * @constructor
+ * @param {number|Fraction=} a
+ * @param {number=} b
+ */
+function Fraction(a, b) {
+
+  parse(a, b);
+
+  if (this instanceof Fraction) {
+    a = gcd(P["d"], P["n"]); // Abuse a
+    this["s"] = P["s"];
+    this["n"] = P["n"] / a;
+    this["d"] = P["d"] / a;
+  } else {
+    return newFraction(P['s'] * P['n'], P['d']);
+  }
+}
+
+const DivisionByZero = function () { return new Error("Division by Zero"); };
+const InvalidParameter = function () { return new Error("Invalid argument"); };
+const NonIntegerParameter = function () { return new Error("Parameters must be integer"); };
+
+Fraction.prototype = {
+
+  "s": C_ONE,
+  "n": C_ZERO,
+  "d": C_ONE,
+
+  /**
+   * Calculates the absolute value
+   *
+   * Ex: new Fraction(-4).abs() => 4
+   **/
+  "abs": function () {
+
+    return newFraction(this["n"], this["d"]);
+  },
+
+  /**
+   * Inverts the sign of the current fraction
+   *
+   * Ex: new Fraction(-4).neg() => 4
+   **/
+  "neg": function () {
+
+    return newFraction(-this["s"] * this["n"], this["d"]);
+  },
+
+  /**
+   * Adds two rational numbers
+   *
+   * Ex: new Fraction({n: 2, d: 3}).add("14.9") => 467 / 30
+   **/
+  "add": function (a, b) {
+
+    parse(a, b);
+    return newFraction(
+      this["s"] * this["n"] * P["d"] + P["s"] * this["d"] * P["n"],
+      this["d"] * P["d"]
+    );
+  },
+
+  /**
+   * Subtracts two rational numbers
+   *
+   * Ex: new Fraction({n: 2, d: 3}).add("14.9") => -427 / 30
+   **/
+  "sub": function (a, b) {
+
+    parse(a, b);
+    return newFraction(
+      this["s"] * this["n"] * P["d"] - P["s"] * this["d"] * P["n"],
+      this["d"] * P["d"]
+    );
+  },
+
+  /**
+   * Multiplies two rational numbers
+   *
+   * Ex: new Fraction("-17.(345)").mul(3) => 5776 / 111
+   **/
+  "mul": function (a, b) {
+
+    parse(a, b);
+    return newFraction(
+      this["s"] * P["s"] * this["n"] * P["n"],
+      this["d"] * P["d"]
+    );
+  },
+
+  /**
+   * Divides two rational numbers
+   *
+   * Ex: new Fraction("-17.(345)").inverse().div(3)
+   **/
+  "div": function (a, b) {
+
+    parse(a, b);
+    return newFraction(
+      this["s"] * P["s"] * this["n"] * P["d"],
+      this["d"] * P["n"]
+    );
+  },
+
+  /**
+   * Clones the actual object
+   *
+   * Ex: new Fraction("-17.(345)").clone()
+   **/
+  "clone": function () {
+    return newFraction(this['s'] * this['n'], this['d']);
+  },
+
+  /**
+   * Calculates the modulo of two rational numbers - a more precise fmod
+   *
+   * Ex: new Fraction('4.(3)').mod([7, 8]) => (13/3) % (7/8) = (5/6)
+   * Ex: new Fraction(20, 10).mod().equals(0) ? "is Integer"
+   **/
+  "mod": function (a, b) {
+
+    if (a === undefined) {
+      return newFraction(this["s"] * this["n"] % this["d"], C_ONE);
+    }
+
+    parse(a, b);
+    if (C_ZERO === P["n"] * this["d"]) {
+      throw DivisionByZero();
+    }
+
+    /**
+     * I derived the rational modulo similar to the modulo for integers
+     *
+     * https://raw.org/book/analysis/rational-numbers/
+     *
+     *    n1/d1 = (n2/d2) * q + r, where 0 ≤ r < n2/d2
+     * => d2 * n1 = n2 * d1 * q + d1 * d2 * r
+     * => r = (d2 * n1 - n2 * d1 * q) / (d1 * d2)
+     *      = (d2 * n1 - n2 * d1 * floor((d2 * n1) / (n2 * d1))) / (d1 * d2)
+     *      = ((d2 * n1) % (n2 * d1)) / (d1 * d2)
+     */
+    return newFraction(
+      this["s"] * (P["d"] * this["n"]) % (P["n"] * this["d"]),
+      P["d"] * this["d"]);
+  },
+
+  /**
+   * Calculates the fractional gcd of two rational numbers
+   *
+   * Ex: new Fraction(5,8).gcd(3,7) => 1/56
+   */
+  "gcd": function (a, b) {
+
+    parse(a, b);
+
+    // https://raw.org/book/analysis/rational-numbers/
+    // gcd(a / b, c / d) = gcd(a, c) / lcm(b, d)
+
+    return newFraction(gcd(P["n"], this["n"]) * gcd(P["d"], this["d"]), P["d"] * this["d"]);
+  },
+
+  /**
+   * Calculates the fractional lcm of two rational numbers
+   *
+   * Ex: new Fraction(5,8).lcm(3,7) => 15
+   */
+  "lcm": function (a, b) {
+
+    parse(a, b);
+
+    // https://raw.org/book/analysis/rational-numbers/
+    // lcm(a / b, c / d) = lcm(a, c) / gcd(b, d)
+
+    if (P["n"] === C_ZERO && this["n"] === C_ZERO) {
+      return newFraction(C_ZERO, C_ONE);
+    }
+    return newFraction(P["n"] * this["n"], gcd(P["n"], this["n"]) * gcd(P["d"], this["d"]));
+  },
+
+  /**
+   * Gets the inverse of the fraction, means numerator and denominator are exchanged
+   *
+   * Ex: new Fraction([-3, 4]).inverse() => -4 / 3
+   **/
+  "inverse": function () {
+    return newFraction(this["s"] * this["d"], this["n"]);
+  },
+
+  /**
+   * Calculates the fraction to some integer exponent
+   *
+   * Ex: new Fraction(-1,2).pow(-3) => -8
+   */
+  "pow": function (a, b) {
+
+    parse(a, b);
+
+    // Trivial case when exp is an integer
+
+    if (P['d'] === C_ONE) {
+
+      if (P['s'] < C_ZERO) {
+        return newFraction((this['s'] * this["d"]) ** P['n'], this["n"] ** P['n']);
+      } else {
+        return newFraction((this['s'] * this["n"]) ** P['n'], this["d"] ** P['n']);
+      }
+    }
+
+    // Negative roots become complex
+    //     (-a/b)^(c/d) = x
+    // ⇔ (-1)^(c/d) * (a/b)^(c/d) = x
+    // ⇔ (cos(pi) + i*sin(pi))^(c/d) * (a/b)^(c/d) = x
+    // ⇔ (cos(c*pi/d) + i*sin(c*pi/d)) * (a/b)^(c/d) = x       # DeMoivre's formula
+    // From which follows that only for c=0 the root is non-complex
+    if (this['s'] < C_ZERO) return null;
+
+    // Now prime factor n and d
+    let N = factorize(this['n']);
+    let D = factorize(this['d']);
+
+    // Exponentiate and take root for n and d individually
+    let n = C_ONE;
+    let d = C_ONE;
+    for (let k in N) {
+      if (k === '1') continue;
+      if (k === '0') {
+        n = C_ZERO;
+        break;
+      }
+      N[k] *= P['n'];
+
+      if (N[k] % P['d'] === C_ZERO) {
+        N[k] /= P['d'];
+      } else return null;
+      n *= BigInt(k) ** N[k];
+    }
+
+    for (let k in D) {
+      if (k === '1') continue;
+      D[k] *= P['n'];
+
+      if (D[k] % P['d'] === C_ZERO) {
+        D[k] /= P['d'];
+      } else return null;
+      d *= BigInt(k) ** D[k];
+    }
+
+    if (P['s'] < C_ZERO) {
+      return newFraction(d, n);
+    }
+    return newFraction(n, d);
+  },
+
+  /**
+   * Calculates the logarithm of a fraction to a given rational base
+   *
+   * Ex: new Fraction(27, 8).log(9, 4) => 3/2
+   */
+  "log": function (a, b) {
+
+    parse(a, b);
+
+    if (this['s'] <= C_ZERO || P['s'] <= C_ZERO) return null;
+
+    const allPrimes = Object.create(null);
+
+    const baseFactors = factorize(P['n']);
+    const T1 = factorize(P['d']);
+
+    const numberFactors = factorize(this['n']);
+    const T2 = factorize(this['d']);
+
+    for (const prime in T1) {
+      baseFactors[prime] = (baseFactors[prime] || C_ZERO) - T1[prime];
+    }
+    for (const prime in T2) {
+      numberFactors[prime] = (numberFactors[prime] || C_ZERO) - T2[prime];
+    }
+
+    for (const prime in baseFactors) {
+      if (prime === '1') continue;
+      allPrimes[prime] = true;
+    }
+    for (const prime in numberFactors) {
+      if (prime === '1') continue;
+      allPrimes[prime] = true;
+    }
+
+    let retN = null;
+    let retD = null;
+
+    // Iterate over all unique primes to determine if a consistent ratio exists
+    for (const prime in allPrimes) {
+
+      const baseExponent = baseFactors[prime] || C_ZERO;
+      const numberExponent = numberFactors[prime] || C_ZERO;
+
+      if (baseExponent === C_ZERO) {
+        if (numberExponent !== C_ZERO) {
+          return null; // Logarithm cannot be expressed as a rational number
+        }
+        continue; // Skip this prime since both exponents are zero
+      }
+
+      // Calculate the ratio of exponents for this prime
+      let curN = numberExponent;
+      let curD = baseExponent;
+
+      // Simplify the current ratio
+      const gcdValue = gcd(curN, curD);
+      curN /= gcdValue;
+      curD /= gcdValue;
+
+      // Check if this is the first ratio; otherwise, ensure ratios are consistent
+      if (retN === null && retD === null) {
+        retN = curN;
+        retD = curD;
+      } else if (curN * retD !== retN * curD) {
+        return null; // Ratios do not match, logarithm cannot be rational
+      }
+    }
+
+    return retN !== null && retD !== null
+      ? newFraction(retN, retD)
+      : null;
+  },
+
+  /**
+   * Check if two rational numbers are the same
+   *
+   * Ex: new Fraction(19.6).equals([98, 5]);
+   **/
+  "equals": function (a, b) {
+
+    parse(a, b);
+    return this["s"] * this["n"] * P["d"] === P["s"] * P["n"] * this["d"];
+  },
+
+  /**
+   * Check if this rational number is less than another
+   *
+   * Ex: new Fraction(19.6).lt([98, 5]);
+   **/
+  "lt": function (a, b) {
+
+    parse(a, b);
+    return this["s"] * this["n"] * P["d"] < P["s"] * P["n"] * this["d"];
+  },
+
+  /**
+   * Check if this rational number is less than or equal another
+   *
+   * Ex: new Fraction(19.6).lt([98, 5]);
+   **/
+  "lte": function (a, b) {
+
+    parse(a, b);
+    return this["s"] * this["n"] * P["d"] <= P["s"] * P["n"] * this["d"];
+  },
+
+  /**
+   * Check if this rational number is greater than another
+   *
+   * Ex: new Fraction(19.6).lt([98, 5]);
+   **/
+  "gt": function (a, b) {
+
+    parse(a, b);
+    return this["s"] * this["n"] * P["d"] > P["s"] * P["n"] * this["d"];
+  },
+
+  /**
+   * Check if this rational number is greater than or equal another
+   *
+   * Ex: new Fraction(19.6).lt([98, 5]);
+   **/
+  "gte": function (a, b) {
+
+    parse(a, b);
+    return this["s"] * this["n"] * P["d"] >= P["s"] * P["n"] * this["d"];
+  },
+
+  /**
+   * Compare two rational numbers
+   * < 0 iff this < that
+   * > 0 iff this > that
+   * = 0 iff this = that
+   *
+   * Ex: new Fraction(19.6).compare([98, 5]);
+   **/
+  "compare": function (a, b) {
+
+    parse(a, b);
+    let t = this["s"] * this["n"] * P["d"] - P["s"] * P["n"] * this["d"];
+
+    return (C_ZERO < t) - (t < C_ZERO);
+  },
+
+  /**
+   * Calculates the ceil of a rational number
+   *
+   * Ex: new Fraction('4.(3)').ceil() => (5 / 1)
+   **/
+  "ceil": function (places) {
+
+    places = C_TEN ** BigInt(places || 0);
+
+    return newFraction(ifloor(this["s"] * places * this["n"] / this["d"]) +
+      (places * this["n"] % this["d"] > C_ZERO && this["s"] >= C_ZERO ? C_ONE : C_ZERO),
+      places);
+  },
+
+  /**
+   * Calculates the floor of a rational number
+   *
+   * Ex: new Fraction('4.(3)').floor() => (4 / 1)
+   **/
+  "floor": function (places) {
+
+    places = C_TEN ** BigInt(places || 0);
+
+    return newFraction(ifloor(this["s"] * places * this["n"] / this["d"]) -
+      (places * this["n"] % this["d"] > C_ZERO && this["s"] < C_ZERO ? C_ONE : C_ZERO),
+      places);
+  },
+
+  /**
+   * Rounds a rational numbers
+   *
+   * Ex: new Fraction('4.(3)').round() => (4 / 1)
+   **/
+  "round": function (places) {
+
+    places = C_TEN ** BigInt(places || 0);
+
+    /* Derivation:
+
+    s >= 0:
+      round(n / d) = ifloor(n / d) + (n % d) / d >= 0.5 ? 1 : 0
+                   = ifloor(n / d) + 2(n % d) >= d ? 1 : 0
+    s < 0:
+      round(n / d) =-ifloor(n / d) - (n % d) / d > 0.5 ? 1 : 0
+                   =-ifloor(n / d) - 2(n % d) > d ? 1 : 0
+
+    =>:
+
+    round(s * n / d) = s * ifloor(n / d) + s * (C + 2(n % d) > d ? 1 : 0)
+        where C = s >= 0 ? 1 : 0, to fix the >= for the positve case.
+    */
+
+    return newFraction(ifloor(this["s"] * places * this["n"] / this["d"]) +
+      this["s"] * ((this["s"] >= C_ZERO ? C_ONE : C_ZERO) + C_TWO * (places * this["n"] % this["d"]) > this["d"] ? C_ONE : C_ZERO),
+      places);
+  },
+
+  /**
+    * Rounds a rational number to a multiple of another rational number
+    *
+    * Ex: new Fraction('0.9').roundTo("1/8") => 7 / 8
+    **/
+  "roundTo": function (a, b) {
+
+    /*
+    k * x/y ≤ a/b < (k+1) * x/y
+    ⇔ k ≤ a/b / (x/y) < (k+1)
+    ⇔ k = floor(a/b * y/x)
+    ⇔ k = floor((a * y) / (b * x))
+    */
+
+    parse(a, b);
+
+    const n = this['n'] * P['d'];
+    const d = this['d'] * P['n'];
+    const r = n % d;
+
+    // round(n / d) = ifloor(n / d) + 2(n % d) >= d ? 1 : 0
+    let k = ifloor(n / d);
+    if (r + r >= d) {
+      k++;
+    }
+    return newFraction(this['s'] * k * P['n'], P['d']);
+  },
+
+  /**
+   * Check if two rational numbers are divisible
+   *
+   * Ex: new Fraction(19.6).divisible(1.5);
+   */
+  "divisible": function (a, b) {
+
+    parse(a, b);
+    if (P['n'] === C_ZERO) return false;
+    return (this['n'] * P['d']) % (P['n'] * this['d']) === C_ZERO;
+  },
+
+  /**
+   * Returns a decimal representation of the fraction
+   *
+   * Ex: new Fraction("100.'91823'").valueOf() => 100.91823918239183
+   **/
+  'valueOf': function () {
+    //if (this['n'] <= MAX_INTEGER && this['d'] <= MAX_INTEGER) {
+    return Number(this['s'] * this['n']) / Number(this['d']);
+    //}
+  },
+
+  /**
+   * Creates a string representation of a fraction with all digits
+   *
+   * Ex: new Fraction("100.'91823'").toString() => "100.(91823)"
+   **/
+  'toString': function (dec = 15) {
+
+    let N = this["n"];
+    let D = this["d"];
+
+    let cycLen = cycleLen(N, D); // Cycle length
+    let cycOff = cycleStart(N, D, cycLen); // Cycle start
+
+    let str = this['s'] < C_ZERO ? "-" : "";
+
+    // Append integer part
+    str += ifloor(N / D);
+
+    N %= D;
+    N *= C_TEN;
+
+    if (N)
+      str += ".";
+
+    if (cycLen) {
+
+      for (let i = cycOff; i--;) {
+        str += ifloor(N / D);
+        N %= D;
+        N *= C_TEN;
+      }
+      str += "(";
+      for (let i = cycLen; i--;) {
+        str += ifloor(N / D);
+        N %= D;
+        N *= C_TEN;
+      }
+      str += ")";
+    } else {
+      for (let i = dec; N && i--;) {
+        str += ifloor(N / D);
+        N %= D;
+        N *= C_TEN;
+      }
+    }
+    return str;
+  },
+
+  /**
+   * Returns a string-fraction representation of a Fraction object
+   *
+   * Ex: new Fraction("1.'3'").toFraction() => "4 1/3"
+   **/
+  'toFraction': function (showMixed = false) {
+
+    let n = this["n"];
+    let d = this["d"];
+    let str = this['s'] < C_ZERO ? "-" : "";
+
+    if (d === C_ONE) {
+      str += n;
+    } else {
+      const whole = ifloor(n / d);
+      if (showMixed && whole > C_ZERO) {
+        str += whole;
+        str += " ";
+        n %= d;
+      }
+
+      str += n;
+      str += '/';
+      str += d;
+    }
+    return str;
+  },
+
+  /**
+   * Returns a latex representation of a Fraction object
+   *
+   * Ex: new Fraction("1.'3'").toLatex() => "\frac{4}{3}"
+   **/
+  'toLatex': function (showMixed = false) {
+
+    let n = this["n"];
+    let d = this["d"];
+    let str = this['s'] < C_ZERO ? "-" : "";
+
+    if (d === C_ONE) {
+      str += n;
+    } else {
+      const whole = ifloor(n / d);
+      if (showMixed && whole > C_ZERO) {
+        str += whole;
+        n %= d;
+      }
+
+      str += "\\frac{";
+      str += n;
+      str += '}{';
+      str += d;
+      str += '}';
+    }
+    return str;
+  },
+
+  /**
+   * Returns an array of continued fraction elements
+   *
+   * Ex: new Fraction("7/8").toContinued() => [0,1,7]
+   */
+  'toContinued': function () {
+
+    let a = this['n'];
+    let b = this['d'];
+    const res = [];
+
+    while (b) {
+      res.push(ifloor(a / b));
+      const t = a % b;
+      a = b;
+      b = t;
+    }
+    return res;
+  },
+
+  "simplify": function (eps = 1e-3) {
+
+    // Continued fractions give best approximations for a max denominator,
+    // generally outperforming mediants in denominator–accuracy trade-offs.
+    // Semiconvergents can further reduce the denominator within tolerance.
+
+    const ieps = BigInt(Math.ceil(1 / eps));
+
+    const thisABS = this['abs']();
+    const cont = thisABS['toContinued']();
+
+    for (let i = 1; i < cont.length; i++) {
+
+      let s = newFraction(cont[i - 1], C_ONE);
+      for (let k = i - 2; k >= 0; k--) {
+        s = s['inverse']()['add'](cont[k]);
+      }
+
+      let t = s['sub'](thisABS);
+      if (t['n'] * ieps < t['d']) { // More robust than Math.abs(t.valueOf()) < eps
+        return s['mul'](this['s']);
+      }
+    }
+    return this;
+  }
+};
+export {
+  Fraction as default, Fraction
+};
Index: node_modules/fraction.js/examples/angles.js
===================================================================
--- node_modules/fraction.js/examples/angles.js	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
+++ node_modules/fraction.js/examples/angles.js	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
@@ -0,0 +1,26 @@
+/*
+Fraction.js v5.0.0 10/1/2024
+https://raw.org/article/rational-numbers-in-javascript/
+
+Copyright (c) 2024, Robert Eisele (https://raw.org/)
+Licensed under the MIT license.
+*/
+
+// This example generates a list of angles with human readable radians
+
+var Fraction = require('fraction.js');
+
+var tab = [];
+for (var d = 1; d <= 360; d++) {
+
+   var pi = Fraction(2, 360).mul(d);
+   var tau = Fraction(1, 360).mul(d);
+
+   if (pi.d <= 6n && pi.d != 5n)
+      tab.push([
+         d,
+         pi.toFraction() + "pi",
+         tau.toFraction() + "tau"]);
+}
+
+console.table(tab);
Index: node_modules/fraction.js/examples/approx.js
===================================================================
--- node_modules/fraction.js/examples/approx.js	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
+++ node_modules/fraction.js/examples/approx.js	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
@@ -0,0 +1,54 @@
+/*
+Fraction.js v5.0.0 10/1/2024
+https://raw.org/article/rational-numbers-in-javascript/
+
+Copyright (c) 2024, Robert Eisele (https://raw.org/)
+Licensed under the MIT license.
+*/
+const Fraction = require('fraction.js');
+
+// Another rational approximation, not using Farey Sequences but Binary Search using the mediant
+function approximate(p, precision) {
+
+  var num1 = Math.floor(p);
+  var den1 = 1;
+
+  var num2 = num1 + 1;
+  var den2 = 1;
+
+  if (p !== num1) {
+
+    while (den1 <= precision && den2 <= precision) {
+
+      var m = (num1 + num2) / (den1 + den2);
+
+      if (p === m) {
+
+        if (den1 + den2 <= precision) {
+          den1 += den2;
+          num1 += num2;
+          den2 = precision + 1;
+        } else if (den1 > den2) {
+          den2 = precision + 1;
+        } else {
+          den1 = precision + 1;
+        }
+        break;
+
+      } else if (p < m) {
+        num2 += num1;
+        den2 += den1;
+      } else {
+        num1 += num2;
+        den1 += den2;
+      }
+    }
+  }
+
+  if (den1 > precision) {
+    den1 = den2;
+    num1 = num2;
+  }
+  return new Fraction(num1, den1);
+}
+
Index: node_modules/fraction.js/examples/egyptian.js
===================================================================
--- node_modules/fraction.js/examples/egyptian.js	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
+++ node_modules/fraction.js/examples/egyptian.js	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
@@ -0,0 +1,24 @@
+/*
+Fraction.js v5.0.0 10/1/2024
+https://raw.org/article/rational-numbers-in-javascript/
+
+Copyright (c) 2024, Robert Eisele (https://raw.org/)
+Licensed under the MIT license.
+*/
+const Fraction = require('fraction.js');
+
+// Based on http://www.maths.surrey.ac.uk/hosted-sites/R.Knott/Fractions/egyptian.html
+function egyptian(a, b) {
+
+  var res = [];
+
+  do {
+    var t = Math.ceil(b / a);
+    var x = new Fraction(a, b).sub(1, t);
+    res.push(t);
+    a = Number(x.n);
+    b = Number(x.d);
+  } while (a !== 0n);
+  return res;
+}
+console.log("1 / " + egyptian(521, 1050).join(" + 1 / "));
Index: node_modules/fraction.js/examples/hesse-convergence.js
===================================================================
--- node_modules/fraction.js/examples/hesse-convergence.js	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
+++ node_modules/fraction.js/examples/hesse-convergence.js	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
@@ -0,0 +1,111 @@
+/*
+Fraction.js v5.0.0 10/1/2024
+https://raw.org/article/rational-numbers-in-javascript/
+
+Copyright (c) 2024, Robert Eisele (https://raw.org/)
+Licensed under the MIT license.
+*/
+const Fraction = require('fraction.js');
+
+/*
+We have the polynom f(x) = 1/3x_1^2 + x_2^2 + x_1 * x_2 + 3
+
+The gradient of f(x):
+
+grad(x) = | x_1^2+x_2 |
+          | 2x_2+x_1  |
+
+And thus the Hesse-Matrix H:
+| 2x_1  1 |
+|  1    2 |
+
+The inverse Hesse-Matrix H^-1 is
+| -2 / (1-4x_1)    1 / (1 - 4x_1)     |
+| 1 / (1 - 4x_1)   -2x_1 / (1 - 4x_1) |
+
+We now want to find lim ->oo x[n], with the starting element of (3 2)^T
+
+*/
+
+// Get the Hesse Matrix
+function H(x) {
+
+  var z = Fraction(1).sub(Fraction(4).mul(x[0]));
+
+  return [
+    Fraction(-2).div(z),
+    Fraction(1).div(z),
+    Fraction(1).div(z),
+    Fraction(-2).mul(x[0]).div(z),
+  ];
+}
+
+// Get the gradient of f(x)
+function grad(x) {
+
+  return [
+    Fraction(x[0]).mul(x[0]).add(x[1]),
+    Fraction(2).mul(x[1]).add(x[0])
+  ];
+}
+
+// A simple matrix multiplication helper
+function matrMult(m, v) {
+
+  return [
+    Fraction(m[0]).mul(v[0]).add(Fraction(m[1]).mul(v[1])),
+    Fraction(m[2]).mul(v[0]).add(Fraction(m[3]).mul(v[1]))
+  ];
+}
+
+// A simple vector subtraction helper
+function vecSub(a, b) {
+
+  return [
+    Fraction(a[0]).sub(b[0]),
+    Fraction(a[1]).sub(b[1])
+  ];
+}
+
+// Main function, gets a vector and the actual index
+function run(V, j) {
+
+  var t = H(V);
+  //console.log("H(X)");
+  for (var i in t) {
+
+    //	console.log(t[i].toFraction());
+  }
+
+  var s = grad(V);
+  //console.log("vf(X)");
+  for (var i in s) {
+
+    //	console.log(s[i].toFraction());
+  }
+
+  //console.log("multiplication");
+  var r = matrMult(t, s);
+  for (var i in r) {
+
+    //	console.log(r[i].toFraction());
+  }
+
+  var R = (vecSub(V, r));
+
+  console.log("X" + j);
+  console.log(R[0].toFraction(), "= " + R[0].valueOf());
+  console.log(R[1].toFraction(), "= " + R[1].valueOf());
+  console.log("\n");
+
+  return R;
+}
+
+
+// Set the starting vector
+var v = [3, 2];
+
+for (var i = 0; i < 15; i++) {
+
+  v = run(v, i);
+}
Index: node_modules/fraction.js/examples/integrate.js
===================================================================
--- node_modules/fraction.js/examples/integrate.js	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
+++ node_modules/fraction.js/examples/integrate.js	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
@@ -0,0 +1,67 @@
+/*
+Fraction.js v5.0.0 10/1/2024
+https://raw.org/article/rational-numbers-in-javascript/
+
+Copyright (c) 2024, Robert Eisele (https://raw.org/)
+Licensed under the MIT license.
+*/
+const Fraction = require('fraction.js');
+
+// NOTE: This is a nice example, but a stable version of this is served with Polynomial.js: 
+// https://github.com/rawify/Polynomial.js
+
+function integrate(poly) {
+
+    poly = poly.replace(/\s+/g, "");
+
+    var regex = /(\([+-]?[0-9/]+\)|[+-]?[0-9/]+)x(?:\^(\([+-]?[0-9/]+\)|[+-]?[0-9]+))?/g;
+    var arr;
+    var res = {};
+    while (null !== (arr = regex.exec(poly))) {
+
+        var a = (arr[1] || "1").replace("(", "").replace(")", "").split("/");
+        var b = (arr[2] || "1").replace("(", "").replace(")", "").split("/");
+
+        var exp = new Fraction(b).add(1);
+        var key = "" + exp;
+
+        if (res[key] !== undefined) {
+            res[key] = { x: new Fraction(a).div(exp).add(res[key].x), e: exp };
+        } else {
+            res[key] = { x: new Fraction(a).div(exp), e: exp };
+        }
+    }
+
+    var str = "";
+    var c = 0;
+    for (var i in res) {
+        if (res[i].x.s !== -1n && c > 0) {
+            str += "+";
+        } else if (res[i].x.s === -1n) {
+            str += "-";
+        }
+        if (res[i].x.n !== res[i].x.d) {
+            if (res[i].x.d !== 1n) {
+                str += res[i].x.n + "/" + res[i].x.d;
+            } else {
+                str += res[i].x.n;
+            }
+        }
+        str += "x";
+        if (res[i].e.n !== res[i].e.d) {
+            str += "^";
+            if (res[i].e.d !== 1n) {
+                str += "(" + res[i].e.n + "/" + res[i].e.d + ")";
+            } else {
+                str += res[i].e.n;
+            }
+        }
+        c++;
+    }
+    return str;
+}
+
+var poly = "-2/3x^3-2x^2+3x+8x^3-1/3x^(4/8)";
+
+console.log("f(x): " + poly);
+console.log("F(x): " + integrate(poly));
Index: node_modules/fraction.js/examples/ratio-chain.js
===================================================================
--- node_modules/fraction.js/examples/ratio-chain.js	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
+++ node_modules/fraction.js/examples/ratio-chain.js	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
@@ -0,0 +1,24 @@
+/*
+Given the ratio a : b : c = 2 : 3 : 4 
+What is c, given a = 40?
+
+A general ratio chain is a_1 : a_2 : a_3 : ... : a_n = r_1 : r2 : r_3 : ... : r_n.
+Now each term can be expressed as a_i = r_i * x for some unknown proportional constant x.
+If a_k is known it follows that x = a_k / r_k. Substituting x into the first equation yields
+a_i = r_i / r_k * a_k.
+
+Given an array r and a given value a_k, the following function calculates all a_i:
+*/
+
+function calculateRatios(r, a_k, k) {    
+    const x = Fraction(a_k).div(r[k]);
+    return r.map(r_i => x.mul(r_i));
+}
+
+// Example usage:
+const r = [2, 3, 4]; // Ratio array representing a : b : c = 2 : 3 : 4
+const a_k = 40; // Given value of a (corresponding to r[0])
+const k = 0; // Index of the known value (a corresponds to r[0])
+
+const result = calculateRatios(r, a_k, k);
+console.log(result); // Output: [40, 60, 80]
Index: node_modules/fraction.js/examples/rational-pow.js
===================================================================
--- node_modules/fraction.js/examples/rational-pow.js	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
+++ node_modules/fraction.js/examples/rational-pow.js	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
@@ -0,0 +1,29 @@
+/*
+Fraction.js v5.0.0 10/1/2024
+https://raw.org/article/rational-numbers-in-javascript/
+
+Copyright (c) 2024, Robert Eisele (https://raw.org/)
+Licensed under the MIT license.
+*/
+const Fraction = require('fraction.js');
+ 
+// Calculates (a/b)^(c/d) if result is rational
+// Derivation: https://raw.org/book/analysis/rational-numbers/
+function root(a, b, c, d) {
+
+  // Initial estimate
+  let x = Fraction(100 * (Math.floor(Math.pow(a / b, c / d)) || 1), 100);
+  const abc = Fraction(a, b).pow(c);
+
+  for (let i = 0; i < 30; i++) {
+    const n = abc.mul(x.pow(1 - d)).sub(x).div(d).add(x)
+
+    if (x.n === n.n && x.d === n.d) {
+      return n;
+    }
+    x = n;
+  }
+  return null;
+}
+
+root(18, 2, 1, 2); // 3/1
Index: node_modules/fraction.js/examples/tape-measure.js
===================================================================
--- node_modules/fraction.js/examples/tape-measure.js	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
+++ node_modules/fraction.js/examples/tape-measure.js	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
@@ -0,0 +1,16 @@
+/*
+Fraction.js v5.0.0 10/1/2024
+https://raw.org/article/rational-numbers-in-javascript/
+
+Copyright (c) 2024, Robert Eisele (https://raw.org/)
+Licensed under the MIT license.
+*/
+const Fraction = require('fraction.js');
+
+function closestTapeMeasure(frac) {
+
+    // A tape measure is usually divided in parts of 1/16
+
+    return Fraction(frac).roundTo("1/16");
+}
+console.log(closestTapeMeasure("1/3")); // 5/16
Index: node_modules/fraction.js/examples/toFraction.js
===================================================================
--- node_modules/fraction.js/examples/toFraction.js	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
+++ node_modules/fraction.js/examples/toFraction.js	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
@@ -0,0 +1,35 @@
+/*
+Fraction.js v5.0.0 10/1/2024
+https://raw.org/article/rational-numbers-in-javascript/
+
+Copyright (c) 2024, Robert Eisele (https://raw.org/)
+Licensed under the MIT license.
+*/
+
+const Fraction = require('fraction.js');
+
+function toFraction(frac) {
+
+  var map = {
+    '1:4': "¼",
+    '1:2': "½",
+    '3:4': "¾",
+    '1:7': "⅐",
+    '1:9': "⅑",
+    '1:10': "⅒",
+    '1:3': "⅓",
+    '2:3': "⅔",
+    '1:5': "⅕",
+    '2:5': "⅖",
+    '3:5': "⅗",
+    '4:5': "⅘",
+    '1:6': "⅙",
+    '5:6': "⅚",
+    '1:8': "⅛",
+    '3:8': "⅜",
+    '5:8': "⅝",
+    '7:8': "⅞"
+  };
+  return map[frac.n + ":" + frac.d] || frac.toFraction(false);
+}
+console.log(toFraction(Fraction(0.25))); // ¼
Index: node_modules/fraction.js/examples/valueOfPi.js
===================================================================
--- node_modules/fraction.js/examples/valueOfPi.js	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
+++ node_modules/fraction.js/examples/valueOfPi.js	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
@@ -0,0 +1,42 @@
+/*
+Fraction.js v5.0.0 10/1/2024
+https://raw.org/article/rational-numbers-in-javascript/
+
+Copyright (c) 2024, Robert Eisele (https://raw.org/)
+Licensed under the MIT license.
+*/
+
+var Fraction = require("fraction.js")
+
+function valueOfPi(val) {
+
+  let minLen = Infinity, minI = 0, min = null;
+  const choose = [val, val * Math.PI, val / Math.PI];
+  for (let i = 0; i < choose.length; i++) {
+    let el = new Fraction(choose[i]).simplify(1e-13);
+    let len = Math.log(Number(el.n) + 1) + Math.log(Number(el.d));
+    if (len < minLen) {
+      minLen = len;
+      minI = i;
+      min = el;
+    }
+  }
+
+  if (minI == 2) {
+    return min.toFraction().replace(/(\d+)(\/\d+)?/, (_, p, q) =>
+      (p == "1" ? "" : p) + "π" + (q || ""));
+  }
+
+  if (minI == 1) {
+    return min.toFraction().replace(/(\d+)(\/\d+)?/, (_, p, q) =>
+      p + (!q ? "/π" : "/(" + q.slice(1) + "π)"));
+  }
+  return min.toFraction();
+}
+
+console.log(valueOfPi(-3)); // -3
+console.log(valueOfPi(4 * Math.PI)); // 4π
+console.log(valueOfPi(3.14)); // 157/50
+console.log(valueOfPi(3 / 2 * Math.PI)); // 3π/2
+console.log(valueOfPi(Math.PI / 2)); // π/2
+console.log(valueOfPi(-1 / (2 * Math.PI))); // -1/(2π)
Index: node_modules/fraction.js/fraction.d.mts
===================================================================
--- node_modules/fraction.js/fraction.d.mts	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
+++ node_modules/fraction.js/fraction.d.mts	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
@@ -0,0 +1,79 @@
+/**
+ * Interface representing a fraction with numerator and denominator.
+ */
+export interface NumeratorDenominator {
+    n: number | bigint;
+    d: number | bigint;
+}
+
+/**
+ * Type for handling multiple types of input for Fraction operations.
+ */
+export type FractionInput =
+    | Fraction
+    | number
+    | bigint
+    | string
+    | [number | bigint | string, number | bigint | string]
+    | NumeratorDenominator;
+
+/**
+ * Function signature for Fraction operations like add, sub, mul, etc.
+ */
+export type FractionParam = {
+    (numerator: number | bigint, denominator: number | bigint): Fraction;
+    (num: FractionInput): Fraction;
+};
+
+/**
+ * Fraction class representing a rational number with numerator and denominator.
+ */
+declare class Fraction {
+    constructor();
+    constructor(num: FractionInput);
+    constructor(numerator: number | bigint, denominator: number | bigint);
+
+    s: bigint;
+    n: bigint;
+    d: bigint;
+
+    abs(): Fraction;
+    neg(): Fraction;
+
+    add: FractionParam;
+    sub: FractionParam;
+    mul: FractionParam;
+    div: FractionParam;
+    pow: FractionParam;
+    log: FractionParam;
+    gcd: FractionParam;
+    lcm: FractionParam;
+
+    mod(): Fraction;
+    mod(num: FractionInput): Fraction;
+
+    ceil(places?: number): Fraction;
+    floor(places?: number): Fraction;
+    round(places?: number): Fraction;
+    roundTo: FractionParam;
+
+    inverse(): Fraction;
+    simplify(eps?: number): Fraction;
+
+    equals(num: FractionInput): boolean;
+    lt(num: FractionInput): boolean;
+    lte(num: FractionInput): boolean;
+    gt(num: FractionInput): boolean;
+    gte(num: FractionInput): boolean;
+    compare(num: FractionInput): number;
+    divisible(num: FractionInput): boolean;
+
+    valueOf(): number;
+    toString(decimalPlaces?: number): string;
+    toLatex(showMixed?: boolean): string;
+    toFraction(showMixed?: boolean): string;
+    toContinued(): bigint[];
+    clone(): Fraction;
+}
+
+export { Fraction as default, Fraction };
Index: node_modules/fraction.js/fraction.d.ts
===================================================================
--- node_modules/fraction.js/fraction.d.ts	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
+++ node_modules/fraction.js/fraction.d.ts	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
@@ -0,0 +1,79 @@
+declare class Fraction {
+  constructor();
+  constructor(num: Fraction.FractionInput);
+  constructor(numerator: number | bigint, denominator: number | bigint);
+
+  s: bigint;
+  n: bigint;
+  d: bigint;
+
+  abs(): Fraction;
+  neg(): Fraction;
+
+  add: Fraction.FractionParam;
+  sub: Fraction.FractionParam;
+  mul: Fraction.FractionParam;
+  div: Fraction.FractionParam;
+  pow: Fraction.FractionParam;
+  log: Fraction.FractionParam;
+  gcd: Fraction.FractionParam;
+  lcm: Fraction.FractionParam;
+
+  mod(): Fraction;
+  mod(num: Fraction.FractionInput): Fraction;
+
+  ceil(places?: number): Fraction;
+  floor(places?: number): Fraction;
+  round(places?: number): Fraction;
+  roundTo: Fraction.FractionParam;
+
+  inverse(): Fraction;
+  simplify(eps?: number): Fraction;
+
+  equals(num: Fraction.FractionInput): boolean;
+  lt(num: Fraction.FractionInput): boolean;
+  lte(num: Fraction.FractionInput): boolean;
+  gt(num: Fraction.FractionInput): boolean;
+  gte(num: Fraction.FractionInput): boolean;
+  compare(num: Fraction.FractionInput): number;
+  divisible(num: Fraction.FractionInput): boolean;
+
+  valueOf(): number;
+  toString(decimalPlaces?: number): string;
+  toLatex(showMixed?: boolean): string;
+  toFraction(showMixed?: boolean): string;
+  toContinued(): bigint[];
+  clone(): Fraction;
+
+  static default: typeof Fraction;
+  static Fraction: typeof Fraction;
+}
+
+declare namespace Fraction {
+  interface NumeratorDenominator { n: number | bigint; d: number | bigint; }
+  type FractionInput =
+    | Fraction
+    | number
+    | bigint
+    | string
+    | [number | bigint | string, number | bigint | string]
+    | NumeratorDenominator;
+
+  type FractionParam = {
+    (numerator: number | bigint, denominator: number | bigint): Fraction;
+    (num: FractionInput): Fraction;
+  };
+}
+
+/**
+ * Export matches CJS runtime:
+ *   module.exports = Fraction;
+ *   module.exports.default  = Fraction;
+ *   module.exports.Fraction = Fraction;
+ */
+declare const FractionExport: typeof Fraction & {
+  default: typeof Fraction;
+  Fraction: typeof Fraction;
+};
+
+export = FractionExport;
Index: node_modules/fraction.js/package.json
===================================================================
--- node_modules/fraction.js/package.json	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
+++ node_modules/fraction.js/package.json	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
@@ -0,0 +1,81 @@
+{
+  "name": "fraction.js",
+  "title": "Fraction.js",
+  "version": "5.3.4",
+  "description": "The RAW rational numbers library",
+  "homepage": "https://raw.org/article/rational-numbers-in-javascript/",
+  "bugs": "https://github.com/rawify/Fraction.js/issues",
+  "keywords": [
+    "math",
+    "numbers",
+    "parser",
+    "ratio",
+    "fraction",
+    "fractions",
+    "rational",
+    "rationals",
+    "rational numbers",
+    "bigint",
+    "arbitrary precision",
+    "mixed numbers",
+    "decimal",
+    "numerator",
+    "denominator",
+    "simplification"
+  ],
+  "private": false,
+  "main": "./dist/fraction.js",
+  "module": "./dist/fraction.mjs",
+  "browser": "./dist/fraction.min.js",
+  "unpkg": "./dist/fraction.min.js",
+  "types": "./fraction.d.mts",
+  "exports": {
+    ".": {
+      "types": {
+        "import": "./fraction.d.mts",
+        "require": "./fraction.d.ts"
+      },
+      "import": "./dist/fraction.mjs",
+      "require": "./dist/fraction.js",
+      "browser": "./dist/fraction.min.js"
+    },
+    "./package.json": "./package.json"
+  },
+  "typesVersions": {
+    "<4.7": {
+      "*": [
+        "fraction.d.ts"
+      ]
+    }
+  },
+  "sideEffects": false,
+  "repository": {
+    "type": "git",
+    "url": "git+ssh://git@github.com/rawify/Fraction.js.git"
+  },
+  "funding": {
+    "type": "github",
+    "url": "https://github.com/sponsors/rawify"
+  },
+  "author": {
+    "name": "Robert Eisele",
+    "email": "robert@raw.org",
+    "url": "https://raw.org/"
+  },
+  "license": "MIT",
+  "engines": {
+    "node": "*"
+  },
+  "directories": {
+    "example": "examples",
+    "test": "tests"
+  },
+  "scripts": {
+    "build": "crude-build Fraction",
+    "test": "mocha tests/*.js"
+  },
+  "devDependencies": {
+    "crude-build": "^0.1.2",
+    "mocha": "*"
+  }
+}
Index: node_modules/fraction.js/src/fraction.js
===================================================================
--- node_modules/fraction.js/src/fraction.js	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
+++ node_modules/fraction.js/src/fraction.js	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
@@ -0,0 +1,1046 @@
+/**
+ * @license Fraction.js v5.3.4 8/22/2025
+ * https://raw.org/article/rational-numbers-in-javascript/
+ *
+ * Copyright (c) 2025, Robert Eisele (https://raw.org/)
+ * Licensed under the MIT license.
+ **/
+
+/**
+ *
+ * This class offers the possibility to calculate fractions.
+ * You can pass a fraction in different formats. Either as array, as double, as string or as an integer.
+ *
+ * Array/Object form
+ * [ 0 => <numerator>, 1 => <denominator> ]
+ * { n => <numerator>, d => <denominator> }
+ *
+ * Integer form
+ * - Single integer value as BigInt or Number
+ *
+ * Double form
+ * - Single double value as Number
+ *
+ * String form
+ * 123.456 - a simple double
+ * 123/456 - a string fraction
+ * 123.'456' - a double with repeating decimal places
+ * 123.(456) - synonym
+ * 123.45'6' - a double with repeating last place
+ * 123.45(6) - synonym
+ *
+ * Example:
+ * let f = new Fraction("9.4'31'");
+ * f.mul([-4, 3]).div(4.9);
+ *
+ */
+
+// Set Identity function to downgrade BigInt to Number if needed
+if (typeof BigInt === 'undefined') BigInt = function (n) { if (isNaN(n)) throw new Error(""); return n; };
+
+const C_ZERO = BigInt(0);
+const C_ONE = BigInt(1);
+const C_TWO = BigInt(2);
+const C_THREE = BigInt(3);
+const C_FIVE = BigInt(5);
+const C_TEN = BigInt(10);
+const MAX_INTEGER = BigInt(Number.MAX_SAFE_INTEGER);
+
+// Maximum search depth for cyclic rational numbers. 2000 should be more than enough.
+// Example: 1/7 = 0.(142857) has 6 repeating decimal places.
+// If MAX_CYCLE_LEN gets reduced, long cycles will not be detected and toString() only gets the first 10 digits
+const MAX_CYCLE_LEN = 2000;
+
+// Parsed data to avoid calling "new" all the time
+const P = {
+  "s": C_ONE,
+  "n": C_ZERO,
+  "d": C_ONE
+};
+
+function assign(n, s) {
+
+  try {
+    n = BigInt(n);
+  } catch (e) {
+    throw InvalidParameter();
+  }
+  return n * s;
+}
+
+function ifloor(x) {
+  return typeof x === 'bigint' ? x : Math.floor(x);
+}
+
+// Creates a new Fraction internally without the need of the bulky constructor
+function newFraction(n, d) {
+
+  if (d === C_ZERO) {
+    throw DivisionByZero();
+  }
+
+  const f = Object.create(Fraction.prototype);
+  f["s"] = n < C_ZERO ? -C_ONE : C_ONE;
+
+  n = n < C_ZERO ? -n : n;
+
+  const a = gcd(n, d);
+
+  f["n"] = n / a;
+  f["d"] = d / a;
+  return f;
+}
+
+const FACTORSTEPS = [C_TWO * C_TWO, C_TWO, C_TWO * C_TWO, C_TWO, C_TWO * C_TWO, C_TWO * C_THREE, C_TWO, C_TWO * C_THREE]; // repeats
+function factorize(n) {
+
+  const factors = Object.create(null);
+  if (n <= C_ONE) {
+    factors[n] = C_ONE;
+    return factors;
+  }
+
+  const add = (p) => { factors[p] = (factors[p] || C_ZERO) + C_ONE; };
+
+  while (n % C_TWO === C_ZERO) { add(C_TWO); n /= C_TWO; }
+  while (n % C_THREE === C_ZERO) { add(C_THREE); n /= C_THREE; }
+  while (n % C_FIVE === C_ZERO) { add(C_FIVE); n /= C_FIVE; }
+
+  // 30-wheel trial division: test only residues coprime to 2*3*5
+  // Residue step pattern after 5: 7,11,13,17,19,23,29,31, ...
+  for (let si = 0, p = C_TWO + C_FIVE; p * p <= n;) {
+    while (n % p === C_ZERO) { add(p); n /= p; }
+    p += FACTORSTEPS[si];
+    si = (si + 1) & 7; // fast modulo 8
+  }
+  if (n > C_ONE) add(n);
+  return factors;
+}
+
+const parse = function (p1, p2) {
+
+  let n = C_ZERO, d = C_ONE, s = C_ONE;
+
+  if (p1 === undefined || p1 === null) { // No argument
+    /* void */
+  } else if (p2 !== undefined) { // Two arguments
+
+    if (typeof p1 === "bigint") {
+      n = p1;
+    } else if (isNaN(p1)) {
+      throw InvalidParameter();
+    } else if (p1 % 1 !== 0) {
+      throw NonIntegerParameter();
+    } else {
+      n = BigInt(p1);
+    }
+
+    if (typeof p2 === "bigint") {
+      d = p2;
+    } else if (isNaN(p2)) {
+      throw InvalidParameter();
+    } else if (p2 % 1 !== 0) {
+      throw NonIntegerParameter();
+    } else {
+      d = BigInt(p2);
+    }
+
+    s = n * d;
+
+  } else if (typeof p1 === "object") {
+    if ("d" in p1 && "n" in p1) {
+      n = BigInt(p1["n"]);
+      d = BigInt(p1["d"]);
+      if ("s" in p1)
+        n *= BigInt(p1["s"]);
+    } else if (0 in p1) {
+      n = BigInt(p1[0]);
+      if (1 in p1)
+        d = BigInt(p1[1]);
+    } else if (typeof p1 === "bigint") {
+      n = p1;
+    } else {
+      throw InvalidParameter();
+    }
+    s = n * d;
+  } else if (typeof p1 === "number") {
+
+    if (isNaN(p1)) {
+      throw InvalidParameter();
+    }
+
+    if (p1 < 0) {
+      s = -C_ONE;
+      p1 = -p1;
+    }
+
+    if (p1 % 1 === 0) {
+      n = BigInt(p1);
+    } else {
+
+      let z = 1;
+
+      let A = 0, B = 1;
+      let C = 1, D = 1;
+
+      let N = 10000000;
+
+      if (p1 >= 1) {
+        z = 10 ** Math.floor(1 + Math.log10(p1));
+        p1 /= z;
+      }
+
+      // Using Farey Sequences
+
+      while (B <= N && D <= N) {
+        let M = (A + C) / (B + D);
+
+        if (p1 === M) {
+          if (B + D <= N) {
+            n = A + C;
+            d = B + D;
+          } else if (D > B) {
+            n = C;
+            d = D;
+          } else {
+            n = A;
+            d = B;
+          }
+          break;
+
+        } else {
+
+          if (p1 > M) {
+            A += C;
+            B += D;
+          } else {
+            C += A;
+            D += B;
+          }
+
+          if (B > N) {
+            n = C;
+            d = D;
+          } else {
+            n = A;
+            d = B;
+          }
+        }
+      }
+      n = BigInt(n) * BigInt(z);
+      d = BigInt(d);
+    }
+
+  } else if (typeof p1 === "string") {
+
+    let ndx = 0;
+
+    let v = C_ZERO, w = C_ZERO, x = C_ZERO, y = C_ONE, z = C_ONE;
+
+    let match = p1.replace(/_/g, '').match(/\d+|./g);
+
+    if (match === null)
+      throw InvalidParameter();
+
+    if (match[ndx] === '-') {// Check for minus sign at the beginning
+      s = -C_ONE;
+      ndx++;
+    } else if (match[ndx] === '+') {// Check for plus sign at the beginning
+      ndx++;
+    }
+
+    if (match.length === ndx + 1) { // Check if it's just a simple number "1234"
+      w = assign(match[ndx++], s);
+    } else if (match[ndx + 1] === '.' || match[ndx] === '.') { // Check if it's a decimal number
+
+      if (match[ndx] !== '.') { // Handle 0.5 and .5
+        v = assign(match[ndx++], s);
+      }
+      ndx++;
+
+      // Check for decimal places
+      if (ndx + 1 === match.length || match[ndx + 1] === '(' && match[ndx + 3] === ')' || match[ndx + 1] === "'" && match[ndx + 3] === "'") {
+        w = assign(match[ndx], s);
+        y = C_TEN ** BigInt(match[ndx].length);
+        ndx++;
+      }
+
+      // Check for repeating places
+      if (match[ndx] === '(' && match[ndx + 2] === ')' || match[ndx] === "'" && match[ndx + 2] === "'") {
+        x = assign(match[ndx + 1], s);
+        z = C_TEN ** BigInt(match[ndx + 1].length) - C_ONE;
+        ndx += 3;
+      }
+
+    } else if (match[ndx + 1] === '/' || match[ndx + 1] === ':') { // Check for a simple fraction "123/456" or "123:456"
+      w = assign(match[ndx], s);
+      y = assign(match[ndx + 2], C_ONE);
+      ndx += 3;
+    } else if (match[ndx + 3] === '/' && match[ndx + 1] === ' ') { // Check for a complex fraction "123 1/2"
+      v = assign(match[ndx], s);
+      w = assign(match[ndx + 2], s);
+      y = assign(match[ndx + 4], C_ONE);
+      ndx += 5;
+    }
+
+    if (match.length <= ndx) { // Check for more tokens on the stack
+      d = y * z;
+      s = /* void */
+        n = x + d * v + z * w;
+    } else {
+      throw InvalidParameter();
+    }
+
+  } else if (typeof p1 === "bigint") {
+    n = p1;
+    s = p1;
+    d = C_ONE;
+  } else {
+    throw InvalidParameter();
+  }
+
+  if (d === C_ZERO) {
+    throw DivisionByZero();
+  }
+
+  P["s"] = s < C_ZERO ? -C_ONE : C_ONE;
+  P["n"] = n < C_ZERO ? -n : n;
+  P["d"] = d < C_ZERO ? -d : d;
+};
+
+function modpow(b, e, m) {
+
+  let r = C_ONE;
+  for (; e > C_ZERO; b = (b * b) % m, e >>= C_ONE) {
+
+    if (e & C_ONE) {
+      r = (r * b) % m;
+    }
+  }
+  return r;
+}
+
+function cycleLen(n, d) {
+
+  for (; d % C_TWO === C_ZERO;
+    d /= C_TWO) {
+  }
+
+  for (; d % C_FIVE === C_ZERO;
+    d /= C_FIVE) {
+  }
+
+  if (d === C_ONE) // Catch non-cyclic numbers
+    return C_ZERO;
+
+  // If we would like to compute really large numbers quicker, we could make use of Fermat's little theorem:
+  // 10^(d-1) % d == 1
+  // However, we don't need such large numbers and MAX_CYCLE_LEN should be the capstone,
+  // as we want to translate the numbers to strings.
+
+  let rem = C_TEN % d;
+  let t = 1;
+
+  for (; rem !== C_ONE; t++) {
+    rem = rem * C_TEN % d;
+
+    if (t > MAX_CYCLE_LEN)
+      return C_ZERO; // Returning 0 here means that we don't print it as a cyclic number. It's likely that the answer is `d-1`
+  }
+  return BigInt(t);
+}
+
+function cycleStart(n, d, len) {
+
+  let rem1 = C_ONE;
+  let rem2 = modpow(C_TEN, len, d);
+
+  for (let t = 0; t < 300; t++) { // s < ~log10(Number.MAX_VALUE)
+    // Solve 10^s == 10^(s+t) (mod d)
+
+    if (rem1 === rem2)
+      return BigInt(t);
+
+    rem1 = rem1 * C_TEN % d;
+    rem2 = rem2 * C_TEN % d;
+  }
+  return 0;
+}
+
+function gcd(a, b) {
+
+  if (!a)
+    return b;
+  if (!b)
+    return a;
+
+  while (1) {
+    a %= b;
+    if (!a)
+      return b;
+    b %= a;
+    if (!b)
+      return a;
+  }
+}
+
+/**
+ * Module constructor
+ *
+ * @constructor
+ * @param {number|Fraction=} a
+ * @param {number=} b
+ */
+function Fraction(a, b) {
+
+  parse(a, b);
+
+  if (this instanceof Fraction) {
+    a = gcd(P["d"], P["n"]); // Abuse a
+    this["s"] = P["s"];
+    this["n"] = P["n"] / a;
+    this["d"] = P["d"] / a;
+  } else {
+    return newFraction(P['s'] * P['n'], P['d']);
+  }
+}
+
+const DivisionByZero = function () { return new Error("Division by Zero"); };
+const InvalidParameter = function () { return new Error("Invalid argument"); };
+const NonIntegerParameter = function () { return new Error("Parameters must be integer"); };
+
+Fraction.prototype = {
+
+  "s": C_ONE,
+  "n": C_ZERO,
+  "d": C_ONE,
+
+  /**
+   * Calculates the absolute value
+   *
+   * Ex: new Fraction(-4).abs() => 4
+   **/
+  "abs": function () {
+
+    return newFraction(this["n"], this["d"]);
+  },
+
+  /**
+   * Inverts the sign of the current fraction
+   *
+   * Ex: new Fraction(-4).neg() => 4
+   **/
+  "neg": function () {
+
+    return newFraction(-this["s"] * this["n"], this["d"]);
+  },
+
+  /**
+   * Adds two rational numbers
+   *
+   * Ex: new Fraction({n: 2, d: 3}).add("14.9") => 467 / 30
+   **/
+  "add": function (a, b) {
+
+    parse(a, b);
+    return newFraction(
+      this["s"] * this["n"] * P["d"] + P["s"] * this["d"] * P["n"],
+      this["d"] * P["d"]
+    );
+  },
+
+  /**
+   * Subtracts two rational numbers
+   *
+   * Ex: new Fraction({n: 2, d: 3}).add("14.9") => -427 / 30
+   **/
+  "sub": function (a, b) {
+
+    parse(a, b);
+    return newFraction(
+      this["s"] * this["n"] * P["d"] - P["s"] * this["d"] * P["n"],
+      this["d"] * P["d"]
+    );
+  },
+
+  /**
+   * Multiplies two rational numbers
+   *
+   * Ex: new Fraction("-17.(345)").mul(3) => 5776 / 111
+   **/
+  "mul": function (a, b) {
+
+    parse(a, b);
+    return newFraction(
+      this["s"] * P["s"] * this["n"] * P["n"],
+      this["d"] * P["d"]
+    );
+  },
+
+  /**
+   * Divides two rational numbers
+   *
+   * Ex: new Fraction("-17.(345)").inverse().div(3)
+   **/
+  "div": function (a, b) {
+
+    parse(a, b);
+    return newFraction(
+      this["s"] * P["s"] * this["n"] * P["d"],
+      this["d"] * P["n"]
+    );
+  },
+
+  /**
+   * Clones the actual object
+   *
+   * Ex: new Fraction("-17.(345)").clone()
+   **/
+  "clone": function () {
+    return newFraction(this['s'] * this['n'], this['d']);
+  },
+
+  /**
+   * Calculates the modulo of two rational numbers - a more precise fmod
+   *
+   * Ex: new Fraction('4.(3)').mod([7, 8]) => (13/3) % (7/8) = (5/6)
+   * Ex: new Fraction(20, 10).mod().equals(0) ? "is Integer"
+   **/
+  "mod": function (a, b) {
+
+    if (a === undefined) {
+      return newFraction(this["s"] * this["n"] % this["d"], C_ONE);
+    }
+
+    parse(a, b);
+    if (C_ZERO === P["n"] * this["d"]) {
+      throw DivisionByZero();
+    }
+
+    /**
+     * I derived the rational modulo similar to the modulo for integers
+     *
+     * https://raw.org/book/analysis/rational-numbers/
+     *
+     *    n1/d1 = (n2/d2) * q + r, where 0 ≤ r < n2/d2
+     * => d2 * n1 = n2 * d1 * q + d1 * d2 * r
+     * => r = (d2 * n1 - n2 * d1 * q) / (d1 * d2)
+     *      = (d2 * n1 - n2 * d1 * floor((d2 * n1) / (n2 * d1))) / (d1 * d2)
+     *      = ((d2 * n1) % (n2 * d1)) / (d1 * d2)
+     */
+    return newFraction(
+      this["s"] * (P["d"] * this["n"]) % (P["n"] * this["d"]),
+      P["d"] * this["d"]);
+  },
+
+  /**
+   * Calculates the fractional gcd of two rational numbers
+   *
+   * Ex: new Fraction(5,8).gcd(3,7) => 1/56
+   */
+  "gcd": function (a, b) {
+
+    parse(a, b);
+
+    // https://raw.org/book/analysis/rational-numbers/
+    // gcd(a / b, c / d) = gcd(a, c) / lcm(b, d)
+
+    return newFraction(gcd(P["n"], this["n"]) * gcd(P["d"], this["d"]), P["d"] * this["d"]);
+  },
+
+  /**
+   * Calculates the fractional lcm of two rational numbers
+   *
+   * Ex: new Fraction(5,8).lcm(3,7) => 15
+   */
+  "lcm": function (a, b) {
+
+    parse(a, b);
+
+    // https://raw.org/book/analysis/rational-numbers/
+    // lcm(a / b, c / d) = lcm(a, c) / gcd(b, d)
+
+    if (P["n"] === C_ZERO && this["n"] === C_ZERO) {
+      return newFraction(C_ZERO, C_ONE);
+    }
+    return newFraction(P["n"] * this["n"], gcd(P["n"], this["n"]) * gcd(P["d"], this["d"]));
+  },
+
+  /**
+   * Gets the inverse of the fraction, means numerator and denominator are exchanged
+   *
+   * Ex: new Fraction([-3, 4]).inverse() => -4 / 3
+   **/
+  "inverse": function () {
+    return newFraction(this["s"] * this["d"], this["n"]);
+  },
+
+  /**
+   * Calculates the fraction to some integer exponent
+   *
+   * Ex: new Fraction(-1,2).pow(-3) => -8
+   */
+  "pow": function (a, b) {
+
+    parse(a, b);
+
+    // Trivial case when exp is an integer
+
+    if (P['d'] === C_ONE) {
+
+      if (P['s'] < C_ZERO) {
+        return newFraction((this['s'] * this["d"]) ** P['n'], this["n"] ** P['n']);
+      } else {
+        return newFraction((this['s'] * this["n"]) ** P['n'], this["d"] ** P['n']);
+      }
+    }
+
+    // Negative roots become complex
+    //     (-a/b)^(c/d) = x
+    // ⇔ (-1)^(c/d) * (a/b)^(c/d) = x
+    // ⇔ (cos(pi) + i*sin(pi))^(c/d) * (a/b)^(c/d) = x
+    // ⇔ (cos(c*pi/d) + i*sin(c*pi/d)) * (a/b)^(c/d) = x       # DeMoivre's formula
+    // From which follows that only for c=0 the root is non-complex
+    if (this['s'] < C_ZERO) return null;
+
+    // Now prime factor n and d
+    let N = factorize(this['n']);
+    let D = factorize(this['d']);
+
+    // Exponentiate and take root for n and d individually
+    let n = C_ONE;
+    let d = C_ONE;
+    for (let k in N) {
+      if (k === '1') continue;
+      if (k === '0') {
+        n = C_ZERO;
+        break;
+      }
+      N[k] *= P['n'];
+
+      if (N[k] % P['d'] === C_ZERO) {
+        N[k] /= P['d'];
+      } else return null;
+      n *= BigInt(k) ** N[k];
+    }
+
+    for (let k in D) {
+      if (k === '1') continue;
+      D[k] *= P['n'];
+
+      if (D[k] % P['d'] === C_ZERO) {
+        D[k] /= P['d'];
+      } else return null;
+      d *= BigInt(k) ** D[k];
+    }
+
+    if (P['s'] < C_ZERO) {
+      return newFraction(d, n);
+    }
+    return newFraction(n, d);
+  },
+
+  /**
+   * Calculates the logarithm of a fraction to a given rational base
+   *
+   * Ex: new Fraction(27, 8).log(9, 4) => 3/2
+   */
+  "log": function (a, b) {
+
+    parse(a, b);
+
+    if (this['s'] <= C_ZERO || P['s'] <= C_ZERO) return null;
+
+    const allPrimes = Object.create(null);
+
+    const baseFactors = factorize(P['n']);
+    const T1 = factorize(P['d']);
+
+    const numberFactors = factorize(this['n']);
+    const T2 = factorize(this['d']);
+
+    for (const prime in T1) {
+      baseFactors[prime] = (baseFactors[prime] || C_ZERO) - T1[prime];
+    }
+    for (const prime in T2) {
+      numberFactors[prime] = (numberFactors[prime] || C_ZERO) - T2[prime];
+    }
+
+    for (const prime in baseFactors) {
+      if (prime === '1') continue;
+      allPrimes[prime] = true;
+    }
+    for (const prime in numberFactors) {
+      if (prime === '1') continue;
+      allPrimes[prime] = true;
+    }
+
+    let retN = null;
+    let retD = null;
+
+    // Iterate over all unique primes to determine if a consistent ratio exists
+    for (const prime in allPrimes) {
+
+      const baseExponent = baseFactors[prime] || C_ZERO;
+      const numberExponent = numberFactors[prime] || C_ZERO;
+
+      if (baseExponent === C_ZERO) {
+        if (numberExponent !== C_ZERO) {
+          return null; // Logarithm cannot be expressed as a rational number
+        }
+        continue; // Skip this prime since both exponents are zero
+      }
+
+      // Calculate the ratio of exponents for this prime
+      let curN = numberExponent;
+      let curD = baseExponent;
+
+      // Simplify the current ratio
+      const gcdValue = gcd(curN, curD);
+      curN /= gcdValue;
+      curD /= gcdValue;
+
+      // Check if this is the first ratio; otherwise, ensure ratios are consistent
+      if (retN === null && retD === null) {
+        retN = curN;
+        retD = curD;
+      } else if (curN * retD !== retN * curD) {
+        return null; // Ratios do not match, logarithm cannot be rational
+      }
+    }
+
+    return retN !== null && retD !== null
+      ? newFraction(retN, retD)
+      : null;
+  },
+
+  /**
+   * Check if two rational numbers are the same
+   *
+   * Ex: new Fraction(19.6).equals([98, 5]);
+   **/
+  "equals": function (a, b) {
+
+    parse(a, b);
+    return this["s"] * this["n"] * P["d"] === P["s"] * P["n"] * this["d"];
+  },
+
+  /**
+   * Check if this rational number is less than another
+   *
+   * Ex: new Fraction(19.6).lt([98, 5]);
+   **/
+  "lt": function (a, b) {
+
+    parse(a, b);
+    return this["s"] * this["n"] * P["d"] < P["s"] * P["n"] * this["d"];
+  },
+
+  /**
+   * Check if this rational number is less than or equal another
+   *
+   * Ex: new Fraction(19.6).lt([98, 5]);
+   **/
+  "lte": function (a, b) {
+
+    parse(a, b);
+    return this["s"] * this["n"] * P["d"] <= P["s"] * P["n"] * this["d"];
+  },
+
+  /**
+   * Check if this rational number is greater than another
+   *
+   * Ex: new Fraction(19.6).lt([98, 5]);
+   **/
+  "gt": function (a, b) {
+
+    parse(a, b);
+    return this["s"] * this["n"] * P["d"] > P["s"] * P["n"] * this["d"];
+  },
+
+  /**
+   * Check if this rational number is greater than or equal another
+   *
+   * Ex: new Fraction(19.6).lt([98, 5]);
+   **/
+  "gte": function (a, b) {
+
+    parse(a, b);
+    return this["s"] * this["n"] * P["d"] >= P["s"] * P["n"] * this["d"];
+  },
+
+  /**
+   * Compare two rational numbers
+   * < 0 iff this < that
+   * > 0 iff this > that
+   * = 0 iff this = that
+   *
+   * Ex: new Fraction(19.6).compare([98, 5]);
+   **/
+  "compare": function (a, b) {
+
+    parse(a, b);
+    let t = this["s"] * this["n"] * P["d"] - P["s"] * P["n"] * this["d"];
+
+    return (C_ZERO < t) - (t < C_ZERO);
+  },
+
+  /**
+   * Calculates the ceil of a rational number
+   *
+   * Ex: new Fraction('4.(3)').ceil() => (5 / 1)
+   **/
+  "ceil": function (places) {
+
+    places = C_TEN ** BigInt(places || 0);
+
+    return newFraction(ifloor(this["s"] * places * this["n"] / this["d"]) +
+      (places * this["n"] % this["d"] > C_ZERO && this["s"] >= C_ZERO ? C_ONE : C_ZERO),
+      places);
+  },
+
+  /**
+   * Calculates the floor of a rational number
+   *
+   * Ex: new Fraction('4.(3)').floor() => (4 / 1)
+   **/
+  "floor": function (places) {
+
+    places = C_TEN ** BigInt(places || 0);
+
+    return newFraction(ifloor(this["s"] * places * this["n"] / this["d"]) -
+      (places * this["n"] % this["d"] > C_ZERO && this["s"] < C_ZERO ? C_ONE : C_ZERO),
+      places);
+  },
+
+  /**
+   * Rounds a rational numbers
+   *
+   * Ex: new Fraction('4.(3)').round() => (4 / 1)
+   **/
+  "round": function (places) {
+
+    places = C_TEN ** BigInt(places || 0);
+
+    /* Derivation:
+
+    s >= 0:
+      round(n / d) = ifloor(n / d) + (n % d) / d >= 0.5 ? 1 : 0
+                   = ifloor(n / d) + 2(n % d) >= d ? 1 : 0
+    s < 0:
+      round(n / d) =-ifloor(n / d) - (n % d) / d > 0.5 ? 1 : 0
+                   =-ifloor(n / d) - 2(n % d) > d ? 1 : 0
+
+    =>:
+
+    round(s * n / d) = s * ifloor(n / d) + s * (C + 2(n % d) > d ? 1 : 0)
+        where C = s >= 0 ? 1 : 0, to fix the >= for the positve case.
+    */
+
+    return newFraction(ifloor(this["s"] * places * this["n"] / this["d"]) +
+      this["s"] * ((this["s"] >= C_ZERO ? C_ONE : C_ZERO) + C_TWO * (places * this["n"] % this["d"]) > this["d"] ? C_ONE : C_ZERO),
+      places);
+  },
+
+  /**
+    * Rounds a rational number to a multiple of another rational number
+    *
+    * Ex: new Fraction('0.9').roundTo("1/8") => 7 / 8
+    **/
+  "roundTo": function (a, b) {
+
+    /*
+    k * x/y ≤ a/b < (k+1) * x/y
+    ⇔ k ≤ a/b / (x/y) < (k+1)
+    ⇔ k = floor(a/b * y/x)
+    ⇔ k = floor((a * y) / (b * x))
+    */
+
+    parse(a, b);
+
+    const n = this['n'] * P['d'];
+    const d = this['d'] * P['n'];
+    const r = n % d;
+
+    // round(n / d) = ifloor(n / d) + 2(n % d) >= d ? 1 : 0
+    let k = ifloor(n / d);
+    if (r + r >= d) {
+      k++;
+    }
+    return newFraction(this['s'] * k * P['n'], P['d']);
+  },
+
+  /**
+   * Check if two rational numbers are divisible
+   *
+   * Ex: new Fraction(19.6).divisible(1.5);
+   */
+  "divisible": function (a, b) {
+
+    parse(a, b);
+    if (P['n'] === C_ZERO) return false;
+    return (this['n'] * P['d']) % (P['n'] * this['d']) === C_ZERO;
+  },
+
+  /**
+   * Returns a decimal representation of the fraction
+   *
+   * Ex: new Fraction("100.'91823'").valueOf() => 100.91823918239183
+   **/
+  'valueOf': function () {
+    //if (this['n'] <= MAX_INTEGER && this['d'] <= MAX_INTEGER) {
+    return Number(this['s'] * this['n']) / Number(this['d']);
+    //}
+  },
+
+  /**
+   * Creates a string representation of a fraction with all digits
+   *
+   * Ex: new Fraction("100.'91823'").toString() => "100.(91823)"
+   **/
+  'toString': function (dec = 15) {
+
+    let N = this["n"];
+    let D = this["d"];
+
+    let cycLen = cycleLen(N, D); // Cycle length
+    let cycOff = cycleStart(N, D, cycLen); // Cycle start
+
+    let str = this['s'] < C_ZERO ? "-" : "";
+
+    // Append integer part
+    str += ifloor(N / D);
+
+    N %= D;
+    N *= C_TEN;
+
+    if (N)
+      str += ".";
+
+    if (cycLen) {
+
+      for (let i = cycOff; i--;) {
+        str += ifloor(N / D);
+        N %= D;
+        N *= C_TEN;
+      }
+      str += "(";
+      for (let i = cycLen; i--;) {
+        str += ifloor(N / D);
+        N %= D;
+        N *= C_TEN;
+      }
+      str += ")";
+    } else {
+      for (let i = dec; N && i--;) {
+        str += ifloor(N / D);
+        N %= D;
+        N *= C_TEN;
+      }
+    }
+    return str;
+  },
+
+  /**
+   * Returns a string-fraction representation of a Fraction object
+   *
+   * Ex: new Fraction("1.'3'").toFraction() => "4 1/3"
+   **/
+  'toFraction': function (showMixed = false) {
+
+    let n = this["n"];
+    let d = this["d"];
+    let str = this['s'] < C_ZERO ? "-" : "";
+
+    if (d === C_ONE) {
+      str += n;
+    } else {
+      const whole = ifloor(n / d);
+      if (showMixed && whole > C_ZERO) {
+        str += whole;
+        str += " ";
+        n %= d;
+      }
+
+      str += n;
+      str += '/';
+      str += d;
+    }
+    return str;
+  },
+
+  /**
+   * Returns a latex representation of a Fraction object
+   *
+   * Ex: new Fraction("1.'3'").toLatex() => "\frac{4}{3}"
+   **/
+  'toLatex': function (showMixed = false) {
+
+    let n = this["n"];
+    let d = this["d"];
+    let str = this['s'] < C_ZERO ? "-" : "";
+
+    if (d === C_ONE) {
+      str += n;
+    } else {
+      const whole = ifloor(n / d);
+      if (showMixed && whole > C_ZERO) {
+        str += whole;
+        n %= d;
+      }
+
+      str += "\\frac{";
+      str += n;
+      str += '}{';
+      str += d;
+      str += '}';
+    }
+    return str;
+  },
+
+  /**
+   * Returns an array of continued fraction elements
+   *
+   * Ex: new Fraction("7/8").toContinued() => [0,1,7]
+   */
+  'toContinued': function () {
+
+    let a = this['n'];
+    let b = this['d'];
+    const res = [];
+
+    while (b) {
+      res.push(ifloor(a / b));
+      const t = a % b;
+      a = b;
+      b = t;
+    }
+    return res;
+  },
+
+  "simplify": function (eps = 1e-3) {
+
+    // Continued fractions give best approximations for a max denominator,
+    // generally outperforming mediants in denominator–accuracy trade-offs.
+    // Semiconvergents can further reduce the denominator within tolerance.
+
+    const ieps = BigInt(Math.ceil(1 / eps));
+
+    const thisABS = this['abs']();
+    const cont = thisABS['toContinued']();
+
+    for (let i = 1; i < cont.length; i++) {
+
+      let s = newFraction(cont[i - 1], C_ONE);
+      for (let k = i - 2; k >= 0; k--) {
+        s = s['inverse']()['add'](cont[k]);
+      }
+
+      let t = s['sub'](thisABS);
+      if (t['n'] * ieps < t['d']) { // More robust than Math.abs(t.valueOf()) < eps
+        return s['mul'](this['s']);
+      }
+    }
+    return this;
+  }
+};
Index: node_modules/fraction.js/tests/fraction.test.js
===================================================================
--- node_modules/fraction.js/tests/fraction.test.js	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
+++ node_modules/fraction.js/tests/fraction.test.js	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
@@ -0,0 +1,1806 @@
+const Fraction = require('fraction.js');
+const assert = require('assert');
+
+var DivisionByZero = function () { return new Error("Division by Zero"); };
+var InvalidParameter = function () { return new Error("Invalid argument"); };
+var NonIntegerParameter = function () { return new Error("Parameters must be integer"); };
+
+var tests = [{
+  set: "",
+  expectError: InvalidParameter()
+}, {
+  set: "foo",
+  expectError: InvalidParameter()
+}, {
+  set: " 123",
+  expectError: InvalidParameter()
+}, {
+  set: 0,
+  expect: 0
+}, {
+  set: .2,
+  expect: "0.2"
+}, {
+  set: .333,
+  expect: "0.333"
+}, {
+  set: 1.1,
+  expect: "1.1"
+}, {
+  set: 1.2,
+  expect: "1.2"
+}, {
+  set: 1.3,
+  expect: "1.3"
+}, {
+  set: 1.4,
+  expect: "1.4"
+}, {
+  set: 1.5,
+  expect: "1.5"
+}, {
+  set: 2.555,
+  expect: "2.555"
+}, {
+  set: 1e12,
+  expect: "1000000000000"
+}, {
+  set: " - ",
+  expectError: InvalidParameter()
+}, {
+  set: ".5",
+  expect: "0.5"
+}, {
+  set: "2_000_000",
+  expect: "2000000"
+}, {
+  set: "-.5",
+  expect: "-0.5"
+}, {
+  set: "123",
+  expect: "123"
+}, {
+  set: "-123",
+  expect: "-123"
+}, {
+  set: "123.4",
+  expect: "123.4"
+}, {
+  set: "-123.4",
+  expect: "-123.4"
+}, {
+  set: "123.",
+  expect: "123"
+}, {
+  set: "-123.",
+  expect: "-123"
+}, {
+  set: "123.4(56)",
+  expect: "123.4(56)"
+}, {
+  set: "-123.4(56)",
+  expect: "-123.4(56)"
+}, {
+  set: "123.(4)",
+  expect: "123.(4)"
+}, {
+  set: "-123.(4)",
+  expect: "-123.(4)"
+}, {
+  set: "0/0",
+  expectError: DivisionByZero()
+}, {
+  set: "9/0",
+  expectError: DivisionByZero()
+}, {
+  label: "0/1+0/1",
+  set: "0/1",
+  param: "0/1",
+  expect: "0"
+}, {
+  label: "1/9+0/1",
+  set: "1/9",
+  param: "0/1",
+  expect: "0.(1)"
+}, {
+  set: "123/456",
+  expect: "0.269(736842105263157894)"
+}, {
+  set: "-123/456",
+  expect: "-0.269(736842105263157894)"
+}, {
+  set: "19 123/456",
+  expect: "19.269(736842105263157894)"
+}, {
+  set: "-19 123/456",
+  expect: "-19.269(736842105263157894)"
+}, {
+  set: "123.(22)123",
+  expectError: InvalidParameter()
+}, {
+  set: "+33.3(3)",
+  expect: "33.(3)"
+}, {
+  set: "3.'09009'",
+  expect: "3.(09009)"
+}, {
+  set: "123.(((",
+  expectError: InvalidParameter()
+}, {
+  set: "123.((",
+  expectError: InvalidParameter()
+}, {
+  set: "123.()",
+  expectError: InvalidParameter()
+}, {
+  set: null,
+  expect: "0" // I would say it's just fine
+}, {
+  set: [22, 7],
+  expect: '3.(142857)' // We got Pi! - almost ;o
+}, {
+  set: "355/113",
+  expect: "3.(1415929203539823008849557522123893805309734513274336283185840707964601769911504424778761061946902654867256637168)" // Yay, a better PI
+}, {
+  set: "3 1/7",
+  expect: '3.(142857)'
+}, {
+  set: [36, -36],
+  expect: "-1"
+}, {
+  set: [1n, 3n],
+  expect: "0.(3)"
+}, {
+  set: 1n,
+  set2: 3n,
+  expect: "0.(3)"
+}, {
+  set: { n: 1n, d: 3n },
+  expect: "0.(3)"
+}, {
+  set: { n: 1n, d: 3n },
+  expect: "0.(3)"
+}, {
+  set: [1n, 3n],
+  expect: "0.(3)"
+}, {
+  set: "9/12",
+  expect: "0.75"
+}, {
+  set: "0.09(33)",
+  expect: "0.09(3)"
+}, {
+  set: 1 / 2,
+  expect: "0.5"
+}, {
+  set: 1 / 3,
+  expect: "0.(3)"
+}, {
+  set: "0.'3'",
+  expect: "0.(3)"
+}, {
+  set: "0.00002",
+  expect: "0.00002"
+}, {
+  set: 7 / 8,
+  expect: "0.875"
+}, {
+  set: 0.003,
+  expect: "0.003"
+}, {
+  set: 4,
+  expect: "4"
+}, {
+  set: -99,
+  expect: "-99"
+}, {
+  set: "-92332.1192",
+  expect: "-92332.1192"
+}, {
+  set: '88.92933(12111)',
+  expect: "88.92933(12111)"
+}, {
+  set: '-192322.823(123)',
+  expect: "-192322.8(231)"
+}, {
+  label: "-99.12 % 0.09(34)",
+  set: '-99.12',
+  fn: "mod",
+  param: "0.09(34)",
+  expect: "-0.07(95)"
+}, {
+  label: "0.4 / 0.1",
+  set: .4,
+  fn: "div",
+  param: ".1",
+  expect: "4"
+}, {
+  label: "1 / -.1",
+  set: 1,
+  fn: "div",
+  param: "-.1",
+  expect: "-10"
+}, {
+  label: "1 - (-1)",
+  set: 1,
+  fn: "sub",
+  param: "-1",
+  expect: "2"
+}, {
+  label: "1 + (-1)",
+  set: 1,
+  fn: "add",
+  param: "-1",
+  expect: "0"
+}, {
+  label: "-187 % 12",
+  set: '-187',
+  fn: "mod",
+  param: "12",
+  expect: "-7"
+}, {
+  label: "Negate by 99 * -1",
+  set: '99',
+  fn: "mul",
+  param: "-1",
+  expect: "-99"
+}, {
+  label: "0.5050000000000000000000000",
+  set: "0.5050000000000000000000000",
+  expect: "101/200",
+  fn: "toFraction",
+  param: true
+}, {
+  label: "0.505000000(0000000000)",
+  set: "0.505000000(0000000000)",
+  expect: "101/200",
+  fn: "toFraction",
+  param: true
+}, {
+  set: [20, -5],
+  expect: "-4",
+  fn: "toFraction",
+  param: true
+}, {
+  set: [-10, -7],
+  expect: "1 3/7",
+  fn: "toFraction",
+  param: true
+}, {
+  set: [21, -6],
+  expect: "-3 1/2",
+  fn: "toFraction",
+  param: true
+}, {
+  set: "10/78",
+  expect: "5/39",
+  fn: "toFraction",
+  param: true
+}, {
+  set: "0/91",
+  expect: "0",
+  fn: "toFraction",
+  param: true
+}, {
+  set: "-0/287",
+  expect: "0",
+  fn: "toFraction",
+  param: true
+}, {
+  set: "-5/20",
+  expect: "-1/4",
+  fn: "toFraction",
+  param: true
+}, {
+  set: "42/9",
+  expect: "4 2/3",
+  fn: "toFraction",
+  param: true
+}, {
+  set: "71/23",
+  expect: "3 2/23",
+  fn: "toFraction",
+  param: true
+}, {
+  set: "6/3",
+  expect: "2",
+  fn: "toFraction",
+  param: true
+}, {
+  set: "28/4",
+  expect: "7",
+  fn: "toFraction",
+  param: true
+}, {
+  set: "105/35",
+  expect: "3",
+  fn: "toFraction",
+  param: true
+}, {
+  set: "4/6",
+  expect: "2/3",
+  fn: "toFraction",
+  param: true
+}, {
+  label: "99.(9) + 66",
+  set: '99.(999999)',
+  fn: "add",
+  param: "66",
+  expect: "166"
+}, {
+  label: "123.32 / 33.'9821'",
+  set: '123.32',
+  fn: "div",
+  param: "33.'9821'",
+  expect: "3.628958880242975"
+}, {
+  label: "-82.124 / 66.(3)",
+  set: '-82.124',
+  fn: "div",
+  param: "66.(3)",
+  expect: "-1.238(050251256281407035175879396984924623115577889447236180904522613065326633165829145728643216080402010)"
+}, {
+  label: "100 - .91",
+  set: '100',
+  fn: "sub",
+  param: ".91",
+  expect: "99.09"
+}, {
+  label: "381.(33411) % 11.119(356)",
+  set: '381.(33411)',
+  fn: "mod",
+  param: "11.119(356)",
+  expect: "3.275(997225017295217)"
+}, {
+  label: "13/26 mod 1",
+  set: '13/26',
+  fn: "mod",
+  param: "1.000",
+  expect: "0.5"
+}, {
+  label: "381.(33411) % 1", // Extract fraction part of a number
+  set: '381.(33411)',
+  fn: "mod",
+  param: "1",
+  expect: "0.(33411)"
+}, {
+  label: "-222/3",
+  set: {
+    n: 3,
+    d: 222,
+    s: -1
+  },
+  fn: "inverse",
+  param: null,
+  expect: "-74"
+}, {
+  label: "inverse",
+  set: 1 / 2,
+  fn: "inverse",
+  param: null,
+  expect: "2"
+}, {
+  label: "abs(-222/3)",
+  set: {
+    n: -222,
+    d: 3
+  },
+  fn: "abs",
+  param: null,
+  expect: "74"
+}, {
+  label: "9 % -2",
+  set: 9,
+  fn: "mod",
+  param: "-2",
+  expect: "1"
+}, {
+  label: "-9 % 2",
+  set: '-9',
+  fn: "mod",
+  param: "-2",
+  expect: "-1"
+}, {
+  label: "1 / 195312500",
+  set: '1',
+  fn: "div",
+  param: "195312500",
+  expect: "0.00000000512"
+}, {
+  label: "10 / 0",
+  set: 10,
+  fn: "div",
+  param: 0,
+  expectError: DivisionByZero()
+}, {
+  label: "-3 / 4",
+  set: [-3, 4],
+  fn: "inverse",
+  param: null,
+  expect: "-1.(3)"
+}, {
+  label: "-19.6",
+  set: [-98, 5],
+  fn: "equals",
+  param: '-19.6',
+  expect: "true" // actually, we get a real bool but we call toString() in the test below
+}, {
+  label: "-19.6",
+  set: [98, -5],
+  fn: "equals",
+  param: '-19.6',
+  expect: "true"
+}, {
+  label: "99/88",
+  set: [99, 88],
+  fn: "equals",
+  param: [88, 99],
+  expect: "false"
+}, {
+  label: "99/88",
+  set: [99, -88],
+  fn: "equals",
+  param: [9, 8],
+  expect: "false"
+}, {
+  label: "12.5",
+  set: 12.5,
+  fn: "add",
+  param: 0,
+  expect: "12.5"
+}, {
+  label: "0/1 -> 1/0",
+  set: 0,
+  fn: "inverse",
+  param: null,
+  expectError: DivisionByZero()
+}, {
+  label: "abs(-100.25)",
+  set: -100.25,
+  fn: "abs",
+  param: null,
+  expect: "100.25"
+}, {
+  label: "0.022222222",
+  set: '0.0(22222222)',
+  fn: "abs",
+  param: null,
+  expect: "0.0(2)"
+}, {
+  label: "1.5 | 100.5",
+  set: 100.5,
+  fn: "divisible",
+  param: '1.5',
+  expect: "true"
+}, {
+  label: "1.5 | 100.6",
+  set: 100.6,
+  fn: "divisible",
+  param: 1.6,
+  expect: "false"
+}, {
+  label: "(1/6) | (2/3)", // == 4
+  set: [2, 3],
+  fn: "divisible",
+  param: [1, 6],
+  expect: "true"
+}, {
+  label: "(1/6) | (2/5)",
+  set: [2, 5],
+  fn: "divisible",
+  param: [1, 6],
+  expect: "false"
+}, {
+  label: "0 | (2/5)",
+  set: [2, 5],
+  fn: "divisible",
+  param: 0,
+  expect: "false"
+}, {
+  label: "6 | 0",
+  set: 0,
+  fn: "divisible",
+  param: 6,
+  expect: "true"
+}, {
+  label: "fmod(4.55, 0.05)", // http://phpjs.org/functions/fmod/ (comment section)
+  set: 4.55,
+  fn: "mod",
+  param: 0.05,
+  expect: "0"
+}, {
+  label: "fmod(99.12, 0.4)",
+  set: 99.12,
+  fn: "mod",
+  param: "0.4",
+  expect: "0.32"
+}, {
+  label: "fmod(fmod(1.0,0.1))", // http://stackoverflow.com/questions/4218961/why-fmod1-0-0-1-1
+  set: 1.0,
+  fn: "mod",
+  param: 0.1,
+  expect: "0"
+}, {
+  label: "bignum",
+  set: [5385020324, 1673196525],
+  fn: "add",
+  param: 0,
+  expect: "3.21(840276592733181776121606516006839065124164060763872313206005492988936251824931324190982287630557922656455433410609073551596098372245902196097377144624418820138297860736950789447760776337973807350574075570710380240599651018280712721418065340531352107607323652551812465663589637206543923464101146157950573080469432602963360804254598843372567965379918536467197121390148715495330113717514444395585868193217769203770011415724163065662594535928766646225254382476081224230369471990147720394052336440275597631903998844367669243157195775313960803259497565595290726533154854597848271290188102679689703515252041298615534717298077104242133182771222884293284077911887845930112722413166618308629346454087334421161315763550250022184333666363549254920906556389124702491239037207539024741878423396797336762338781453063321417070239253574830368476888869943116813489676593728283053898883754853602746993512910863832926021645903191198654921901657666901979730085800889408373591978384009612977172541043856160291750546158945674358246709841810124486123947693472528578195558946669459524487119048971249805817042322628538808374587079661786890216019304725725509141850506771761314768448972244907094819599867385572056456428511886850828834945135927771544947477105237234460548500123140047759781236696030073335228807028510891749551057667897081007863078128255137273847732859712937785356684266362554153643129279150277938809369688357439064129062782986595074359241811119587401724970711375341877428295519559485099934689381452068220139292962014728066686607540019843156200674036183526020650801913421377683054893985032630879985)"
+}, {
+  label: "ceil(0.4)",
+  set: 0.4,
+  fn: "ceil",
+  param: null,
+  expect: "1"
+},
+
+
+{
+  label: "1 < 2",
+  set: 1,
+  fn: "lt",
+  param: 2,
+  expect: "true"
+}, {
+  label: "2 < 2",
+  set: 2,
+  fn: "lt",
+  param: 2,
+  expect: "false"
+}, {
+  label: "3 > 2",
+  set: 3,
+  fn: "gt",
+  param: 2,
+  expect: "true"
+}, {
+  label: "2 > 2",
+  set: 2,
+  fn: "gt",
+  param: 2,
+  expect: "false"
+}, {
+  label: "1 <= 2",
+  set: 1,
+  fn: "lte",
+  param: 2,
+  expect: "true"
+}, {
+  label: "2 <= 2",
+  set: 2,
+  fn: "lte",
+  param: 2,
+  expect: "true"
+}, {
+  label: "3 <= 2",
+  set: 3,
+  fn: "lte",
+  param: 2,
+  expect: "false"
+}, {
+  label: "3 >= 2",
+  set: 3,
+  fn: "gte",
+  param: 2,
+  expect: "true"
+}, {
+  label: "2 >= 2",
+  set: 2,
+  fn: "gte",
+  param: 2,
+  expect: "true"
+}, {
+  label: "ceil(0.5)",
+  set: 0.5,
+  fn: "ceil",
+  param: null,
+  expect: "1"
+}, {
+  label: "ceil(0.23, 2)",
+  set: 0.23,
+  fn: "ceil",
+  param: 2,
+  expect: "0.23"
+}, {
+  label: "ceil(0.6)",
+  set: 0.6,
+  fn: "ceil",
+  param: null,
+  expect: "1"
+}, {
+  label: "ceil(-0.4)",
+  set: -0.4,
+  fn: "ceil",
+  param: null,
+  expect: "0"
+}, {
+  label: "ceil(-0.5)",
+  set: -0.5,
+  fn: "ceil",
+  param: null,
+  expect: "0"
+}, {
+  label: "ceil(-0.6)",
+  set: -0.6,
+  fn: "ceil",
+  param: null,
+  expect: "0"
+}, {
+  label: "floor(0.4)",
+  set: 0.4,
+  fn: "floor",
+  param: null,
+  expect: "0"
+}, {
+  label: "floor(0.4, 1)",
+  set: 0.4,
+  fn: "floor",
+  param: 1,
+  expect: "0.4"
+}, {
+  label: "floor(0.5)",
+  set: 0.5,
+  fn: "floor",
+  param: null,
+  expect: "0"
+}, {
+  label: "floor(0.6)",
+  set: 0.6,
+  fn: "floor",
+  param: null,
+  expect: "0"
+}, {
+  label: "floor(-0.4)",
+  set: -0.4,
+  fn: "floor",
+  param: null,
+  expect: "-1"
+}, {
+  label: "floor(-0.5)",
+  set: -0.5,
+  fn: "floor",
+  param: null,
+  expect: "-1"
+}, {
+  label: "floor(-0.6)",
+  set: -0.6,
+  fn: "floor",
+  param: null,
+  expect: "-1"
+}, {
+  label: "floor(10.4)",
+  set: 10.4,
+  fn: "floor",
+  param: null,
+  expect: "10"
+}, {
+  label: "floor(10.4, 1)",
+  set: 10.4,
+  fn: "floor",
+  param: 1,
+  expect: "10.4"
+}, {
+  label: "floor(10.5)",
+  set: 10.5,
+  fn: "floor",
+  param: null,
+  expect: "10"
+}, {
+  label: "floor(10.6)",
+  set: 10.6,
+  fn: "floor",
+  param: null,
+  expect: "10"
+}, {
+  label: "floor(-10.4)",
+  set: -10.4,
+  fn: "floor",
+  param: null,
+  expect: "-11"
+}, {
+  label: "floor(-10.5)",
+  set: -10.5,
+  fn: "floor",
+  param: null,
+  expect: "-11"
+}, {
+  label: "floor(-10.6)",
+  set: -10.6,
+  fn: "floor",
+  param: null,
+  expect: "-11"
+}, {
+  label: "floor(-10.543,3)",
+  set: -10.543,
+  fn: "floor",
+  param: 3,
+  expect: "-10.543"
+}, {
+  label: "floor(10.543,3)",
+  set: 10.543,
+  fn: "floor",
+  param: 3,
+  expect: "10.543"
+}, {
+  label: "round(-10.543,3)",
+  set: -10.543,
+  fn: "round",
+  param: 3,
+  expect: "-10.543"
+}, {
+  label: "round(10.543,3)",
+  set: 10.543,
+  fn: "round",
+  param: 3,
+  expect: "10.543"
+}, {
+  label: "round(10.4)",
+  set: 10.4,
+  fn: "round",
+  param: null,
+  expect: "10"
+}, {
+  label: "round(10.5)",
+  set: 10.5,
+  fn: "round",
+  param: null,
+  expect: "11"
+}, {
+  label: "round(10.5, 1)",
+  set: 10.5,
+  fn: "round",
+  param: 1,
+  expect: "10.5"
+}, {
+  label: "round(10.6)",
+  set: 10.6,
+  fn: "round",
+  param: null,
+  expect: "11"
+}, {
+  label: "round(-10.4)",
+  set: -10.4,
+  fn: "round",
+  param: null,
+  expect: "-10"
+}, {
+  label: "round(-10.5)",
+  set: -10.5,
+  fn: "round",
+  param: null,
+  expect: "-10"
+}, {
+  label: "round(-10.6)",
+  set: -10.6,
+  fn: "round",
+  param: null,
+  expect: "-11"
+}, {
+  label: "round(-0.4)",
+  set: -0.4,
+  fn: "round",
+  param: null,
+  expect: "0"
+}, {
+  label: "round(-0.5)",
+  set: -0.5,
+  fn: "round",
+  param: null,
+  expect: "0"
+}, {
+  label: "round(-0.6)",
+  set: -0.6,
+  fn: "round",
+  param: null,
+  expect: "-1"
+}, {
+  label: "round(-0)",
+  set: -0,
+  fn: "round",
+  param: null,
+  expect: "0"
+}, {
+  label: "round(big fraction)",
+  set: [
+    '409652136432929109317120'.repeat(100),
+    '63723676445298091081155'.repeat(100)
+  ],
+  fn: "round",
+  param: null,
+  expect: "6428570341270001560623330590225448467479093479780591305451264291405695842465355472558570608574213642"
+}, {
+  label: "round(big numerator)",
+  set: ['409652136432929109317'.repeat(100), 10],
+  fn: "round",
+  param: null,
+  expect: '409652136432929109317'.repeat(99) + '40965213643292910932'
+}, {
+  label: "17402216385200408/5539306332998545",
+  set: [17402216385200408, 5539306332998545],
+  fn: "add",
+  param: 0,
+  expect: "3.141587653589870"
+}, {
+  label: "17402216385200401/553930633299855",
+  set: [17402216385200401, 553930633299855],
+  fn: "add",
+  param: 0,
+  expect: "31.415876535898660"
+}, {
+  label: "1283191/418183",
+  set: [1283191, 418183],
+  fn: "add",
+  param: 0,
+  expect: "3.068491545567371"
+}, {
+  label: "1.001",
+  set: "1.001",
+  fn: "add",
+  param: 0,
+  expect: "1.001"
+}, {
+  label: "99+1",
+  set: [99, 1],
+  fn: "add",
+  param: 1,
+  expect: "100"
+}, {
+  label: "gcd(5/8, 3/7)",
+  set: [5, 8],
+  fn: "gcd",
+  param: [3, 7],
+  expect: "0.017(857142)" // == 1/56
+}, {
+  label: "gcd(52, 39)",
+  set: 52,
+  fn: "gcd",
+  param: 39,
+  expect: "13"
+}, {
+  label: "gcd(51357, 3819)",
+  set: 51357,
+  fn: "gcd",
+  param: 3819,
+  expect: "57"
+}, {
+  label: "gcd(841, 299)",
+  set: 841,
+  fn: "gcd",
+  param: 299,
+  expect: "1"
+}, {
+  label: "gcd(2/3, 7/5)",
+  set: [2, 3],
+  fn: "gcd",
+  param: [7, 5],
+  expect: "0.0(6)" // == 1/15
+}, {
+  label: "lcm(-3, 3)",
+  set: -3,
+  fn: "lcm",
+  param: 3,
+  expect: "3"
+}, {
+  label: "lcm(3,-3)",
+  set: 3,
+  fn: "lcm",
+  param: -3,
+  expect: "3"
+}, {
+  label: "lcm(0,3)",
+  set: 0,
+  fn: "lcm",
+  param: 3,
+  expect: "0"
+}, {
+  label: "lcm(3, 0)",
+  set: 3,
+  fn: "lcm",
+  param: 0,
+  expect: "0"
+}, {
+  label: "lcm(0, 0)",
+  set: 0,
+  fn: "lcm",
+  param: 0,
+  expect: "0"
+}, {
+  label: "lcm(200, 333)",
+  set: 200,
+  fn: "lcm",
+  param: 333, expect: "66600"
+},
+{
+  label: "1 + -1",
+  set: 1,
+  fn: "add",
+  param: -1,
+  expect: "0"
+}, {
+  label: "3/10+3/14",
+  set: "3/10",
+  fn: "add",
+  param: "3/14",
+  expect: "0.5(142857)"
+}, {
+  label: "3/10-3/14",
+  set: "3/10",
+  fn: "sub",
+  param: "3/14",
+  expect: "0.0(857142)"
+}, {
+  label: "3/10*3/14",
+  set: "3/10",
+  fn: "mul",
+  param: "3/14",
+  expect: "0.06(428571)"
+}, {
+  label: "3/10 / 3/14",
+  set: "3/10",
+  fn: "div",
+  param: "3/14",
+  expect: "1.4"
+}, {
+  label: "1-2",
+  set: "1",
+  fn: "sub",
+  param: "2",
+  expect: "-1"
+}, {
+  label: "1--1",
+  set: "1",
+  fn: "sub",
+  param: "-1",
+  expect: "2"
+}, {
+  label: "0/1*1/3",
+  set: "0/1",
+  fn: "mul",
+  param: "1/3",
+  expect: "0"
+}, {
+  label: "3/10 * 8/12",
+  set: "3/10",
+  fn: "mul",
+  param: "8/12",
+  expect: "0.2"
+}, {
+  label: ".5+5",
+  set: ".5",
+  fn: "add",
+  param: 5, expect: "5.5"
+},
+{
+  label: "10/12-5/60",
+  set: "10/12",
+  fn: "sub",
+  param: "5/60",
+  expect: "0.75"
+}, {
+  label: "10/15 / 3/4",
+  set: "10/15",
+  fn: "div",
+  param: "3/4",
+  expect: "0.(8)"
+}, {
+  label: "1/4 + 3/8",
+  set: "1/4",
+  fn: "add",
+  param: "3/8",
+  expect: "0.625"
+}, {
+  label: "2-1/3",
+  set: "2",
+  fn: "sub",
+  param: "1/3",
+  expect: "1.(6)"
+}, {
+  label: "5*6",
+  set: "5",
+  fn: "mul",
+  param: 6,
+  expect: "30"
+}, {
+  label: "1/2-1/5",
+  set: "1/2",
+  fn: "sub",
+  param: "1/5",
+  expect: "0.3"
+}, {
+  label: "1/2-5",
+  set: "1/2",
+  fn: "add",
+  param: -5,
+  expect: "-4.5"
+}, {
+  label: "1*-1",
+  set: "1",
+  fn: "mul",
+  param: -1,
+  expect: "-1"
+}, {
+  label: "5/10",
+  set: 5.0,
+  fn: "div",
+  param: 10,
+  expect: "0.5"
+}, {
+  label: "1/-1",
+  set: "1",
+  fn: "div",
+  param: -1,
+  expect: "-1"
+}, {
+  label: "4/5 + 13/2",
+  set: "4/5",
+  fn: "add",
+  param: "13/2",
+  expect: "7.3"
+}, {
+  label: "4/5 + 61/2",
+  set: "4/5",
+  fn: "add",
+  param: "61/2",
+  expect: "31.3"
+}, {
+  label: "0.8 + 6.5",
+  set: "0.8",
+  fn: "add",
+  param: "6.5",
+  expect: "7.3"
+}, {
+  label: "2/7 inverse",
+  set: "2/7",
+  fn: "inverse",
+  param: null,
+  expect: "3.5"
+}, {
+  label: "neg 1/3",
+  set: "1/3",
+  fn: "neg",
+  param: null,
+  expect: "-0.(3)"
+}, {
+  label: "1/2+1/3",
+  set: "1/2",
+  fn: "add",
+  param: "1/3",
+  expect: "0.8(3)"
+}, {
+  label: "1/2+3",
+  set: ".5",
+  fn: "add",
+  param: 3,
+  expect: "3.5"
+}, {
+  label: "1/2+3.14",
+  set: "1/2",
+  fn: "add",
+  param: "3.14",
+  expect: "3.64"
+}, {
+  label: "3.5 < 4.1",
+  set: 3.5,
+  fn: "compare",
+  param: 4.1,
+  expect: "-1"
+}, {
+  label: "3.5 > 4.1",
+  set: 4.1,
+  fn: "compare",
+  param: 3.1,
+  expect: "1"
+}, {
+  label: "-3.5 > -4.1",
+  set: -3.5,
+  fn: "compare",
+  param: -4.1,
+  expect: "1"
+}, {
+  label: "-3.5 > -4.1",
+  set: -4.1,
+  fn: "compare",
+  param: -3.5,
+  expect: "-1"
+}, {
+  label: "4.3 == 4.3",
+  set: 4.3,
+  fn: "compare",
+  param: 4.3,
+  expect: "0"
+}, {
+  label: "-4.3 == -4.3",
+  set: -4.3,
+  fn: "compare",
+  param: -4.3,
+  expect: "0"
+}, {
+  label: "-4.3 < 4.3",
+  set: -4.3,
+  fn: "compare",
+  param: 4.3,
+  expect: "-1"
+}, {
+  label: "4.3 == -4.3",
+  set: 4.3,
+  fn: "compare",
+  param: -4.3,
+  expect: "1"
+}, {
+  label: "2^0.5",
+  set: 2,
+  fn: "pow",
+  param: 0.5,
+  expect: "null"
+}, {
+  label: "(-8/27)^(1/3)",
+  set: [-8, 27],
+  fn: "pow",
+  param: [1, 3],
+  expect: "null"
+}, {
+  label: "(-8/27)^(2/3)",
+  set: [-8, 27],
+  fn: "pow",
+  param: [2, 3],
+  expect: "null"
+}, {
+  label: "(-32/243)^(5/3)",
+  set: [-32, 243],
+  fn: "pow",
+  param: [5, 3],
+  expect: "null"
+}, {
+  label: "sqrt(0)",
+  set: 0,
+  fn: "pow",
+  param: 0.5,
+  expect: "0"
+}, {
+  label: "27^(2/3)",
+  set: 27,
+  fn: "pow",
+  param: "2/3",
+  expect: "9"
+}, {
+  label: "(243/1024)^(2/5)",
+  set: "243/1024",
+  fn: "pow",
+  param: "2/5",
+  expect: "0.5625"
+}, {
+  label: "-0.5^-3",
+  set: -0.5,
+  fn: "pow",
+  param: -3,
+  expect: "-8"
+}, {
+  label: "",
+  set: -3,
+  fn: "pow",
+  param: -3,
+  expect: "-0.(037)"
+}, {
+  label: "-3",
+  set: -3,
+  fn: "pow",
+  param: 2,
+  expect: "9"
+}, {
+  label: "-3",
+  set: -3,
+  fn: "pow",
+  param: 3,
+  expect: "-27"
+}, {
+  label: "0^0",
+  set: 0,
+  fn: "pow",
+  param: 0,
+  expect: "1"
+}, {
+  label: "2/3^7",
+  set: [2, 3],
+  fn: "pow",
+  param: 7,
+  expect: "0.(058527663465935070873342478280749885688157293095564700502972107910379515317786922725194330132601737540009144947416552354823959762231367169638774577046181984453589391860996799268404206675811614083219021490626428898033836305441243712848651120256)"
+}, {
+  label: "-0.6^4",
+  set: -0.6,
+  fn: "pow",
+  param: 4,
+  expect: "0.1296"
+}, {
+  label: "8128371:12394 - 8128371/12394",
+  set: "8128371:12394",
+  fn: "sub",
+  param: "8128371/12394",
+  expect: "0"
+}, {
+  label: "3/4 + 1/4",
+  set: "3/4",
+  fn: "add",
+  param: "1/4",
+  expect: "1"
+}, {
+  label: "1/10 + 2/10",
+  set: "1/10",
+  fn: "add",
+  param: "2/10",
+  expect: "0.3"
+}, {
+  label: "5/10 + 2/10",
+  set: "5/10",
+  fn: "add",
+  param: "2/10",
+  expect: "0.7"
+}, {
+  label: "18/10 + 2/10",
+  set: "18/10",
+  fn: "add",
+  param: "2/10",
+  expect: "2"
+}, {
+  label: "1/3 + 1/6",
+  set: "1/3",
+  fn: "add",
+  param: "1/6",
+  expect: "0.5"
+}, {
+  label: "1/3 + 2/6",
+  set: "1/3",
+  fn: "add",
+  param: "2/6",
+  expect: "0.(6)"
+}, {
+  label: "3/4 / 1/4",
+  set: "3/4",
+  fn: "div",
+  param: "1/4",
+  expect: "3"
+}, {
+  label: "1/10 / 2/10",
+  set: "1/10",
+  fn: "div",
+  param: "2/10",
+  expect: "0.5"
+}, {
+  label: "5/10 / 2/10",
+  set: "5/10",
+  fn: "div",
+  param: "2/10",
+  expect: "2.5"
+}, {
+  label: "18/10 / 2/10",
+  set: "18/10",
+  fn: "div",
+  param: "2/10",
+  expect: "9"
+}, {
+  label: "1/3 / 1/6",
+  set: "1/3",
+  fn: "div",
+  param: "1/6",
+  expect: "2"
+}, {
+  label: "1/3 / 2/6",
+  set: "1/3",
+  fn: "div",
+  param: "2/6",
+  expect: "1"
+}, {
+  label: "3/4 * 1/4",
+  set: "3/4",
+  fn: "mul",
+  param: "1/4",
+  expect: "0.1875"
+}, {
+  label: "1/10 * 2/10",
+  set: "1/10",
+  fn: "mul",
+  param: "2/10",
+  expect: "0.02"
+}, {
+  label: "5/10 * 2/10",
+  set: "5/10",
+  fn: "mul",
+  param: "2/10",
+  expect: "0.1"
+}, {
+  label: "18/10 * 2/10",
+  set: "18/10",
+  fn: "mul",
+  param: "2/10",
+  expect: "0.36"
+}, {
+  label: "1/3 * 1/6",
+  set: "1/3",
+  fn: "mul",
+  param: "1/6",
+  expect: "0.0(5)"
+}, {
+  label: "1/3 * 2/6",
+  set: "1/3",
+  fn: "mul",
+  param: "2/6",
+  expect: "0.(1)"
+}, {
+  label: "5/4 - 1/4",
+  set: "5/4",
+  fn: "sub",
+  param: "1/4",
+  expect: "1"
+}, {
+  label: "5/10 - 2/10",
+  set: "5/10",
+  fn: "sub",
+  param: "2/10",
+  expect: "0.3"
+}, {
+  label: "9/10 - 2/10",
+  set: "9/10",
+  fn: "sub",
+  param: "2/10",
+  expect: "0.7"
+}, {
+  label: "22/10 - 2/10",
+  set: "22/10",
+  fn: "sub",
+  param: "2/10",
+  expect: "2"
+}, {
+  label: "2/3 - 1/6",
+  set: "2/3",
+  fn: "sub",
+  param: "1/6",
+  expect: "0.5"
+}, {
+  label: "3/3 - 2/6",
+  set: "3/3",
+  fn: "sub",
+  param: "2/6",
+  expect: "0.(6)"
+}, {
+  label: "0.006999999999999999",
+  set: 0.006999999999999999,
+  fn: "add",
+  param: 0,
+  expect: "0.007"
+}, {
+  label: "1/7 - 1",
+  set: 1 / 7,
+  fn: "add",
+  param: -1,
+  expect: "-0.(857142)"
+}, {
+  label: "0.0065 + 0.0005",
+  set: 0.0065,
+  fn: "add",
+  param: 0.0005,
+  expect: "0.007"
+}, {
+  label: "6.5/.5",
+  set: 6.5,
+  fn: "div",
+  param: .5,
+  expect: "13"
+}, {
+  label: "0.999999999999999999999999999",
+  set: 0.999999999999999999999999999,
+  fn: "sub",
+  param: 1,
+  expect: "0"
+}, {
+  label: "0.5833333333333334",
+  set: 0.5833333333333334,
+  fn: "add",
+  param: 0,
+  expect: "0.58(3)"
+}, {
+  label: "1.75/3",
+  set: 1.75 / 3,
+  fn: "add",
+  param: 0,
+  expect: "0.58(3)"
+}, {
+  label: "3.3333333333333",
+  set: 3.3333333333333,
+  fn: "add",
+  param: 0,
+  expect: "3.(3)"
+}, {
+  label: "4.285714285714285714285714",
+  set: 4.285714285714285714285714,
+  fn: "add",
+  param: 0,
+  expect: "4.(285714)"
+}, {
+  label: "-4",
+  set: -4,
+  fn: "neg",
+  param: 0,
+  expect: "4"
+}, {
+  label: "4",
+  set: 4,
+  fn: "neg",
+  param: 0,
+  expect: "-4"
+}, {
+  label: "0",
+  set: 0,
+  fn: "neg",
+  param: 0,
+  expect: "0"
+}, {
+  label: "6869570742453802/5329686054127205",
+  set: "6869570742453802/5329686054127205",
+  fn: "neg",
+  param: 0,
+  expect: "-1.288925965373540"
+}, {
+  label: "686970702/53212205",
+  set: "686970702/53212205",
+  fn: "neg",
+  param: 0,
+  expect: "-12.910021338149772"
+}, {
+  label: "1/3000000000000000",
+  set: "1/3000000000000000",
+  fn: "add",
+  param: 0,
+  expect: "0.000000000000000(3)"
+}, {
+  label: "toString(15) .0000000000000003",
+  set: ".0000000000000003",
+  fn: "toString",
+  param: 15,
+  expect: "0.000000000000000"
+}, {
+  label: "toString(16) .0000000000000003",
+  set: ".0000000000000003",
+  fn: "toString",
+  param: 16,
+  expect: "0.0000000000000003"
+}, {
+  label: "12 / 4.3",
+  set: 12,
+  set2: 4.3,
+  fn: "toString",
+  param: null,
+  expectError: NonIntegerParameter()
+}, {
+  label: "12.5 / 4",
+  set: 12.5,
+  set2: 4,
+  fn: "toString",
+  param: null,
+  expectError: NonIntegerParameter()
+}, {
+  label: "0.9 round to multiple of 1/8",
+  set: .9,
+  fn: "roundTo",
+  param: "1/8",
+  expect: "0.875"
+}, {
+  label: "1/3 round to multiple of 1/16",
+  set: 1 / 3,
+  fn: "roundTo",
+  param: "1/16",
+  expect: "0.3125"
+}, {
+  label: "1/3 round to multiple of 1/16",
+  set: -1 / 3,
+  fn: "roundTo",
+  param: "1/16",
+  expect: "-0.3125"
+}, {
+  label: "1/2 round to multiple of 1/4",
+  set: 1 / 2,
+  fn: "roundTo",
+  param: "1/4",
+  expect: "0.5"
+}, {
+  label: "1/4 round to multiple of 1/2",
+  set: 1 / 4,
+  fn: "roundTo",
+  param: "1/2",
+  expect: "0.5"
+}, {
+  label: "10/3 round to multiple of 1/2",
+  set: "10/3",
+  fn: "roundTo",
+  param: "1/2",
+  expect: "3.5"
+}, {
+  label: "-10/3 round to multiple of 1/2",
+  set: "-10/3",
+  fn: "roundTo",
+  param: "1/2",
+  expect: "-3.5"
+}, {
+  label: "log_2(8)",
+  set: "8",
+  fn: "log",
+  param: "2",
+  expect: "3" // because 2^3 = 8
+}, {
+  label: "log_2(3)",
+  set: "3",
+  fn: "log",
+  param: "2",
+  expect: 'null' // because 2^(p/q) != 3
+}, {
+  label: "log_10(1000)",
+  set: "1000",
+  fn: "log",
+  param: "10",
+  expect: "3" // because 10^3 = 1000
+}, {
+  label: "log_27(81)",
+  set: "81",
+  fn: "log",
+  param: "27",
+  expect: "1.(3)" // because 27^(4/3) = 81
+}, {
+  label: "log_27(9)",
+  set: "9",
+  fn: "log",
+  param: "27",
+  expect: "0.(6)" // because 27^(2/3) = 9
+}, {
+  label: "log_9/4(27/8)",
+  set: "27/8",
+  fn: "log",
+  param: "9/4",
+  expect: "1.5" // because (9/4)^(3/2) = 27/8
+}];
+
+describe('Fraction', function () {
+  for (var i = 0; i < tests.length; i++) {
+
+    (function (i) {
+      var action;
+
+      if (tests[i].fn) {
+        action = function () {
+          var x = Fraction(tests[i].set, tests[i].set2)[tests[i].fn](tests[i].param);
+          if (x === null) return "null";
+          return x.toString();
+        };
+      } else {
+        action = function () {
+          var x = new Fraction(tests[i].set, tests[i].set2);
+          if (x === null) return "null";
+          return x.toString();
+        };
+      }
+
+      it(String(tests[i].label || tests[i].set), function () {
+        if (tests[i].expectError) {
+          assert.throws(action, tests[i].expectError);
+        } else {
+          assert.equal(action(), tests[i].expect);
+        }
+      });
+
+    })(i);
+  }
+});
+
+describe('JSON', function () {
+
+  it("Should be possible to stringify the object", function () {
+
+    if (typeof Fraction(1).n !== 'number') {
+      return;
+    }
+    assert.equal('{"s":1,"n":14623,"d":330}', JSON.stringify(new Fraction("44.3(12)")));
+    assert.equal('{"s":-1,"n":2,"d":1}', JSON.stringify(new Fraction(-1 / 2).inverse()));
+  });
+});
+
+describe('Arguments', function () {
+
+  it("Should be possible to use different kind of params", function () {
+
+    // String
+    var fraction = new Fraction("0.1");
+    assert.equal("1/10", fraction.n + "/" + fraction.d);
+
+    var fraction = new Fraction("6234/6460");
+    assert.equal("3117/3230", fraction.n + "/" + fraction.d);
+
+    // Two params
+    var fraction = new Fraction(1, 2);
+    assert.equal("1/2", fraction.n + "/" + fraction.d);
+
+    // Object
+    var fraction = new Fraction({ n: 1, d: 3 });
+    assert.equal("1/3", fraction.n + "/" + fraction.d);
+
+    // Array
+    var fraction = new Fraction([1, 4]);
+    assert.equal("1/4", fraction.n + "/" + fraction.d);
+  });
+});
+
+describe('fractions', function () {
+
+  it("Should pass 0.08 = 2/25", function () {
+
+    var fraction = new Fraction("0.08");
+    assert.equal("2/25", fraction.n + "/" + fraction.d);
+  });
+
+  it("Should pass 0.200 = 1/5", function () {
+
+    var fraction = new Fraction("0.200");
+    assert.equal("1/5", fraction.n + "/" + fraction.d);
+  });
+
+  it("Should pass 0.125 = 1/8", function () {
+
+    var fraction = new Fraction("0.125");
+    assert.equal("1/8", fraction.n + "/" + fraction.d);
+  });
+
+  it("Should pass 8.36 = 209/25", function () {
+
+    var fraction = new Fraction(8.36);
+    assert.equal("209/25", fraction.n + "/" + fraction.d);
+  });
+
+});
+
+describe('constructors', function () {
+
+  it("Should pass 0.08 = 2/25", function () {
+
+    var tmp = new Fraction({ d: 4, n: 2, s: -1 });
+    assert.equal("-1/2", tmp.s * tmp.n + "/" + tmp.d);
+
+    var tmp = new Fraction(-88.3);
+    assert.equal("-883/10", tmp.s * tmp.n + "/" + tmp.d);
+
+    var tmp = new Fraction(-88.3).clone();
+    assert.equal("-883/10", tmp.s * tmp.n + "/" + tmp.d);
+
+    var tmp = new Fraction("123.'3'");
+    assert.equal("370/3", tmp.s * tmp.n + "/" + tmp.d);
+
+    var tmp = new Fraction("123.'3'").clone();
+    assert.equal("370/3", tmp.s * tmp.n + "/" + tmp.d);
+
+    var tmp = new Fraction([-1023461776, 334639305]);
+    tmp = tmp.add([4, 25]);
+    assert.equal("-4849597436/1673196525", tmp.s * tmp.n + "/" + tmp.d);
+  });
+});
+
+describe('Latex Output', function () {
+
+  it("Should pass 123.'3' = \\frac{370}{3}", function () {
+
+    var tmp = new Fraction("123.'3'");
+    assert.equal("\\frac{370}{3}", tmp.toLatex());
+  });
+
+  it("Should pass 1.'3' = \\frac{4}{3}", function () {
+
+    var tmp = new Fraction("1.'3'");
+    assert.equal("\\frac{4}{3}", tmp.toLatex());
+  });
+
+  it("Should pass -1.0000000000 = -1", function () {
+
+    var tmp = new Fraction("-1.0000000000");
+    assert.equal('-1', tmp.toLatex());
+  });
+
+  it("Should pass -0.0000000000 = 0", function () {
+
+    var tmp = new Fraction("-0.0000000000");
+    assert.equal('0', tmp.toLatex());
+  });
+});
+
+describe('Fraction Output', function () {
+
+  it("Should pass 123.'3' = 123 1/3", function () {
+
+    var tmp = new Fraction("123.'3'");
+    assert.equal('370/3', tmp.toFraction());
+  });
+
+  it("Should pass 1.'3' = 1 1/3", function () {
+
+    var tmp = new Fraction("1.'3'");
+    assert.equal('4/3', tmp.toFraction());
+  });
+
+  it("Should pass -1.0000000000 = -1", function () {
+
+    var tmp = new Fraction("-1.0000000000");
+    assert.equal('-1', tmp.toFraction());
+  });
+
+  it("Should pass -0.0000000000 = 0", function () {
+
+    var tmp = new Fraction("-0.0000000000");
+    assert.equal('0', tmp.toFraction());
+  });
+
+  it("Should pass 1/-99/293 = -1/29007", function () {
+
+    var tmp = new Fraction(-99).inverse().div(293);
+    assert.equal('-1/29007', tmp.toFraction());
+  });
+
+  it('Should work with large calculations', function () {
+    var x = Fraction(1123875);
+    var y = Fraction(1238750184);
+    var z = Fraction(1657134);
+    var r = Fraction(77344464613500, 92063);
+    assert.equal(x.mul(y).div(z).toFraction(), r.toFraction());
+  });
+});
+
+describe('Fraction toContinued', function () {
+
+  it("Should pass 415/93", function () {
+
+    var tmp = new Fraction(415, 93);
+    assert.equal('4,2,6,7', tmp.toContinued().toString());
+  });
+
+  it("Should pass 0/2", function () {
+
+    var tmp = new Fraction(0, 2);
+    assert.equal('0', tmp.toContinued().toString());
+  });
+
+  it("Should pass 1/7", function () {
+
+    var tmp = new Fraction(1, 7);
+    assert.equal('0,7', tmp.toContinued().toString());
+  });
+
+  it("Should pass 23/88", function () {
+
+    var tmp = new Fraction('23/88');
+    assert.equal('0,3,1,4,1,3', tmp.toContinued().toString());
+  });
+
+  it("Should pass 1/99", function () {
+
+    var tmp = new Fraction('1/99');
+    assert.equal('0,99', tmp.toContinued().toString());
+  });
+
+  it("Should pass 1768/99", function () {
+
+    var tmp = new Fraction('1768/99');
+    assert.equal('17,1,6,14', tmp.toContinued().toString());
+  });
+
+  it("Should pass 1768/99", function () {
+
+    var tmp = new Fraction('7/8');
+    assert.equal('0,1,7', tmp.toContinued().toString());
+  });
+
+});
+
+
+describe('Fraction simplify', function () {
+
+  it("Should pass 415/93", function () {
+
+    var tmp = new Fraction(415, 93);
+    assert.equal('9/2', tmp.simplify(0.1).toFraction());
+    assert.equal('58/13', tmp.simplify(0.01).toFraction());
+    assert.equal('415/93', tmp.simplify(0.0001).toFraction());
+  });
+
+});
