Skip to content

Commit 1cc791a

Browse files
committed
New utility functions
1 parent 5ce245a commit 1cc791a

File tree

4 files changed

+423
-100
lines changed

4 files changed

+423
-100
lines changed

bint.lua

Lines changed: 135 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,10 @@ Then when you need create a bint, you can use one of the following functions:
5959
6060
* @{bint.fromuinteger} (convert from lua integers, but read as unsigned integer)
6161
* @{bint.frominteger} (convert from lua integers, preserving the sign)
62-
* @{bint.fromnumber} (convert from lua floats, truncating the fractional part)
6362
* @{bint.frombase} (convert from arbitrary bases, like hexadecimal)
63+
* @{bint.trunc} (convert from lua numbers, truncating the fractional part)
6464
* @{bint.new} (convert from anything, asserts on invalid integers)
65-
* @{bint.convert} (convert form anything, returns nil on invalid integers)
65+
* @{bint.tobint} (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}
@@ -153,14 +153,20 @@ end
153153
-- Convert a value to a lua integer without losing precision.
154154
local function tointeger(x)
155155
x = tonumber(x)
156-
if math.type(x) == 'float' then
156+
local ty = math.type(x)
157+
if ty == 'float' then
157158
local floorx = math.floor(x)
158159
if floorx ~= x then
159160
return nil
160161
end
161162
x = floorx
163+
ty = math.type(x)
164+
end
165+
if ty == 'integer' then
166+
return x
167+
else
168+
return nil
162169
end
163-
return x
164170
end
165171

166172
-- Check if the input is a bint.
@@ -219,23 +225,6 @@ function bint.frominteger(x)
219225
return n
220226
end
221227

222-
--- Create a bint from a number.
223-
-- Floats values are truncated, that is, the fractional port is discarded.
224-
-- @param x A value to initialize from convertible to a lua number.
225-
-- @return A new bint or nil in case the input cannot be represented by an integer.
226-
function bint.fromnumber(x)
227-
x = tonumber(x)
228-
if not x then
229-
return nil
230-
end
231-
local ty = math.type(x)
232-
if ty == 'float' then
233-
-- truncate to integer
234-
x = math.modf(x)
235-
end
236-
return bint.frominteger(x)
237-
end
238-
239228
local basesteps = {}
240229

241230
-- Compute the read/write step for frombase/tobase functions.
@@ -361,11 +350,11 @@ local function bint_assert_tointeger(x)
361350
return assert(bint.tointeger(x), 'value has no integer representation')
362351
end
363352

364-
--- Convert a bint to a lua number.
353+
--- Convert a bint to a lua float in case integer would wrap around or lua integer otherwise.
365354
-- Different from @{bint.tointeger} the operation does not wraps around integers,
366-
-- but digits precision may be lost in the process of converting to a float.
367-
-- @param x A bint or value to be converted into a number.
368-
-- @return An integer or nil in case the input cannot be represented by a number.
355+
-- but digits precision are lost in the process of converting to a float.
356+
-- @param x A bint or value to be converted into a lua number.
357+
-- @return A lua number or nil in case the input cannot be represented by a number.
369358
-- @see bint.tointeger
370359
function bint.tonumber(x)
371360
if isbint(x) then
@@ -403,7 +392,7 @@ end
403392
-- @return A string representing the input.
404393
-- @raise An assert is thrown in case the base is invalid.
405394
function bint.tobase(x, base, unsigned)
406-
x = bint.convert(x)
395+
x = bint.tobint(x)
407396
if not x then
408397
-- x is a fractional float or something else
409398
return nil
@@ -475,7 +464,7 @@ end
475464
-- @param x A value convertible to a bint (string, number or another bint).
476465
-- @return A new bint, guaranteed to be a new reference in case needed.
477466
-- @raise An assert is thrown in case x is not convertible to a bint.
478-
-- @see bint.convert
467+
-- @see bint.tobint
479468
-- @see bint.parse
480469
function bint.new(x)
481470
if isbint(x) then
@@ -499,7 +488,7 @@ end
499488
-- @return A bint or nil in case the conversion failed.
500489
-- @see bint.new
501490
-- @see bint.parse
502-
function bint.convert(x, clone)
491+
function bint.tobint(x, clone)
503492
if isbint(x) then
504493
if not clone then
505494
return x
@@ -512,7 +501,7 @@ function bint.convert(x, clone)
512501
end
513502

514503
local function bint_assert_convert(x)
515-
return assert(bint.convert(x), 'value has not integer representation')
504+
return assert(bint.tobint(x), 'value has not integer representation')
516505
end
517506

