npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@cursorsdottsx/infint

v1.0.0

Published

Lightning-fast arbitrary precision integers using strings + walkthrough and explanation.

Downloads

1

Readme

infint

Lightning-fast arbitrary precision integers using strings + walkthrough and explanation.

Supported operations:

- Addition
- Subtraction
- Multiplication
- Exponentiation
- Division
- Modulo
- Greatest common divisor
- Least common multiple
- N-th root

Algorithms used:

- Grade-school addition
- Grade-school subtraction
- Grid multiplication
- Exponentiation by squaring
- Long division
- Long division with remainder
- Euclidean algorithm
- Derived by definition
- Shifting n-th root algorithm
/**
 * ---------------------------------------------------------
 *   I N F I N I T E   P R E C I S I O N   I N T E G E R S
 * ---------------------------------------------------------
 * 
 * = A b o u t =============================================
 * 
 * In the olden days of JavaScript, we were limited by
 * only the precision of extremely large numbers. Either
 * sacrifice accuracy for convenience, or sacrifice
 * convenience for accuracy. Now that we have BigInt and
 * a plethora of libraries for arbitrary precision
 * numbers, it is a rather trivial problem.
 * 
 * So why re-invent the wheel and write another library
 * to implement arbitrary precision in JavaScript?
 * 
 * First, this is strictly only integers. There is no
 * support for decimals and fractions. Because it is
 * strictly integers it will be slightly faster than
 * implementations supporting other types of numbers.
 * 
 * Second, it uses modern language features with
 * TypeScript, unlike some other libraries written
 * with the unfriendly var keyword.
 * 
 * Third, this also supports other common operations, not
 * just simple grade school arithmetic. Note that these
 * are naturally more computationally expensive.
 * 
 * Fourth, the implementation uses fast algorithms
 * and is fast enough for most applications
 * using arbitrary precision integers. Some aspects
 * of the algorithms have been replaced by slower
 * alternatives for readability and to follow the
 * notes below.
 * 
 * Enjoy true infinite precision integers now.
 * 
 * = N o t e s =============================================
 * 
 * Addition is rather trivial if we only consider positive 
 * integers. However, if we want to support negative
 * integers then implementing proper subtraction would
 * greatly reduce the effort required.
 * 
 * Subtraction is also trivial, but not as trivial, if 
 * we only consider positive integers, and the minuend is 
 * greater than the subtrahend.
 * 
 * The complicated part of true subtraction is that
 * the signs of the numbers influence the sign of the
 * output. This can be easily simplified because of
 * how subtraction can be thought of as adding a 
 * negative number.
 * 
 * And so we list all possible cases:
 * 
 *   a > b & +a +b => a - b      |  22 - 13  => 22 - 13
 *   a < b & +a +b => -(b - a)   |  13 - 22  => -(22 - 13)
 *   - - - & +a -b => a + b      |  13 - -22 => 13 + 22
 *   - - - & -a +b => -(-a + b)  | -13 - 22  => -(13 + 22)
 *   a > b & -a -b => b - a      | -13 - -22 => 22 - 13
 *   a < b & -a -b => -(-a - -b) | -22 - -13 => -(22 - 13)
 * 
 * After implementing all the cases and then simplifying
 * the resulting code greatly, we now have true subtraction
 * which supports both negative and positive numbers.
 * 
 * For multiplication we will use the extremely simple
 * grid method, in which you split up the multiplicand and
 * multiplier into their respective place values and align
 * them on a grid, like so:
 * 
 *   +-----+-----+-----+
 *   | xxx |  20 |   2 |
 *   +-----+-----+-----+
 *   |  10 | 200 |  20 |
 *   +-----+-----+-----+
 *   |   3 |  60 |   6 |
 *   +-----+-----+-----+
 * 
 * Then it's just an addition of all the cells in the grid.
 * This should be slightly faster and easier to implement
 * than traditional grade school multiplication.
 * 
 * The only expensive computation here is the splitting and
 * finding combonations of the place values.
 * 
 * Now for the inverse of multiplication, division.
 * 
 * Since we are using only integers, we are going to
 * implement integer division. The simplest way to implement
 * unsigned integer division would be a while loop with
 * repeated subtraction.
 * 
 * Obviously because we are dealing with extremely large
 * numbers, this would be too slow to perform.
 * 
 * Instead we will vie for a simple implementation
 * of traditional long division. Here is our example:
 * 
 *      +------
 *   13 | 2213
 * 
 * We first need to take the first two digits of the dividend
 * because the divisor is two digits.
 * Then we check if the divisor is greater than the two digits.
 * 
 * In this case it isn't, so we are free to continue. Next,
 * we use naive integer division because it will not be slow
 * when used with operands of similar magnitude.
 * 
 * With our example, we get the quotient 1:
 * 
 *        1
 *      +------
 *   13 | 2213
 * 
 * And then we subtract 13 from the two digits, but what
 * that is actually doing is subtracting 1300 from the entire
 * dividend.
 * 
 *        1
 *      +------
 *   13 | 2213
 *      - 1300
 *         913
 * 
 * We then repeat these steps with our new dividend.
 * 
 *        17
 *      +------
 *   13 | 2213
 *      - 1300
 *         913
 *      -  910
 *           3
 * 
 * One step later and we have another dividend. However,
 * this time, the dividend is smaller than the divisor,
 * which marks the end of division. 
 * 
 * Our quotient is left, 17, and we can clearly see that
 * the remainder is 3.
 * 
 * With division, we can now easily implement modulo.
 * 
 * The code is exactly the same, but with a few different
 * edge cases to check for at the beginning. Note that 
 * we could also instead use an efficient modulus 
 * algorithm, but for the sake of brevity it is not 
 * used.
 * 
 * Exponentiation is next. A naive implementation might 
 * use a loop and multiply the number by itself inside
 * the loop. Remember that we are using arbitrary
 * precision integers and that the integers will be
 * extremely large.
 * 
 * So, we cannot use a plain loop. The optimization we 
 * observe and utilize is exponentiation by squaring.
 * 
 * The core concept is that:
 * 
 *   if x is even
 *     
 *     a^x = a^(x / 2) * a^(x / 2)
 * 
 *   if x is odd 
 * 
 *     a^x = a^((x - 1) / 2) * a^((x - 1) / 2) * a
 * 
 * Because we are squaring, we need less computations
 * to find the result. For example, if 36 was x and
 * 2 was the base:
 * 
 *   36 is even 
 * 
 *   2^36 = 2^18 * 2^18
 * 
 *   18 is even
 *  
 *   2^18 = 2^9 * 2^9
 * 
 *   9 is odd 
 * 
 *   2^9 = 2^4 * 2^4 * 2
 *   
 *   4 is even
 * 
 *   2^4 = 2^2 * 2^2
 * 
 *   2 is even 
 * 
 *   2^2 = 2^1 * 2^1
 * 
 * Compared to 36 multiplications with the simple loop,
 * exponentiation by squaring is considerably more 
 * efficient.
 * 
 * The next functions we will implement are lcm and gcd,
 * more commonly known as least common multiple and 
 * greatest common divisior.
 * 
 * Since lcm can be computed much more easily using gcd,
 * we will implement gcd first. It is common knowledge 
 * that Euclid's algorithm is quite fast and easy to 
 * implment, so that is what we will use. 
 * 
 * Now that we can use the gcd function, we can compute 
 * the least common multiple as follows:
 * 
 *   lcm(a, b) = a * b / gcd(a, b)
 * 
 * Which should be trivial to write code for.
 * 
 * Finally, we have our roots to calculate. Because there
 * is a general algorithm to get the nth root, we will 
 * instead implement the algorithm, named the shifting
 * nth root algorithm.
 * 
 * Even if we are using an algorithm for all indices,
 * it is worth to take note of the Newton-Raphson
 * method to approximate roots very closely, although 
 * it does not work well for larger indices as it becomes
 * harder to find an initial guess that will require
 * little iterations.
 * 
 * This last algorithm is quite long and requires rather
 * more number theory and mathematics, so be prepared.
 * 
 * We first define a few variables: let n be the degree
 * of the root, x be the radicand, y be the root, and 
 * r be the remainder.
 * 
 * Let x' be the value of x in the next iteration,
 * and y' and r' in the same manner.
 * 
 * Now for the actual algorithm. Split the radicand
 * into chunks of digits, each with the length as the 
 * degree of the root. Align the chunks so that the 
 * decimal place is between them. 
 * 
 * Take the first chunk, alpha, and find beta, so that
 * 
 *   beta ^ n <= alpha
 * 
 * Then set y equal to beta, and r to
 *   
 *   alpha - beta ^ n
 * 
 * beause it is the remainder.
 * 
 * You an think of this as a very general approximation
 * of the root. The next step is to iterate over each 
 * of the remaining chunks, where each chunk will be 
 * henceforth referred to as alpha.
 * 
 * Find beta such that
 * 
 *   (10 ^ y + beta) ^ n - 10 ^ n * y ^ x <= 10 ^ n * r + alpha
 * 
 * This will give us the next digit of the root, beta.
 * So in the next iteration, we will append our newfound 
 * digit to the answer.
 * 
 *   y' = 10 ^ y + beta
 *
 *   r' = 10 ^ n * r + alpha - (y' ^ x - 10 ^ x * y ^ x)
 *
 * And we also calculate the new remainder to be used for the 
 * next iteration. Now before the next iteration, we update 
 * the values of y and r with y' and r', respectively.
 * 
 * Repeat this process until you have reached the desired
 * precision or when you have iterated through all chunks.
 * 
 * This algorithm is easy enough to implement with our existing
 * operations from above.
 * 
 * = C r e d i t s =========================================
 * 
 * "Give credit where credit is due."
 * 
 * https://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic
 * 
 * https://cheonhyangzhang.gitbooks.io/leetcode-solutions/content/415-add-strings.html
 * https://www.geeksforgeeks.org/sum-two-large-numbers/
 * 
 * https://stackoverflow.com/questions/40708444/add-or-subtract-two-numbers-represented-as-strings-without-using-int-double-lo
 * https://www.geeksforgeeks.org/difference-of-two-large-numbers/
 *
 * https://en.wikipedia.org/wiki/Multiplication_algorithm
 * 
 * https://en.wikipedia.org/wiki/Division_algorithm
 * https://en.wikipedia.org/wiki/Long_division
 * 
 * https://en.wikipedia.org/wiki/Euclidean_algorithm 
 *
 * https://stackoverflow.com/questions/101439/the-most-efficient-way-to-implement-an-integer-based-power-function-powint-int
 * https://en.wikipedia.org/wiki/Exponentiation_by_squaring
 * 
 * https://en.wikipedia.org/wiki/Nth_root
 * https://stackoverflow.com/questions/20730053/algorithm-to-find-nth-root-of-a-number
 * https://en.wikipedia.org/wiki/Shifting_nth_root_algorithm
 * https://math.stackexchange.com/questions/1066790/shifting-nth-root-algorithm
 */