B = require("bigint")
local S = string.format

function printf(...)
    io.write(string.format(...))
end

function bc(exp)
    local cmd = string.format('echo "%s" | bc', exp)
    f = io.popen(cmd, 'r')
    s = f:read('*a')
    f:close()
    return s:gsub("\\", ""):gsub("\n", "")
end

ops = {
    ['+'] = function(a, b) return a + b end,
    ['-'] = function(a, b) return a - b end,
    ['*'] = function(a, b) return a * b end,
    ['/'] = function(a, b) return a / b end,
    ['^'] = function(a, b) return a ^ b end,
}

local signs = {"", "-", "+"}

function test_op(a, b, op, msg)
    local sa = tostring(a):gsub("%s+", ""):gsub("^+", "")
    local sb = tostring(b):gsub("%s+", ""):gsub("^+", "")
    local r = ops[op](B(a), B(b))
    local sr = tostring(r)
    local s = S('%s %s %s', sa, op, sb)
    local s2 = msg or S('%s %s %s', a, op, b)
    local br = bc(s)
    assert(sr==br, S("%s: '%s' != '%s'", s2, sr, br))
    print(S("[PASSED] %s", s2))
end

function test_four(a, b)
    print(("-"):rep(50))
    print(S("A = '%s'", a))
    print(S("B = '%s'", b))
    for op in pairs(ops) do
        if op == '^' then goto continue end
        for _, s1 in ipairs(signs) do
            for _, s2 in ipairs(signs) do
                local sa = s1..a
                local sb = s2..b
                local ma = s1.."A"
                local mb = s2.."B"
                test_op(sa, sb, op, S("%s %s %s", ma, op, mb))
                test_op(sb, sa, op, S("%s %s %s", mb, op, ma))
            end
        end
::continue::
    end
end

function test_divmod(a, b)
    local q, r = B(a):divmod(B(b))
    local calculatedQuotient = tostring(q)
    local calculatedRemainder = tostring(r)
    local sq = S("%s / %s", a, b)
    local sr = S("%s %% %s", a, b)
    local expectedQuotient = bc(sq)
    local expectedRemainder = bc(sr)
    assert(calculatedQuotient==expectedQuotient,
        S("%s: '%s' != '%s'", sq, calculatedQuotient, expectedQuotient))
    print(S("[PASSED] %s", sq))
    assert(calculatedRemainder == expectedRemainder,
        S("%s: '%s' != '%s'", sr, calculatedRemainder, expectedRemainder))
    print(S("[PASSED] %s", sr))
end


test_four(
    "432094820938402398402983415987430593923832340",
    "234290830983245674250132908423290489320329209458645349721"
)

test_four("123", "12")

test_four(
    "10000000000000000000000000000000000000000000000000000000000000000",
    "99999999999999999999999999999999999999999999999999999999"
)

test_divmod("12312321", "322")
test_divmod("322", "12312321")

test_op("2", "100", "^")
test_op("5", "1024", "^")