diceLibrarian 10e907f1f4 1
2022-08-16 15:45:06 -04:00

379 lines
8.6 KiB
Lua

--------------------------------------------------------
-- Minetest :: Bitwise Helpers Mod v1.2 (bitwise)
--
-- See README.txt for licensing and other information.
-- Copyright (c) 2020, Leslie E. Krause
--
-- ./games/minetest_game/mods/bitwise/init.lua
--------------------------------------------------------
local on = true
local off = false
local max = math.max
local floor = math.floor
local byte = string.byte
local char = string.char
local insert = table.insert
local remove = table.remove
local nib_map = {
[0] = { off, off, off, off },
[1] = { off, off, off, on },
[2] = { off, off, on, off },
[3] = { off, off, on, on },
[4] = { off, on, off, off },
[5] = { off, on, off, on },
[6] = { off, on, on, off },
[7] = { off, on, on, on },
[8] = { on, off, off, off },
[9] = { on, off, off, on },
[10] = { on, off, on, off },
[11] = { on, off, on, on },
[12] = { on, on, off, off },
[13] = { on, on, off, on },
[14] = { on, on, on, off },
[15] = { on, on, on, on }
}
local _ = nil
local function is_match( text, glob )
_ = { string.match( text, glob ) }
return #_ > 0
end
---------------------------------
-- Constructor Function
---------------------------------
local function bits_new( size, value )
local bits = { }
for idx = 1, size do
table.insert( bits, value ~= nil and value )
end
return bits
end
---------------------------------
-- Type Conversion Functions
---------------------------------
local function bits_pack( bits )
-- ensure bit field can fit correctly within 56-bit integer
if #bits > 56 then error( "Invalid length of bit field" ) end
num = 0
for idx = 1, #bits do
if bits[ idx ] then
num = num + 2 ^ ( #bits - idx )
end
end
return num
end
local function bits_pack_string( bits )
local num = 0
local str = ""
for idx = 1, #bits do
local pow = 7 - ( idx - 1 ) % 8 -- rollover every 8-bits
if bits[ idx ] then
num = num + 2 ^ pow
end
if pow == 0 then
str = str .. char( num )
num = 0
end
end
return str
end
local function bits_unpack( num, size )
if not tonumber( num ) or num < 0 then
error( "Number outside of allowable range" )
elseif not size then
size = num < 256 and 8 or num < 65536 and 16 or 32
elseif size > 56 then
error( "Size outside of allowable range" )
end
local bits = { }
local idx = 0
while idx < size do
local idx2 = size - idx
local nib = nib_map[ floor( num / 2 ^ idx ) % 2 ^ 4 ] -- get the nibbles
bits[ idx2 - 3 ] = nib[ 1 ]
bits[ idx2 - 2 ] = nib[ 2 ]
bits[ idx2 - 1 ] = nib[ 3 ]
bits[ idx2 - 0 ] = nib[ 4 ]
idx = idx + 4
end
return bits
end
local function bits_to_string( bits, div )
local str = ""
for idx, val in ipairs( bits ) do
if div and idx > 1 and idx % div == 1 then
str = str .. " " .. ( val and "1" or "0" )
else
str = str .. ( val and "1" or "0" )
end
end
return str
end
local function bits_from_string( str )
local bits = { }
for idx = 1, #str do
local val = ( { [49] = true, [48] = false } )[ byte( str, idx ) ]
if val ~= nil then
insert( bits, val )
end
end
return bits
end
function binary( str )
return tonumber( str, 2 )
end
---------------------------------
-- String Formatting Function
---------------------------------
local function bits_format( str, ... )
local args = { ... }
local function parse_token( num, attr1, attr2, attr3 )
if not tonumber( num ) or num < 0 then error( "Invalid or missing argument, expected number." ) end
local len = math.min( tonumber( attr2 ) or 0, 56 ) or 0 -- limit to 56-bits
local has_pad = attr1 == "0"
local has_rev = attr1 == "-"
local div = tonumber( attr3 ) or 0
local res = ""
local idx = 0
while num > 0 or idx < len do
local rem = num % 2
-- prepend digits, since working from least to most significant bit
if div > 0 and idx % div == 0 then
res = " " .. res -- insert space between sets
end
if rem == 1 then
res = "1" .. res -- insert digit 1
elseif has_pad or num > 0 then
res = "0" .. res -- else, insert digit 0 or insert zero padding
else
res = has_rev and res .. " " or " " .. res -- else, insert space padding
end
num = ( num - rem ) / 2
idx = idx + 1
end
return res
end
str = string.gsub( str, "(%%(.-)([A-Za-z]))", function ( token, attrs, option )
if option == "b" and ( is_match( attrs, "^([0-]?)([0-9]*)$" ) or is_match( attrs, "^([0-]?)([0-9]*):([0-9]+)$" ) ) then
return parse_token( remove( args, 1 ), _[ 1 ], _[ 2 ], _[ 3 ] )
else
return string.format( token, remove( args, 1 ) )
end
end )
return str
end
---------------------------------
-- Numeric Bitwise Operations
---------------------------------
function AND( num1, num2 )
local exp = 1
local res = 0
local idx = 1
while num1 > 0 and num2 > 0 do
local rem1 = num1 % 2
local rem2 = num2 % 2
if rem1 + rem2 > 1 then
-- set each bit
res = res + exp
end
num1 = ( num1 - rem1 ) / 2
num2 = ( num2 - rem2 ) / 2
exp = exp * 2
end
return res
end
function OR( num1, num2 )
local exp = 1
local res = 0
while num1 + num2 > 0 do
local rem1 = num1 % 2
local rem2 = num2 % 2
if rem1 + rem2 > 0 then
-- set each bit
res = res + exp
end
num1 = ( num1 - rem1 ) / 2
num2 = ( num2 - rem2 ) / 2
exp = exp * 2
end
return res
end
function XOR( num1, num2 )
local exp = 1
local res = 0
while num1 > 0 or num2 > 0 do
local rem1 = num1 % 2
local rem2 = num2 % 2
if rem1 ~= rem2 then
-- set each bit
res = res + exp
end
num1 = ( num1 - rem1 ) / 2
num2 = ( num2 - rem2 ) / 2
exp = exp * 2
end
return res
end
function NOT( num )
local exp = 1
local res = 0
while num > 0 do
local rem = num % 2
if rem == 0 then
-- set each bit
res = res + exp
end
num = ( num - rem ) / 2
exp = exp * 2
end
return res
end
function NOT16( num )
return XOR( num, 0xFFFF ) -- 16-bit logical NOT
end
function NOT32( num )
return XOR( num, 0xFFFFFFFF ) -- 32-bit logical NOT
end
function LSHIFT( num, off )
return num * 2 ^ off
end
function RSHIFT( num, off )
return floor( num / 2 ^ off )
end
---------------------------------
-- Tabular Bitwise Operations
---------------------------------
local function bits_and( b1, b2 )
if #b1 ~= #b2 then error( "Mismatched length of bit field" ) end
local bits = { }
for idx = 1, #b1 do
bits[ idx ] = b1[ idx ] and b2[ idx ]
end
return bits
end
local function bits_or( b1, b2 )
if #b1 ~= #b2 then error( "Mismatched length of bit field" ) end
local bits = { }
for idx = 1, #b1 do
bits[ idx ] = b1[ idx ] or b2[ idx ]
end
return bits
end
local function bits_xor( b1, b2 )
if #b1 ~= #b2 then error( "Mismatched length of bit field" ) end
local bits = { }
for idx = 1, #b1 do
bits[ idx ] = b1[ idx ] ~= b2[ idx ]
end
return bits
end
local function bits_not( b )
local bits = { }
for idx = 1, #b do
bits[ idx ] = not b[ idx ]
end
return bits
end
local function bits_rshift( b, off )
local bits = { }
for idx = 1, #b do
bits[ idx ] = idx > off and b[ idx - off ]
end
return bits
end
local function bits_lshift( b, off )
local bits = { }
for idx = 1, #b do
bits[ idx ] = idx <= #b - off and b[ idx + off ]
end
return bits
end
---------------------------------
-- Runtime Consistency Checks
---------------------------------
local function bits_check( )
assert( bits_to_string( bits_and( bits_unpack( 255 ), bits_unpack( 15 ) ) ) == "00001111" )
assert( bits_pack( bits_or( bits_unpack( 24 ), bits_unpack( 7 ) ) ) == 31 )
assert( bits_pack_string( bits_unpack( 124 ) ) == "|" )
assert( bits_pack( bits_from_string( "1011 1111 1001 1111" ) ) == 49055 )
assert( bits_to_string( bits_not( bits_from_string( "1011 1111 1001 1111" ) ), 4 ) == "0100 0000 0110 0000" )
assert( bits_to_string( bits_rshift( bits_from_string( "1001 1111" ), 4 ), 4 ) == "0000 1001" )
assert( bits_to_string( bits_xor( bits_from_string( "11111100" ), bits_from_string( "00010110" ) ) ) == bits_format( "%b", XOR( binary( "11111100" ), binary( "00010110" ) ) ) )
assert( bits_pack( bits_xor( bits_from_string( "00011111100" ), bits_from_string( "10100000000" ) ) ) == XOR( binary( "11111100" ), binary( "10100000000" ) ) )
assert( bits_pack( bits_new( 4, false ) ) == 0 )
assert( bits_pack( bits_new( 32, true ) ) == 0xFFFFFFFF )
print( "[bitwise] All consistency checks passed!" )
end
---------------------------------
-- Export Public Functions
---------------------------------
return {
new = bits_new,
pack = bits_pack,
pack_string = bits_pack_string,
unpack = bits_unpack,
to_string = bits_to_string,
from_string = bits_from_string,
format = bits_format,
LSHIFT = bits_lshift,
RSHIFT = bits_rshift,
AND = bits_and,
OR = bits_or,
XOR = bits_xor,
NOT = bits_not,
check = bits_check,
}