BigNum Module
BigNum Module
--
-- File Name: bignum.lua
-- Package Name: BigNum
--
-- Project: Big Numbers library for Lua
-- Mantainers: fmp - Frederico Macedo Pessoa
-- msm - Marco Serpa Molinaro
--
-- History:
-- Version Autor Date Notes
-- 1.1 fmp/msm 12/11/2004 Some bug fixes (thanks Isaac Gouy)
-- alfa fmp/msm 03/22/2003 Start of Development
-- beta fmp/msm 07/11/2003 Release
--
-- Description:
-- Big numbers manipulation library for Lua.
-- A Big Number is a table with as many numbers as necessary to represent
-- its value in base 'RADIX'. It has a field 'len' containing the num-
-- ber of such numbers and a field 'signal' that may assume the values
-- '+' and '-'.
--
--$.%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
BigNum = {} ;
BigNum.mt = {} ;
--BigNum.new{{{1
--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
--
-- Function: New
--
--
-- Description:
-- Creates a new Big Number based on the parameter num.
--
-- Parameters:
-- num - a string, number or BigNumber.
--
-- Returns:
-- A Big Number, or a nil value if an error occured.
--
--
-- %%%%%%%% --
--BigNum.mt.add{{{2
function BigNum.mt.add( num1 , num2 )
local temp = BigNum.new() ;
local bnum1 = BigNum.new( num1 ) ;
local bnum2 = BigNum.new( num2 ) ;
BigNum.add( bnum1 , bnum2 , temp ) ;
return temp ;
end
--BigNum.mt.mul{{{2
function BigNum.mt.mul( num1 , num2 )
local temp = BigNum.new() ;
local bnum1 = BigNum.new( num1 ) ;
local bnum2 = BigNum.new( num2 ) ;
BigNum.mul( bnum1 , bnum2 , temp ) ;
return temp ;
end
--BigNum.mt.div{{{2
function BigNum.mt.div( num1 , num2 )
local bnum1 = {} ;
local bnum2 = {} ;
local bnum3 = BigNum.new() ;
local bnum4 = BigNum.new() ;
bnum1 = BigNum.new( num1 ) ;
bnum2 = BigNum.new( num2 ) ;
BigNum.div( bnum1 , bnum2 , bnum3 , bnum4 ) ;
return bnum3 , bnum4 ;
end
--BigNum.mt.tostring{{{2
function BigNum.mt.tostring( bnum )
local i = 0 ;
local j = 0 ;
local str = "" ;
local temp = "" ;
if bnum == nil then
return "nil" ;
elseif bnum.len > 0 then
for i = bnum.len - 2 , 0 , -1 do
for j = 0 , RADIX_LEN - string.len( bnum[i] ) - 1 do
temp = temp .. '0' ;
end
temp = temp .. bnum[i] ;
end
temp = bnum[bnum.len - 1] .. temp ;
if bnum.signal == '-' then
temp = bnum.signal .. temp ;
end
return temp ;
else
return "" ;
end
end
--BigNum.mt.pow{{{2
function BigNum.mt.pow( num1 , num2 )
local bnum1 = BigNum.new( num1 ) ;
local bnum2 = BigNum.new( num2 ) ;
return BigNum.pow( bnum1 , bnum2 ) ;
end
--BigNum.mt.eq{{{2
function BigNum.mt.eq( num1 , num2 )
local bnum1 = BigNum.new( num1 ) ;
local bnum2 = BigNum.new( num2 ) ;
return BigNum.eq( bnum1 , bnum2 ) ;
end
--BigNum.mt.lt{{{2
function BigNum.mt.lt( num1 , num2 )
local bnum1 = BigNum.new( num1 ) ;
local bnum2 = BigNum.new( num2 ) ;
return BigNum.lt( bnum1 , bnum2 ) ;
end
--BigNum.mt.le{{{2
function BigNum.mt.le( num1 , num2 )
local bnum1 = BigNum.new( num1 ) ;
local bnum2 = BigNum.new( num2 ) ;
return BigNum.le( bnum1 , bnum2 ) ;
end
--BigNum.mt.unm{{{2
function BigNum.mt.unm( num )
local ret = BigNum.new( num )
if ret.signal == '+' then
ret.signal = '-'
else
ret.signal = '+'
end
return ret
end
--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%{{{2
--
-- Function: SUB
--
--
-- Description:
-- Subtracts two Big Numbers.
--
-- Parameters:
-- bnum1, bnum2 - Numbers to be subtracted.
-- bnum3 - result
--
-- Returns:
-- 0
--
-- Exit assertions:
-- bnum3 is the result of the subtraction.
--
-- %%%%%%%% --
--Funcao BigNum.sub{{{2
function BigNum.sub( bnum1 , bnum2 , bnum3 )
local maxlen = 0 ;
local i = 0 ;
local carry = 0 ;
local old_len = 0 ;
--Handle the signals
if bnum3[i] ~= 0 then
bnum3.len = i + 1 ;
end
end
bnum3.signal = '+' ;
--Check if answer's size if zero
if bnum3.len == 0 then
bnum3.len = 1 ;
bnum3[0] = 0 ;
end
if carry == 1 then
error( "Error in function sub" ) ;
end
for i = bnum3.len , max( old_len , maxlen - 1 ) do
bnum3[i] = nil ;
end
return 0 ;
end
--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%{{{2
--
-- Function: MUL
--
--
-- Description:
-- Multiplies two Big Numbers.
--
-- Parameters:
-- bnum1, bnum2 - Numbers to be multiplied.
-- bnum3 - result
--
-- Returns:
-- 0
--
-- Exit assertions:
-- bnum3 is the result of the multiplication.
--
-- %%%%%%%% --
--BigNum.mul{{{2
--can't be made in place
function BigNum.mul( bnum1 , bnum2 , bnum3 )
local i = 0 ; j = 0 ;
local temp = BigNum.new( ) ;
local temp2 = 0 ;
local carry = 0 ;
local oldLen = bnum3.len ;
if bnum1 == nil or bnum2 == nil or bnum3 == nil then
error("Function BigNum.mul: parameter nil") ;
--Handle the signals
elseif bnum1.signal ~= bnum2.signal then
BigNum.mul( bnum1 , -bnum2 , bnum3 ) ;
bnum3.signal = '-' ;
return 0 ;
end
bnum3.len = ( bnum1.len ) + ( bnum2.len ) ;
--Fill with zeros
for i = 1 , bnum3.len do
bnum3[i - 1] = 0 ;
end
--Places nil where passes through this
for i = bnum3.len , oldLen do
bnum3[i] = nil ;
end
--School grade multiplication
for i = 0 , bnum1.len - 1 do
for j = 0 , bnum2.len - 1 do
carry = ( bnum1[i] * bnum2[j] + carry ) ;
carry = carry + bnum3[i + j] ;
bnum3[i + j] = math.mod ( carry , RADIX ) ;
temp2 = bnum3[i + j] ;
carry = math.floor ( carry / RADIX ) ;
end
if carry ~= 0 then
bnum3[i + bnum2.len] = carry ;
end
carry = 0 ;
end
--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%{{{2
--
-- Function: DIV
--
--
-- Description:
-- Divides bnum1 by bnum2.
--
-- Parameters:
-- bnum1, bnum2 - Numbers to be divided.
-- bnum3 - result
-- bnum4 - remainder
--
-- Returns:
-- 0
--
-- Exit assertions:
-- bnum3 is the result of the division.
-- bnum4 is the remainder of the division.
--
-- %%%%%%%% --
--BigNum.div{{{2
function BigNum.div( bnum1 , bnum2 , bnum3 , bnum4 )
local temp = BigNum.new() ;
local temp2 = BigNum.new() ;
local one = BigNum.new( "1" ) ;
local zero = BigNum.new( "0" ) ;
--Check division by zero
if BigNum.compareAbs( bnum2 , zero ) == 0 then
error( "Function BigNum.div: Division by zero" ) ;
end
--Handle the signals
if bnum1 == nil or bnum2 == nil or bnum3 == nil or bnum4 == nil then
error( "Function BigNum.div: parameter nil" ) ;
elseif bnum1.signal == "+" and bnum2.signal == "-" then
bnum2.signal = "+" ;
BigNum.div( bnum1 , bnum2 , bnum3 , bnum4 ) ;
bnum2.signal = "-" ;
bnum3.signal = "-" ;
return 0 ;
elseif bnum1.signal == "-" and bnum2.signal == "+" then
bnum1.signal = "+" ;
BigNum.div( bnum1 , bnum2 , bnum3 , bnum4 ) ;
bnum1.signal = "-" ;
if bnum4 < zero then --Check if remainder is negative
BigNum.add( bnum3 , one , bnum3 ) ;
BigNum.sub( bnum2 , bnum4 , bnum4 ) ;
end
bnum3.signal = "-" ;
return 0 ;
elseif bnum1.signal == "-" and bnum2.signal == "-" then
bnum1.signal = "+" ;
bnum2.signal = "+" ;
BigNum.div( bnum1 , bnum2 , bnum3 , bnum4 ) ;
bnum1.signal = "-" ;
if bnum4 < zero then --Check if remainder is negative
BigNum.add( bnum3 , one , bnum3 ) ;
BigNum.sub( bnum2 , bnum4 , bnum4 ) ;
end
bnum2.signal = "-" ;
return 0 ;
end
temp.len = bnum1.len - bnum2.len - 1 ;
--Reset variables
BigNum.change( bnum3 , "0" ) ;
BigNum.change( bnum4 , "0" ) ;
--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%{{{2
--
-- Function: POW / EXP
--
--
-- Description:
-- Computes a big number which represents the bnum2-th power of bnum1.
--
-- Parameters:
-- bnum1 - base
-- bnum2 - expoent
--
-- Returns:
-- Returns a big number which represents the bnum2-th power of bnum1.
--
-- %%%%%%%% --
--BigNum.exp{{{2
function BigNum.pow( bnum1 , bnum2 )
local n = BigNum.new( bnum2 ) ;
local y = BigNum.new( 1 ) ;
local z = BigNum.new( bnum1 ) ;
local zero = BigNum.new( "0" ) ;
if bnum2 < zero then
error( "Function BigNum.exp: domain error" ) ;
elseif bnum2 == zero then
return y ;
end
while 1 do
if math.mod( n[0] , 2 ) == 0 then
n = n / 2 ;
else
n = n / 2 ;
y = z * y ;
if n == zero then
return y ;
end
end
z = z * z ;
end
end
-- Português :
BigNum.exp = BigNum.pow
--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%{{{2
--
-- Function: GCD / MMC
--
--
-- Description:
-- Computes the greatest commom divisor of bnum1 and bnum2.
--
-- Parameters:
-- bnum1, bnum2 - positive numbers
--
-- Returns:
-- Returns a big number witch represents the gcd between bnum1 and bnum2.
--
-- %%%%%%%% --
--BigNum.gcd{{{2
function BigNum.gcd( bnum1 , bnum2 )
local a = {} ;
local b = {} ;
local c = {} ;
local d = {} ;
local zero = {} ;
zero = BigNum.new( "0" ) ;
if bnum1 == zero or bnum2 == zero then
return BigNum.new( "1" ) ;
end
a = BigNum.new( bnum1 ) ;
b = BigNum.new( bnum2 ) ;
a.signal = '+' ;
b.signal = '+' ;
c = BigNum.new() ;
d = BigNum.new() ;
while b > zero do
BigNum.div( a , b , c , d ) ;
a , b , d = b , d , a ;
end
return a ;
end
-- Português:
BigNum.mmc = BigNum.gcd
--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%{{{2
--
-- Function: EQ
--
--
-- Description:
-- Compares two Big Numbers.
--
-- Parameters:
-- bnum1, bnum2 - numbers
--
-- Returns:
-- Returns true if they are equal or false otherwise.
--
-- %%%%%%%% --
--BigNum.eq{{{2
function BigNum.eq( bnum1 , bnum2 )
if BigNum.compare( bnum1 , bnum2 ) == 0 then
return true ;
else
return false ;
end
end
--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%{{{2
--
-- Function: LT
--
--
-- Description:
-- Verifies if bnum1 is lesser than bnum2.
--
-- Parameters:
-- bnum1, bnum2 - numbers
--
-- Returns:
-- Returns true if bnum1 is lesser than bnum2 or false otherwise.
--
-- %%%%%%%% --
--BigNum.lt{{{2
function BigNum.lt( bnum1 , bnum2 )
if BigNum.compare( bnum1 , bnum2 ) == 2 then
return true ;
else
return false ;
end
end
--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%{{{2
--
-- Function: LE
--
--
-- Description:
-- Verifies if bnum1 is lesser or equal than bnum2.
--
-- Parameters:
-- bnum1, bnum2 - numbers
--
-- Returns:
-- Returns true if bnum1 is lesser or equal than bnum2 or false otherwise.
--
-- %%%%%%%% --
--BigNum.le{{{2
function BigNum.le( bnum1 , bnum2 )
local temp = -1 ;
temp = BigNum.compare( bnum1 , bnum2 )
if temp == 0 or temp == 2 then
return true ;
else
return false ;
end
end
--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%{{{2
--
-- Function: Compare Absolute Values
--
--
-- Description:
-- Compares absolute values of bnum1 and bnum2.
--
-- Parameters:
-- bnum1, bnum2 - numbers
--
-- Returns:
-- 1 - |bnum1| > |bnum2|
-- 2 - |bnum1| < |bnum2|
-- 0 - |bnum1| = |bnum2|
--
-- %%%%%%%% --
--BigNum.compareAbs{{{2
function BigNum.compareAbs( bnum1 , bnum2 )
if bnum1 == nil or bnum2 == nil then
error("Function compare: parameter nil") ;
elseif bnum1.len > bnum2.len then
return 1 ;
elseif bnum1.len < bnum2.len then
return 2 ;
else
local i ;
for i = bnum1.len - 1 , 0 , -1 do
if bnum1[i] > bnum2[i] then
return 1 ;
elseif bnum1[i] < bnum2[i] then
return 2 ;
end
end
end
return 0 ;
end
--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%{{{2
--
-- Function: Compare
--
--
-- Description:
-- Compares values of bnum1 and bnum2.
--
-- Parameters:
-- bnum1, bnum2 - numbers
--
-- Returns:
-- 1 - |bnum1| > |bnum2|
-- 2 - |bnum1| < |bnum2|
-- 0 - |bnum1| = |bnum2|
--
-- %%%%%%%% --
--BigNum.compare{{{2
function BigNum.compare( bnum1 , bnum2 )
local signal = 0 ;
if bnum1 == nil or bnum2 == nil then
error("Funtion BigNum.compare: parameter nil") ;
elseif bnum1.signal == '+' and bnum2.signal == '-' then
return 1 ;
elseif bnum1.signal == '-' and bnum2.signal == '+' then
return 2 ;
elseif bnum1.signal == '-' and bnum2.signal == '-' then
signal = 1 ;
end
if bnum1.len > bnum2.len then
return 1 + signal ;
elseif bnum1.len < bnum2.len then
return 2 - signal ;
else
local i ;
for i = bnum1.len - 1 , 0 , -1 do
if bnum1[i] > bnum2[i] then
return 1 + signal ;
elseif bnum1[i] < bnum2[i] then
return 2 - signal ;
end
end
end
return 0 ;
end
--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%{{{2
--
-- Function: Change
--
-- Description:
-- Changes the value of a BigNum.
-- This function is called by BigNum.new.
--
-- Parameters:
-- bnum1, bnum2 - numbers
--
-- Returns:
-- 1 - |bnum1| > |bnum2|
-- 2 - |bnum1| < |bnum2|
-- 0 - |bnum1| = |bnum2|
--
-- %%%%%%%% --
--BigNum.change{{{2
function BigNum.change( bnum1 , num )
local j = 0 ;
local len = 0 ;
local num = num ;
local l ;
local oldLen = 0 ;
if bnum1 == nil then
error( "BigNum.change: parameter nil" ) ;
elseif type( bnum1 ) ~= "table" then
error( "BigNum.change: parameter error, type unexpected" ) ;
elseif num == nil then
bnum1.len = 1 ;
bnum1[0] = 0 ;
bnum1.signal = "+";
elseif type( num ) == "table" and num.len ~= nil then --check if num is a big
number
--copy given table to the new one
for i = 0 , num.len do
bnum1[i] = num[i] ;
end
if num.signal ~= '-' and num.signal ~= '+' then
bnum1.signal = '+' ;
else
bnum1.signal = num.signal ;
end
oldLen = bnum1.len ;
bnum1.len = num.len ;
elseif type( num ) == "string" or type( num ) == "number" then
if string.sub( num , 1 , 1 ) == '+' or string.sub( num , 1 , 1 ) == '-' then
bnum1.signal = string.sub( num , 1 , 1 ) ;
num = string.sub(num, 2) ;
else
bnum1.signal = '+' ;
end
num = string.gsub( num , " " , "" ) ;
local sf = string.find( num , "e" ) ;
--Handles if the number is in exp notation
if sf ~= nil then
num = string.gsub( num , "%." , "" ) ;
local e = string.sub( num , sf + 1 ) ;
e = tonumber(e) ;
if e ~= nil and e > 0 then
e = tonumber(e) ;
else
error( "Function BigNum.change: string is not a valid number" ) ;
end
num = string.sub( num , 1 , sf - 2 ) ;
for i = string.len( num ) , e do
num = num .. "0" ;
end
else
sf = string.find( num , "%." ) ;
if sf ~= nil then
num = string.sub( num , 1 , sf - 1 ) ;
end
end
l = string.len( num ) ;
oldLen = bnum1.len ;
if (l > RADIX_LEN) then
local mod = l-( math.floor( l / RADIX_LEN ) * RADIX_LEN ) ;
for i = 1 , l-mod, RADIX_LEN do
bnum1[j] = tonumber( string.sub( num, -( i + RADIX_LEN - 1 ) , -i ) );
--Check if string dosn't represents a number
if bnum1[j] == nil then
error( "Function BigNum.change: string is not a valid number" ) ;
bnum1.len = 0 ;
return 1 ;
end
j = j + 1 ;
len = len + 1 ;
end
if (mod ~= 0) then
bnum1[j] = tonumber( string.sub( num , 1 , mod ) ) ;
bnum1.len = len + 1 ;
else
bnum1.len = len ;
end
--Eliminate trailing zeros
for i = bnum1.len - 1 , 1 , -1 do
if bnum1[i] == 0 then
bnum1[i] = nil ;
bnum1.len = bnum1.len - 1 ;
else
break ;
end
end
else
-- string.len(num) <= RADIX_LEN
bnum1[j] = tonumber( num ) ;
bnum1.len = 1 ;
end
else
error( "Function BigNum.change: parameter error, type unexpected" ) ;
end
return 0 ;
end
--BigNum.put{{{2
--Places int in the position pos of bignum, fills before with zeroes and
--after with nil.
function BigNum.put( bnum , int , pos )
if bnum == nil then
error("Function BigNum.put: parameter nil") ;
end
local i = 0 ;
for i = 0 , pos - 1 do
bnum[i] = 0 ;
end
bnum[pos] = int ;
for i = pos + 1 , bnum.len do
bnum[i] = nil ;
end
bnum.len = pos ;
return 0 ;
end
--printraw{{{2
function printraw( bnum )
local i = 0 ;
if bnum == nil then
error( "Function printraw: parameter nil" ) ;
end
while 1 == 1 do
if bnum[i] == nil then
io.write( ' len '..bnum.len ) ;
if i ~= bnum.len then
io.write( ' ERRO!!!!!!!!' ) ;
end
io.write( "\n" ) ;
return 0 ;
end
io.write( 'r'..bnum[i] ) ;
i = i + 1 ;
end
end
--max{{{2
function max( int1 , int2 )
if int1 > int2 then
return int1 ;
else
return int2 ;
end
end
--decr{{{2
function decr( bnum1 )
local temp = {} ;
temp = BigNum.new( "1" ) ;
BigNum.sub( bnum1 , temp , bnum1 ) ;
return 0 ;
end