Skip to content

Instantly share code, notes, and snippets.

@mjf
Last active January 17, 2025 11:49
Show Gist options
  • Save mjf/c63f06b7e90a88fa20c8177c22e93f2e to your computer and use it in GitHub Desktop.
Save mjf/c63f06b7e90a88fa20c8177c22e93f2e to your computer and use it in GitHub Desktop.

Revisions

  1. mjf revised this gist Jan 17, 2025. 1 changed file with 0 additions and 1 deletion.
    1 change: 0 additions & 1 deletion radix_fun.lua
    Original file line number Diff line number Diff line change
    @@ -140,7 +140,6 @@ local function assert_eq(actual, expected, message)
    success = actual == expected
    end
    end

    if not success then
    error(
    "Assertion failed: " .. message .. "\n" ..
  2. mjf created this gist Jan 17, 2025.
    214 changes: 214 additions & 0 deletions radix_fun.lua
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,214 @@
    local function from_radix(digits, base)
    local ip = 0
    local fp = 0
    local dp = nil
    for i, v in ipairs(digits) do
    if v == "." then
    dp = i
    break
    end
    if type(v) ~= "number" then
    error("Invalid digit in input")
    end
    if v < 0 or v >= base then
    error("Digit out of range for given base")
    end
    end
    if dp then
    for i = 1, dp - 1 do
    ip = ip * base + digits[i]
    end
    for i = dp + 1, #digits do
    fp = fp + digits[i] / (base ^ (i - dp))
    end
    else
    for i = 1, #digits do
    ip = ip * base + digits[i]
    end
    end
    return ip + fp
    end

    local function to_radix(x, base, frac_digits)
    if base < 2 then
    error("Base must be at least 2")
    end
    if frac_digits and frac_digits < 0 then
    error("Number of fractional digits must be non-negative")
    end
    local result = {}
    local ip = math.floor(x)
    local fp = x - ip
    repeat
    table.insert(result, 1, ip % base)
    ip = math.floor(ip / base)
    until ip == 0
    if frac_digits and frac_digits > 0 then
    table.insert(result, ".")
    for _ = 1, frac_digits do
    fp = fp * base
    local digit = math.floor(fp)
    table.insert(result, digit)
    fp = fp - digit
    end
    end
    return result
    end

    local function clock_from_moment(tee)
    local seconds = math.fmod(tee, 86400)
    if seconds < 0 then
    seconds = seconds + 86400
    end
    local hours = math.floor(seconds / 3600)
    seconds = math.fmod(seconds, 3600)
    local minutes = math.floor(seconds / 60)
    seconds = math.fmod(seconds, 60)
    return {hours, minutes, math.floor(seconds)}
    end

    local function time_from_clock(hms)
    if #hms ~= 3 then
    error("Clock time must have 3 elements: {hours, minutes, seconds}")
    end
    for _, v in ipairs(hms) do
    if type(v) ~= "number" then
    error("Clock time elements must be numbers")
    end
    end
    if hms[1] < 0 or hms[1] > 23 then
    error("Hours must be between 0 and 23")
    end
    if hms[2] < 0 or hms[2] > 59 then
    error("Minutes must be between 0 and 59")
    end
    if hms[3] < 0 or hms[3] > 59 then
    error("Seconds must be between 0 and 59")
    end
    return ((hms[1] * 3600) + (hms[2] * 60) + hms[3]) / 86400
    end

    local function table_tostring(t)
    if type(t) ~= "table" then
    return tostring(t)
    end
    local s = "{"
    for k, v in pairs(t) do
    s = s .. "[" .. tostring(k) .. "] = " .. tostring(v) .. ", "
    end
    if #s > 1 then
    s = s:sub(1, #s - 2)
    end
    return s .. "}"
    end

    local function float_equal(a, b, tolerance)
    tolerance = tolerance or 1e-9
    return math.abs(a - b) < tolerance
    end

    local function assert_eq(actual, expected, message)
    local success = false
    if type(actual) == type(expected) then
    if type(actual) == "table" then
    local actual_count = 0
    local expected_count = 0
    for _ in pairs(actual) do
    actual_count = actual_count + 1
    end
    for _ in pairs(expected) do
    expected_count = expected_count + 1
    end
    if actual_count == expected_count then
    success = true
    for k, exp_v in pairs(expected) do
    local v = actual[k]
    if type(v) == "number" and type(exp_v) == "number" then
    if not float_equal(v, exp_v) then
    success = false
    break
    end
    elseif tostring(v) ~= tostring(exp_v) then
    success = false
    break
    end
    end
    end
    elseif type(actual) == "number" then
    success = float_equal(actual, expected)
    else
    success = actual == expected
    end
    end

    if not success then
    error(
    "Assertion failed: " .. message .. "\n" ..
    "Expected: " .. table_tostring(expected) .. "\n" ..
    "Got: " .. table_tostring(actual)
    )
    end
    end

    local tests = {
    {function()
    return from_radix({1, 0, 1, ".", 1, 1}, 2)
    end, 5.75, "Binary 101.11"},
    {function()
    return to_radix(5.75, 2, 5)
    end, {1, 0, 1, ".", 1, 1, 0, 0, 0}, "Decimal 5.75 to binary"},
    {function()
    return to_radix(12345, 16)
    end, {3, 0, 3, 9}, "Decimal 12345 to hex"},
    {function()
    return to_radix(0.3, 2, 10)
    end, {0, ".", 0, 1, 0, 0, 1, 1, 0, 0, 1, 1}, "Decimal 0.3 to binary"},
    {function()
    return clock_from_moment(3723)
    end, {1, 2, 3}, "3723 seconds to clock time"},
    {function()
    return clock_from_moment(0)
    end, {0, 0, 0}, "0 seconds to clock time"},
    {function()
    return clock_from_moment(86400)
    end, {0, 0, 0}, "86400 seconds to clock time"},
    {function()
    return clock_from_moment(86461)
    end, {0, 1, 1}, "86461 seconds to clock time"},
    {function()
    return clock_from_moment(123456)
    end, {10, 17, 36}, "123456 seconds to clock time"},
    {function()
    return time_from_clock({1, 2, 3})
    end, 0.043090277777778, "Clock time 1:2:3 to fraction of day"},
    {function()
    return time_from_clock({0, 0, 0})
    end, 0, "Clock time 0:0:0 to fraction of day"},
    {function()
    return time_from_clock({12, 0, 0})
    end, 0.5, "Clock time 12:0:0 to fraction of day"},
    {function()
    return time_from_clock({23, 59, 59})
    end, 0.99998842592593, "Clock time 23:59:59 to fraction of day"},
    {function()
    return time_from_clock({1, 10, 56})
    end, 0.049259259259259, "Clock time 1:10:56 to fraction of day"}
    }

    local function run_tests()
    local passed = 0
    local total = #tests

    for i, test in ipairs(tests) do
    local description = test[3]
    local actual = test[1]()
    local expected = test[2]
    assert_eq(actual, expected, description)
    print("Test " .. i .. ": " .. description .. " - PASSED")
    passed = passed + 1
    end

    print("\nPassed " .. passed .. " of " .. total .. " tests.")
    end

    run_tests()