local function lassert(val, err, level)
    if not val then error(err, level + 1) end
    return val
end

--- Converts a little-endian array from one power-of-two base to another.
--- @param a number[] The array to convert, in little-endian.
--- @param base1 number The base to convert from. Must be a power of 2.
--- @param base2 number The base to convert to. Must be a power of 2.
--- @return number[]
local function rebaseLE(a, base1, base2) -- TODO Write contract properly.
    local out = {}
    local outlen = 1
    local acc = 0
    local mul = 1
    for i = 1, #a do
        acc = acc + a[i] * mul
        mul = mul * base1
        while mul >= base2 do
            local rem = acc % base2
            acc = (acc - rem) / base2
            mul = mul / base2
            out[outlen] = rem
            outlen = outlen + 1
        end
    end
    if mul > 0 then
        out[outlen] = acc
    end
    return out
end

--- Decodes bits with X25519/Ed25519 exponent clamping.
--- @param str string The 32-byte encoded exponent.
--- @return number[] bits The decoded clamped bits.
local function bits(str)
    -- Decode.
    local bytes = {str:byte(1, 32)}
    local out = {}
    for i = 1, 32 do
        local byte = bytes[i]
        for j = -7, 0 do
            local bit = byte % 2
            out[8 * i + j] = bit
            byte = (byte - bit) / 2
        end
    end

    -- Clamp.
    out[1] = 0
    out[2] = 0
    out[3] = 0
    out[255] = 1
    out[256] = 0

    return out
end

--- Decodes bits with X25519/Ed25519 exponent clamping and division by 8.
--- @param str string The 32-byte encoded exponent.
--- @return number[] bits The decoded clamped bits, divided by 8.
local function bits8(str)
    return {unpack(bits(str), 4)}
end

return {
    lassert = lassert,
    rebaseLE = rebaseLE,
    bits = bits,
    bits8 = bits8,
}
