跳转到内容

模組:PythonScript/Runtime/Utils

维基百科,自由的百科全书
-- Module:PythonScript/Runtime/Utils
-- Runtime 內部輔助函式、例外繼承樹、格式化引擎與迭代器實作
local Utils = {}

local Runtime, PyClass
function Utils.bind_runtime(rt, cls)
    Runtime = rt
    PyClass = cls
end

local SAFE_LIMIT = 9007199254740990
Utils.SAFE_LIMIT = SAFE_LIMIT

local function safe_pcall(fn, ...)
    local prev_depth = Runtime.Traceback.eval_depth
    local ok, res = pcall(fn, ...)
    Runtime.Traceback.eval_depth = prev_depth
    return ok, res
end
Utils.safe_pcall = safe_pcall

local function unwrap_num(obj)
    if type(obj) == "table" and obj.__type == "instance" and obj.__builtin_value ~= nil then
        obj = obj.__builtin_value
    end
    if type(obj) == "boolean" then return obj and 1 or 0 end
    if type(obj) == "table" and obj.__type == "int" and obj.is_big then
        local MathCore = require("Module:PythonScript/Runtime/Cmath")
        return tonumber(MathCore.BigInt.to_string(obj.sign, obj.limbs))
    end
    return obj
end
Utils.unwrap_num = unwrap_num

local function get_type_str(obj)
    local t = type(obj)
    if t == "number" then return (math.floor(obj) == obj and math.abs(obj) <= SAFE_LIMIT) and "int" or "float"
    elseif t == "string" then return "str"
    elseif t == "boolean" then return "bool"
    elseif t == "nil" then return "NoneType"
    elseif t == "table" then
        if obj.__class and obj.__class.__name then return obj.__class.__name end
        return obj.__type or "object"
    end
    return "unknown"
end
Utils.get_type_str = get_type_str

local function format_bytes_repr(s)
    local out = {}
    for i = 1, #s do
        local b = string.byte(s, i)
        if b == 92 then table.insert(out, "\\\\")
        elseif b == 39 then table.insert(out, "\\'")
        elseif b == 10 then table.insert(out, "\\n")
        elseif b == 13 then table.insert(out, "\\r")
        elseif b == 9 then table.insert(out, "\\t")
        elseif b >= 32 and b <= 126 then table.insert(out, string.char(b))
        else table.insert(out, string.format("\\x%02x", b)) end
    end
    return "b'" .. table.concat(out) .. "'"
end

