Skip to content

Commit 3fcee7f

Browse files
committed
Micro optimizations and add benchmark example
1 parent c9f932d commit 3fcee7f

File tree

4 files changed

+180
-100
lines changed

4 files changed

+180
-100
lines changed

bint.lua

Lines changed: 73 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@ Then when you need create a bint, you can use one of the following functions:
6161
* @{bint.frominteger} (convert from lua integers, preserving the sign)
6262
* @{bint.fromnumber} (convert from lua floats, truncating the fractional part)
6363
* @{bint.frombase} (convert from arbitrary bases, like hexadecimal)
64-
* @{bint.new} (convert from anything, asserts on invalid values)
65-
* @{bint.convert} (convert form anything, returns nil on invalid values)
64+
* @{bint.new} (convert from anything, asserts on invalid integers)
65+
* @{bint.convert} (convert form anything, returns nil on invalid integers)
6666
* @{bint.parse} (convert from anything, returns a lua number as fallback)
6767
* @{bint.zero}
6868
* @{bint.one}
@@ -77,13 +77,12 @@ When you are done computing and need to get the result,
7777
get the output from one of the following functions:
7878
7979
* @{bint.touinteger} (convert to a lua integer, wraps around as an unsigned integer)
80-
* @{bint.tointeger} (convert to a lua integer, always preserving the sign)
80+
* @{bint.tointeger} (convert to a lua integer, wraps around, preserves the sign)
8181
* @{bint.tonumber} (convert to lua float, losing precision)
8282
* @{bint.tobase} (convert to a string in any base)
8383
* @{bint.__tostring} (convert to a string in base 10)
8484
85-
Note that outputting to a lua number will make the integer overflow and its value wraps around.
86-
To output very large integer with no loss of precision you probably want to use @{bint.tobase}
85+
To output very large integer with no loss you probably want to use @{bint.tobase}
8786
or call `tostring` to get a string representation.
8887
8988
## Precautions
@@ -258,9 +257,7 @@ end
258257

