APInt is an Arbitrary Precision Integer library, built to calculate large numbers without losing a single bit of precision. ๐ข
This library is engineered to be effortlessly easy to use, integrating as seamlessly as possible into existing Lua projects by overloading standard arithmetic metatables.
- ๐ง Arbitrary-Precision Integers: Create and manipulate integers far larger than Lua's maximum int value.
All standard arithmetic operators are overloaded:
- โ Operations:
- Addition (
+) - Subtraction (
-) - Multiplication (
*) - Division (
/) (floor division) - Modulo (
%) - Exponentiation (
^) - Unary Minus (
-)
- Addition (
- โ๏ธ Comparison Operators:
- Less Than (
<) - Equality (
==) - (Greater than, less than or equal to, etc., also work, inferred from
<and==)
- Less Than (
- ๐ String Conversion:
tostringmethod also works. - โ๏ธ Additional Operations:
- Bitwise-like Shifts: Implemented for internal use (
__lsland__lsrare available in the source, though not aliased to<<or>>to maintain Lua 5.2 standards). - ๐ง Flexible Type Handling: Configure the library to operate in different modes (
STRICT,WARNING,NOT-STRICT) to manage operations with mixedAPIntand standard number types.
- Bitwise-like Shifts: Implemented for internal use (
To use APInt ๐บ, simply require the APInt.lua file in your project.
local APInt = require("APInt")If you're on Roblox: just make the library a ModuleScript and require it from a Local or Server Script.
Or also on wally (a roblox package manager) thanks to Vran-n!
You can create new arbitrary-precision integers from a number, a string, or even another APInt object (a table of integers between 0 and BASE).
-- From a number
local a = APInt.new(12345)
local b = APInt(98765) -- You can also call the library object directly!
-- From a string for massive numbers
local very_large_number = APInt.new("123456789012345678901234567890")
local another_large_one = APInt("987654321098765432109876543210")All the standard arithmetic operators work seemlessly as you'd expect.
local APInt = require("APInt")
local a = APInt("23456789012345678901")
local b = APInt("98765432109876543210")
-- Addition
local sum = a + b
print("Sum:", sum)
-- Subtraction
local difference = b - a
print("Difference:", difference)
-- Multiplication
local product = APInt(2)^APInt(256)
print("Product:", product)
-- Division
local quotient, remainder = a / b
print("Quotient:", quotient)
print("Remainder:", remainder)
-- Modulo
local mod = b % a
print("Modulo:", mod)
-- Exponentiation
local power = APInt(5)^APInt(100)
print("5^100:", power)local APInt = require("APInt")
local a = APInt("100000000000000000000")
local b = APInt("100000000000000000001")
if a < b then
print("a is less than b")
end
if a == a then
print("a is equal to itself")
endYou can convert APInt objects to strings for printing or serialization.
local APInt = require("APInt")
local large_number = APInt(2)^APInt(128)
-- Implicitly calls __tostring
print("2^128 is: " .. large_number)
local as_string = tostring(large_number)
print(as_string)You can tweak the library's mode for handling non-APInt types in operations.
"NOT-STRICT"(default): Automatically converts numbers toAPInt. โ"WARNING": Converts numbers but gives you a heads-up with a warning.โ ๏ธ "STRICT": Throws an error if an operation involves a non-APInttype. ๐
local APInt = require("APInt")
APInt.MODE = "STRICT"
local a = APInt(100)
-- This will now throw an error instead of silently converting 50!
local result = a + 50APInt competes with the BigNum library by the great programmer Validark. It has equal or better performance for correct results in Roblox Studio, which I am satisfied with!
The benchmark file is in the repository, so you can test it on your own machine! (You'll need the BigNum and APInt libraries in the same directory).
The results files are also in the repository: "benchmark_results_computer" for tests on my computerโข and "benchmark_results_studio" for tests in Roblox Studio.
Here are some of the most egregious results:
| Operation | BigNum Time | APInt Time | Speedup |
|---|---|---|---|
| Creation (.new) | 1.003874 | 0.009388 | ~107x |
| Division (Large) | 175.193368 | 49.426132 | ~3.5x |
| Modulo (Large) | 170.684935 | 48.997882 | ~3.5x |
| To String (Large) | 152.764841 | 11.557140 | ~13.2x |
| Operation | BigNum Time | APInt Time | Speedup |
|---|---|---|---|
| Creation (.new) | 0.166527 | 0.020295 | ~8.2x |
| Division (Large) | 6.890840 | 1.265368 | ~5.4x |
| Modulo (Large) | 7.019249 | 1.278361 | ~5.5x |
| To String (Large) | 6.511121 | 0.333968 | ~19.5x |
The numbers are stored as a table (array) of numbers in base 2^52 by default, with the last number also storing the sign. This structure takes advantage of Lua's float64 number type without sacrificing precision. The table is variable-sized, and every number is immutable. The algorithms used are linked in the source code!
The library is unit-tested using Busted in test.lua. It runs as a standalone file with Lua 5.2 (with Busted installed).
- This library aims to replicate the simplicity and elegance of how Python handles big integers. ๐
- I implemented karatsuba's algorithm for multiplication (and division) but the performance was worse even for big numbers so it got cut in the final release.
- My favourite beer is Guinness
- The library was built for Roblox games but doesn't yet leverage Roblox's
bufferlibrary, which could be faster.