--
-- BITLIB_WRAP.LUA
--
local m= {}

Gluahost( 'bitlib_module', m )

-----
-- uint= hex'<str>'  ('str' contains 0..9,a..f,A..F, may start with '0x'
--
-- Note: This is NOT optimal implementation. Use C if speed is critical.
--
function m.hex( str )
    --
    local org_str= str
    local v= 0
    local b0,ba,bs= string.byte("0"), string.byte("a"), string.byte(" ")
    local b

    str= string.lower(str)

    if (string.find( str, "^0x" )) then  -- remove possible prefix
        str= string.sub( str,3 )
    end

    if (not string.find( str, "^[0-9 a-f]+$" )) then  -- allow internal spaces
        error( "Bad hex string: '".. org_str .."'!" )
    end

    for i=1, string.len(str) do
        --
        b= string.byte(str,i)

        if (b ~= bs) then
            --
            b= (b>=ba) and (b-ba +10) or (b-b0)
            v= v*16 + b
        end
    end

    return v
end

-----
-- uint= bin'<str>'  ('str' contains 0..1)
--
-- Note: This is NOT optimal implementation. Use C if speed is critical.
--
function m.bin( str )
    --
    local org_str= str
    local v= 0
    local c

    str= string.lower(str)

    if (not string.find( str, "^[0-1 ]+$" )) then  -- allow internal spaces
        error( "Bad bin string: '".. org_str .."'!" )
    end

    for i=1, string.len(str) do
        --
        c= string.sub(str,i,i)

        if (c ~= ' ') then
            v= v*2 + ((c=='1') and 1 or 0)
        end
    end

    return v
end


-----
-- div_int, mod_int= idiv( a_int, b_int )
--
function m.idiv( a, b )
    --
    local div, mod
    
    ASSUME( math.floor(a) == a )    -- only for integers!
    ASSUME( math.floor(b) == b )
    
    mod= math.mod( a, b )

    div= (a-mod) / b;
    
    -- This is the invariant used:
    --
    ASSUME( (div*b) + mod == a )
    
    return div, mod
end


-----
-- str= hexstr( val_uint [,width_uint] )
--
function m.hexstr( val, width )
    --
    local ret
    
    ret= string.format( '%x', val )
    
    if (width) then
        ret= string.rep("0", width-string.len(ret)) .. ret
    end

    return ret
end

-----
-- uint= bfield( val_uint, hi_uint [,lo_uint] )
--
-- Returns the numeric value of a bitfield from within an integer, taking
-- care of both masking the other bits out _and_ shifting to 0..2^x-1 range.
--
function m.bfield( val, hi, lo )
    --
    lo= lo or hi    -- default: just one bit

    ASSUME( hi >= lo )    -- assume higher bit first (could also swap them!)

    return m.band( m.rshift(val,lo), 2^(hi-lo+1)-1 )

    -- Also this would work, and does not need bitlib functions:
    --
    --return math.floor( (val- math.floor(val/2^(hi+1))*2^(hi+1)) / 2^lo )
end

-----
-- uint= bmap{ ..., [ {...} ] }
--
-- Bitmap constructor, which returns an integer ('bor' of all the param bits)
-- but looks like an object constructor. 
--
-- In the future, we could do a proper 'bmap' object thing (with bor, band
-- operators in +,*) from this?
--
function m.bmap( tbl )
    --
    if type(tbl)~="table" then return tbl end   -- nil & numbers pass through

    local ret= 0

    for _,v in tbl do   -- bor() all values found in table & subtables..
        --
        if type(v)=="table" then
            ret= m.bor( ret,m.bmap(v) )    -- recursive
        elseif type(v)=="number" then
            ret= m.bor( ret,v )
        else
            -- Catch 'nil' etc. within a bmap() table - may be due to a
            -- mistyped variable name?
            --
            ASSUME(false)
        end
    end
    return ret
end

local raw_bor= m.bor  -- the C module implementation

function m.bor( ... )
    --
    if type(arg[1])=='table' then   -- this one's for 'bmap'..
        return m.bmap( arg )
    else
        return raw_bor( unpack(arg) ) -- faster, directly to C.
    end
end

-----
-- uint= bset( flags_uint, ... )
-- uint= bclr( flags_uint, ... )
--
-- Setting and clearing a bitfield's bits.
--
m.bset= ASSUME( m.bor )

function m.bclr( val, ... )
    --
    local mask=0

    arg= arg or {}

    for _,v in arg do
        mask= m.bor( mask, v )
    end

    return m.band( val, m.bnot(mask) )  -- clear the mask bits
end


return m
