Skip to content

Commit fd8b29b

Browse files
committed
Add big unsigned integers
1 parent b2ae738 commit fd8b29b

File tree

2 files changed

+646
-0
lines changed

2 files changed

+646
-0
lines changed

.spec/math/uint_spec.lua

Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
describe("Unsigned integers", function()
2+
local uint = require("math.uint")
3+
local hex = uint(16) -- use a small base for testing
4+
5+
local function randuint(uint_cls)
6+
uint_cls = uint_cls or hex
7+
local n_digits = math.random(0, 50)
8+
local digits = {}
9+
for i = 1, n_digits - 1 do
10+
digits[i] = math.random(0, uint_cls.base - 1)
11+
end
12+
if n_digits > 0 then
13+
digits[n_digits] = math.random(1, uint_cls.base - 1)
14+
end
15+
return uint_cls.from_digits(digits)
16+
end
17+
18+
it("class tables are cached", function()
19+
assert.equal(hex, uint(16))
20+
end)
21+
22+
describe("constructors & equality testing", function()
23+
assert.equal(hex.from_number(0), hex.zero())
24+
assert.equal(hex.from_number(1), hex.one())
25+
assert.equal(hex.from_number(0xF), hex.from_digits({ 0xF }))
26+
assert.equal(hex.from_number(hex.base), hex.from_digits({ 0, 1 }))
27+
assert.equal(hex.from_number(0xC0FFEE), hex.from_digits({ 0xE, 0xE, 0xF, 0xF, 0x0, 0xC }))
28+
end)
29+
30+
it("copy (from)", function()
31+
local n = hex.from_number(42)
32+
local m = n:copy()
33+
m:increment()
34+
assert.equal(hex.from_number(42), n)
35+
assert.equal(hex.from_number(43), m)
36+
m:copy_from(n)
37+
assert.equal(hex.from_number(42), m)
38+
end)
39+
40+
describe("to number", function()
41+
it("works for exactly representable uints", function()
42+
assert.equal(123456789, hex.from_number(123456789):to_number())
43+
end)
44+
it("returns nothing for not exactly representable uints if exact=true", function()
45+
assert.equal(nil, ((hex.from_number(2 ^ 53) + 1):to_number()))
46+
end)
47+
it("returns rounded uint if exact=false", function()
48+
assert.equal(2 ^ 53, (hex.from_number(2 ^ 53) + 1):to_number(false))
49+
end)
50+
end)
51+
52+
describe("base conversions", function()
53+
local dec = uint(10)
54+
it("small numbers", function()
55+
assert.equal(hex.from_number(1234), dec.from_number(1234):convert_base_to(hex))
56+
for _ = 1, 100 do
57+
-- Workaround for a limitation of Lua random, which uses 32-bit ints internally
58+
local n = math.random(2 ^ 26) * 2 ^ 26 + math.random(0, 2 ^ 26 - 1)
59+
assert.equal(hex.from_number(n), dec.from_number(n):convert_base_to(hex))
60+
assert.equal(dec.from_number(n), hex.from_number(n):convert_base_to(dec))
61+
end
62+
end)
63+
it("round-trip random numbers", function()
64+
for _ = 1, 100 do
65+
local n, m = randuint(), randuint(dec)
66+
assert.equal(n, n:convert_base_to(dec):convert_base_to(hex))
67+
assert.equal(m, m:convert_base_to(hex):convert_base_to(dec))
68+
end
69+
end)
70+
end)
71+
72+
describe("comparisons", function()
73+
local function test_compare(name, fn)
74+
local function rand_uint()
75+
return math.random(0, 2 ^ 30)
76+
end
77+
local function test(a, b)
78+
assert.equal(fn(a, b), fn(hex.from_number(a), hex.from_number(b)))
79+
end
80+
it(name, function()
81+
for _ = 1, 100 do
82+
local a = rand_uint()
83+
test(a, a)
84+
test(rand_uint(), rand_uint())
85+
end
86+
end)
87+
end
88+
test_compare("less than", function(a, b)
89+
return a < b
90+
end)
91+
test_compare("less than or equal", function(a, b)
92+
return a <= b
93+
end)
94+
test_compare("equal to", function(a, b)
95+
return a == b
96+
end)
97+
test_compare("compare", function(a, b)
98+
if type(a) == "number" then
99+
if a < b then
100+
return -1
101+
elseif b < a then
102+
return 1
103+
end
104+
return 0
105+
end
106+
return a:compare(b)
107+
end)
108+
end)
109+
110+
describe("arithmetic", function()
111+
local function test_law(name, n_params, f)
112+
it(name, function()
113+
local params = {}
114+
for _ = 1, 100 do
115+
for i = 1, n_params do
116+
params[i] = randuint()
117+
end
118+
assert(f(unpack(params)))
119+
end
120+
end)
121+
end
122+
describe("addition", function()
123+
it("is consistent with Lua numbers", function()
124+
local function rand_uint()
125+
return math.random(0, 2 ^ 50)
126+
end
127+
local function test(a, b)
128+
assert.equal(hex.from_number(a + b), hex.from_number(a) + hex.from_number(b))
129+
end
130+
for _ = 1, 100 do
131+
local a = rand_uint()
132+
test(a, a)
133+
test(rand_uint(), 0)
134+
test(0, rand_uint())
135+
test(rand_uint(), rand_uint())
136+
end
137+
end)
138+
test_law("associativity", 3, function(a, b, c)
139+
return a + (b + c) == (a + b) + c
140+
end)
141+
test_law("commutativity", 2, function(a, b)
142+
return a + b == b + a
143+
end)
144+
test_law("neutral element", 1, function(a)
145+
return a + 0 == a
146+
end)
147+
test_law("a + b - b = a", 2, function(a, b)
148+
return a + b - b == a
149+
end)
150+
end)
151+
describe("subtraction", function()
152+
it("is consistent with Lua numbers", function()
153+
local function rand_uint()
154+
return math.random(0, 2 ^ 50)
155+
end
156+
local function test(a, b)
157+
assert.equal(hex.from_number(a - b), hex.from_number(a) - hex.from_number(b))
158+
end
159+
for _ = 1, 100 do
160+
local a = rand_uint()
161+
test(a, a)
162+
test(rand_uint(), 0)
163+
test(a + rand_uint(), a)
164+
end
165+
end)
166+
test_law("a - a = 0", 1, function(a)
167+
return a - a == hex.zero()
168+
end)
169+
test_law("a - 0 = a", 1, function(a)
170+
return a - 0 == a
171+
end)
172+
end)
173+
describe("multiplication", function()
174+
it("is consistent with Lua numbers", function()
175+
local function rand_uint()
176+
return math.random(0, 2 ^ 24)
177+
end
178+
local function test(a, b)
179+
assert.equal(hex.from_number(a * b), hex.from_number(a) * hex.from_number(b))
180+
end
181+
for _ = 1, 100 do
182+
local a = rand_uint()
183+
test(a, a)
184+
test(rand_uint(), 0)
185+
test(0, rand_uint())
186+
test(rand_uint(), 1)
187+
test(1, rand_uint())
188+
test(rand_uint(), rand_uint())
189+
end
190+
end)
191+
test_law("distributivity", 3, function(a, b, c)
192+
return a * (b + c) == a * b + a * c
193+
end)
194+
test_law("associativity", 3, function(a, b, c)
195+
return a * (b * c) == (a * b) * c
196+
end)
197+
test_law("commutativity", 2, function(a, b)
198+
return a * b == b * a
199+
end)
200+
test_law("neutral element", 1, function(a)
201+
return a * 1 == a
202+
end)
203+
end)
204+
describe("exponentiation", function()
205+
it("throws on 0^0", function()
206+
assert.has_error(function()
207+
return hex.zero() ^ hex.zero()
208+
end)
209+
end)
210+
it("0^n = 0", function()
211+
assert.equal(hex.zero(), hex.zero() ^ 42)
212+
end)
213+
it("1^n = 1", function()
214+
assert.equal(hex.one(), hex.one() ^ 42)
215+
end)
216+
it("supports small uint exponents", function()
217+
assert.equal(hex.from_number(13 ^ 7), 13 ^ hex.from_number(7))
218+
end)
219+
it("is consistent with naive implementation", function()
220+
for _ = 1, 10 do
221+
local base = randuint()
222+
local exp = math.random(1, 10)
223+
local pow = hex.one()
224+
for _ = 1, exp do
225+
pow = pow * base
226+
end
227+
assert.equal(pow, base ^ exp)
228+
end
229+
end)
230+
end)
231+
end)
232+
describe("updates", function()
233+
local function test_unary(initial, expected, update)
234+
local n = hex.from_number(initial)
235+
update(n)
236+
assert.equal(n, hex.from_number(expected))
237+
end
238+
describe("increment", function()
239+
it("without carry", function()
240+
test_unary(1234, 1235, hex.increment)
241+
end)
242+
it("with carry", function()
243+
test_unary(hex.base - 1, hex.base, hex.increment)
244+
end)
245+
end)
246+
describe("decrement", function()
247+
it("without carry", function()
248+
test_unary(1235, 1234, hex.decrement)
249+
end)
250+
it("with carry", function()
251+
test_unary(hex.base - 1, hex.base, hex.increment)
252+
end)
253+
end)
254+
local function test_binary(name, fn)
255+
it(name, function()
256+
for _ = 1, 100 do
257+
local n, m = randuint(), randuint()
258+
if name == "subtract" then
259+
n = n + m
260+
end
261+
local res = fn(n, m)
262+
n[name](n, m)
263+
assert.equal(res, n)
264+
end
265+
end)
266+
end
267+
test_binary("add", function(a, b)
268+
return a + b
269+
end)
270+
test_binary("subtract", function(a, b)
271+
return a - b
272+
end)
273+
test_binary("multiply", function(a, b)
274+
return a * b
275+
end)
276+
end)
277+
end)

0 commit comments

Comments
 (0)