--[[

LuaBit v0.4 http://luaforge.net/projects/bit/
copyright(c) 2006~2007 hanzhao (abrash_han@hotmail.com)
Under the MIT license.

Modified 2013 Stephen McDonald:

- Converted to anonymous functions for embedding in Redis
- Removed functions: check_int, bit_xor2, bit_logic_rshift
- Reformatting: 1 space indent to 4 spaces, removed blank lines

--]]

local to_bits = function(n)
    if n < 0 then
        -- negative
        return to_bits(bit_not(math.abs(n)) + 1)
    end
    -- to bits table
    local tbl = {}
    local cnt = 1
    while n > 0 do
        local last = math.mod(n,2)
        if last == 1 then
            tbl[cnt] = 1
        else
            tbl[cnt] = 0
        end
        n = (n - last) / 2
        cnt = cnt + 1
    end
    return tbl
end

local tbl_to_number = function(tbl)
    local n = table.getn(tbl)
    local rslt = 0
    local power = 1
    for i = 1, n do
        rslt = rslt + tbl[i]*power
        power = power*2
    end
    return rslt
end

local expand = function(tbl_m, tbl_n)
    local big = {}
    local small = {}
    if table.getn(tbl_m) > table.getn(tbl_n) then
        big = tbl_m
        small = tbl_n
    else
        big = tbl_n
        small = tbl_m
    end
    -- expand small
    for i = table.getn(small) + 1, table.getn(big) do
        small[i] = 0
    end
end

local bit_and = function(m, n)
    local tbl_m = to_bits(m)
    local tbl_n = to_bits(n)
    expand(tbl_m, tbl_n)
    local tbl = {}
    local rslt = math.max(table.getn(tbl_m), table.getn(tbl_n))
    for i = 1, rslt do
        if tbl_m[i] == 0 or tbl_n[i] == 0 then
            tbl[i] = 0
        else
            tbl[i] = 1
        end
    end
    return tbl_to_number(tbl)
end

local bit_or = function(m, n)
    local tbl_m = to_bits(m)
    local tbl_n = to_bits(n)
    expand(tbl_m, tbl_n)
    local tbl = {}
    local rslt = math.max(table.getn(tbl_m), table.getn(tbl_n))
    for i = 1, rslt do
        if tbl_m[i]== 0 and tbl_n[i] == 0 then
            tbl[i] = 0
        else
            tbl[i] = 1
        end
    end
    return tbl_to_number(tbl)
end

local bit_not = function(n)
    local tbl = to_bits(n)
    local size = math.max(table.getn(tbl), 32)
    for i = 1, size do
        if tbl[i] == 1 then
            tbl[i] = 0
        else
            tbl[i] = 1
        end
    end
    return tbl_to_number(tbl)
end

local bit_xor = function(m, n)
    local tbl_m = to_bits(m)
    local tbl_n = to_bits(n)
    expand(tbl_m, tbl_n)
    local tbl = {}
    local rslt = math.max(table.getn(tbl_m), table.getn(tbl_n))
    for i = 1, rslt do
        if tbl_m[i] ~= tbl_n[i] then
            tbl[i] = 1
        else
            tbl[i] = 0
        end
    end
    return tbl_to_number(tbl)
end

local bit_lshift = function(n, bits)
    if n < 0 then
        -- negative
        n = bit_not(math.abs(n)) + 1
    end
    for i=1, bits do
        n = n*2
    end
    return bit_and(n, 4294967295) -- 0xFFFFFFFF
end

local bit_rshift = function(n, bits)
    local high_bit = 0
    if n < 0 then
        -- negative
        n = bit_not(math.abs(n)) + 1
        high_bit = 2147483648 -- 0x80000000
    end
    for i = 1, bits do
        n = n / 2
        n = bit_or(math.floor(n), high_bit)
    end
    return math.floor(n)
end