local function to_bool(obj)
    if obj == nil or obj == false then return false end
    if type(obj) == "table" and obj.__class and obj.__class.__name == "NoneType" then return false end
    if type(obj) == "number" and obj == 0 then return false end
    if type(obj) == "table" and obj.__type == "int" and obj.is_big then
        return not (#obj.limbs == 1 and obj.limbs[1] == 0)
    end
    if type(obj) == "table" and obj.__type == "complex" then
        return not (obj.real == 0 and obj.imag == 0 and (obj.k or 0) == 0 and (obj.jk or 0) == 0)
    end
    if type(obj) == "table" and obj.__type == "range" then
        if obj.step > 0 then return obj.start < obj.stop else return obj.start > obj.stop end
    end
    if type(obj) == "table" and obj.__type == "instance" then
        local success, attr = safe_pcall(PyClass.get_attr, obj, "__bool__")
        if success and attr and attr.__type == "bound_method" then
            local Evaluator = require("Module:PythonScript/Evaluator")
            local res = Evaluator.invoke(attr.__func, {}, {}, attr.__self)
            return res == true or res == 1
        end
        success, attr = safe_pcall(PyClass.get_attr, obj, "__len__")
        if success and attr and attr.__type == "bound_method" then
            local Evaluator = require("Module:PythonScript/Evaluator")
            local len_val = Evaluator.invoke(attr.__func, {}, {}, attr.__self)
            return unwrap_num(len_val) > 0
        end
        if obj.__builtin_value ~= nil then return to_bool(obj.__builtin_value) end
        return true
    end
    if type(obj) == "string" and obj == "" then return false end
    if type(obj) == "table" then
        if obj.__type == "bytes" or obj.__type == "bytearray" or obj.__type == "memoryview" then
            return #(obj._data or "") > 0
        end
        if obj.__type == "list" or obj.__type == "tuple" or obj.__type == "dict" or obj.__type == "set" or obj.__type == "frozenset" then
            if obj.elements and #obj.elements == 0 then return false end
            if obj.pairs and #obj.pairs == 0 then return false end
        end
    end
    return true
end
Utils.to_bool = to_bool

local function to_str(obj, in_repr, seen)
    local t = type(obj)
    if t == "nil" then return "None"
    elseif t == "table" and obj.__class and obj.__class.__name == "NoneType" then return "None"
    elseif t == "boolean" then return obj and "True" or "False"
    elseif t == "number" then
        if obj ~= obj then return "nan"
        elseif obj == math.huge then return "inf"
        elseif obj == -math.huge then return "-inf" end
        local s = tostring(obj)
        if s:match("%.0$") then return s:sub(1, -3) else return s end
    elseif t == "string" then
        return in_repr and ("'" .. obj:gsub("\\", "\\\\"):gsub("'", "\\'") .. "'") or obj
    elseif t == "table" then
        if obj.__type == "int" and obj.is_big then
            local MathCore = require("Module:PythonScript/Runtime/Cmath")
            return MathCore.BigInt.to_string(obj.sign, obj.limbs)
		elseif obj._unpacked_to_str then
			if (obj.__class or{}).__name == "QuaternionImagPart" then --保持複數行為不變
				obj._unpacked_to_str = nil --復位
			else obj.__type_orig = obj.__type --保存舊型態名稱
				obj.__type = "tuple" --讓下方的tuple分支輸出內容
			end
        elseif obj.__type == "bytes" then
            return format_bytes_repr(obj._data or "")
        elseif obj.__type == "bytearray" then
            return "bytearray(" .. format_bytes_repr(obj._data or "") .. ")"
        elseif obj.__type == "memoryview" then
            return "<memory at " .. (tostring(obj):match("0x%w+") or (obj.__module__ and ("'"..obj.__module__.."'")) or '<unknown>') .. ">"
        elseif obj.__type == "complex" then
            return require("Module:PythonScript/Runtime/Cmath").Cmath.to_str(obj)
        elseif obj.__type == "range" then
            return "range(" .. obj.start .. ", " .. obj.stop .. (obj.step == 1 and "" or ", " .. obj.step) .. ")"
        elseif obj.__type == "class" then
            local meta = obj.__class
            local B = Runtime.builtins
            if type(meta) == "table" and meta.__type == "class" and B and meta ~= B.type then
                local magic = in_repr and "__repr__" or "__str__"
                for _, m_base in ipairs(meta.__mro or {}) do
                    if m_base == B.type or m_base == B.object then break end
                    local fn_m = m_base.__dict and m_base.__dict[magic]
                    if not fn_m and not in_repr then fn_m = m_base.__dict and m_base.__dict["__repr__"] end
                    if fn_m then
                        local Evaluator = require("Module:PythonScript/Evaluator")
                        local ok_inv, inv_res = safe_pcall(Evaluator.invoke, fn_m, { obj }, {})
                        if ok_inv then return inv_res else error(inv_res, 0) end
                    end
                end
            end
            local m_name = obj.__dict and obj.__dict["__module__"]
            local c_name = obj.__name or "unknown"
            if m_name and m_name ~= "__main__" and m_name ~= "builtins" then return "<class '" .. m_name .. "." .. c_name .. "'>" end
            return "<class '" .. c_name .. "'>"
        elseif obj.__type == "function" then
            local name = obj.__qualname__ or obj.__name or "<lambda>"
            return "<function " .. name .. " at " .. (tostring(obj):match("0x%w+") or (obj.__module__ and ("'"..obj.__module__.."'")) or '<unknown>') .. ">"
        elseif obj.__type == "builtin_method" then
            return "<built-in function " .. (obj.__name or "unknown") .. ">"
        elseif obj.__type == "property" then
            return "<property object at " .. (tostring(obj):match("0x%w+") or (obj.__module__ and ("'"..obj.__module__.."'")) or '<unknown>') .. ">"
        elseif obj.__type == "LazyNamespace" then
            return "<module '" .. (obj._name or obj.__module__ or "unknown") .. "'>"
        end

        seen = seen or {}
        if seen[obj] then
            if obj.__type == "list" then return "[...]" end
            if obj.__type == "dict" then return "{...}" end
            if obj.__type == "set" or obj.__type == "frozenset" then return obj.__type .. "(...)" end
            if obj.__type == "tuple" then return "(...)" end
            return "<...>"
        end
        seen[obj] = true

        local result_str
        if obj.__type == "list" or obj.__type == "tuple" then
            local open = obj.__type == "list" and "[" or (obj._unpacked_to_str and "" or "(") --如果是unpacked tuple就沒有括號
            local close = obj.__type == "list" and "]" or (obj._unpacked_to_str and "" or ")")
            local res = {}
			local elements = obj.elements or {}
			if obj._unpacked_to_str then elements = obj._elements or {} end
            for _, v in ipairs(elements) do table.insert(res, to_str(v, true, seen)) end
            if obj.__type == "tuple" and #res == 1 then result_str = open .. res[1] .. "," .. close
            else result_str = open .. table.concat(res, ", ") .. close end
			if obj._unpacked_to_str then --字串產製完畢,復位成原始型態
				obj.__type = obj.__type_orig
				obj.__type_orig = nil
				obj._unpacked_to_str = nil
			end
        elseif obj.__type == "set" or obj.__type == "frozenset" then
            if not obj.elements or #obj.elements == 0 then result_str = obj.__type .. "()"
            else
                local res = {}
                for _, v in ipairs(obj.elements) do table.insert(res, to_str(v, true, seen)) end
                local inner = "{" .. table.concat(res, ", ") .. "}"
                result_str = (obj.__type == "frozenset") and ("frozenset(" .. inner .. ")") or inner
            end
        elseif obj.__type == "dict" then
            local res = {}
            for _, p in ipairs(obj.pairs or {}) do table.insert(res, to_str(p.key, true, seen) .. ": " .. to_str(p.value, true, seen)) end
            result_str = "{" .. table.concat(res, ", ") .. "}"
        elseif obj.__type == "instance" then
            local magic = in_repr and "__repr__" or "__str__"
            local success, attr = safe_pcall(PyClass.get_attr, obj, magic)
            if not success and not in_repr then success, attr = safe_pcall(PyClass.get_attr, obj, "__repr__") end
            if success and attr and attr.__type == "bound_method" then
                local Evaluator = require("Module:PythonScript/Evaluator")
                local ok_inv, inv_res = safe_pcall(Evaluator.invoke, attr.__func, {}, {}, attr.__self)
                seen[obj] = nil
                if ok_inv then return inv_res else error(inv_res, 0) end
            end
            if obj.__builtin_value ~= nil then
                seen[obj] = nil
                return to_str(obj.__builtin_value, in_repr, seen)
            end
            if obj.__class and obj.__class.__name == "module" then
                local m_name = (obj.__dict and obj.__dict["__name__"]) or (obj.__dict and obj.__dict._name)
                local m_file = obj.__dict and obj.__dict["__file__"]
                local is_ext = obj.__dict and obj.__dict._is_external
                if m_file then result_str = "<module '" .. m_name .. "' from '" .. m_file .. "'>"
                elseif is_ext then result_str = "<module '" .. m_name .. "'>"
                else result_str = "<module '" .. (m_name or "unknown") .. "' (built-in)>" end
            else
                result_str = "<" .. ((obj.__class and obj.__class.__name) or "object") .. " object>"
            end
        elseif obj.__type == "bound_method" then
            local fname = (obj.__func and obj.__func.__name) or "unknown"
            local class_name = (obj.__self and obj.__self.__class and obj.__self.__class.__name) or (obj.__self and obj.__self.__name) or "?"
            result_str = "<bound method " .. class_name .. "." .. fname .. " of " .. to_str(obj.__self, true, seen) .. ">"
        else
            result_str = "<object>"
        end

        seen[obj] = nil
        return result_str
    end
    return tostring(obj)
end
Utils.to_str = to_str

-- ==========================================
-- 1. 內部通用相等與大小比較器
-- ==========================================
local function runtime_is_equal(a, b, seen)
    if a == b then return true end
    if type(a) == "boolean" and type(b) == "number" then return (a and 1 or 0) == b end
    if type(a) == "number" and type(b) == "boolean" then return a == (b and 1 or 0) end

    if (type(a) == "table" and a.__type == "instance" and a.__builtin_value ~= nil) or (type(b) == "table" and b.__type == "instance" and b.__builtin_value ~= nil) then
        local ua = (type(a) == "table" and a.__type == "instance" and a.__builtin_value ~= nil) and a.__builtin_value or a
        local ub = (type(b) == "table" and b.__type == "instance" and b.__builtin_value ~= nil) and b.__builtin_value or b
        return runtime_is_equal(ua, ub, seen)
    end

    if type(a) ~= type(b) then
        local is_big_a = type(a) == "table" and a.__type == "int"
        local is_big_b = type(b) == "table" and b.__type == "int"
        if is_big_a or is_big_b then
            local na = type(a) == "boolean" and (a and 1 or 0) or a
            local nb = type(b) == "boolean" and (b and 1 or 0) or b
            if (type(na) == "number" or is_big_a) and (type(nb) == "number" or is_big_b) then
                local MathCore = require("Module:PythonScript/Runtime/Cmath")
                local sa = is_big_a and na.sign or (na < 0 and -1 or 1)
                local sb = is_big_b and nb.sign or (nb < 0 and -1 or 1)
                if na == 0 then sa = 1 end; if nb == 0 then sb = 1 end
                return sa == sb and MathCore.BigInt.cmp_limbs(MathCore.BigInt.to_limbs(na), MathCore.BigInt.to_limbs(nb)) == 0
            end
        end
        if (type(a) == "table" and a.__type == "complex") or (type(b) == "table" and b.__type == "complex") then
            return require("Module:PythonScript/Runtime/Cmath").Cmath.eq(a, b)
        end
        return false
    end

    if type(a) == "table" then
        local is_b1 = a.__type == "bytes" or a.__type == "bytearray" or a.__type == "memoryview"
        local is_b2 = b.__type == "bytes" or b.__type == "bytearray" or b.__type == "memoryview"
        if is_b1 or is_b2 then return (is_b1 and is_b2) and (a._data == b._data) or false end
        if a.__type == "int" and b.__type == "int" then
            local MathCore = require("Module:PythonScript/Runtime/Cmath")
            return a.sign == b.sign and MathCore.BigInt.cmp_limbs(a.limbs, b.limbs) == 0
        end
        if a.__type == "complex" and b.__type == "complex" then
            return require("Module:PythonScript/Runtime/Cmath").Cmath.eq(a, b)
        end
        seen = seen or {}
        if seen[a] and seen[a][b] then return true end
        seen[a] = seen[a] or {}; seen[a][b] = true
        if (a.__type == "list" and b.__type == "list") or (a.__type == "tuple" and b.__type == "tuple") then
            if #a.elements ~= #b.elements then return false end
            for i = 1, #a.elements do if not runtime_is_equal(a.elements[i], b.elements[i], seen) then return false end end
            return true
        end
        local is_s1 = a.__type == "set" or a.__type == "frozenset"
        local is_s2 = b.__type == "set" or b.__type == "frozenset"
        if is_s1 and is_s2 then
            if #a.elements ~= #b.elements then return false end
            for _, ea in ipairs(a.elements) do
                local found = false
                for _, eb in ipairs(b.elements) do if runtime_is_equal(ea, eb, seen) then found = true; break end end
                if not found then return false end
            end
            return true
        end
        if a.__type == "dict" and b.__type == "dict" then
            if #a.pairs ~= #b.pairs then return false end
            for _, pa in ipairs(a.pairs) do
                local found = false
                for _, pb in ipairs(b.pairs) do
                    if runtime_is_equal(pa.key, pb.key, seen) and runtime_is_equal(pa.value, pb.value, seen) then found = true; break end
                end
                if not found then return false end
            end
            return true
        end
    end
    return a == b
end
Utils.runtime_is_equal = runtime_is_equal

local function runtime_is_less(a, b)
    if type(a) == "boolean" then a = a and 1 or 0 end
    if type(b) == "boolean" then b = b and 1 or 0 end
    if type(a) == "number" and type(b) == "number" then return a < b end
    if type(a) == "string" and type(b) == "string" then return a < b end
    if type(a) == "table" and type(b) == "table" then
        local is_b1 = a.__type == "bytes" or a.__type == "bytearray"
        local is_b2 = b.__type == "bytes" or b.__type == "bytearray"
        if is_b1 and is_b2 then return a._data < b._data end
        if a.__type == b.__type and (a.__type == "list" or a.__type == "tuple") then
            for i = 1, math.min(#a.elements, #b.elements) do
                if not runtime_is_equal(a.elements[i], b.elements[i]) then
                    return runtime_is_less(a.elements[i], b.elements[i])
                end
            end
            return #a.elements < #b.elements
        end
        if a.__type == "instance" then
            local ok, lt_method = safe_pcall(PyClass.get_attr, a, "__lt__")
            if ok and lt_method then
                local Evaluator = require("Module:PythonScript/Evaluator")
                return to_bool(Evaluator.invoke(lt_method, {b}, {}))
            end
        end
    end
    local is_big_a = type(a) == "table" and a.__type == "int"
    local is_big_b = type(b) == "table" and b.__type == "int"
    if is_big_a or is_big_b then
        if (type(a) == "number" or is_big_a) and (type(b) == "number" or is_big_b) then
            local MathCore = require("Module:PythonScript/Runtime/Cmath")
            local sa = is_big_a and a.sign or (a < 0 and -1 or 1)
            local sb = is_big_b and b.sign or (b < 0 and -1 or 1)
            if a == 0 then sa = 1 end; if b == 0 then sb = 1 end
            if sa ~= sb then return sa < sb end
            local c = MathCore.BigInt.cmp_limbs(MathCore.BigInt.to_limbs(a), MathCore.BigInt.to_limbs(b))
            return (sa == 1) and (c < 0) or (c > 0)
        end
    end
    Runtime.raise("TypeError", "'<' not supported between instances of '" .. get_type_str(a) .. "' and '" .. get_type_str(b) .. "'")
end
Utils.runtime_is_less = runtime_is_less

-- ==========================================
-- 2. 內部迭代器與類別包裝工廠
-- ==========================================
local function get_iterator(iterable)
    if type(iterable) == "table" then
        if iterable.__type == "bytes" or iterable.__type == "bytearray" or iterable.__type == "memoryview" then
            if iterable.__type == "memoryview" and iterable._released then
                Runtime.raise("ValueError", "operation forbidden on released memoryview object")
            end
            local idx, data = 0, iterable._data
            return function() idx = idx + 1; if idx <= #data then return true, string.byte(data, idx) else return false, nil end end
        elseif iterable.__type == "list" or iterable.__type == "tuple" or iterable.__type == "set" or iterable.__type == "frozenset" then
            local idx, elts = 0, iterable.elements
            return function() idx = idx + 1; if idx <= #elts then return true, elts[idx] else return false, nil end end
        elseif iterable.__type == "dict" then
            local idx, pairs_list = 0, iterable.pairs
            return function() idx = idx + 1; if idx <= #pairs_list then return true, pairs_list[idx].key else return false, nil end end
        elseif iterable.__type == "range" then
            local curr, stop, step = iterable.start, iterable.stop, iterable.step
            return function()
                if (step > 0 and curr < stop) or (step < 0 and curr > stop) then
                    local ret = curr; curr = curr + step; return true, ret
                end
                return false, nil
            end
        end
    elseif type(iterable) == "string" then
        local idx, len = 0, mw.ustring.len(iterable)
        return function()
            idx = idx + 1
            if idx <= len then return true, mw.ustring.sub(iterable, idx, idx) else return false, nil end
        end
    end
    local B = Runtime.builtins
    local iter_obj = B.iter({iterable}); local sentinel = {}
    return function()
        local val = B.next({iter_obj, sentinel})
        if val == sentinel then return false, nil end
        return true, val
    end
end
Utils.get_iterator = get_iterator

function Utils.wrap_as_class(name, func)
    local object_class = Runtime.builtins.object
    local is_mutable_container = (name == "dict" or name == "list" or name == "set" or name == "bytearray")
    local cls
    local init_method = is_mutable_container and {
        __type = "builtin_method",
        __name = "__init__",
        __call = function(args, kwargs)
            local self = args and args[1]
            if type(self) == "table" and self.__type == "instance" then
                local rest_args = {}
                for i = 2, #(args or {}) do table.insert(rest_args, args[i]) end
                if #rest_args > 0 or (kwargs and next(kwargs) ~= nil) or self.__builtin_value == nil then
                    self.__builtin_value = func(rest_args, kwargs)
                end
            end
            return nil
        end
    } or nil

    local new_method = {
        __type = "builtin_method",
        __name = "__new__",
        __call = function(args, kwargs)
            local target_cls = args and args[1]
            if type(target_cls) ~= "table" or target_cls.__type ~= "class" then
                Runtime.raise("TypeError", name .. ".__new__(X): X is not a type object")
            end
            local rest_args = {}
            for i = 2, #(args or {}) do table.insert(rest_args, args[i]) end
            if target_cls == cls then return func(rest_args, kwargs) end
            local inst = PyClass.make_instance(target_cls)
            if is_mutable_container then
                local c_init = PyClass.lookup_class_attr(inst, "__init__")
                if c_init and c_init ~= init_method and c_init ~= (object_class and object_class.__dict.__init__) then
                    inst.__builtin_value = func({}, {})
                    return inst
                end
            end
            inst.__builtin_value = func(rest_args, kwargs)
            return inst
        end
    }
    cls = {
        __type = "class", __name = name, __bases = { object_class }, __call = func,
        __dict = {
            __new__ = { __type = "staticmethod", __func = new_method },
            __init__ = init_method
        }
    }
    cls.__mro = { cls, object_class }
    return setmetatable(cls, { __call = function(self, args, kwargs) return self.__call(args, kwargs) end })
end

-- ==========================================
-- 3. 二進位、複數與進位轉換輔助函式
-- ==========================================
function Utils.construct_bytes_data(args, kwargs, func_name)
    local val = args and args[1]
    if val == nil then return "" end
    if type(val) == "string" then
        return val
    elseif type(val) == "number" or (type(val) == "table" and val.__type == "int") then
        local n = unwrap_num(val)
        if type(n) ~= "number" or math.floor(n) ~= n then
            Runtime.raise("TypeError", "'" .. get_type_str(val) .. "' object cannot be interpreted as an integer")
        end
        if n < 0 then Runtime.raise("ValueError", "negative count") end
        if n > 1000000 then Runtime.raise("ValueError", "byte count exceeds maximum limit") end
        return string.rep("\0", n)
    elseif type(val) == "table" then
        if val.__type == "bytes" or val.__type == "bytearray" or val.__type == "memoryview" then
            if val.__type == "memoryview" and val._released then
                Runtime.raise("ValueError", "operation forbidden on released memoryview object")
            end
            return val._data
        end
        if val.__type == "instance" then
            local ok, bytes_m = safe_pcall(PyClass.get_attr, val, "__bytes__")
            if ok and bytes_m and bytes_m.__type == "bound_method" then
                local Evaluator = require("Module:PythonScript/Evaluator")
                local res = Evaluator.invoke(bytes_m.__func, {}, {}, bytes_m.__self)
                if type(res) == "table" and res.__type == "bytes" then
                    return res._data
                end
                Runtime.raise("TypeError", "__bytes__ returned non-bytes")
            end
        end
        local iter_fn = get_iterator(val)
        local chars = {}
        while true do
            local has_next, item = iter_fn()
            if not has_next then break end
            local b = unwrap_num(item)
            if type(b) ~= "number" or math.floor(b) ~= b then
                Runtime.raise("TypeError", "'" .. get_type_str(item) .. "' object cannot be interpreted as an integer")
            end
            if b < 0 or b > 255 then
                Runtime.raise("ValueError", func_name .. " must be in range(0, 256)")
            end
            table.insert(chars, string.char(b))
        end
        return table.concat(chars)
    end
    Runtime.raise("TypeError", "cannot convert '" .. get_type_str(val) .. "' object to " .. func_name)
end

function Utils.make_bytes_fromhex(target_type)
    return {
        __type = "staticmethod",
        __func = {
            __type = "builtin_method",
            __call = function(args)
                local s = args[1]
                if type(s) == "table" and (s.__type == "class" or s.__type == "bytes" or s.__type == "bytearray") then s = args[2] end
                if type(s) ~= "string" then Runtime.raise("TypeError", "fromhex() argument must be str") end
                local clean = s:gsub("%s+", "")
                if #clean % 2 ~= 0 or clean:match("[^0-9a-fA-F]") then
                    Runtime.raise("ValueError", "non-hexadecimal number found in fromhex() arg")
                end
                local data = (clean:gsub("(%x%x)", function(h) return string.char(tonumber(h, 16)) end))
                return { __type = target_type, _data = data }
            end
        }
    }
end

local function parse_float_token(ns)
    if not ns or ns == "" then return nil end
    if ns:sub(1, 1) == "_" or ns:sub(-1) == "_" or ns:find("__") then return nil end
    local clean = ns
    while clean:find("(%d)_(%d)") do clean = clean:gsub("(%d)_(%d)", "%1%2") end
    if clean:find("_") then return nil end
    if clean == "inf" or clean == "+inf" or clean == "infinity" or clean == "+infinity" then return math.huge
    elseif clean == "-inf" or clean == "-infinity" then return -math.huge
    elseif clean == "nan" or clean == "+nan" or clean == "-nan" then return 0/0 end
    return tonumber(clean)
end

function Utils.construct_complex(args, kwargs)
    local Cmath = require("Module:PythonScript/Runtime/Cmath").Cmath
    local v1 = (args and args[1] ~= nil) and args[1] or (kwargs and kwargs.real)
    local v2 = (args and args[2] ~= nil) and args[2] or (kwargs and kwargs.imag)
    if v1 == nil and v2 == nil then return Cmath.make_complex(0, 0) end
    if type(v1) == "string" then
        if v2 ~= nil or (kwargs and next(kwargs)) then
            Runtime.raise("TypeError", "complex() can't take second arg if first is a string")
        end
        local s = v1:match("^%s*(.-)%s*$"):lower()
        if s:sub(1, 1) == "(" and s:sub(-1) == ")" then s = s:sub(2, -2):match("^%s*(.-)%s*$") end
        if s == "" or s:find("%s") then Runtime.raise("ValueError", "complex() arg is a malformed string") end
        local real, imag = 0, 0
        if s:sub(-1) == "j" then
            s = s:sub(1, -2)
            if s == "" or s == "+" then return Cmath.make_complex(0, 1) end
            if s == "-" then return Cmath.make_complex(0, -1) end
            local split_idx = nil
            for i = #s, 1, -1 do
                local c = s:sub(i, i)
                if c == "+" or c == "-" then
                    if i == 1 then break end
                    if s:sub(i-1, i-1) ~= "e" then split_idx = i; break end
                end
            end
            if split_idx then
                local r_str, i_str = s:sub(1, split_idx - 1), s:sub(split_idx)
                if i_str == "+" then i_str = "1" elseif i_str == "-" then i_str = "-1" end
                real, imag = parse_float_token(r_str), parse_float_token(i_str)
                if not real or not imag then Runtime.raise("ValueError", "complex() arg is a malformed string") end
            else
                imag = parse_float_token(s)
                if not imag then Runtime.raise("ValueError", "complex() arg is a malformed string") end
            end
        else
            real = parse_float_token(s)
            if not real then Runtime.raise("ValueError", "complex() arg is a malformed string") end
        end
        return Cmath.make_complex(real, imag)
    end
    local function get_r_i(v)
        if type(v) == "table" and v.__type == "instance" and v.__builtin_value ~= nil then v = v.__builtin_value end
        if type(v) == "number" then return v, 0 end
        if type(v) == "boolean" then return v and 1 or 0, 0 end
        if type(v) == "table" and v.__type == "int" then return unwrap_num(v), 0 end
        if type(v) == "table" and v.__type == "complex" then
            if (v.k and v.k ~= 0) or (v.jk and v.jk ~= 0) then
                Runtime.raise("TypeError", "can't convert quaternion to complex")
            end
            return v.real or 0, v.imag or 0
        end
        if v == nil then return 0, 0 end
        Runtime.raise("TypeError", "complex() argument must be a string or a number")
    end
    local r1, i1 = get_r_i(v1); local r2, i2 = get_r_i(v2)
    return Cmath.make_complex(r1 - i2, i1 + r2)
end

function Utils.construct_quaternion(args, kwargs)
    local Cmath = require("Module:PythonScript/Runtime/Cmath").Cmath
    local n_args = args and #args or 0
    if n_args > 4 then
        Runtime.raise("TypeError", "Quaternion() takes at most 4 arguments (" .. n_args .. " given)")
    end

    local v1 = (args and args[1] ~= nil) and args[1] or (kwargs and (kwargs.real or kwargs.r))
    local v2 = (args and args[2] ~= nil) and args[2] or (kwargs and (kwargs.imag or kwargs.j))
    local v3 = (args and args[3] ~= nil) and args[3] or (kwargs and kwargs.k)
    local v4 = (args and args[4] ~= nil) and args[4] or (kwargs and kwargs.jk)

    if v1 == nil and v2 == nil and v3 == nil and v4 == nil then
        return Cmath.make_complex(0, 0, 0, 0)
    end

    -- 1. 字串解析模式:支援如 "3+4j+5k"、"(1 - 2j + 3.5k - 4jk)"、"-5k" 等格式
    if type(v1) == "string" then
        if n_args > 1 or (kwargs and next(kwargs)) then
            Runtime.raise("TypeError", "Quaternion() can't take second arg if first is a string")
        end
        local s = v1:match("^%s*(.-)%s*$"):lower()
        if s:sub(1, 1) == "(" and s:sub(-1) == ")" then
            s = s:sub(2, -2):match("^%s*(.-)%s*$")
        end
        if s == "" then Runtime.raise("ValueError", "Quaternion() arg is a malformed string") end
        s = s:gsub("%s*([%+%-])%s*", "%1")
        if s:find("%s") then Runtime.raise("ValueError", "Quaternion() arg is a malformed string") end

        local terms = {}
        local start_i = 1
        for i = 2, #s do
            local c = s:sub(i, i)
            if (c == "+" or c == "-") and s:sub(i - 1, i - 1) ~= "e" then
                table.insert(terms, s:sub(start_i, i - 1))
                start_i = i
            end
        end
        table.insert(terms, s:sub(start_i))

        local comps = { real = 0, j = 0, k = 0, jk = 0 }
        local seen = {}
        for _, t in ipairs(terms) do
            if t == "" then Runtime.raise("ValueError", "Quaternion() arg is a malformed string") end
            local unit, num_str
            if t:sub(-2) == "jk" then
                unit = "jk"; num_str = t:sub(1, -3)
            elseif t:sub(-1) == "j" then
                unit = "j"; num_str = t:sub(1, -2)
            elseif t:sub(-1) == "k" then
                unit = "k"; num_str = t:sub(1, -2)
            else
                unit = "real"; num_str = t
            end

            if seen[unit] then
                Runtime.raise("ValueError", "Quaternion() arg is a malformed string")
            end
            seen[unit] = true

            local val
            if unit ~= "real" and (num_str == "" or num_str == "+") then
                val = 1.0
            elseif unit ~= "real" and num_str == "-" then
                val = -1.0
            else
                val = parse_float_token(num_str)
            end
            if not val then
                Runtime.raise("ValueError", "Quaternion() arg is a malformed string")
            end
            comps[unit] = val
        end
        return Cmath.make_complex(comps.real, comps.j, comps.k, comps.jk)
    end

    -- 2. 支援 Quaternion(r, q.imag) 或 Quaternion(r, (b, c, d)) 自動展開三維虛部向量
    if n_args == 2 and v3 == nil and v4 == nil and type(v2) == "table" then
        if v2._is_unpacked_tuple and v2._elements then
            v2, v3, v4 = v2._elements[1], v2._elements[2], v2._elements[3]
        elseif (v2.__type == "tuple" or v2.__type == "list") and v2.elements and #v2.elements <= 3 then
            v2, v3, v4 = v2.elements[1], v2.elements[2], v2.elements[3]
        end
    end

    -- 3. 數值與超複數分量合成:v1 + v2*j + v3*k + v4*jk
    local function to_q_obj(v)
        if v == nil then return Cmath.make_complex(0, 0, 0, 0) end
        if type(v) == "table" and v.__type == "instance" and v.__builtin_value ~= nil then v = v.__builtin_value end
        if type(v) == "number" then return Cmath.make_complex(v, 0, 0, 0) end
        if type(v) == "boolean" then return Cmath.make_complex(v and 1 or 0, 0, 0, 0) end
        if type(v) == "table" and v.__type == "int" then return Cmath.make_complex(unwrap_num(v), 0, 0, 0) end
        if type(v) == "table" and v.__type == "complex" then return v end
        Runtime.raise("TypeError", "Quaternion() argument must be a string or a number")
    end

    local q1 = to_q_obj(v1)
    local q2 = Cmath.mul(to_q_obj(v2), Cmath.make_complex(0, 1, 0, 0))
    local q3 = Cmath.mul(to_q_obj(v3), Cmath.make_complex(0, 0, 1, 0))
    local q4 = Cmath.mul(to_q_obj(v4), Cmath.make_complex(0, 0, 0, 1))
    return Cmath.add(Cmath.add(q1, q2), Cmath.add(q3, q4))
end

function Utils.int_to_base_str(val, base, prefix)
    local digits = "0123456789abcdef"
    if type(val) == "table" and val.__type == "instance" then
        local ok, idx_m = safe_pcall(PyClass.get_attr, val, "__index__")
        if ok and idx_m and idx_m.__type == "bound_method" then
            local Evaluator = require("Module:PythonScript/Evaluator")
            val = Evaluator.invoke(idx_m.__func, {}, {}, idx_m.__self)
        elseif val.__builtin_value ~= nil then
            val = val.__builtin_value
        end
    end
    if type(val) == "boolean" then val = val and 1 or 0 end
    if type(val) == "number" and math.floor(val) == val and math.abs(val) <= 2147483647 then
        if val == 0 then return prefix .. "0" end
        local is_neg = val < 0; local n = math.abs(val); local t = {}
        while n > 0 do
            local r = n % base
            table.insert(t, 1, digits:sub(r + 1, r + 1))
            n = math.floor(n / base)
        end
        return (is_neg and "-" or "") .. prefix .. table.concat(t)
    elseif (type(val) == "number" and math.floor(val) == val) or (type(val) == "table" and val.__type == "int") then
        local MathCore = require("Module:PythonScript/Runtime/Cmath")
        local BigInt = MathCore.BigInt
        local sign = (type(val) == "table" and val.sign) or (val < 0 and -1 or 1)
        local limbs = BigInt.to_limbs(val)
        if #limbs == 1 and limbs[1] == 0 then return prefix .. "0" end
        local base_limbs, t = { base }, {}
        while not (#limbs == 1 and limbs[1] == 0) do
            local q, r_limbs = BigInt.divmod_limbs(limbs, base_limbs)
            local r = r_limbs[1] or 0
            table.insert(t, 1, digits:sub(r + 1, r + 1))
            limbs = q
        end
        return (sign < 0 and "-" or "") .. prefix .. table.concat(t)
    end
    Runtime.raise("TypeError", "'" .. get_type_str(val) .. "' object cannot be interpreted as an integer")
end

-- ==========================================
-- 4. Format Mini-Language 排版引擎
-- ==========================================
local function apply_grouping(digits_str, sep, group_size)
    if not sep or sep == "" or #digits_str <= group_size then return digits_str end
    local parts, len = {}, #digits_str
    local i = len
    while i > 0 do
        local start_i = math.max(1, i - group_size + 1)
        table.insert(parts, 1, digits_str:sub(start_i, i))
        i = start_i - 1
    end
    return table.concat(parts, sep)
end

local function pad_formatted_string(prefix, body, width, align, fill)
    local total_len = mw.ustring.len(prefix) + mw.ustring.len(body)
    if not width or width <= total_len then return prefix .. body end
    local pad_count = width - total_len
    local pad_str = mw.ustring.rep(fill, pad_count)
    if align == "<" then return prefix .. body .. pad_str
    elseif align == "=" then return prefix .. pad_str .. body
    elseif align == "^" then
        local left_p = math.floor(pad_count / 2)
        local right_p = pad_count - left_p
        return mw.ustring.rep(fill, left_p) .. prefix .. body .. mw.ustring.rep(fill, right_p)
    else return pad_str .. prefix .. body end
end

function Utils.format_value(args)
    local B = Runtime.builtins
    local object_class = B.object
    local val = args and args[1]
    local spec = (args and args[2] ~= nil) and args[2] or ""
    if type(spec) == "table" and spec.__type == "instance" and spec.__builtin_value ~= nil then
        spec = spec.__builtin_value
    end
    if type(spec) ~= "string" then
        Runtime.raise("TypeError", "format() argument 2 must be str, not " .. get_type_str(spec))
    end

    if type(val) == "table" and val.__type == "instance" then
        local custom_fmt = nil
        for _, base_cls in ipairs(val.__class.__mro or {}) do
            local fn = base_cls.__dict and base_cls.__dict["__format__"]
            if fn ~= nil then
                if base_cls ~= object_class and base_cls.__name ~= "object" then custom_fmt = fn end
                break
            end
        end
        if custom_fmt then
            local Evaluator = require("Module:PythonScript/Evaluator")
            local res = Evaluator.invoke(custom_fmt, {val, spec}, {})
            if type(res) == "table" and res.__type == "instance" and res.__builtin_value ~= nil then
                res = res.__builtin_value
            end
            if type(res) ~= "string" then
                Runtime.raise("TypeError", "__format__ must return a str, not " .. get_type_str(res))
            end
            return res
        elseif val.__builtin_value ~= nil then
            if spec == "" then return B.str.__call({val}) end
            val = val.__builtin_value
        else
            return object_class.__dict.__format__.__call({val, spec})
        end
    end

    if spec == "" then return to_str(val, false) end

    local fill, align, rem = nil, nil, spec
    local ulen = mw.ustring.len(spec)
    if ulen >= 2 and mw.ustring.sub(spec, 2, 2):match("^[<>^=]$") then
        fill = mw.ustring.sub(spec, 1, 1); align = mw.ustring.sub(spec, 2, 2); rem = mw.ustring.sub(spec, 3)
    elseif ulen >= 1 and mw.ustring.sub(spec, 1, 1):match("^[<>^=]$") then
        fill = " "; align = mw.ustring.sub(spec, 1, 1); rem = mw.ustring.sub(spec, 2)
    end

    local sign, alt, zero, width_s, grp, dot_prec, ftype = rem:match("^([%+%- ]?)(#?)(0?)(%d*)([,_]?)(%.?%d*)([sbdoxXceEfFgG%%]?)$")
    if not sign or dot_prec == "." then
        Runtime.raise("ValueError", "Invalid format specifier '" .. spec .. "' for object of type '" .. get_type_str(val) .. "'")
    end

    local width = (width_s ~= "") and tonumber(width_s) or nil
    local prec = (dot_prec ~= "") and tonumber(dot_prec:sub(2)) or nil

    if type(val) == "string" or (type(val) == "boolean" and ftype == "s") then
        if type(val) == "boolean" then Runtime.raise("ValueError", "Unknown format code 's' for object of type 'bool'") end
        if ftype ~= "" and ftype ~= "s" then Runtime.raise("ValueError", "Unknown format code '" .. ftype .. "' for object of type 'str'") end
        if sign ~= "" or alt ~= "" or zero ~= "" or grp ~= "" or align == "=" then
            Runtime.raise("ValueError", "Invalid format specifier '" .. spec .. "' for object of type 'str'")
        end
        local s_body = prec and mw.ustring.sub(val, 1, prec) or val
        return pad_formatted_string("", s_body, width, align or "<", fill or " ")
    end

    if type(val) == "boolean" then val = val and 1 or 0 end
    local is_big = (type(val) == "table" and val.__type == "int")
    local is_num = (type(val) == "number") or is_big
    if not is_num then return object_class.__dict.__format__.__call({val, spec}) end
    if ftype == "s" then Runtime.raise("ValueError", "Unknown format code 's' for object of type '" .. get_type_str(val) .. "'") end

    if zero == "0" then
        fill = fill or "0"; align = align or "="
    else
        fill = fill or " "; align = align or ">"
    end

    local is_int_val = is_big or (math.floor(val) == val and math.abs(val) <= SAFE_LIMIT)
    local int_codes = { d = true, b = true, o = true, x = true, X = true, c = true }

    if int_codes[ftype] or (ftype == "" and prec == nil and is_int_val) then
        if not is_int_val then Runtime.raise("ValueError", "Unknown format code '" .. ftype .. "' for object of type 'float'") end
        if prec ~= nil then Runtime.raise("ValueError", "Precision not allowed in integer format specifier") end
        if ftype == "c" then
            if sign ~= "" or alt ~= "" or grp ~= "" then Runtime.raise("ValueError", "Invalid format specifier for 'c'") end
            return pad_formatted_string("", B.chr({val}), width, align, fill)
        end

        local is_neg = is_big and (val.sign < 0) or (not is_big and val < 0)
        local digits_str, alt_prefix = "", ""

        if ftype == "b" then
            digits_str = B.bin({val}):gsub("^%-?0b", ""); if alt == "#" then alt_prefix = "0b" end
        elseif ftype == "o" then
            digits_str = B.oct({val}):gsub("^%-?0o", ""); if alt == "#" then alt_prefix = "0o" end
        elseif ftype == "x" or ftype == "X" then
            digits_str = B.hex({val}):gsub("^%-?0x", "")
            if ftype == "X" then digits_str = digits_str:upper() end
            if alt == "#" then alt_prefix = "0" .. ftype end
        else
            if is_big then
                digits_str = require("Module:PythonScript/Runtime/Cmath").BigInt.to_string(1, val.limbs)
            else
                digits_str = string.format("%.0f", math.abs(val))
            end
        end

        local g_size = (ftype == "b" or ftype == "o" or ftype == "x" or ftype == "X") and 4 or 3
        digits_str = apply_grouping(digits_str, grp, g_size)
        local sign_str = is_neg and "-" or (sign == "+" and "+" or (sign == " " and " " or ""))
        return pad_formatted_string(sign_str .. alt_prefix, digits_str, width, align, fill)
    else
        local num = unwrap_num(val)
        local is_neg = (num < 0)
        local abs_num = math.abs(num)
        local suffix, eff_type = "", ftype
        if ftype == "%" then abs_num = abs_num * 100; eff_type = "f"; suffix = "%"
        elseif ftype == "" then eff_type = prec and "g" or "f"
        elseif ftype == "F" then eff_type = "f" end

        local fmt_p = dot_prec ~= "" and dot_prec or (ftype == "%" and ".6" or (ftype == "" and "" or ".6"))
        local body_str
        if ftype == "" and dot_prec == "" then body_str = to_str(abs_num, false)
        else
            local ok_f, res_f = pcall(string.format, "%" .. fmt_p .. eff_type, abs_num)
            body_str = ok_f and res_f or tostring(abs_num)
        end

        if grp ~= "" then
            local int_part, rest_part = body_str:match("^(%d+)(.*)$")
            if int_part then body_str = apply_grouping(int_part, grp, 3) .. rest_part end
        end

        local sign_str = is_neg and "-" or (sign == "+" and "+" or (sign == " " and " " or ""))
        return pad_formatted_string(sign_str, body_str .. suffix, width, align, fill)
    end
end

-- ==========================================
-- 5. 內建高階函式實作 (min/max/sum/reversed/dir/hash/compile/eval/exec/map/filter/iter/next)
-- ==========================================
function Utils.min_or_max(args, kwargs, is_min)
    local B = Runtime.builtins
    local key_func = kwargs and kwargs.key
    local has_default = kwargs and kwargs.default ~= nil
    local default_val = kwargs and kwargs.default
    local best_val, best_key, has_item = nil, nil, false
    local iter_fn
    if #args == 1 then iter_fn = get_iterator(args[1]) else
        local idx = 0
        iter_fn = function() idx = idx + 1; if idx <= #args then return true, args[idx] else return false, nil end end
    end
    local Evaluator = key_func and require("Module:PythonScript/Evaluator") or nil
    while true do
        local has_next, v = iter_fn(); if not has_next then break end
        local k = (key_func and key_func ~= B.None) and Evaluator.invoke(key_func, {v}, {}) or v
        if not has_item or (is_min and runtime_is_less(k, best_key) or (not is_min and runtime_is_less(best_key, k))) then
            best_val = v; best_key = k; has_item = true
        end
    end
    if not has_item then
        if has_default then return default_val end
        Runtime.raise("ValueError", (is_min and "min" or "max") .. "() arg is an empty sequence")
    end
    return best_val
end

function Utils.sum_impl(args, kwargs)
    local total = (args[2] ~= nil and args[2]) or (kwargs and kwargs.start) or 0
    if type(total) == "string" then
        Runtime.raise("TypeError", "sum() can't sum strings [use ''.join(seq) instead]")
    end
    local iter_fn = get_iterator(args[1])
    local Evaluator = require("Module:PythonScript/Evaluator")
    while true do
        local has_next, v = iter_fn(); if not has_next then break end
        if type(total) == "number" and type(v) == "number" then
            local s = total + v
            if math.abs(s) > SAFE_LIMIT and math.floor(total) == total and math.floor(v) == v then
                total = Evaluator.bigint_op(total, v, "+")
            else total = s end
        elseif (type(total) == "table" and total.__type == "int") or (type(v) == "table" and v.__type == "int") then
            total = Evaluator.bigint_op(total, v, "+")
        elseif type(total) == "table" and total.__type == "list" and type(v) == "table" and v.__type == "list" then
            local combined = {}
            for _, e in ipairs(total.elements) do table.insert(combined, e) end
            for _, e in ipairs(v.elements) do table.insert(combined, e) end
            total = { __type = "list", elements = combined }
        else
            local res = nil
            -- 嘗試呼叫左側物件的 __add__
            if type(total) == "table" and total.__type == "instance" then
                local ok, attr = pcall(PyClass.get_attr, total, "__add__")
                if ok and attr and attr.__type == "bound_method" then
                    res = Evaluator.invoke(attr.__func, {v}, {}, attr.__self)
                    if res == Runtime.builtins.NotImplemented then res = nil end
                end
            end
            -- 若左側沒有 __add__ 或是回傳 NotImplemented,嘗試呼叫右側的 __radd__
            if res == nil and type(v) == "table" and v.__type == "instance" then
                local ok, attr = pcall(PyClass.get_attr, v, "__radd__")
                if ok and attr and attr.__type == "bound_method" then
                    res = Evaluator.invoke(attr.__func, {total}, {}, attr.__self)
                    if res == Runtime.builtins.NotImplemented then res = nil end
                end
            end
            
            if res ~= nil then
                total = res
            else
                -- 若雙方都不是 Python 實例物件,才退回原本的轉型與 Lua 加法
                local u_tot, u_v = unwrap_num(total), unwrap_num(v)
                if type(u_tot) ~= "number" or type(u_v) ~= "number" then
                    Runtime.raise("TypeError", "unsupported operand type(s) for + in sum()")
                end
                total = u_tot + u_v
            end
        end
    end
    return total
end

function Utils.reversed_impl(args)
    local B = Runtime.builtins
    local obj = args[1]; local elements = {}
    if type(obj) == "table" and obj.__type == "instance" then
        local success, attr = safe_pcall(PyClass.get_attr, obj, "__reversed__")
        if success and attr and attr.__type == "bound_method" then
            return require("Module:PythonScript/Evaluator").invoke(attr.__func, {}, {}, attr.__self)
        end
    end
    if type(obj) == "table" and (obj.__type == "list" or obj.__type == "tuple") then
        for i = #obj.elements, 1, -1 do table.insert(elements, obj.elements[i]) end
    elseif type(obj) == "table" and obj.__type == "range" then
        local len = B.len({obj})
        for i = len - 1, 0, -1 do table.insert(elements, obj.start + i * obj.step) end
    elseif type(obj) == "string" then
        for i = mw.ustring.len(obj), 1, -1 do table.insert(elements, mw.ustring.sub(obj, i, i)) end
    elseif type(obj) == "table" and (obj.__type == "bytes" or obj.__type == "bytearray" or obj.__type == "memoryview") then
        for i = #obj._data, 1, -1 do table.insert(elements, string.byte(obj._data, i)) end
    else Runtime.raise("TypeError", "'" .. get_type_str(obj) .. "' object is not reversible") end
    return B.iter({{ __type = "list", elements = elements }})
end

function Utils.dir_impl(args)
    local obj = args and args[1]
    if not obj then
        local scope = Runtime.Traceback.current_scope
        local keys = {}
        if scope then for k, _ in pairs(scope.vars) do table.insert(keys, k) end end
        table.sort(keys)
        return { __type = "list", elements = keys }
    end
    local keys, seen = {}, {}
    local function add_key(k) if type(k) == "string" and not seen[k] then table.insert(keys, k); seen[k] = true end end
    if type(obj) == "table" then
        if obj.__type == "instance" or obj.__type == "class" or obj.__type == "module" then
            if obj.__class and obj.__class.__name == "LazyNamespace" and type(obj.__dict._raw_lua_tbl) == "table" then
                for k, _ in pairs(obj.__dict._raw_lua_tbl) do add_key(tostring(k)) end
            end
            if obj.__dict and type(obj.__dict["__all__"]) == "table" and obj.__dict["__all__"].elements then
                for _, k in ipairs(obj.__dict["__all__"].elements) do add_key(tostring(k)) end
            end
            for k, _ in pairs(obj.__dict or {}) do add_key(k) end
            local mro = obj.__mro or (obj.__class and obj.__class.__mro) or {}
            for _, cls in ipairs(mro) do for k, _ in pairs(cls.__dict or {}) do add_key(k) end end
        elseif obj.__type == "dict" then
            for _, m in ipairs({"clear", "copy", "get", "items", "keys", "pop", "update", "values", "__iter__"}) do add_key(m) end
        elseif obj.__type == "list" or obj.__type == "tuple" then
            for _, m in ipairs({"append", "clear", "copy", "count", "extend", "index", "insert", "pop", "remove", "reverse", "sort", "__iter__", "shape"}) do add_key(m) end
        elseif obj.__type == "set" or obj.__type == "frozenset" then
            for _, m in ipairs({"add", "clear", "difference", "discard", "intersection", "remove", "union", "__iter__"}) do add_key(m) end
        end
    elseif type(obj) == "string" then
        for _, m in ipairs({"capitalize", "count", "encode", "endswith", "find", "format", "index", "isalnum", "isalpha", "isdigit", "join", "lower", "lstrip", "replace", "rstrip", "split", "startswith", "strip", "title", "upper", "__iter__", "__mod__"}) do add_key(m) end
    end
    table.sort(keys); return { __type = "list", elements = keys }
end

function Utils.hash_impl(args)
    local raw = args[1]
    if type(raw) == "table" and (raw.__type == "list" or raw.__type == "dict" or raw.__type == "set" or raw.__type == "bytearray") then
        Runtime.raise("TypeError", "unhashable type: '" .. raw.__type .. "'")
    end
    local obj = unwrap_num(raw)
    if type(obj) == "number" then return math.floor(obj) end
    if type(obj) == "string" then
        local h = 0; for i = 1, #obj do h = (h * 31 + string.byte(obj, i)) % 4294967296 end; return h
    end
    if type(obj) == "boolean" then return obj and 1 or 0 end
    if type(obj) == "table" then
        if obj.__type == "bytes" or obj.__type == "memoryview" then
            local s, h = obj._data, 0
            for i = 1, #s do h = (h * 31 + string.byte(s, i)) % 4294967296 end
            return h
        end
        if obj.__type == "instance" then
            local ok, hm = safe_pcall(PyClass.get_attr, obj, "__hash__")
            if ok and hm and hm.__type == "bound_method" then
                return require("Module:PythonScript/Evaluator").invoke(hm.__func, {}, {}, hm.__self)
            end
        end
        return tonumber(tostring(obj):match("0x%w+") or "0", 16) or 0
    end
    return 0
end

function Utils.compile_impl(args, kwargs)
    local B = Runtime.builtins
    local source = (args and args[1] ~= nil) and args[1] or (kwargs and kwargs.source)
    local filename = (args and args[2] ~= nil) and args[2] or (kwargs and kwargs.filename)
    local mode = (args and args[3] ~= nil) and args[3] or (kwargs and kwargs.mode)

    if source == nil or filename == nil or mode == nil then
        Runtime.raise("TypeError", "compile() missing required argument (source, filename, or mode)")
    end
    if type(source) == "table" and (source.__type == "bytes" or source.__type == "bytearray" or source.__type == "memoryview") then
        source = source._data
    elseif type(source) == "table" and source.__type == "instance" and source.__builtin_value ~= nil then
        source = source.__builtin_value
    end
    if type(source) ~= "string" then Runtime.raise("TypeError", "compile() arg 1 must be a string, bytes or AST object") end
    if type(filename) ~= "string" then filename = to_str(filename, false) end
    if mode ~= "exec" and mode ~= "eval" and mode ~= "single" then
        Runtime.raise("ValueError", "compile() mode must be 'exec', 'eval' or 'single'")
    end

    local clean_src = (mode == "eval") and source:gsub("^[ \t]+", "") or source
    local new_file_idx = #Runtime.Traceback.files + 1
    Runtime.Traceback.files[new_file_idx] = { name = filename, raw_code = clean_src }

    local prev_file_idx = Runtime.Traceback.current_file_idx
    local prev_func_name = Runtime.Traceback.current_func_name or "<module>"
    local prev_line = Runtime.Traceback.current_line
    table.insert(Runtime.Traceback.stack, { prev_file_idx, prev_func_name, prev_line })

    Runtime.Traceback.current_file_idx = new_file_idx
    Runtime.Traceback.current_func_name = "<module>"
    Runtime.Traceback.current_line = 1

    local Lexer = require("Module:PythonScript/Lexer")
    local Parser = require("Module:PythonScript/Parser")
    local ok, ast_or_err = safe_pcall(function()
        return Parser.parse(Lexer.tokenize(clean_src), mode)
    end)

    table.remove(Runtime.Traceback.stack)
    Runtime.Traceback.current_file_idx = prev_file_idx
    Runtime.Traceback.current_func_name = prev_func_name
    Runtime.Traceback.current_line = prev_line

    if not ok then error(ast_or_err, 0) end

    local code_inst = PyClass.make_instance(B.code_type)
    code_inst.__dict._ast = ast_or_err
    code_inst.__dict._mode = mode
    code_inst.__dict._file_idx = new_file_idx
    code_inst.__dict.co_filename = filename
    code_inst.__dict.co_name = "<module>"
    return code_inst
end

function Utils.eval_impl(args, kwargs)
    if not args or #args == 0 then Runtime.raise("TypeError", "eval expected at least 1 argument, got 0") end
    local B = Runtime.builtins
    local src_or_code = args[1]
    local globals_arg = args[2] or (kwargs and kwargs.globals)
    local locals_arg = args[3] or (kwargs and kwargs.locals)
    local code_obj
    if type(src_or_code) == "table" and src_or_code.__type == "instance" and src_or_code.__class == B.code_type then
        code_obj = src_or_code
    elseif type(src_or_code) == "string" or (type(src_or_code) == "table" and (src_or_code.__type == "bytes" or src_or_code.__type == "bytearray" or src_or_code.__type == "memoryview" or src_or_code.__builtin_value ~= nil)) then
        code_obj = B.compile({ src_or_code, "<string>", "eval" })
    else Runtime.raise("TypeError", "eval() arg 1 must be a string, bytes or code object") end
    return require("Module:PythonScript/Evaluator").eval_code(code_obj, globals_arg, locals_arg, "eval")
end

function Utils.exec_impl(args, kwargs)
    if not args or #args == 0 then Runtime.raise("TypeError", "exec expected at least 1 argument, got 0") end
    local B = Runtime.builtins
    local src_or_code = args[1]
    local globals_arg = args[2] or (kwargs and kwargs.globals)
    local locals_arg = args[3] or (kwargs and kwargs.locals)
    local code_obj
    if type(src_or_code) == "table" and src_or_code.__type == "instance" and src_or_code.__class == B.code_type then
        code_obj = src_or_code
    elseif type(src_or_code) == "string" or (type(src_or_code) == "table" and (src_or_code.__type == "bytes" or src_or_code.__type == "bytearray" or src_or_code.__type == "memoryview" or src_or_code.__builtin_value ~= nil)) then
        code_obj = B.compile({ src_or_code, "<string>", "exec" })
    else Runtime.raise("TypeError", "exec() arg 1 must be a string, bytes or code object") end
    require("Module:PythonScript/Evaluator").eval_code(code_obj, globals_arg, locals_arg, "exec")
    return B.None
end

function Utils.map_impl(args)
    local object_class = Runtime.builtins.object
    local func, iterables = args[1], {}
    for i = 2, #args do table.insert(iterables, get_iterator(args[i])) end
    local map_dict = {
        __iter__ = { __type = "builtin_method", __call = function(self_args) return self_args[1] end },
        __next__ = {
            __type = "builtin_method",
            __call = function(self_args)
                local self, call_args = self_args[1], {}
                local Evaluator = require("Module:PythonScript/Evaluator")
                for _, it in ipairs(self.__dict.iterables) do
                    local has_next, v = it(); if not has_next then Runtime.raise("StopIteration") end
                    table.insert(call_args, v)
                end
                return Evaluator.invoke(self.__dict.func, call_args, {})
            end
        }
    }
    local inst = PyClass.make_instance(PyClass.make_class("map", { object_class }, map_dict))
    inst.__dict.func = func; inst.__dict.iterables = iterables
    return inst
end

function Utils.filter_impl(args)
    local B = Runtime.builtins
    local func, it = args[1], get_iterator(args[2])
    local filter_dict = {
        __iter__ = { __type = "builtin_method", __call = function(self_args) return self_args[1] end },
        __next__ = {
            __type = "builtin_method",
            __call = function(self_args)
                local self = self_args[1]; local Evaluator = require("Module:PythonScript/Evaluator")
                while true do
                    local has_next, v = self.__dict.it(); if not has_next then Runtime.raise("StopIteration") end
                    local keep = (self.__dict.func ~= nil and self.__dict.func ~= B.None)
                        and to_bool(Evaluator.invoke(self.__dict.func, {v}, {})) or to_bool(v)
                    if keep then return v end
                end
            end
        }
    }
    local inst = PyClass.make_instance(PyClass.make_class("filter", { B.object }, filter_dict))
    inst.__dict.func = func; inst.__dict.it = it
    return inst
end

function Utils.iter_impl(args)
    local object_class = Runtime.builtins.object
    local obj = args[1]
    if type(obj) == "table" and obj.__type == "instance" then
        local success, attr = safe_pcall(PyClass.get_attr, obj, "__iter__")
        if success and attr and attr.__type == "bound_method" then
            return require("Module:PythonScript/Evaluator").invoke(attr.__func, {}, {}, attr.__self)
        end
        if obj.__builtin_value ~= nil then obj = obj.__builtin_value end
    end

    local iter_list = {}
    if type(obj) == "string" then
        for i = 1, mw.ustring.len(obj) do table.insert(iter_list, mw.ustring.sub(obj, i, i)) end
    elseif type(obj) == "table" then
        if obj.__type == "bytes" or obj.__type == "bytearray" or obj.__type == "memoryview" then
            if obj.__type == "memoryview" and obj._released then Runtime.raise("ValueError", "operation forbidden on released memoryview object") end
            for i = 1, #obj._data do table.insert(iter_list, string.byte(obj._data, i)) end
        elseif obj.__type == "list" or obj.__type == "tuple" or obj.__type == "set" or obj.__type == "frozenset" then
            iter_list = obj.elements
        elseif obj.__type == "dict" then
            for _, p in ipairs(obj.pairs) do table.insert(iter_list, p.key) end
        elseif obj.__type == "range" then
            local curr, stop, step = obj.start, obj.stop, obj.step
            if step > 0 then while curr < stop do table.insert(iter_list, curr); curr = curr + step end
            else while curr > stop do table.insert(iter_list, curr); curr = curr + step end end
        else Runtime.raise("TypeError", "'" .. get_type_str(obj) .. "' object is not iterable") end
    else Runtime.raise("TypeError", "'" .. get_type_str(obj) .. "' object is not iterable") end

    local iter_dict = {
        __iter__ = { __type = "builtin_method", __call = function(self_args) return self_args[1] end },
        __next__ = {
            __type = "builtin_method",
            __call = function(self_args)
                local self = self_args[1]
                self.__dict.idx = self.__dict.idx + 1
                if self.__dict.idx > #self.__dict.elements then Runtime.raise("StopIteration") end
                return self.__dict.elements[self.__dict.idx]
            end
        }
    }
    local inst = PyClass.make_instance(PyClass.make_class("list_iterator", { object_class }, iter_dict))
    inst.__dict.elements = iter_list; inst.__dict.idx = 0
    return inst
end

function Utils.next_impl(args)
    local obj, has_default, default = args[1], (#args >= 2), args[2]
    local success, next_method = safe_pcall(PyClass.get_attr, obj, "__next__")
    if not success or not next_method then
        Runtime.raise("TypeError", "'" .. get_type_str(obj) .. "' object is not an iterator")
    end
    local eval_success, res = safe_pcall(function()
        local Evaluator = require("Module:PythonScript/Evaluator")
        if next_method.__type == "bound_method" then return Evaluator.invoke(next_method.__func, {}, {}, next_method.__self) end
    end)
    if not eval_success then
        if type(res) == "table" and res.__class and res.__class.__name == "StopIteration" then
            if has_default then return default end
        end
        if type(res) == "table" and res.__type == "instance" then error(res, 0) end
        Runtime.raise("RuntimeError", tostring(res))
    end
    return res
end

-- ==========================================
-- 6. 內建例外樹、單例與基礎系統類別初始化
-- ==========================================
function Utils.init_exceptions_and_singletons(B, object_class)
    local exception_dict = {
        __init__ = {
            __type = "builtin_method",
            __call = function(args)
                local self = args[1]
                local call_args = {}
                for i = 2, #args do table.insert(call_args, args[i]) end
                self.__dict.args = { __type = "tuple", elements = call_args }
                self.__dict.msg = (args[2] ~= nil) and to_str(args[2], false) or ""
                if self.__class and self.__class.__name == "StopIteration" then self.__dict.value = args[2]
                elseif self.__class and self.__class.__name == "SystemExit" then
                    self.code = args[2] or 0; self.__dict.code = self.code
                end
                return nil
            end
        },
        __str__ = {
            __type = "builtin_method",
            __call = function(args)
                local self = args[1]
                if self.__dict.msg ~= nil and self.__dict.msg ~= "" then return tostring(self.__dict.msg) end
                local elts = self.__dict.args and self.__dict.args.elements
                if elts and #elts == 1 then return to_str(elts[1], false)
                elseif elts and #elts > 1 then return to_str(self.__dict.args, true) end
                return ""
            end
        },
        __repr__ = {
            __type = "builtin_method",
            __call = function(args)
                local self = args[1]
                local cname = (self.__class and self.__class.__name) or "Exception"
                local elts = self.__dict.args and self.__dict.args.elements
                if elts and #elts > 0 then
                    local parts = {}
                    for _, v in ipairs(elts) do table.insert(parts, to_str(v, true)) end
                    return cname .. "(" .. table.concat(parts, ", ") .. ")"
                elseif self.__dict.msg and self.__dict.msg ~= "" then
                    return cname .. "('" .. tostring(self.__dict.msg) .. "')"
                end
                return cname .. "()"
            end
        }
    }

    local function make_exc_class(name, parent)
        local dict = {}
        for k, v in pairs(exception_dict) do dict[k] = v end
        local cls = PyClass.make_class(name, { parent or object_class }, dict)
        B[name] = cls
        return cls
    end

    local BaseException = make_exc_class("BaseException", object_class)
    local Exception = make_exc_class("Exception", BaseException)
    make_exc_class("SystemExit", BaseException)
    make_exc_class("GeneratorExit", BaseException)
    make_exc_class("KeyboardInterrupt", BaseException)

    local ArithmeticError = make_exc_class("ArithmeticError", Exception)
    make_exc_class("ZeroDivisionError", ArithmeticError)
    make_exc_class("OverflowError", ArithmeticError)
    make_exc_class("FloatingPointError", ArithmeticError)

    local LookupError = make_exc_class("LookupError", Exception)
    make_exc_class("IndexError", LookupError)
    make_exc_class("KeyError", LookupError)

    local RuntimeErrorCls = make_exc_class("RuntimeError", Exception)
    make_exc_class("NotImplementedError", RuntimeErrorCls)
    make_exc_class("RecursionError", RuntimeErrorCls)

    local SyntaxErrorCls = make_exc_class("SyntaxError", Exception)
    make_exc_class("IndentationError", SyntaxErrorCls)

    local OSErrorCls = make_exc_class("OSError", Exception)
    B.IOError = OSErrorCls
    make_exc_class("FileNotFoundError", OSErrorCls)
    make_exc_class("FileExistsError", OSErrorCls)
    make_exc_class("PermissionError", OSErrorCls)

    local ValueErrorCls = make_exc_class("ValueError", Exception)
    local UnicodeErrorCls = make_exc_class("UnicodeError", ValueErrorCls)
    make_exc_class("UnicodeDecodeError", UnicodeErrorCls)
    make_exc_class("UnicodeEncodeError", UnicodeErrorCls)

    local ImportErrorCls = make_exc_class("ImportError", Exception)
    make_exc_class("ModuleNotFoundError", ImportErrorCls)

    make_exc_class("TypeError", Exception)
    make_exc_class("AttributeError", Exception)
    make_exc_class("NameError", Exception)
    make_exc_class("UnboundLocalError", B.NameError)
    make_exc_class("StopIteration", Exception)
    make_exc_class("StopAsyncIteration", Exception)
    make_exc_class("AssertionError", Exception)
    make_exc_class("EOFError", Exception)
    make_exc_class("MemoryError", Exception)
    make_exc_class("BufferError", Exception)
    make_exc_class("SystemError", Exception)

    local WarningCls = make_exc_class("Warning", Exception)
    make_exc_class("UserWarning", WarningCls)
    make_exc_class("DeprecationWarning", WarningCls)
    make_exc_class("RuntimeWarning", WarningCls)

    -- 特殊單例物件
    B.NoneType = PyClass.make_class("NoneType", { object_class }, {})
    B.None = PyClass.make_instance(B.NoneType)

    local NotImplementedType = PyClass.make_class("NotImplementedType", { object_class }, {
        __call = { __type = "builtin_method", __call = function() return B.NotImplemented end },
        __repr__ = { __type = "builtin_method", __call = function() return "NotImplemented" end }
    })
    NotImplementedType.__dict.__str__ = NotImplementedType.__dict.__repr__
    B.NotImplemented = PyClass.make_instance(NotImplementedType)

    local EllipsisType = PyClass.make_class("ellipsis", { object_class }, {
        __call = { __type = "builtin_method", __call = function() return B.Ellipsis end },
        __repr__ = { __type = "builtin_method", __call = function() return "Ellipsis" end }
    })
    EllipsisType.__dict.__str__ = EllipsisType.__dict.__repr__
    B.Ellipsis = PyClass.make_instance(EllipsisType)

    -- code, slice, super 與元型別定義
    B.code_type = PyClass.make_class("code", { object_class }, {
        __repr__ = {
            __type = "builtin_method",
            __call = function(args)
                local self = args[1]
                local fname = (self.__dict and self.__dict.co_filename) or "<string>"
                return string.format('<code object <module> at %s, file "%s", line 1>', tostring(self):match("0x%w+") or "0x0", fname)
            end
        }
    })
    B.code_type.__dict.__str__ = B.code_type.__dict.__repr__

    local slice_dict = {
        __init__ = {
            __type = "builtin_method",
            __call = function(args)
                local self = args[1]
                if #args == 2 then
                    self.__dict.start = B.None; self.__dict.stop = args[2] == nil and B.None or args[2]; self.__dict.step = B.None
                elseif #args >= 3 then
                    self.__dict.start = args[2] == nil and B.None or args[2]; self.__dict.stop = args[3] == nil and B.None or args[3]; self.__dict.step = args[4] == nil and B.None or args[4]
                else Runtime.raise("TypeError", "slice expected at least 1 argument, got " .. (#args - 1)) end
                return nil
            end
        },
        __repr__ = {
            __type = "builtin_method",
            __call = function(args)
                local self = args[1]
                local function fmt_v(v) return (v == nil or v == B.None) and "None" or to_str(v, true) end
                return "slice(" .. fmt_v(self.__dict.start) .. ", " .. fmt_v(self.__dict.stop) .. ", " .. fmt_v(self.__dict.step) .. ")"
            end
        }
    }
    slice_dict.__str__ = slice_dict.__repr__
    B.slice = PyClass.make_class("slice", { object_class }, slice_dict)

    local super_proxy_dict = {
        __getattr__ = {
            __type = "builtin_method",
            __call = function(args)
                local self, attr_name = args[1], args[2]
                local cls, obj = self.__dict._cls, self.__dict._obj
                local mro
                if type(obj) == "table" and obj.__type == "instance" then
                    mro = obj.__class.__mro
                elseif type(obj) == "table" and obj.__type == "class" then
                    mro = obj.__mro
                    local found_in_cls = false
                    for _, base in ipairs(mro or {}) do if base == cls then found_in_cls = true; break end end
                    if not found_in_cls and obj.__class and obj.__class.__mro then
                        mro = obj.__class.__mro
                    end
                else Runtime.raise("TypeError", "super(type, obj): obj must be an instance or subtype of type") end
                local start_idx = -1
                for i, base in ipairs(mro or {}) do if base == cls then start_idx = i; break end end
                if start_idx == -1 then Runtime.raise("TypeError", "super(type, obj): obj must be an instance or subtype of type") end
                for i = start_idx + 1, #mro do
                    local val = mro[i].__dict[attr_name]
                    if val ~= nil then
                        if type(val) == "table" then
                            if val.__type == "function" or val.__type == "builtin_method" then return { __type = "bound_method", __self = obj, __func = val }
                            elseif val.__type == "classmethod" then return { __type = "bound_method", __self = obj.__type == "class" and obj or obj.__class, __func = val.__func }
                            elseif val.__type == "staticmethod" then return val.__func
                            elseif val.__type == "property" then
                                if val.fget then return require("Module:PythonScript/Evaluator").invoke(val.fget, {obj}, {}) end
                                return val
                            end
                        end
                        return val
                    end
                end
                if type(obj) == "table" and obj.__type == "instance" and obj.__builtin_value ~= nil then
                    local ok_b, bm = safe_pcall(PyClass.get_attr, obj.__builtin_value, attr_name)
                    if ok_b and bm ~= nil then return bm end
                end
                if object_class.__dict[attr_name] then
                    local oval = object_class.__dict[attr_name]
                    if oval.__type == "staticmethod" then return oval.__func end
                    return { __type = "bound_method", __self = obj, __func = oval }
                end
                Runtime.raise("AttributeError", "'super' object has no attribute '" .. attr_name .. "'")
            end
        },
        __repr__ = {
            __type = "builtin_method",
            __call = function(args)
                local self = args[1]
                local cls_name = self.__dict._cls and self.__dict._cls.__name or "unknown"
                local obj_name = self.__dict._obj and (self.__dict._obj.__class and self.__dict._obj.__class.__name or self.__dict._obj.__type) or "unknown"
                return "<super: <class '" .. cls_name .. "'>, <" .. obj_name .. " object>>"
            end
        }
    }
    super_proxy_dict.__str__ = super_proxy_dict.__repr__
    local SuperProxyClass = PyClass.make_class("super_proxy", { object_class }, super_proxy_dict)

    B.super = Utils.wrap_as_class("super", function(args)
        local cls, obj = args and args[1], args and args[2]
        if not cls or not obj then
            local curr_scope = Runtime.Traceback.current_scope
            while curr_scope do
                if curr_scope.__super_cls then
                    cls = cls or curr_scope.__super_cls; obj = obj or curr_scope.__super_self; break
                end
                curr_scope = curr_scope.parent
            end
            if not cls or not obj then Runtime.raise("RuntimeError", "super(): no arguments") end
        end
        local inst = PyClass.make_instance(SuperProxyClass)
        inst.__dict._cls = cls; inst.__dict._obj = obj
        return inst
    end)

    B.builtin_function_or_method = PyClass.make_class("builtin_function_or_method", { object_class }, {})
    B.function_type = PyClass.make_class("function", { object_class }, {})
    B.module_type = PyClass.make_class("module", { object_class }, {})
    B.method_type = PyClass.make_class("method", { object_class }, {})
    B.property_type = PyClass.make_class("property", { object_class }, {})

    -- ==========================================
    -- UnpackedTuple 與 QuaternionImagPart 代理類別
    -- ==========================================
    local function unwrap_ut(v)
        if type(v) == "table" and v._is_unpacked_tuple then return v.__builtin_value end
        return v
    end

    local function ut_bin_op(op_str, is_right)
        return {
            __type = "builtin_method",
            __call = function(args)
                local a, b = unwrap_ut(args[1]), unwrap_ut(args[2])
                if is_right then a, b = b, a end
                local eval_expr = require("Module:PythonScript/Evaluator/Core").getCoreFunction()
                local dummy_scope = require("Module:PythonScript/Evaluator/Utils").create_scope(nil)
                return eval_expr({
                    type = "BinaryOp", op = op_str,
                    left = { type = "DirectValue", value = a },
                    right = { type = "DirectValue", value = b }
                }, dummy_scope)
            end
        }
    end

    local function ut_unary_op(op_str)
        return {
            __type = "builtin_method",
            __call = function(args)
                local a = unwrap_ut(args[1])
                local eval_expr = require("Module:PythonScript/Evaluator/Core").getCoreFunction()
                local dummy_scope = require("Module:PythonScript/Evaluator/Utils").create_scope(nil)
                return eval_expr({
                    type = "UnaryOp", op = op_str,
                    right = { type = "DirectValue", value = a }
                }, dummy_scope)
            end
        }
    end

    local function ut_cmp_op(op_str)
        return {
            __type = "builtin_method",
            __call = function(args)
                local a, b = unwrap_ut(args[1]), unwrap_ut(args[2])
                local EvalUtils = require("Module:PythonScript/Evaluator/Utils")
                if op_str == "==" then return EvalUtils.py_eq(a, b)
                elseif op_str == "!=" then return not EvalUtils.py_eq(a, b)
                elseif op_str == "<" then return EvalUtils.py_cmp(a, b) < 0
                elseif op_str == "<=" then return EvalUtils.py_cmp(a, b) <= 0
                elseif op_str == ">" then return EvalUtils.py_cmp(a, b) > 0
                elseif op_str == ">=" then return EvalUtils.py_cmp(a, b) >= 0 end
            end
        }
    end

    local unpacked_tuple_dict = {
        __new__ = {
            __type = "staticmethod",
            __func = {
                __type = "builtin_method",
                __call = function(args)
                    return PyClass.make_instance(args[1])
                end
            }
        },
        __init__ = {
            __type = "builtin_method",
            __call = function(args)
                local self = args[1]
                local elts = {}
                for i = 2, #args do table.insert(elts, args[i]) end
                self._is_unpacked_tuple = true
                self._elements = elts
                self.__builtin_value = (elts[1] ~= nil) and elts[1] or B.None
                return nil
            end
        },
        __iter__ = {
            __type = "builtin_method",
            __call = function(args)
                return B.iter({ { __type = "tuple", elements = args[1]._elements or {} } })
            end
        },
        __getitem__ = {
            __type = "builtin_method",
            __call = function(args)
                local self, idx = args[1], unwrap_num(args[2])
                local elts = self._elements or {}
                if type(idx) == "number" and math.floor(idx) == idx then
                    if idx < 0 then idx = #elts + idx end
                    if idx < 0 or idx >= #elts then Runtime.raise("IndexError", "UnpackedTuple index out of range") end
                    return elts[idx + 1]
                end
                Runtime.raise("TypeError", "UnpackedTuple indices must be integers")
            end
        },
        __repr__  = { __type = "builtin_method", __call = function(args) return to_str(args[1].__builtin_value, true) end },
        __str__   = { __type = "builtin_method", __call = function(args) return to_str(args[1].__builtin_value, false) end },
        __bool__  = { __type = "builtin_method", __call = function(args) return to_bool(args[1].__builtin_value) end },
        __int__   = { __type = "builtin_method", __call = function(args) return B.int.__call({ args[1].__builtin_value }) end },
        __float__ = { __type = "builtin_method", __call = function(args) return B.float.__call({ args[1].__builtin_value }) end },
        __index__ = { __type = "builtin_method", __call = function(args) return B.int.__call({ args[1].__builtin_value }) end },
        __hash__  = { __type = "builtin_method", __call = function(args) return B.hash({ args[1].__builtin_value }) end },
        __abs__   = { __type = "builtin_method", __call = function(args) return B.abs({ args[1].__builtin_value }) end },
        __round__ = { __type = "builtin_method", __call = function(args) return B.round({ args[1].__builtin_value, args[2] }) end },

        __add__      = ut_bin_op("+", false),  __radd__      = ut_bin_op("+", true),
        __sub__      = ut_bin_op("-", false),  __rsub__      = ut_bin_op("-", true),
        __mul__      = ut_bin_op("*", false),  __rmul__      = ut_bin_op("*", true),
        __truediv__  = ut_bin_op("/", false),  __rtruediv__  = ut_bin_op("/", true),
        __floordiv__ = ut_bin_op("//", false), __rfloordiv__ = ut_bin_op("//", true),
        __mod__      = ut_bin_op("%", false),  __rmod__      = ut_bin_op("%", true),
        __pow__      = ut_bin_op("**", false), __rpow__      = ut_bin_op("**", true),
        __matmul__   = ut_bin_op("@", false),  __rmatmul__   = ut_bin_op("@", true),
        __and__      = ut_bin_op("&", false),  __rand__      = ut_bin_op("&", true),
        __or__       = ut_bin_op("|", false),  __ror__       = ut_bin_op("|", true),
        __xor__      = ut_bin_op("^", false),  __rxor__      = ut_bin_op("^", true),
        __lshift__   = ut_bin_op("<<", false), __rlshift__   = ut_bin_op("<<", true),
        __rshift__   = ut_bin_op(">>", false), __rrshift__   = ut_bin_op(">>", true),

        __neg__    = ut_unary_op("-"),
        __pos__    = ut_unary_op("+"),
        __invert__ = ut_unary_op("~"),

        __eq__ = ut_cmp_op("=="), __ne__ = ut_cmp_op("!="),
        __lt__ = ut_cmp_op("<"),  __le__ = ut_cmp_op("<="),
        __gt__ = ut_cmp_op(">"),  __ge__ = ut_cmp_op(">=")
    }

    PyClass._UnpackedTuple = PyClass.make_class("UnpackedTuple", { object_class }, unpacked_tuple_dict)
    PyClass._QuaternionImagPart = PyClass.make_class("QuaternionImagPart", { PyClass._UnpackedTuple }, {})

    function Utils.make_quaternion_imag(b, c, d)
        local inst = PyClass.make_instance(PyClass._QuaternionImagPart)
        inst._is_unpacked_tuple = true
        inst._elements = { b or 0, c or 0, d or 0 }
        inst.__builtin_value = b or 0
        return inst
    end

    local function extract_mapping_to_table(dict_obj)
        local class_dict = {}
        if type(dict_obj) ~= "table" then
            Runtime.raise("TypeError", "type.__new__() argument 3 must be dict or mapping")
        end
        if dict_obj._class_vars then
            for k, v in pairs(dict_obj._class_vars) do class_dict[k] = v end
        end
        if dict_obj.__type == "dict" then
            for _, p in ipairs(dict_obj.pairs or {}) do
                if type(p.key) == "string" then class_dict[p.key] = p.value end
            end
       elseif dict_obj.__type == "instance" then
            if type(dict_obj.__builtin_value) == "table" and dict_obj.__builtin_value.__type == "dict" then
                for _, p in ipairs(dict_obj.__builtin_value.pairs or {}) do
                    if type(p.key) == "string" then class_dict[p.key] = p.value end
                end
            elseif dict_obj.__dict and type(dict_obj.__dict.data) == "table" and dict_obj.__dict.data.__type == "dict" then
                for _, p in ipairs(dict_obj.__dict.data.pairs or {}) do
                    if type(p.key) == "string" then class_dict[p.key] = p.value end
                end
            else
                local ok_k, keys_m = safe_pcall(PyClass.get_attr, dict_obj, "keys")
                local ok_g, get_m = safe_pcall(PyClass.get_attr, dict_obj, "__getitem__")
                if ok_k and keys_m and ok_g and get_m then
                    local Evaluator = require("Module:PythonScript/Evaluator")
                    local k_iter = get_iterator(Evaluator.invoke(keys_m, {}, {}))
                    while true do
                        local hn, k = k_iter()
                        if not hn then break end
                        if type(k) == "string" then
                            class_dict[k] = Evaluator.invoke(get_m, { k }, {})
                        end
                    end
                end
            end
        else
            Runtime.raise("TypeError", "type.__new__() argument 3 must be dict, not " .. get_type_str(dict_obj))
        end
        return class_dict
    end

    local function create_class_from_type(mcls, name, bases_tup, dict_obj, kwargs)
        if type(name) ~= "string" then Runtime.raise("TypeError", "type() argument 1 must be str") end
        if type(bases_tup) ~= "table" or bases_tup.__type ~= "tuple" then Runtime.raise("TypeError", "type() argument 2 must be tuple") end
        local bases = {}
        for _, b in ipairs(bases_tup.elements) do table.insert(bases, b) end
        local winner_meta = PyClass.resolve_metaclass(mcls, bases)
        local class_dict = extract_mapping_to_table(dict_obj)
        if winner_meta ~= mcls and type(winner_meta) == "table" and winner_meta.__type == "class" then
            local Evaluator = require("Module:PythonScript/Evaluator")
            return Evaluator.invoke(winner_meta, { name, bases_tup, dict_obj }, kwargs or {})
        end
        local cls = PyClass.make_class(name, bases, class_dict)
        if not cls.__dict["__module__"] then
            local scope = Runtime.Traceback.current_scope
            if scope then
                local root = scope; while root.parent do root = root.parent end
                cls.__dict["__module__"] = root.vars["__name__"] or "__main__"
            end
        end
        return PyClass.init_class_hooks(cls, winner_meta, kwargs)
    end

    B.type = Utils.wrap_as_class("type", function(args, kwargs)
        if #args == 3 then
            return create_class_from_type(B.type, args[1], args[2], args[3], kwargs)
        elseif #args == 1 then
            local obj = args[1]; local t = type(obj)
            if t == "number" then return (math.floor(obj) == obj and math.abs(obj) <= SAFE_LIMIT) and B.int or B.float
            elseif t == "string" then return B.str
            elseif t == "boolean" then return B.bool
            elseif t == "nil" then return B.NoneType
            elseif t == "function" then return B.builtin_function_or_method
            elseif t == "table" then
                if obj.__class and obj.__class.__name == "NoneType" then return B.NoneType end
                if obj.__class then return obj.__class end
                if obj.__type == "class" then return B.type end
                local pt = obj.__type
                if pt == "int" then return B.int
                elseif pt == "bytes" then return B.bytes
                elseif pt == "bytearray" then return B.bytearray
                elseif pt == "memoryview" then return B.memoryview
                elseif pt == "list" then return B.list
                elseif pt == "dict" then return B.dict
                elseif pt == "tuple" then return B.tuple
                elseif pt == "set" then return B.set
                elseif pt == "frozenset" then return B.frozenset
                elseif pt == "complex" then return B.complex
                elseif pt == "function" then return B.function_type
                elseif pt == "builtin_method" then return B.builtin_function_or_method
                elseif pt == "bound_method" then return B.method_type
                elseif pt == "property" then return B.property_type
                elseif pt == "LazyNamespace" or pt == "module" then return B.module_type end
            end
            return B.type
        else Runtime.raise("TypeError", "type() takes 1 or 3 arguments") end
    end)

    B.type.__dict["__new__"] = {
        __type = "staticmethod",
        __func = {
            __type = "builtin_method",
            __name = "__new__",
            __call = function(args, kwargs)
                local mcls = args and args[1]
                if type(mcls) ~= "table" or mcls.__type ~= "class" or not PyClass.is_subclass(mcls, B.type) then
                    Runtime.raise("TypeError", "type.__new__(X): X is not a type object (subtype of type)")
                end
                if #args == 2 and mcls == B.type then
                    return B.type.__call({ args[2] })
                end
                if #args ~= 4 then
                    Runtime.raise("TypeError", "type.__new__() takes exactly 3 arguments (" .. (#args - 1) .. " given)")
                end
                return create_class_from_type(mcls, args[2], args[3], args[4], kwargs)
            end
        }
    }
    B.type.__dict["__init__"] = {
        __type = "builtin_method",
        __name = "__init__",
        __call = function() return B.None end
    }
    B.type.__dict["__prepare__"] = {
        __type = "classmethod",
        __func = {
            __type = "builtin_method",
            __name = "__prepare__",
            __call = function() return { __type = "dict", pairs = {} } end
        }
    }
    B.type.__dict["__call__"] = {
        __type = "builtin_method",
        __name = "__call__",
        __call = function(args, kwargs)
            local cls = args and args[1]
            local call_args = {}
            for i = 2, #(args or {}) do table.insert(call_args, args[i]) end
            return require("Module:PythonScript/Evaluator").default_instantiate_class(cls, call_args, kwargs)
        end
    }
    B.type.__dict["__getattribute__"] = object_class.__dict["__getattribute__"]
    B.type.__dict["__setattr__"] = object_class.__dict["__setattr__"]
    B.type.__dict["__delattr__"] = object_class.__dict["__delattr__"]
end

return Utils