Skip to content

Commit 29dcc93

Browse files
committed
Optimize tobase function using a better algorithm
1 parent 01ab72c commit 29dcc93

File tree

3 files changed

+32
-37
lines changed

3 files changed

+32
-37
lines changed

bint.lua

Lines changed: 29 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ end
245245

246246
local basesteps = {}
247247

248-
-- Compute the read/write step for frombase/tobase functions.
248+
-- Compute the read step for frombase function
249249
local function getbasestep(base)
250250
local step = basesteps[base]
251251
if step then
@@ -394,11 +394,18 @@ do
394394
end
395395
end
396396

397-
-- Get the quotient and remainder for a lua integer division
398-
local function idivmod(x, y)
399-
local quot = x // y
400-
local rem = x - (quot * y)
401-
return quot, rem
397+
-- Get the quotient and remainder for base digits
398+
local function xremainder(dividend, divisor)
399+
local quo = bint_newempty()
400+
local rem
401+
local carry = 0
402+
for i=BINT_SIZE,1,-1 do
403+
carry = carry | dividend[i]
404+
quo[i] = carry // divisor
405+
rem = carry % divisor
406+
carry = rem << BINT_WORDBITS
407+
end
408+
return quo, rem
402409
end
403410

404411
--- Convert a bint to a string in the desired base.
@@ -441,26 +448,13 @@ function bint.tobase(x, base, unsigned)
441448
if neg then
442449
x = x:abs()
443450
end
444-
local step = getbasestep(base)
445-
local divisor = bint.new(ipow(1, base, step))
446-
local stop = x:iszero()
447-
if stop then
448-
return '0'
451+
while not x:iszero() do
452+
local d
453+
x, d = xremainder(x, base)
454+
table.insert(ss, 1, BASE_LETTERS[d])
449455
end
450-
while not stop do
451-
local ix
452-
x, ix = bint.udivmod(x, divisor)
453-
ix = ix:tointeger()
454-
stop = x:iszero()
455-
for _=1,step do
456-
local d
457-
ix, d = idivmod(ix, base)
458-
if stop and ix == 0 and d == 0 then
459-
-- stop on leading zeros
460-
break
461-
end
462-
table.insert(ss, 1, BASE_LETTERS[d])
463-
end
456+
if #ss == 0 then
457+
return '0'
464458
end
465459
if neg then
466460
table.insert(ss, 1, '-')
@@ -1063,12 +1057,12 @@ end
10631057
function bint.udivmod(x, y)
10641058
local dividend = bint.new(x)
10651059
local divisor = bint_assert_convert(y)
1066-
local quot = bint.zero()
1060+
local quo = bint.zero()
10671061
assert(not divisor:iszero(), 'attempt to divide by zero')
10681062
if divisor:isone() then
10691063
return dividend, bint.zero()
10701064
elseif dividend:ult(divisor) then
1071-
return quot, dividend
1065+
return quo, dividend
10721066
end
10731067
-- align leftmost digits in dividend and divisor
10741068
local divisorlbit = findleftbit(divisor)
@@ -1100,7 +1094,7 @@ function bint.udivmod(x, y)
11001094
end
11011095
-- concatenate 1 to the right bit of the quotient
11021096
local i = (bit // BINT_WORDBITS) + 1
1103-
quot[i] = quot[i] | (1 << (bit % BINT_WORDBITS))
1097+
quo[i] = quo[i] | (1 << (bit % BINT_WORDBITS))
11041098
end
11051099
-- shift right the divisor in one bit
11061100
for i=1,divisorsize-1 do
@@ -1121,7 +1115,7 @@ function bint.udivmod(x, y)
11211115
bit = bit - 1
11221116
end
11231117
-- the remaining dividend is the remainder
1124-
return quot, dividend
1118+
return quo, dividend
11251119
end
11261120

11271121
--- Perform unsigned division between two integers considering bints.
@@ -1159,13 +1153,13 @@ function bint.idivmod(x, y)
11591153
if iy:isminusone() then
11601154
return -ix, bint.zero()
11611155
end
1162-
local quot, rem = bint.udivmod(ix:abs(), iy:abs())
1156+
local quo, rem = bint.udivmod(ix:abs(), iy:abs())
11631157
local isnumneg, isdenomneg = ix:isneg(), iy:isneg()
11641158
if isnumneg ~= isdenomneg then
1165-
quot:_unm()
1159+
quo:_unm()
11661160
-- round quotient towards minus infinity
11671161
if not rem:iszero() then
1168-
quot:_dec()
1162+
quo:_dec()
11691163
-- adjust the remainder
11701164
if isnumneg and not isdenomneg then
11711165
rem:_unm():_add(y)
@@ -1177,9 +1171,10 @@ function bint.idivmod(x, y)
11771171
-- adjust the remainder
11781172
rem:_unm()
11791173
end
1180-
return quot, rem
1174+
return quo, rem
11811175
else
1182-
return idivmod(bint.tonumber(x), bint.tonumber(y))
1176+
local nx, ny = bint.tonumber(x), bint.tonumber(y)
1177+
return nx // ny, nx % ny
11831178
end
11841179
end
11851180

docs/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2368,7 +2368,7 @@ <h2 class="section-header "><a name="Fields"></a>Fields</h2>
23682368
</div> <!-- id="main" -->
23692369
<div id="about">
23702370
<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i>
2371-
<i style="float:right;">Last updated 2020-07-10 14:02:03 </i>
2371+
<i style="float:right;">Last updated 2020-07-10 22:49:06 </i>
23722372
</div> <!-- id="about" -->
23732373
</div> <!-- id="container" -->
23742374
</body>

tests.lua

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -614,9 +614,9 @@ local function test(bits, wordbits)
614614
end
615615
end
616616

617+
test(64)
618+
test(64, 16)
617619
test(64, 4)
618620
test(64, 8)
619-
test(64, 16)
620-
test(64)
621621
test(128)
622622
test(256)

0 commit comments

Comments
 (0)