518507
--- Convert a value to a bint if possible otherwise to a lua number.
@@ -522,9 +511,9 @@ end
522511
-- Defaults to false.
523512
-- @return A bint or a lua number or nil in case the conversion failed.
524513
-- @see bint.new
525-
-- @see bint.convert
514+
-- @see bint.tobint
526515
function bint.parse(x, clone)
527-
local i = bint.convert(x, clone)
516+
local i = bint.tobint(x, clone)
528517
if i then
529518
return i
530519
else
@@ -586,6 +575,24 @@ function bint.isbint(x)
586575
return isbint(x)
587576
end
588577

578+
--- Check if the input is a lua integer or a bint.
579+
-- @param x Any lua value.
580+
function bint.isinteger(x)
581+
return isbint(x) or math.type(x) == 'integer'
582+
end
583+
584+
--- Check the number type of the input (bint, integer or float).
585+
-- @param x Any lua value.
586+
-- Returns "bint" for bints, "integer" fot lua integers,
587+
-- "float" from lua floats or nil otherwise.
588+
function bint.type(x)
589+
if isbint(x) then
590+
return 'bint'
591+
else
592+
return math.type(x)
593+
end
594+
end
595+
589596
--- Check if a number is negative considering bints.
590597
-- Zero is guaranteed to never be negative for bints.
591598
-- @param x A bint or a lua number.
@@ -646,6 +653,26 @@ function bint.one()
646653
return x
647654
end
648655

656+
--- Create a new bint with the maximum possible integer value.
657+
function bint.maxinteger()
658+
local x = bint_newempty()
659+
for i=1,BIGINT_SIZE-1 do
660+
x[i] = BIGINT_WORDMAX
661+
end
662+
x[BIGINT_SIZE] = BIGINT_WORDMAX ~ BIGINT_WORDMSB
663+
return x
664+
end
665+
666+
--- Create a new bint with the minimum possible integer value.
667+
function bint.mininteger()
668+
local x = bint_newempty()
669+
for i=1,BIGINT_SIZE-1 do
670+
x[i] = 0
671+
end
672+
x[BIGINT_SIZE] = BIGINT_WORDMSB
673+
return x
674+
end
675+
649676
--- Bitwise left shift a bint in one bit (in-place).
650677
function bint:_shlone()
651678
local wordbitsm1 = BIGINT_WORDBITS - 1
@@ -710,7 +737,7 @@ end
710737
--- Increment a number by one considering bints.
711738
-- @param x A bint or a lua number to increment.
712739
function bint.inc(x)
713-
local ix = bint.convert(x, true)
740+
local ix = bint.tobint(x, true)
714741
if ix then
715742
return ix:_inc()
716743
else
@@ -734,7 +761,7 @@ end
734761
--- Decrement a number by one considering bints.
735762
-- @param x A bint or a lua number to decrement.
736763
function bint.dec(x)
737-
local ix = bint.convert(x, true)
764+
local ix = bint.tobint(x, true)
738765
if ix then
739766
return ix:_dec()
740767
else
@@ -764,14 +791,81 @@ end
764791
--- Take absolute of a number considering bints.
765792
-- @param x A bint or a lua number to take the absolute.
766793
function bint.abs(x)
767-
local ix = bint.convert(x, true)
794+
local ix = bint.tobint(x, true)
768795
if ix then
769796
return ix:_abs()
770797
else
771798
return math.abs(x)
772799
end
773800
end
774801