259258
-- Compute power with lua integers.
260259
local function ipow(y, x, n)
261-
if n == 0 then
262-
return y
263-
elseif n == 1 then
260+
if n == 1 then
264261
return y * x
265262
elseif n & 1 == 0 then --even
266263
return ipow(y, x * x, n // 2)
@@ -463,11 +460,11 @@ end
463460
function bint.new(x)
464461
if isbint(x) then
465462
-- return a clone
466-
local n = {}
463+
local n = bint_newempty()
467464
for i=1,BIGINT_SIZE do
468465
n[i] = x[i]
469466
end
470-
return setmetatable(n, bint)
467+
return n
471468
else
472469
x = bint_fromvalue(x)
473470
end
@@ -563,6 +560,12 @@ function bint.isminusone(x)
563560
end
564561
end
565562

563+
--- Check if the input is a bint.
564+
-- @param x Any lua value.
565+
function bint.isbint(x)
566+
return isbint(x)
567+
end
568+
566569
--- Check if a number is negative considering bints.
567570
-- Zero is guaranteed to never be negative for bints.
568571
-- @param x A bint or a lua number.
@@ -604,31 +607,23 @@ function bint.isodd(x)
604607
end
605608
end
606609

607-
--- Assign a bint to zero (in-place).
608-
function bint:_zero()
609-
for i=1,BIGINT_SIZE do
610-
self[i] = 0
611-
end
612-
return self
613-
end
614-
615610
--- Create a new bint with 0 value.
616611
function bint.zero()
617-
return bint_newempty():_zero()
618-
end
619-
620-
--- Assign a bint to 1 (in-place).
621-
function bint:_one()
622-
self[1] = 1
623-
for i=2,BIGINT_SIZE do
624-
self[i] = 0
612+
local x = bint_newempty()
613+
for i=1,BIGINT_SIZE do
614+
x[i] = 0
625615
end
626-
return self
616+
return x
627617
end
628618

629619
--- Create a new bint with 1 value.
630620
function bint.one()
631-
return bint_newempty():_one()
621+
local x = bint_newempty()
622+
x[1] = 1
623+
for i=2,BIGINT_SIZE do
624+
x[i] = 0
625+
end
626+
return x
632627
end
633628

634629
--- Bitwise left shift a bint in one bit (in-place).
@@ -775,10 +770,17 @@ end
775770
-- @param x A bint or a lua number to be added.
776771
-- @param y A bint or a lua number to be added.
777772
function bint.__add(x, y)
778-
local ix = bint.convert(x, true)
773+
local ix = bint.convert(x)
779774
local iy = bint.convert(y)
780775
if ix and iy then
781-
return ix:_add(iy)
776+
local z = bint_newempty()
777+
local carry = 0
778+
for i=1,BIGINT_SIZE do
779+
local tmp = ix[i] + iy[i] + carry
780+
carry = tmp > BIGINT_WORDMAX and 1 or 0
781+
z[i] = tmp & BIGINT_WORDMAX
782+
end
783+
return z
782784
else
783785
return bint.tonumber(x) + bint.tonumber(y)
784786
end
@@ -803,10 +805,18 @@ end
803805
-- @param x A bint or a lua number to be subtract from.
804806
-- @param y A bint or a lua number to subtract.
805807
function bint.__sub(x, y)
806-
local ix = bint.convert(x, true)
808+
local ix = bint.convert(x)
807809
local iy = bint.convert(y)
808810
if ix and iy then
809-
return ix:_sub(iy)
811+
local z = bint_newempty()
812+
local borrow = 0
813+
local wordmaxp1 = BIGINT_WORDMAX + 1
814+
for i=1,BIGINT_SIZE do
815+
local res = (ix[i] + wordmaxp1) - (iy[i] + borrow)
816+
z[i] = res & BIGINT_WORDMAX
817+
borrow = res <= BIGINT_WORDMAX and 1 or 0
818+
end
819+
return z
810820
else
811821
return bint.tonumber(x) - bint.tonumber(y)
812822
end
@@ -873,19 +883,18 @@ end
873883
-- @see bint.udiv
874884
-- @see bint.umod
875885
function bint.udivmod(x, y)
876-
local dividend = bint.convert(x, true)
877-
local divisor = bint.convert(y)
886+
local dividend = bint.new(x)
887+
local divisor = bint_assert_convert(y)
888+
local quot = bint.zero()
878889
assert(not divisor:iszero(), 'attempt to divide by zero')
879890
if dividend:ult(divisor) then
880-
return bint.zero(), dividend
891+
return quot, dividend
881892
end
882893
-- align leftmost digits in dividend and divisor
883894
local divisorlbit = findleftbit(divisor)
884895
local divdendlbit, size = findleftbit(dividend)
885896
local bit = divdendlbit - divisorlbit
886897
divisor = divisor << bit
887-
-- set quotient to 0
888-
local quot = bint.zero()
889898
local wordmaxp1 = BIGINT_WORDMAX + 1
890899
local wordbitsm1 = BIGINT_WORDBITS - 1
891900
while bit >= 0 do
@@ -1202,7 +1211,11 @@ end
12021211
-- @param x An integer to perform bitwise NOT.
12031212
-- @raise Asserts in case inputs are not convertible to integers.
12041213
function bint.__bnot(x)
1205-
return bint.new(x):_bnot()
1214+
local y = bint_newempty()
1215+
for i=1,BIGINT_SIZE do
1216+
y[i] = (~x[i]) & BIGINT_WORDMAX
1217+
end
1218+
return y
12061219
end
12071220

12081221
--- Negate a bint (in-place). This apply effectively apply two's complements.
@@ -1213,7 +1226,7 @@ end
12131226
--- Negate a bint. This apply effectively apply two's complements.
12141227
-- @param x A bint to perform negation.
12151228
function bint.__unm(x)
1216-
return bint.new(x):_unm()
1229+
return (~x):_inc()
12171230
end
12181231

12191232
--- Check if bints are equal.
@@ -1232,13 +1245,7 @@ end
12321245
-- @param x A bint or lua number to compare.
12331246
-- @param y A bint or lua number to compare.
12341247
function bint.eq(x, y)
1235-
local ix = bint.convert(x)
1236-
local iy = bint.convert(y)
1237-
if ix and iy then
1238-
return ix == iy
1239-
else
1240-
return x == y
1241-
end
1248+
return bint.convert(x) == bint.convert(y)
12421249
end
12431250

12441251
--- Compare if integer x is less than y considering bints (unsigned version).
@@ -1278,12 +1285,18 @@ end
12781285
-- @param y Right value to compare, a bint or lua number.
12791286
-- @see bint.ult
12801287
function bint.__lt(x, y)
1281-
local ix = bint.convert(x)
1282-
local iy = bint.convert(y)
1288+
local ix, iy = bint.convert(x), bint.convert(y)
12831289
if ix and iy then
1284-
local xneg, yneg = ix:isneg(), iy:isneg()
1290+
local xneg = ix[BIGINT_SIZE] & BIGINT_WORDMSB ~= 0
1291+
local yneg = iy[BIGINT_SIZE] & BIGINT_WORDMSB ~= 0
12851292
if xneg == yneg then
1286-
return bint.ult(ix, iy)
1293+
for i=BIGINT_SIZE,1,-1 do
1294+
local a, b = ix[i], iy[i]
1295+
if a ~= b then
1296+
return a < b
1297+
end
1298+
end
1299+
return false
12871300
else
12881301
return xneg and not yneg
12891302
end
@@ -1297,12 +1310,18 @@ end
12971310
-- @param y Right value to compare, a bint or lua number.
12981311
-- @see bint.ule
12991312
function bint.__le(x, y)
1300-
local ix = bint.convert(x)
1301-
local iy = bint.convert(y)
1313+
local ix, iy = bint.convert(x), bint.convert(y)
13021314
if ix and iy then
1303-
local xneg, yneg = ix:isneg(), iy:isneg()
1315+
local xneg = ix[BIGINT_SIZE] & BIGINT_WORDMSB ~= 0
1316+
local yneg = iy[BIGINT_SIZE] & BIGINT_WORDMSB ~= 0
13041317
if xneg == yneg then
1305-
return bint.ule(ix, iy)
1318+
for i=BIGINT_SIZE,1,-1 do
1319+
local a, b = ix[i], iy[i]
1320+
if a ~= b then
1321+
return a < b
1322+
end
1323+
end
1324+
return true
13061325
else
13071326
return xneg and not yneg
13081327
end

docs/index.html

Lines changed: 29 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,8 @@ <h2>Usage</h2>
8484
<li><a href="index.html#frominteger">bint.frominteger</a> (convert from lua integers, preserving the sign)</li>
8585
<li><a href="index.html#fromnumber">bint.fromnumber</a> (convert from lua floats, truncating the fractional part)</li>
8686
<li><a href="index.html#frombase">bint.frombase</a> (convert from arbitrary bases, like hexadecimal)</li>
87-
<li><a href="index.html#new">bint.new</a> (convert from anything, asserts on invalid values)</li>
88-
<li><a href="index.html#convert">bint.convert</a> (convert form anything, returns nil on invalid values)</li>
87+
<li><a href="index.html#new">bint.new</a> (convert from anything, asserts on invalid integers)</li>
88+
<li><a href="index.html#convert">bint.convert</a> (convert form anything, returns nil on invalid integers)</li>
8989
<li><a href="index.html#parse">bint.parse</a> (convert from anything, returns a lua number as fallback)</li>
9090
<li><a href="index.html#zero">bint.zero</a></li>
9191
<li><a href="index.html#one">bint.one</a></li>
@@ -102,14 +102,13 @@ <h2>Usage</h2>
102102

103103
<ul>
104104
<li><a href="index.html#touinteger">bint.touinteger</a> (convert to a lua integer, wraps around as an unsigned integer)</li>
105-
<li><a href="index.html#tointeger">bint.tointeger</a> (convert to a lua integer, always preserving the sign)</li>
105+
<li><a href="index.html#tointeger">bint.tointeger</a> (convert to a lua integer, wraps around, preserves the sign)</li>
106106
<li><a href="index.html#tonumber">bint.tonumber</a> (convert to lua float, losing precision)</li>
107107
<li><a href="index.html#tobase">bint.tobase</a> (convert to a string in any base)</li>
108108
<li><a href="index.html#__tostring">bint.__tostring</a> (convert to a string in base 10)</li>
109109
</ul>
110110

111-
<p>Note that outputting to a lua number will make the integer overflow and its value wraps around.
112-
To output very large integer with no loss of precision you probably want to use <a href="index.html#tobase">bint.tobase</a>
111+
<p>To output very large integer with no loss you probably want to use <a href="index.html#tobase">bint.tobase</a>
113112
or call <a href="https://www.lua.org/manual/5.3/manual.html#pdf-tostring">tostring</a> to get a string representation.</p>
114113

115114
<h2>Precautions</h2>
@@ -197,6 +196,10 @@ <h2><a href="#Functions">Functions</a></h2>
197196
<td class="summary">Check if a number is -1 considering bints.</td>
198197
</tr>
199198
<tr>
199+
<td class="name" nowrap><a href="#isbint">isbint (x)</a></td>
200+
<td class="summary">Check if the input is a bint.</td>
201+
</tr>
202+
<tr>
200203
<td class="name" nowrap><a href="#isneg">isneg (x)</a></td>
201204
<td class="summary">Check if a number is negative considering bints.</td>
202205
</tr>
@@ -213,18 +216,10 @@ <h2><a href="#Functions">Functions</a></h2>
213216
<td class="summary">Check if a number is odd considering bints.</td>
214217
</tr>
215218
<tr>
216-
<td class="name" nowrap><a href="#_zero">_zero ()</a></td>
217-
<td class="summary">Assign a bint to zero (in-place).</td>
218-
</tr>
219-
<tr>
220219
<td class="name" nowrap><a href="#zero">zero ()</a></td>
221220
<td class="summary">Create a new bint with 0 value.</td>
222221
</tr>
223222
<tr>
224-
<td class="name" nowrap><a href="#_one">_one ()</a></td>
225-
<td class="summary">Assign a bint to 1 (in-place).</td>
226-
</tr>
227-
<tr>
228223
<td class="name" nowrap><a href="#one">one ()</a></td>
229224
<td class="summary">Create a new bint with 1 value.</td>
230225
</tr>
@@ -845,6 +840,26 @@ <h3>Parameters:</h3>
845840

846841

847842

843+
</dd>
844+
<dt>
845+
<a name = "isbint"></a>
846+
<strong>isbint (x)</strong>
847+
</dt>
848+
<dd>
849+
Check if the input is a bint.
850+
851+
852+
<h3>Parameters:</h3>
853+
<ul>
854+
<li><span class="parameter">x</span>
855+
Any lua value.
856+
</li>
857+
</ul>
858+
859+
860+
861+
862+
848863
</dd>
849864
<dt>
850865
<a name = "isneg"></a>
@@ -926,20 +941,6 @@ <h3>Parameters:</h3>
926941

927942

928943

929-
</dd>
930-
<dt>
931-
<a name = "_zero"></a>
932-
<strong>_zero ()</strong>
933-
</dt>
934-
<dd>
935-
Assign a bint to zero (in-place).
936-
937-
938-
939-
940-
941-
942-
943944
</dd>
944945
<dt>
945946
<a name = "zero"></a>
@@ -954,20 +955,6 @@ <h3>Parameters:</h3>
954955

955956

956957

957-
</dd>
958-
<dt>
959-
<a name = "_one"></a>
960-
<strong>_one ()</strong>
961-
</dt>
962-
<dd>
963-
Assign a bint to 1 (in-place).
964-
965-
966-
967-
968-
969-
970-
971958
</dd>
972959
<dt>
973960
<a name = "one"></a>
@@ -2037,7 +2024,7 @@ <h3>See also:</h3>
20372024
</div> <!-- id="main" -->
20382025
<div id="about">
20392026
<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i>
2040-
<i style="float:right;">Last updated 2020-07-07 23:25:14 </i>
2027+
<i style="float:right;">Last updated 2020-07-08 10:57:04 </i>
20412028
</div> <!-- id="about" -->
20422029
</div> <!-- id="container" -->
20432030
</body>

0 commit comments

Comments
 (0)