802+
--- Take floor of a number considering bints.
803+
-- @param x A bint or a lua number to perform the floor.
804+
function bint.floor(x)
805+
if isbint(x) then
806+
return bint.new(x)
807+
else
808+
return bint.new(math.floor(tonumber(x)))
809+
end
810+
end
811+
812+
--- Take ceil of a number considering bints.
813+
-- @param x A bint or a lua number to perform the ceil.
814+
function bint.ceil(x)
815+
if isbint(x) then
816+
return bint.new(x)
817+
else
818+
return bint.new(math.ceil(tonumber(x)))
819+
end
820+
end
821+
822+
--- Truncate a lua number to a bint.
823+
-- Floats numbers are truncated, that is, the fractional port is discarded.
824+
-- @param x A value to initialize from convertible to a lua number.
825+
-- @return A new bint or nil in case the input cannot be represented by an integer.
826+
function bint.trunc(x)
827+
if not isbint(x) then
828+
x = tonumber(x)
829+
if not x then
830+
return nil
831+
end
832+
local ty = math.type(x)
833+
if ty == 'float' then
834+
-- truncate to integer
835+
x = math.modf(x)
836+
end
837+
return bint.frominteger(x)
838+
else
839+
return bint.new(x)
840+
end
841+
end
842+
843+
--- Take maximum between two numbers considering bints.
844+
-- @param x A bint or lua number to compare.
845+
-- @param y A bint or lua number to compare.
846+
-- @return A bint or a lua number. Guarantees to return a new bint for integer values.
847+
function bint.max(x, y)
848+
local ix, iy = bint.tobint(x), bint.tobint(y)
849+
if ix and iy then
850+
return bint.new(ix > iy and ix or iy)
851+
else
852+
return bint.parse(math.max(x, y))
853+
end
854+
end
855+
856+
--- Take minimum between two numbers considering bints.
857+
-- @param x A bint or lua number to compare.
858+
-- @param y A bint or lua number to compare.
859+
-- @return A bint or a lua number. Guarantees to return a new bint for integer values.
860+
function bint.min(x, y)
861+
local ix, iy = bint.tobint(x), bint.tobint(y)
862+
if ix and iy then
863+
return bint.new(ix < iy and ix or iy)
864+
else
865+
return bint.parse(math.min(x, y))
866+
end
867+
end
868+
775869
--- Add an integer to a bint (in-place).
776870
-- @param y An integer to be added.
777871
-- @raise Asserts in case inputs are not convertible to integers.
@@ -790,8 +884,7 @@ end
790884
-- @param x A bint or a lua number to be added.
791885
-- @param y A bint or a lua number to be added.
792886
function bint.__add(x, y)
793-
local ix = bint.convert(x)
794-
local iy = bint.convert(y)
887+
local ix, iy = bint.tobint(x), bint.tobint(y)
795888
if ix and iy then
796889
local z = bint_newempty()
797890
local carry = 0
@@ -825,8 +918,7 @@ end
825918
-- @param x A bint or a lua number to be subtract from.
826919
-- @param y A bint or a lua number to subtract.
827920
function bint.__sub(x, y)
828-
local ix = bint.convert(x)
829-
local iy = bint.convert(y)
921+
local ix, iy = bint.tobint(x), bint.tobint(y)
830922
if ix and iy then
831923
local z = bint_newempty()
832924
local borrow = 0
@@ -846,8 +938,7 @@ end
846938
-- @param x A bint or a lua number to multiply.
847939
-- @param y A bint or a lua number to multiply.
848940
function bint.__mul(x, y)
849-
local ix = bint.convert(x)
850-
local iy = bint.convert(y)
941+
local ix, iy = bint.tobint(x), bint.tobint(y)
851942
if ix and iy then
852943
local z = bint.zero()
853944
local sizep1 = BIGINT_SIZE+1
@@ -982,8 +1073,7 @@ end
9821073
-- @see bint.__idiv
9831074
-- @see bint.__mod
9841075
function bint.idivmod(x, y)
985-
local ix = bint.convert(x)
986-
local iy = bint.convert(y)
1076+
local ix, iy = bint.tobint(x), bint.tobint(y)
9871077
if ix and iy then
9881078
if iy:isminusone() then
9891079
return -ix, bint.zero()
@@ -1265,7 +1355,7 @@ end
12651355
-- @param x A bint or lua number to compare.
12661356
-- @param y A bint or lua number to compare.
12671357
function bint.eq(x, y)
1268-
return bint.convert(x) == bint.convert(y)
1358+
return bint.tobint(x) == bint.tobint(y)
12691359
end
12701360

12711361
--- Compare if integer x is less than y considering bints (unsigned version).
@@ -1305,7 +1395,7 @@ end
13051395
-- @param y Right value to compare, a bint or lua number.
13061396
-- @see bint.ult
13071397
function bint.__lt(x, y)
1308-
local ix, iy = bint.convert(x), bint.convert(y)
1398+
local ix, iy = bint.tobint(x), bint.tobint(y)
13091399
if ix and iy then
13101400
local xneg = ix[BIGINT_SIZE] & BIGINT_WORDMSB ~= 0
13111401
local yneg = iy[BIGINT_SIZE] & BIGINT_WORDMSB ~= 0
@@ -1330,7 +1420,7 @@ end
13301420
-- @param y Right value to compare, a bint or lua number.
13311421
-- @see bint.ule
13321422
function bint.__le(x, y)
1333-
local ix, iy = bint.convert(x), bint.convert(y)
1423+
local ix, iy = bint.tobint(x), bint.tobint(y)
13341424
if ix and iy then
13351425
local xneg = ix[BIGINT_SIZE] & BIGINT_WORDMSB ~= 0
13361426
local yneg = iy[BIGINT_SIZE] & BIGINT_WORDMSB ~= 0

0 commit comments

Comments
 (0)