跳转到内容

模組:PythonScript/Librarys/Cmath

维基百科,自由的百科全书
-- Module:PythonScript/Librarys/Cmath
-- 複雜(Complex)的數學(Math)庫 
local MathCore = {}
-- 內含
-- 1. math庫
-- 2. cmath庫
local MathCoreMain = require("Module:PythonScript/Runtime/Cmath")
local Runtime = require("Module:PythonScript/Runtime")
local PyClass = require("Module:PythonScript/Class")
local Cmath = MathCoreMain.Cmath
local Matrix = MathCoreMain.Matrix
local BigInt = MathCoreMain.BigInt
local function raise_err(err_type, msg)
    require("Module:PythonScript/Runtime").raise(err_type, msg)
end
local CmathLib = {}

local function is_inf(x) return x == math.huge or x == -math.huge end
local function is_nan(x) return x ~= x end

local function is_decimal_obj(x)
    return type(x) == "table" and x.__type == "instance" and x.__dict and x.__dict._is_decimal == true
end

local function is_fraction_obj(x)
    return type(x) == "table" and x.__type == "instance" and x.__dict and x.__dict._is_fraction == true
end


local function unwrap_num(x)
	if type(x) == "number" then return x end
	if type(x) == "boolean" then return x and 1 or 0 end
	if type(x) == "table" then
		if x.__type == "int" then return x.value or tonumber(BigInt.to_string(x.sign, x.limbs))
		elseif x.__type == "instance" and x.__dict and x.__dict._is_decimal then
			return require("Module:PythonScript/Librarys/Decimal").to_number(x)
		elseif x.__type == "instance" then
			local ok, flt_m = pcall(PyClass.get_attr, x, "__float__")
			if ok and flt_m and flt_m.__type == "bound_method" then
				return require("Module:PythonScript/Evaluator").invoke(flt_m.__func, {}, {}, flt_m.__self)
			end
			if x.__builtin_value ~= nil then return unwrap_num(x.__builtin_value) end
		end
	end
	if type(x) == "string" then return tonumber(x) end
	raise_err("TypeError", "must be real number")
end

-- 判斷是否為 2D 矩陣 (走矩陣泛函 expm, sinm 等)
local function is_2d_matrix(x)
    return type(x) == "table" and (x.__type == "list" or x.__type == "tuple")
        and #x.elements > 0 and type(x.elements[1]) == "table"
        and (x.elements[1].__type == "list" or x.elements[1].__type == "tuple")
end

-- 判斷是否為任意序列 (1D 陣列走逐元素映射)
local function is_any_seq(x)
    return type(x) == "table" and (x.__type == "list" or x.__type == "tuple")
end

-- =========================================================================
-- 無損 BigInt 自動升降級引擎 (閥值:MAX_SAFE_INT = 9007199254740990)
-- =========================================================================
local MAX_SAFE_INT = 9007199254740990
-- 自動探測底層 MathCoreMain.BigInt 的基底 (預設 10^7)
local BI_BASE = tonumber(BigInt.to_string and BigInt.to_string(1, { 0, 1 })) or 10000000

local function is_bigint_obj(x)
	return type(x) == "table" and x.__type == "int"
end

local function ul_trim(limbs)
	local n = #limbs
	while n > 1 and limbs[n] == 0 do
		limbs[n] = nil
		n = n - 1
	end
	if n == 0 then limbs[1] = 0 end
	return limbs
end

local function ul_is_zero(limbs)
	return #limbs == 1 and limbs[1] == 0
end

local function ul_cmp(a, b)
	if #a ~= #b then return (#a < #b) and -1 or 1 end
	for i = #a, 1, -1 do
		if a[i] ~= b[i] then return (a[i] < b[i]) and -1 or 1 end
	end
	return 0
end

local function ul_add(a, b)
	local res = {}
	local carry = 0
	local n = math.max(#a, #b)
	for i = 1, n do
		local s = (a[i] or 0) + (b[i] or 0) + carry
		if s >= BI_BASE then
			res[i] = s - BI_BASE
			carry = 1
		else
			res[i] = s
			carry = 0
		end
	end
	if carry > 0 then res[n + 1] = carry end
	return res
end

-- 假設 a >= b
local function ul_sub(a, b)
	local res = {}
	local borrow = 0
	for i = 1, #a do
		local d = a[i] - (b[i] or 0) - borrow
		if d < 0 then
			d = d + BI_BASE
			borrow = 1
		else
			borrow = 0
		end
		res[i] = d
	end
	return ul_trim(res)
end

local function ul_mul(a, b)
	if ul_is_zero(a) or ul_is_zero(b) then return { 0 } end
	local res = {}
	for i = 1, #a + #b do res[i] = 0 end
	for i = 1, #a do
		local ai = a[i]
		if ai ~= 0 then
			local carry = 0
			for j = 1, #b do
				local idx = i + j - 1
				local prod = ai * b[j] + res[idx] + carry
				res[idx] = prod % BI_BASE
				carry = math.floor(prod / BI_BASE)
			end
			local idx = i + #b
			while carry > 0 do
				local s = (res[idx] or 0) + carry
				res[idx] = s % BI_BASE
				carry = math.floor(s / BI_BASE)
				idx = idx + 1
			end
		end
	end
	return ul_trim(res)
end

-- 無號長整數除法:回傳商 q, 餘數 r (支援任意大數除數)
local function ul_divmod(a, b)
	if ul_is_zero(b) then raise_err("ZeroDivisionError", "integer division or modulo by zero") end
	local cmp = ul_cmp(a, b)
	if cmp < 0 then return { 0 }, { unpack(a) } end
	if cmp == 0 then return { 1 }, { 0 } end

	-- 單一 limb 快速通道
	if #b == 1 then
		local d = b[1]
		local q = {}
		local rem = 0
		for i = #a, 1, -1 do
			local cur = rem * BI_BASE + a[i]
			q[i] = math.floor(cur / d)
			rem = cur % d
		end
		return ul_trim(q), { rem }
	end

	-- 多 limb 二分搜尋長除法
	local q = {}
	local rem = { 0 }
	for i = #a, 1, -1 do
		if not ul_is_zero(rem) then
			table.insert(rem, 1, a[i])
		else
			rem[1] = a[i]
		end
		ul_trim(rem)
		if ul_cmp(rem, b) < 0 then
			q[i] = 0
		else
			local lo, hi = 1, BI_BASE - 1
			local best = 0
			while lo <= hi do
				local mid = math.floor((lo + hi) * 0.5)
				local prod = ul_mul(b, { mid })
				local c = ul_cmp(prod, rem)
				if c <= 0 then
					best = mid
					lo = mid + 1
				else
					hi = mid - 1
				end
			end
			q[i] = best
			if best > 0 then
				rem = ul_sub(rem, ul_mul(b, { best }))
			end
		end
	end
	return ul_trim(q), ul_trim(rem)
end

local function make_bi(sign, limbs)
	limbs = ul_trim(limbs)
	if ul_is_zero(limbs) then sign = 1 end
	return { __type = "int", sign = sign, limbs = limbs }
end

local function bi_add(A, B)
	if A.sign == B.sign then
		return make_bi(A.sign, ul_add(A.limbs, B.limbs))
	end
	local c = ul_cmp(A.limbs, B.limbs)
	if c == 0 then return make_bi(1, { 0 })
	elseif c > 0 then return make_bi(A.sign, ul_sub(A.limbs, B.limbs))
	else return make_bi(B.sign, ul_sub(B.limbs, A.limbs)) end
end

local function bi_sub(A, B)
	return bi_add(A, { __type = "int", sign = -B.sign, limbs = B.limbs })
end

local function bi_mul(A, B)
	return make_bi(A.sign * B.sign, ul_mul(A.limbs, B.limbs))
end

local function bi_divExact(A, B)
	local q, _ = ul_divmod(A.limbs, B.limbs)
	return make_bi(A.sign * B.sign, q)
end

local function bi_mod_abs(A, B)
	local _, r = ul_divmod(A.limbs, B.limbs)
	return make_bi(1, r)
end

local function bi_pow_uint(base_bi, exp_bi)
	local res, _ = make_bi(1, { 1 }), {}
	local b = make_bi(base_bi.sign, { unpack(base_bi.limbs) })
	local e_limbs = { unpack(exp_bi.limbs) }
	while not ul_is_zero(e_limbs) do
		if e_limbs[1] % 2 == 1 then
			res = bi_mul(res, b)
		end
		b = bi_mul(b, b)
		e_limbs, _ = ul_divmod(e_limbs, { 2 })
	end
	return res
end

-- 將精确小整數 (|n| <= 2^53) 轉為 BigInt
local function small_int_to_bi(n)
	if n == 0 then return make_bi(1, { 0 }) end
	local sign = (n < 0) and -1 or 1
	local v = math.abs(n)
	local limbs = {}
	while v > 0 do
		table.insert(limbs, v % BI_BASE)
		v = math.floor(v / BI_BASE)
	end
	return make_bi(sign, limbs)
end

-- 將任意 IEEE 754 整數浮點數 (含 > 2^53 甚至 1e300) 無損轉為 BigInt
local function float_int_to_bi(x)
	if x == 0 then return make_bi(1, { 0 }) end
	local sign = (x < 0) and -1 or 1
	local ax = math.abs(x)
	if ax <= 9007199254740991 then
		return small_int_to_bi(x)
	end
	local m, e = math.frexp(ax)
	local mant = math.ldexp(m, 53) -- 精確落在 [2^52, 2^53 - 1]
	local b = small_int_to_bi(mant)
	local shift = e - 53
	if shift > 0 then
		local p2 = bi_pow_uint(small_int_to_bi(2), small_int_to_bi(shift))
		b = bi_mul(b, p2)
	end
	b.sign = sign
	return b
end

-- 將 BigInt 依閥值 9007199254740990 決定回傳 Lua number 或保持 BigInt
local MAX_SAFE_BI = small_int_to_bi(MAX_SAFE_INT)
local function bi_to_py_or_num(B_obj)
	if ul_cmp(B_obj.limbs, MAX_SAFE_BI.limbs) <= 0 then
		local val = 0
		for i = #B_obj.limbs, 1, -1 do
			val = val * BI_BASE + B_obj.limbs[i]
		end
		return B_obj.sign * val
	end
	-- 💡 關鍵修復:將原始表轉換為字串,並呼叫 Python int 類別重新包裝!
	local s = BigInt.to_string(B_obj.sign, B_obj.limbs)
	return Runtime.builtins.int.__call({ s })
end

-- 判斷輸入是否為整數 (含 BigInt 或誤差 <= 1e-17 的浮點數)
-- 回傳: is_int (boolean), num_val (number 或 nil), bi_val (BigInt)
local function inspect_int_arg(v)
	if is_bigint_obj(v) then
		local norm_bi = make_bi(v.sign or 1, v.limbs or { 0 })
		local maybe_num = bi_to_py_or_num(norm_bi)
		if type(maybe_num) == "number" then
			return true, maybe_num, norm_bi
		else
			return true, (norm_bi.sign * math.huge), norm_bi
		end
	end
	local x = unwrap_num(v)
	if is_nan(x) or is_inf(x) then return false, x, nil end
	local nearest = math.floor(x + 0.5)
	if math.abs(x - nearest) <= 1e-17 then
		return true, nearest, float_int_to_bi(nearest)
	end
	return false, x, nil
end

-- 偵測輸入是否為實數整數 (支援 int, BigInt, float 整數,以及虛部為 0 的 complex 如 3+0j)
local function inspect_complex_int_arg(v)
	if v == nil or v == Runtime.builtins.None or is_any_seq(v) then
		return false, nil, nil
	end
	if is_bigint_obj(v) then
		return inspect_int_arg(v)
	end
	if type(v) == "table" and v.__type == "complex" then
		local r, i, k, jk = Cmath.get_parts(v)
		if math.abs(i) <= 1e-17 and math.abs(k) <= 1e-17 and math.abs(jk) <= 1e-17 then
			return inspect_int_arg(r)
		end
		return false, nil, nil
	end
	local ok, is_i, num_v, bi_v = pcall(inspect_int_arg, v)
	if ok and is_i then return true, num_v, bi_v end
	return false, nil, nil
end

-- 計算正整數組合 C(N_bi, K_num) 並自動處理 BigInt 溢位
local function comb_pos_exact(n_is_huge, n_num, n_bi, k_num)
	if k_num == 0 then return 1 end
	if not n_is_huge then
		if k_num > n_num then return 0 end
		if k_num == n_num then return 1 end
		k_num = math.min(k_num, n_num - k_num)
		if k_num == 0 then return 1 end

		local c = 1
		local is_big = false
		local c_bi = nil

		local function num_gcd(a, b)
			while b ~= 0 do a, b = b, a % b end
			return a
		end

		for i = 1, k_num do
			local num = n_num - i + 1
			local den = i
			if not is_big then
				local g = num_gcd(num, den)
				num = num / g
				den = den / g
				c = c / den -- 必定整除
				if c * num > MAX_SAFE_INT then
					is_big = true
					c_bi = bi_mul(float_int_to_bi(c), float_int_to_bi(num))
				else
					c = c * num
				end
			else
				c_bi = bi_divExact(bi_mul(c_bi, float_int_to_bi(num)), float_int_to_bi(den))
			end
		end
		return is_big and bi_to_py_or_num(c_bi) or c
	else
		-- N 本身已超過 9007199254740990
		local c_bi = make_bi(1, { 1 })
		local one_bi = make_bi(1, { 1 })
		local cur_n = n_bi
		for i = 1, k_num do
			c_bi = bi_divExact(bi_mul(c_bi, cur_n), float_int_to_bi(i))
			cur_n = bi_sub(cur_n, one_bi)
		end
		return bi_to_py_or_num(c_bi)
	end
end

-- Python cmath 標準庫 (支援複數、四元數純量、1D 向量逐元素映射與 2D 矩陣泛函微積分)
function CmathLib.init()
    local get_parts = Cmath.get_parts

    -- 修補內建 abs() 以支援矩陣 Frobenius 範數與四元數模長
    local B = Runtime.builtins
    if B and B.abs and not B._matrix_abs_patched then
        B._matrix_abs_patched = true
        local orig_abs = B.abs
        B.abs = function(args)
            local x = args and args[1]
            if is_any_seq(x) then return Matrix.frobenius_norm(x, false)
            elseif type(x) == "table" and x.__type == "complex" then return Cmath.abs(x) end
            return orig_abs(args)
        end
    end

    local function to_q4(val)
        local a, b, c, d = get_parts(val)
        return { a, b, c, d }
    end

    local function from_q4(q, force_complex)
        local a = Cmath.clean_zero(q[1])
        local b = (math.abs(q[2]) < 1e-12) and 0 or Cmath.clean_zero(q[2])
        local c = (math.abs(q[3]) < 1e-12) and 0 or Cmath.clean_zero(q[3])
        local d = (math.abs(q[4]) < 1e-12) and 0 or Cmath.clean_zero(q[4])
        if not force_complex and b == 0 and c == 0 and d == 0 then return a end
        return Cmath.make_complex(a, b, c, d)
    end

    -- 四元數純量底層算子
    local function q_add(p, q) return { p[1]+q[1], p[2]+q[2], p[3]+q[3], p[4]+q[4] } end
    local function q_sub(p, q) return { p[1]-q[1], p[2]-q[2], p[3]-q[3], p[4]-q[4] } end
    local function q_neg(p)    return { -p[1], -p[2], -p[3], -p[4] } end
    local function q_scale(p, s) return { p[1]*s, p[2]*s, p[3]*s, p[4]*s } end
    local function q_abs_sq(p) return p[1]*p[1] + p[2]*p[2] + p[3]*p[3] + p[4]*p[4] end
    local function q_abs(p)    return math.sqrt(q_abs_sq(p)) end
    local function q_vec_norm(p) return math.sqrt(p[2]*p[2] + p[3]*p[3] + p[4]*p[4]) end

    local function q_mul(p, q)
        local a, b, c, d = p[1], p[2], p[3], p[4]
        local t, x, y, z = q[1], q[2], q[3], q[4]
        return {
            a*t - b*x - c*y - d*z,
            a*x + b*t + c*z - d*y,
            a*y - b*z + c*t + d*x,
            a*z + b*y - c*x + d*t
        }
    end

    local function q_inv(p)
        local n2 = q_abs_sq(p)
        if n2 == 0 then raise_err("ZeroDivisionError", "complex/quaternion division by zero") end
        return { p[1]/n2, -p[2]/n2, -p[3]/n2, -p[4]/n2 }
    end

    local function q_rdiv(p, q) return q_mul(p, q_inv(q)) end

    local function q_vec_unit(p, default_j)
        local vn = q_vec_norm(p)
        if vn == 0 then return default_j and { 0, 1, 0, 0 } or { 0, 0, 0, 0 }, 0 end
        return { 0, p[2]/vn, p[3]/vn, p[4]/vn }, vn
    end

    local function q_arg(p)
        local vn = q_vec_norm(p)
        if p[1] == 0 and vn == 0 then return 0.0 end
        return math.atan2(vn, p[1])
    end

    local function q_exp(p)
        local ea = math.exp(p[1])
        local u, vn = q_vec_unit(p, false)
        if vn == 0 then return { ea, 0, 0, 0 } end
        local cv, sv = ea * math.cos(vn), ea * math.sin(vn)
        return { cv, u[2]*sv, u[3]*sv, u[4]*sv }
    end

    local function q_log(p)
        local mag = q_abs(p)
        if mag == 0 then raise_err("ValueError", "math domain error") end
        local u, vn = q_vec_unit(p, true)
        local theta = math.atan2(vn, p[1])
        return { math.log(mag), u[2]*theta, u[3]*theta, u[4]*theta }
    end

    local function q_sqrt(p)
        local mag = q_abs(p)
        if mag == 0 then return { 0, 0, 0, 0 } end
        local u, vn = q_vec_unit(p, true)
        if vn == 0 and p[1] >= 0 then return { math.sqrt(p[1]), 0, 0, 0 } end
        local wr = math.sqrt(math.max(0, (mag + p[1]) * 0.5))
        local wv = math.sqrt(math.max(0, (mag - p[1]) * 0.5))
        return { wr, u[2]*wv, u[3]*wv, u[4]*wv }
    end

    local function r_sinh(x) if x == 0 then return 0.0 end; local e = math.exp(x); return (e - 1.0/e)*0.5 end
    local function r_cosh(x) if x == 0 then return 1.0 end; local e = math.exp(x); return (e + 1.0/e)*0.5 end

    local function q_sin(p)
        local a = p[1]; local u, vn = q_vec_unit(p, false)
        local sc, cs = math.sin(a) * r_cosh(vn), math.cos(a) * r_sinh(vn)
        return { sc, u[2]*cs, u[3]*cs, u[4]*cs }
    end
    local function q_cos(p)
        local a = p[1]; local u, vn = q_vec_unit(p, false)
        local cc, ss = math.cos(a) * r_cosh(vn), -math.sin(a) * r_sinh(vn)
        return { cc, u[2]*ss, u[3]*ss, u[4]*ss }
    end
    local function q_tan(p) return q_rdiv(q_sin(p), q_cos(p)) end

    local function q_sinh(p)
        local a = p[1]; local u, vn = q_vec_unit(p, false)
        local sc, cs = r_sinh(a) * math.cos(vn), r_cosh(a) * math.sin(vn)
        return { sc, u[2]*cs, u[3]*cs, u[4]*cs }
    end
    local function q_cosh(p)
        local a = p[1]; local u, vn = q_vec_unit(p, false)
        local cc, ss = r_cosh(a) * math.cos(vn), r_sinh(a) * math.sin(vn)
        return { cc, u[2]*ss, u[3]*ss, u[4]*ss }
    end
    local function q_tanh(p) return q_rdiv(q_sinh(p), q_cosh(p)) end

    local function q_asinh(p)
        local p2 = q_mul(p, p)
        return q_log(q_add(p, q_sqrt({ p2[1] + 1, p2[2], p2[3], p2[4] })))
    end
    local function q_acosh(p)
        local sp1 = q_sqrt({ p[1] + 1, p[2], p[3], p[4] })
        local sm1 = q_sqrt({ p[1] - 1, p[2], p[3], p[4] })
        return q_log(q_add(p, q_mul(sp1, sm1)))
    end
    local function q_atanh(p)
        local one_plus_p  = { 1 + p[1],  p[2],  p[3],  p[4] }
        local one_minus_p = { 1 - p[1], -p[2], -p[3], -p[4] }
        return q_scale(q_sub(q_log(one_plus_p), q_log(one_minus_p)), 0.5)
    end
    local function q_asin(p) local u = q_vec_unit(p, true); return q_mul(q_neg(u), q_asinh(q_mul(p, u))) end
    local function q_acos(p) local u = q_vec_unit(p, true); return q_mul(q_neg(u), q_acosh(p)) end
    local function q_atan(p) local u = q_vec_unit(p, true); return q_mul(q_neg(u), q_atanh(q_mul(p, u))) end

    local cmath_api = {}
    cmath_api.pi   = math.pi; cmath_api.e = math.exp(1); cmath_api.tau = math.pi * 2
    cmath_api.inf  = math.huge; cmath_api.nan = 0/0
    cmath_api.infj = Cmath.make_complex(0, math.huge, 0, 0); cmath_api.nanj = Cmath.make_complex(0, 0/0, 0, 0)
    cmath_api.infk = Cmath.make_complex(0, 0, math.huge, 0); cmath_api.nank = Cmath.make_complex(0, 0, 0/0, 0)
    cmath_api.infjk = Cmath.make_complex(0, 0, 0, math.huge); cmath_api.nanjk = Cmath.make_complex(0, 0, 0, 0/0)
    cmath_api.j  = Cmath.make_complex(0, 1, 0, 0)
    cmath_api.k  = Cmath.make_complex(0, 0, 1, 0)
    cmath_api.jk = Cmath.make_complex(0, 0, 0, 1)

    cmath_api.quaternion = function(args)
        return Cmath.make_complex(
            args[1] and unwrap_num(args[1]) or 0,
            args[2] and unwrap_num(args[2]) or 0,
            args[3] and unwrap_num(args[3]) or 0,
            args[4] and unwrap_num(args[4]) or 0
        )
    end
    cmath_api.sgn = function(args)
        if is_any_seq(args[1]) then return Matrix.unary_op(args[1], Cmath.sgn) end
        return Cmath.sgn(args[1])
    end
    cmath_api.arg = function(args)
        if is_any_seq(args[1]) then return Matrix.unary_op(args[1], Cmath.arg) end
        return Cmath.arg(args[1])
    end
    cmath_api.dot       = function(args) return Cmath.dot(args[1], args[2]) end
    cmath_api.cross     = function(args) return Cmath.cross(args[1], args[2]) end
    cmath_api.conjugate = function(args) return Cmath.conj(args[1]) end
    cmath_api.scalar    = function(args) return Cmath.scalar(args[1]) end
    cmath_api.vector    = function(args) return Cmath.vector(args[1]) end

    cmath_api.phase = function(args)
        local function scalar_phase(z)
            local a, b, k_val, d_val = get_parts(z)
            if k_val == 0 and d_val == 0 then return math.atan2(b, a) end
            return q_arg({ a, b, k_val, d_val })
        end
        if is_any_seq(args[1]) then return Matrix.unary_op(args[1], scalar_phase) end
        return scalar_phase(args[1])
    end
    cmath_api.polar = function(args)
        local q = to_q4(args[1])
        local ph = (q[3] == 0 and q[4] == 0) and math.atan2(q[2], q[1]) or q_arg(q)
        return { __type = "tuple", elements = { q_abs(q), ph } }
    end
    cmath_api.rect = function(args)
        local mag, ph = unwrap_num(args[1]), unwrap_num(args[2])
        return Cmath.make_complex(mag * math.cos(ph), mag * math.sin(ph), 0, 0)
    end

    -- 泛函與逐元素自動分流器 (2D 走矩陣泛函,1D 走逐元素,純量走四元數算子)
    local function make_unary_dispatcher(mat_fn, q_fn)
        local function scalar_op(x) return from_q4(q_fn(to_q4(x)), true) end
        return function(args)
            local a1 = args[1]
            if is_2d_matrix(a1) then return mat_fn(a1, false)
            elseif is_any_seq(a1) then return Matrix.unary_op(a1, scalar_op) end
            return scalar_op(a1)
        end
    end

	cmath_api.gamma = function(args)
        local function scalar_cgamma(v)
            local r, i = Cmath.extract_complex_2d(v, "cmath.gamma")
            local gr, gi = Cmath.c2_gamma(r, i)
            return from_q4({ gr, gi, 0, 0 }, true)
        end
        if is_any_seq(args[1]) then return Matrix.unary_op(args[1], scalar_cgamma) end
        return scalar_cgamma(args[1])
    end

    cmath_api.lgamma = function(args)
        local function scalar_clgamma(v)
            local r, i = Cmath.extract_complex_2d(v, "cmath.lgamma")
            local lgr, lgi = Cmath.c2_lgamma(r, i)
            return from_q4({ lgr, lgi, 0, 0 }, true)
        end
        if is_any_seq(args[1]) then return Matrix.unary_op(args[1], scalar_clgamma) end
        return scalar_clgamma(args[1])
    end

	cmath_api.factorial = function(args)
        local py_int = Runtime.builtins.int
        local Evaluator = require("Module:PythonScript/Evaluator")
        
        local function scalar_cfact(v)
            local r, i, k, jk = get_parts(v)
            
            -- 🛡️ 拒絕四元數不交換律的複雜操作
            if (k and k ~= 0) or (jk and jk ~= 0) then
                raise_err("NotImplementedError", "cmath.factorial() does not support Quaternions")
            end
            
            local nearest_r = math.floor(r + 0.5)
            
            -- 判斷是否為純實數整數 (虛部趨近於 0,且實部與整數誤差小於 1e-17)
            if math.abs(i) <= 1e-17 and math.abs(r - nearest_r) <= 1e-17 then
                if nearest_r < 0 then
                    raise_err("ValueError", "factorial() not defined for negative integers")
                end
                
                local res = 1
                local is_big = false
                for idx = 1, nearest_r do
                    if not is_big and res * idx > 9007199254740990 then
                        res = py_int.__call({ tostring(math.floor(res)) })
                        is_big = true
                    end
                    
                    if is_big then
                        local i_obj = py_int.__call({ tostring(idx) })
                        local mul_m = PyClass.get_attr(res, "__mul__")
                        res = Evaluator.invoke(mul_m.__func, {i_obj}, {}, mul_m.__self)
                    else
                        res = res * idx
                    end
                end
                return res
            else
                -- 複數或非整數,使用高精度複變 Gamma 函數計算
                return cmath_api.gamma({ Cmath.make_complex(r + 1.0, i, 0, 0) })
            end
        end
        
        -- 同樣支援陣列廣播
        if is_any_seq(args[1]) then return Matrix.unary_op(args[1], scalar_cfact) end
        return scalar_cfact(args[1])
    end

    cmath_api.pow = function(args)
        local a1, a2, a3 = args[1], args[2], args[3]
        local has_mod = (a3 ~= nil and a3 ~= Runtime.builtins.None)
        if not is_any_seq(a1) and not is_any_seq(a2) then
            local ok1, x_num, x_bi = inspect_complex_int_arg(a1)
            local ok2, y_num, y_bi = inspect_complex_int_arg(a2)
            if has_mod then
                local ok3, _, z_bi = inspect_complex_int_arg(a3)
                if not (ok1 and ok2 and ok3) then
                    raise_err("TypeError", "pow() 3rd argument not allowed unless all arguments are integers")
                end
                return BigInt.powmod(x_bi, y_bi, z_bi)
            end
            if ok1 and ok2 then
                if ul_is_zero(y_bi.limbs) then return 1 end
                if y_bi.sign > 0 then
                    local need_big = (math.abs(x_num) > MAX_SAFE_INT) or (y_num > MAX_SAFE_INT)
                    if not need_big then
                        local est = math.abs(x_num) ^ y_num
                        if est > MAX_SAFE_INT or is_inf(est) then need_big = true end
                    end
                    if need_big then
                        return bi_to_py_or_num(bi_pow_uint(x_bi, y_bi))
                    end
                    return x_num ^ y_num
                end
            end
        elseif has_mod then
            raise_err("TypeError", "pow() 3rd argument not allowed unless all arguments are integers")
        end
        return Cmath.pow(a1, a2)
    end
	
    cmath_api.exp   = make_unary_dispatcher(Matrix.expm,   q_exp)
    cmath_api.sqrt  = make_unary_dispatcher(Matrix.sqrtm,  q_sqrt)
    cmath_api.sin   = make_unary_dispatcher(Matrix.sinm,   q_sin)
    cmath_api.cos   = make_unary_dispatcher(Matrix.cosm,   q_cos)
    cmath_api.tan   = make_unary_dispatcher(Matrix.tanm,   q_tan)
    cmath_api.sinh  = make_unary_dispatcher(Matrix.sinhm,  q_sinh)
    cmath_api.cosh  = make_unary_dispatcher(Matrix.coshm,  q_cosh)
    cmath_api.tanh  = make_unary_dispatcher(Matrix.tanhm,  q_tanh)
    cmath_api.asin  = make_unary_dispatcher(Matrix.asinm,  q_asin)
    cmath_api.acos  = make_unary_dispatcher(Matrix.acosm,  q_acos)
    cmath_api.atan  = make_unary_dispatcher(Matrix.atanm,  q_atan)
    cmath_api.asinh = make_unary_dispatcher(Matrix.asinhm, q_asinh)
    cmath_api.acosh = make_unary_dispatcher(Matrix.acoshm, q_acosh)
    cmath_api.atanh = make_unary_dispatcher(Matrix.atanhm, q_atanh)

    cmath_api.log = function(args)
        local z, base = args[1], args[2]
        if is_2d_matrix(z) then return Matrix.logm(z, base, false) end
        local function scalar_log(v)
            local lz = q_log(to_q4(v))
            if base ~= nil and not is_2d_matrix(base) then
                return from_q4(q_rdiv(lz, q_log(to_q4(base))), true)
            end
            return from_q4(lz, true)
        end
        if is_any_seq(z) then return Matrix.unary_op(z, scalar_log) end
        local lz = q_log(to_q4(z))
        if is_2d_matrix(base) then return Matrix.rdiv(from_q4(lz, true), Matrix.logm(base, nil, false)) end
        return scalar_log(z)
    end

    cmath_api.log10 = function(args)
        local a1 = args[1]
        if is_2d_matrix(a1) then return Matrix.logm(a1, 10, false) end
        local function scalar_log10(v) return from_q4(q_scale(q_log(to_q4(v)), 1 / math.log(10)), true) end
        if is_any_seq(a1) then return Matrix.unary_op(a1, scalar_log10) end
        return scalar_log10(a1)
    end

    cmath_api.isfinite = function(args)
        local function fn_fin(a)
            local r, i, k_val, d_val = get_parts(a)
            return not (is_inf(r) or is_inf(i) or is_inf(k_val) or is_inf(d_val) or is_nan(r) or is_nan(i) or is_nan(k_val) or is_nan(d_val))
        end
        if is_any_seq(args[1]) then return Matrix.unary_op(args[1], fn_fin) end
        return fn_fin(args[1])
    end
    cmath_api.isinf = function(args)
        local function fn_inf(a)
            local r, i, k_val, d_val = get_parts(a)
            return is_inf(r) or is_inf(i) or is_inf(k_val) or is_inf(d_val)
        end
        if is_any_seq(args[1]) then return Matrix.unary_op(args[1], fn_inf) end
        return fn_inf(args[1])
    end
    cmath_api.isnan = function(args)
        local function fn_nan(a)
            local r, i, k_val, d_val = get_parts(a)
            return is_nan(r) or is_nan(i) or is_nan(k_val) or is_nan(d_val)
        end
        if is_any_seq(args[1]) then return Matrix.unary_op(args[1], fn_nan) end
        return fn_nan(args[1])
    end

    cmath_api.isclose = function(args, kwargs)
        local rel_tol = (kwargs and kwargs.rel_tol) or 1e-09
        local abs_tol = (kwargs and kwargs.abs_tol) or 0.0
        local function check_close(x, y)
            if is_any_seq(x) and is_any_seq(y) then
                if #x.elements ~= #y.elements then return false end
                for i = 1, #x.elements do
                    if not check_close(x.elements[i], y.elements[i]) then return false end
                end
                return true
            end
            local qa, qb = to_q4(x), to_q4(y)
            local diff_mag = q_abs(q_sub(qa, qb))
            local max_mag = math.max(q_abs(qa), q_abs(qb))
            return diff_mag <= math.max(rel_tol * max_mag, abs_tol)
        end
        return check_close(args[1], args[2])
    end

    local inst = PyClass.make_instance(PyClass.make_class("module", {}, {}))
    inst.__dict["__name__"] = "cmath"
    for k, v in pairs(cmath_api) do
        if type(v) == "function" then inst.__dict[k] = { __type = "builtin_method", __call = v } else inst.__dict[k] = v end
    end
    for k, v in pairs(MathCoreMain.linalg) do
        if type(v) == "function" then inst.__dict[k] = { __type = "builtin_method", __call = v } else inst.__dict[k] = v end
    end
    return inst
end

MathCore.CmathLib = CmathLib

-- Python math 標準庫 (支援純量、1D 偽 NumPy 逐元素向量化與 2D 矩陣泛函微積分)
local MathLib = {}

function MathLib.init()
    -- 修補內建 abs()
    local Builtins = Runtime.builtins
    if Builtins and Builtins.abs and not Builtins._matrix_abs_patched then
        Builtins._matrix_abs_patched = true
        local orig_abs = Builtins.abs
        Builtins.abs = function(args)
            local x = args and args[1]
            if is_any_seq(x) then return Matrix.frobenius_norm(x, false)
            elseif type(x) == "table" and x.__type == "complex" then return Cmath.abs(x) end
            return orig_abs(args)
        end
    end

    local math_api = {}
    math_api.pi = math.pi; math_api.e = math.exp(1); math_api.tau = math.pi * 2
    math_api.inf = math.huge; math_api.nan = 0/0

    -- 自動分流器:2D 矩陣 -> 矩陣泛函;1D 陣列 -> 逐元素運算;純量 -> 實數運算
    local function make_real_dispatcher(mat_fn, real_fn)
        return function(args)
            local a1 = args[1]
            if is_2d_matrix(a1) then return mat_fn(a1, true)
            elseif is_any_seq(a1) then return Matrix.unary_op(a1, real_fn) end
            return real_fn(a1)
        end
    end

    math_api.exp   = make_real_dispatcher(Matrix.expm,  function(v) return math.exp(unwrap_num(v)) end)
    math_api.expm1 = make_real_dispatcher(Matrix.expm1, function(v) return math.exp(unwrap_num(v)) - 1 end)
    math_api.log1p = make_real_dispatcher(Matrix.log1p, function(v) return math.log(1 + unwrap_num(v)) end)
    math_api.sqrt  = make_real_dispatcher(Matrix.sqrtm, function(v)
        local x = unwrap_num(v); if x < 0 then raise_err("ValueError", "math domain error") end
        return math.sqrt(x)
    end)
    math_api.cbrt  = make_real_dispatcher(Matrix.cbrtm, function(v)
        local x = unwrap_num(v); return x < 0 and -math.pow(-x, 1/3) or math.pow(x, 1/3)
    end)
	math_api.gamma = function(args)
        local function scalar_gamma(v)
            local x = unwrap_num(v)
            if x == -math.huge then raise_err("ValueError", "math domain error") end
            if x == math.huge then return math.huge end
            local gr, _ = Cmath.c2_gamma(x, 0.0)
            if is_inf(gr) then raise_err("OverflowError", "math range error") end
            return Cmath.clean_zero(gr)
        end
        if is_any_seq(args[1]) then return Matrix.unary_op(args[1], scalar_gamma) end
        return scalar_gamma(args[1])
    end

    math_api.lgamma = function(args)
        local function scalar_lgamma(v)
            local res = Cmath.r_lgamma(unwrap_num(v))
            return Cmath.clean_zero(res)
        end
        if is_any_seq(args[1]) then return Matrix.unary_op(args[1], scalar_lgamma) end
        return scalar_lgamma(args[1])
    end

    math_api.sumprod = function(args)
        local p, q = args[1], args[2]
        if not (is_any_seq(p) and is_any_seq(q)) then
            raise_err("TypeError", "sumprod() arguments must be sequences")
        end
        if #p.elements ~= #q.elements then
            raise_err("ValueError", "sumprod() inputs do not have the same length")
        end
        local total = 0.0
        for i = 1, #p.elements do
            total = total + unwrap_num(p.elements[i]) * unwrap_num(q.elements[i])
        end
        return total
    end

    math_api.pow = function(args)
        local a1, a2, a3 = args[1], args[2], args[3]
        local has_mod = (a3 ~= nil and a3 ~= Runtime.builtins.None)
        if is_any_seq(a1) or is_any_seq(a2) then
            if has_mod then
                raise_err("TypeError", "pow() 3rd argument not allowed unless all arguments are integers")
            end
            return Matrix.pow(a1, a2)
        end
        local ok1, x_num, x_bi = inspect_int_arg(a1)
        local ok2, y_num, y_bi = inspect_int_arg(a2)
        if has_mod then
            local ok3, _, z_bi = inspect_int_arg(a3)
            if not (ok1 and ok2 and ok3) then
                raise_err("TypeError", "pow() 3rd argument not allowed unless all arguments are integers")
            end
            return BigInt.powmod(x_bi, y_bi, z_bi)
        end

        if ok1 and ok2 and y_bi.sign > 0 and not ul_is_zero(y_bi.limbs) then
            local need_big = (math.abs(x_num) > MAX_SAFE_INT) or (y_num > MAX_SAFE_INT)
            if not need_big then
                local est = math.abs(x_num) ^ y_num
                if est > MAX_SAFE_INT or is_inf(est) then need_big = true end
            end
            if need_big then
                return bi_to_py_or_num(bi_pow_uint(x_bi, y_bi))
            end
        elseif ok1 and ok2 and ul_is_zero(y_bi.limbs) then
            return 1
        end
        return unwrap_num(a1) ^ unwrap_num(a2)
    end

    math_api.log = function(args)
        local a1, a2 = args[1], args[2]
        if is_2d_matrix(a1) then return Matrix.logm(a1, a2, true) end
        if is_2d_matrix(a2) then
            local x = unwrap_num(a1); if x <= 0 then raise_err("ValueError", "math domain error") end
            return Matrix.rdiv(math.log(x), Matrix.logm(a2, nil, true))
        end
        local function scalar_log(v)
            if is_decimal_obj(v) or is_decimal_obj(a2) or (type(v) == "table" and v.__type == "int") or (type(a2) == "table" and a2.__type == "int") then
                return require("Module:PythonScript/Librarys/Decimal").math_log(v, a2)
            end
            local x = unwrap_num(v)
            if x <= 0 then raise_err("ValueError", "math domain error") end
            if a2 ~= nil then return math.log(x) / math.log(unwrap_num(a2)) end
            return math.log(x)
        end
        if is_any_seq(a1) then return Matrix.unary_op(a1, scalar_log) end
        return scalar_log(a1)
    end

    math_api.log10 = function(args)
        local a1 = args[1]
        if is_2d_matrix(a1) then return Matrix.logm(a1, 10, true) end
        local function scalar_log10(v)
            if is_decimal_obj(v) or (type(v) == "table" and v.__type == "int") then
                return require("Module:PythonScript/Librarys/Decimal").math_log10(v)
            end
            local x = unwrap_num(v); if x <= 0 then raise_err("ValueError", "math domain error") end
            return math.log10(x)
        end
        if is_any_seq(a1) then return Matrix.unary_op(a1, scalar_log10) end
        return scalar_log10(a1)
    end

    math_api.log2 = function(args)
        local a1 = args[1]
        if is_2d_matrix(a1) then return Matrix.logm(a1, 2, true) end
        local function scalar_log2(v)
            if is_decimal_obj(v) or (type(v) == "table" and v.__type == "int") then
                return require("Module:PythonScript/Librarys/Decimal").math_log2(v)
            end
            local x = unwrap_num(v); if x <= 0 then raise_err("ValueError", "math domain error") end
            return math.log(x) / math.log(2)
        end
        if is_any_seq(a1) then return Matrix.unary_op(a1, scalar_log2) end
        return scalar_log2(a1)
    end

    math_api.sin   = make_real_dispatcher(Matrix.sinm,   function(v) return math.sin(unwrap_num(v)) end)
    math_api.cos   = make_real_dispatcher(Matrix.cosm,   function(v) return math.cos(unwrap_num(v)) end)
    math_api.tan   = make_real_dispatcher(Matrix.tanm,   function(v) return math.tan(unwrap_num(v)) end)
    math_api.sinh  = make_real_dispatcher(Matrix.sinhm,  function(v) local x = unwrap_num(v); return (math.exp(x) - math.exp(-x)) / 2 end)
    math_api.cosh  = make_real_dispatcher(Matrix.coshm,  function(v) local x = unwrap_num(v); return (math.exp(x) + math.exp(-x)) / 2 end)
    math_api.tanh  = make_real_dispatcher(Matrix.tanhm,  function(v) local e = math.exp(2 * unwrap_num(v)); return (e - 1) / (e + 1) end)
    math_api.asin  = make_real_dispatcher(Matrix.asinm,  function(v) return math.asin(unwrap_num(v)) end)
    math_api.acos  = make_real_dispatcher(Matrix.acosm,  function(v) return math.acos(unwrap_num(v)) end)
    math_api.atan  = make_real_dispatcher(Matrix.atanm,  function(v) return math.atan(unwrap_num(v)) end)
    math_api.asinh = make_real_dispatcher(Matrix.asinhm, function(v) local x = unwrap_num(v); return math.log(x + math.sqrt(x*x + 1)) end)
    math_api.acosh = make_real_dispatcher(Matrix.acoshm, function(v) local x = unwrap_num(v); return math.log(x + math.sqrt(x*x - 1)) end)
    math_api.atanh = make_real_dispatcher(Matrix.atanhm, function(v) local x = unwrap_num(v); return 0.5 * math.log((1 + x) / (1 - x)) end)

    -- 純量降維與逐元素映射
    math_api.fabs = function(args)
        if is_any_seq(args[1]) then return Matrix.frobenius_norm(args[1], true) end
        return math.abs(unwrap_num(args[1]))
    end
    math_api.fsum = function(args)
        local sum = 0
        local function accum(obj)
            if is_any_seq(obj) then for _, v in ipairs(obj.elements) do accum(v) end
            else sum = sum + unwrap_num(obj) end
        end
        accum(args[1]); return sum
    end
    math_api.prod = function(args)
        local p = 1
        local function accum(obj)
            if is_any_seq(obj) then for _, v in ipairs(obj.elements) do accum(v) end
            else p = p * unwrap_num(obj) end
        end
        accum(args[1]); return p
    end
    math_api.dist = function(args)
        local p, q = args[1], args[2]
        local diff = Matrix.elementwise_op(p, q, "-")
        return Matrix.frobenius_norm(diff, true)
    end
    math_api.hypot = function(args)
        local sum = 0
        for _, v in ipairs(args) do
            local num = is_any_seq(v) and Matrix.frobenius_norm(v, true) or unwrap_num(v)
            sum = sum + num * num
        end
        return math.sqrt(sum)
    end
	local function wrap_int_output(val)
        if is_bigint_obj(val) then return bi_to_py_or_num(val) end
        if type(val) == "number" and not is_nan(val) and not is_inf(val) and math.abs(val) > MAX_SAFE_INT then
            return float_int_to_bi(val)
        end
        return val
    end
    local function scalar_ceil(x)
        if is_bigint_obj(x) then return bi_to_py_or_num(x) end
        if is_decimal_obj(x) then return wrap_int_output(require("Module:PythonScript/Librarys/Decimal").math_ceil(x)) end
        if is_fraction_obj(x) then return wrap_int_output(require("Module:PythonScript/Librarys/Fractions").math_ceil(x)) end
        return wrap_int_output(math.ceil(unwrap_num(x)))
    end

    local function scalar_floor(x)
        if is_bigint_obj(x) then return bi_to_py_or_num(x) end
        if is_decimal_obj(x) then return wrap_int_output(require("Module:PythonScript/Librarys/Decimal").math_floor(x)) end
        if is_fraction_obj(x) then return wrap_int_output(require("Module:PythonScript/Librarys/Fractions").math_floor(x)) end
        return wrap_int_output(math.floor(unwrap_num(x)))
    end

    local function scalar_trunc(x)
        if is_bigint_obj(x) then return bi_to_py_or_num(x) end
        if is_decimal_obj(x) then return wrap_int_output(require("Module:PythonScript/Librarys/Decimal").math_trunc(x)) end
        if is_fraction_obj(x) then return wrap_int_output(require("Module:PythonScript/Librarys/Fractions").math_trunc(x)) end
        local n = unwrap_num(x)
        return wrap_int_output(n > 0 and math.floor(n) or math.ceil(n))
    end

    local function scalar_isqrt(x)
        if is_decimal_obj(x) or is_bigint_obj(x) then
            return wrap_int_output(require("Module:PythonScript/Librarys/Decimal").math_isqrt(x))
        end
        local n = unwrap_num(x)
        if n < 0 then raise_err("ValueError", "isqrt() argument must be nonnegative") end
        if n > MAX_SAFE_INT then
            local b = float_int_to_bi(math.floor(n))
            return wrap_int_output(require("Module:PythonScript/Librarys/Decimal").math_isqrt(b))
        end
        return math.floor(math.sqrt(n))
    end
    local function scalar_erf(x)
        local n = unwrap_num(x); local sign = n < 0 and -1 or 1; n = math.abs(n)
        local t = 1.0 / (1.0 + 0.3275911 * n)
        local y = 1.0 - (((((1.061405429 * t - 1.453152027) * t) + 1.421413741) * t - 0.284496736) * t + 0.254829592) * t * math.exp(-n * n)
        return sign * y
    end

    math_api.ceil    = function(args) if is_any_seq(args[1]) then return Matrix.unary_op(args[1], scalar_ceil) end; return scalar_ceil(args[1]) end
    math_api.floor   = function(args) if is_any_seq(args[1]) then return Matrix.unary_op(args[1], scalar_floor) end; return scalar_floor(args[1]) end
    math_api.trunc   = function(args) if is_any_seq(args[1]) then return Matrix.unary_op(args[1], scalar_trunc) end; return scalar_trunc(args[1]) end
    math_api.isqrt   = function(args) if is_any_seq(args[1]) then return Matrix.unary_op(args[1], scalar_isqrt) end; return scalar_isqrt(args[1]) end
    math_api.degrees = function(args) if is_any_seq(args[1]) then return Matrix.unary_op(args[1], function(x) return math.deg(unwrap_num(x)) end) end; return math.deg(unwrap_num(args[1])) end
    math_api.radians = function(args) if is_any_seq(args[1]) then return Matrix.unary_op(args[1], function(x) return math.rad(unwrap_num(x)) end) end; return math.rad(unwrap_num(args[1])) end
    math_api.erf     = function(args) if is_any_seq(args[1]) then return Matrix.unary_op(args[1], scalar_erf) end; return scalar_erf(args[1]) end
    math_api.erfc    = function(args) if is_any_seq(args[1]) then return Matrix.unary_op(args[1], function(x) return 1.0 - scalar_erf(x) end) end; return 1.0 - scalar_erf(args[1]) end

    math_api.isinf = function(args)
        local function fn_inf(a) if is_decimal_obj(a) then return a.__dict.special == "Inf" end; local x = unwrap_num(a); return x == math.huge or x == -math.huge end
        if is_any_seq(args[1]) then return Matrix.unary_op(args[1], fn_inf) end
        return fn_inf(args[1])
    end
    math_api.isnan = function(args)
        local function fn_nan(a) if is_decimal_obj(a) then return a.__dict.special == "NaN" or a.__dict.special == "sNaN" end; local x = unwrap_num(a); return x ~= x end
        if is_any_seq(args[1]) then return Matrix.unary_op(args[1], fn_nan) end
        return fn_nan(args[1])
    end
    math_api.isfinite = function(args)
        local function fn_fin(a) if is_decimal_obj(a) then return a.__dict.special == nil end; local x = unwrap_num(a); return x ~= math.huge and x ~= -math.huge and x == x end
        if is_any_seq(args[1]) then return Matrix.unary_op(args[1], fn_fin) end
        return fn_fin(args[1])
    end

    math_api.atan2    = function(args) return math.atan2(unwrap_num(args[1]), unwrap_num(args[2])) end
    math_api.fmod     = function(args) return math.fmod(unwrap_num(args[1]), unwrap_num(args[2])) end
    math_api.frexp    = function(args) local m, e = math.frexp(unwrap_num(args[1])); return {__type="tuple", elements={m, e}} end
    math_api.ldexp    = function(args) return math.ldexp(unwrap_num(args[1]), unwrap_num(args[2])) end
    math_api.modf     = function(args) local f, i = math.modf(unwrap_num(args[1])); return {__type="tuple", elements={f, i}} end
    math_api.copysign = function(args) local x, y = unwrap_num(args[1]), unwrap_num(args[2]); return (y < 0) and -math.abs(x) or math.abs(x) end
    math_api.fma = function(args)
        local ok1, x_num, x_bi = inspect_int_arg(args[1])
        local ok2, y_num, y_bi = inspect_int_arg(args[2])
        local ok3, z_num, z_bi = inspect_int_arg(args[3])

        if ok1 and ok2 and ok3 then
            local prod_est = math.abs(x_num) * math.abs(y_num)
            if prod_est > MAX_SAFE_INT or math.abs(z_num) > MAX_SAFE_INT or (prod_est + math.abs(z_num) > MAX_SAFE_INT) then
                local res_bi = bi_add(bi_mul(x_bi, y_bi), z_bi)
                return bi_to_py_or_num(res_bi)
            end
            return x_num * y_num + z_num
        end
        return unwrap_num(args[1]) * unwrap_num(args[2]) + unwrap_num(args[3])
    end

    math_api.factorial = function(args)
        local function scalar_fact(v)
            local ok_i, nearest, _ = inspect_int_arg(v)
            if ok_i then
                if nearest < 0 then raise_err("ValueError", "factorial() not defined for negative values") end
                local res = 1
                local is_big = false
                local res_bi = nil
                for idx = 2, nearest do
                    if not is_big then
                        if res * idx > MAX_SAFE_INT then
                            is_big = true
                            res_bi = bi_mul(small_int_to_bi(res), small_int_to_bi(idx))
                        else
                            res = res * idx
                        end
                    else
                        res_bi = bi_mul(res_bi, small_int_to_bi(idx))
                    end
                end
                return is_big and bi_to_py_or_num(res_bi) or res
            else
                return math_api.gamma({ unwrap_num(v) + 1.0 })
            end
        end
        if is_any_seq(args[1]) then return Matrix.unary_op(args[1], scalar_fact) end
        return scalar_fact(args[1])
    end
    math_api.exp2 = function(args)
        local function scalar_exp2(v)
            local ok_i, x_num, x_bi = inspect_int_arg(v)
            if ok_i and x_num >= 53 then
                return bi_to_py_or_num(bi_pow_uint(small_int_to_bi(2), x_bi))
            end
            return math.pow(2, unwrap_num(v))
        end
        if is_any_seq(args[1]) then return Matrix.unary_op(args[1], scalar_exp2) end
        return scalar_exp2(args[1])
    end
	math_api.ulp = function(args)
        local function scalar_ulp(v)
            local x = unwrap_num(v)
            if is_nan(x) then return 0/0 end
            if is_inf(x) then return math.huge end
            x = math.abs(x)
            if x == 0 then return 5e-324 end -- IEEE 754 雙精度最小非正規數 (2^-1074)
            
            local _, e = math.frexp(x)
            local ulp_val = math.ldexp(1, e - 53)
            -- 防護:ULP 不可小於最小非正規數
            if ulp_val < 5e-324 then return 5e-324 end
            return ulp_val
        end
        if is_any_seq(args[1]) then return Matrix.unary_op(args[1], scalar_ulp) end
        return scalar_ulp(args[1])
    end
    math_api.nextafter = function(args)
        local x, y = unwrap_num(args[1]), unwrap_num(args[2])
        if is_nan(x) or is_nan(y) then return 0/0 end
        if x == y then return y end
        if x == 0 then return y > 0 and 5e-324 or -5e-324 end
        
        local abs_x = math.abs(x)
        local m, e = math.frexp(abs_x)
        
        -- 判斷是向外擴張 (step up) 還是向 0 縮小 (step down)
        local step_up = (x > 0 and y > x) or (x < 0 and y < x)
        
        -- 如果向 0 縮小且 x 剛好是 2 的冪次 (尾數 m == 0.5),則間距要縮小一半 (e - 54)
        local exp_offset = step_up and 53 or ((m == 0.5 and abs_x > 5e-324) and 54 or 53)
        local diff = math.ldexp(1, e - exp_offset)
        if diff < 5e-324 then diff = 5e-324 end
        
        local res = x + (x > 0 and (step_up and diff or -diff) or (step_up and -diff or diff))
        if is_inf(res) then return x > 0 and math.huge or -math.huge end
        if math.abs(res) < 5e-324 and res ~= 0 then return 0 end
        return res
    end
    math_api.remainder = function(args)
        local x, y = unwrap_num(args[1]), unwrap_num(args[2])
        if is_nan(x) or is_nan(y) then return 0/0 end
        if y == 0 or is_inf(x) then raise_err("ValueError", "math domain error") end
        if is_inf(y) then return x end
        
        local q = x / y
        local f = math.floor(q)
        local c = math.ceil(q)
        local n
        
        -- IEEE 754 捨入到最接近的整數;若剛好在正中間 (x.5),則捨入到偶數
        if c - q == q - f then
            n = (f % 2 == 0) and f or c
        else
            n = math.floor(q + 0.5)
        end
        return x - n * y
    end
	local function bi_gcd_core(A, B)
        local a = make_bi(1, { unpack(A.limbs) })
        local b = make_bi(1, { unpack(B.limbs) })
        while not ul_is_zero(b.limbs) do
            a, b = b, bi_mod_abs(a, b)
        end
        return a
    end
    math_api.gcd = function(args)
        if #args == 0 then return 0 end
        local g_bi = make_bi(1, { 0 })
        for i = 1, #args do
            local ok_i, _, x_bi = inspect_int_arg(args[i])
            if not ok_i then raise_err("TypeError", "gcd() arguments must be integers") end
            g_bi = bi_gcd_core(g_bi, x_bi)
        end
        return bi_to_py_or_num(g_bi)
    end
    math_api.lcm = function(args)
        if #args == 0 then return 1 end
        local l_bi = make_bi(1, { 1 })
        for i = 1, #args do
            local ok_i, _, x_bi = inspect_int_arg(args[i])
            if not ok_i then raise_err("TypeError", "lcm() arguments must be integers") end
            if ul_is_zero(x_bi.limbs) then return 0 end
            local g_bi = bi_gcd_core(l_bi, x_bi)
            local q_bi = bi_divExact(l_bi, g_bi)
            l_bi = bi_mul(q_bi, make_bi(1, x_bi.limbs))
        end
        return bi_to_py_or_num(l_bi)
    end
	local function apply_neg1_power(exp_is_int, exp_num, exp_bi, val)
        if exp_is_int then
            local is_odd = (exp_bi.limbs[1] % 2 == 1)
            if not is_odd then return val end
            if is_bigint_obj(val) then
                return make_bi(-val.sign, val.limbs)
            else
                return -val
            end
        else
            -- 非整數次方:(-1)^exp = cos(pi * exp) + j * sin(pi * exp)
            local phase = math.pi * exp_num
            local sign_c = Cmath.make_complex(Cmath.clean_zero(math.cos(phase)), Cmath.clean_zero(math.sin(phase)), 0, 0)
            return Cmath.mul(sign_c, val)
        end
    end
    -- 安全計算 Gamma(a) / (Gamma(b) * Gamma(c)),自動處理分母極點 (1 / inf = 0) 與大數對數防溢位
    local function gamma_comb_ratio(a, b, c)
        local function is_nonpos_int(z)
            return z <= 0 and math.abs(z - math.floor(z + 0.5)) <= 1e-15
        end
        -- 若分母落在 Gamma 極點 (非正整數) 且分子不在極點,則極限值為 0
        if not is_nonpos_int(a) and (is_nonpos_int(b) or is_nonpos_int(c)) then
            return 0.0
        end
        local ga = Cmath.c2_gamma(a, 0.0)
        local gb = Cmath.c2_gamma(b, 0.0)
        local gc = Cmath.c2_gamma(c, 0.0)
        local denom = gb * gc
        if not is_inf(ga) and not is_inf(denom) and denom ~= 0 then
            return Cmath.clean_zero(ga / denom)
        end
        -- 大數防溢位:改用 lgamma 相減並追蹤正負號
        local function gamma_sign(z)
            if z > 0 then return 1.0 end
            return (math.floor(z) % 2 == 0) and -1.0 or 1.0
        end
        local sgn = gamma_sign(a) * gamma_sign(b) * gamma_sign(c)
        local lg = Cmath.r_lgamma(a) - Cmath.r_lgamma(b) - Cmath.r_lgamma(c)
        return Cmath.clean_zero(sgn * math.exp(lg))
    end
	math_api.comb = function(args)
        local n_ok, n_num, n_bi = inspect_int_arg(args[1])
        local k_ok, k_num, k_bi = inspect_int_arg(args[2])
        local both_int = n_ok and k_ok

        -- Case 1: k == 0 -> 1
        if k_ok and ul_is_zero(k_bi.limbs) then
            return 1
        end

        -- Case 2: (0 <= n < k) or (k < 0 <= n) -> 0
        if (n_num >= 0 and n_num < k_num) or (k_num < 0 and 0 <= n_num) then
            return 0
        end

        -- Case 3: (n < 0) and (k > 0)
        if n_num < 0 and k_num > 0 then
            if both_int then
                -- ((-1)^k) * C(|n| + k - 1, k)
                local abs_n_bi = make_bi(1, n_bi.limbs)
                local top_bi = bi_sub(bi_add(abs_n_bi, k_bi), make_bi(1, { 1 }))
                local top_maybe_num = bi_to_py_or_num(top_bi)
                local c_val = comb_pos_exact(type(top_maybe_num) ~= "number", type(top_maybe_num) == "number" and top_maybe_num or math.huge, top_bi, k_num)
                return apply_neg1_power(true, k_num, k_bi, c_val)
            else
                -- ((-1)^k) * Gamma(|n| + k) / (Gamma(k + 1) * Gamma(|n|))
                local abs_n = math.abs(n_num)
                local g_val = gamma_comb_ratio(abs_n + k_num, k_num + 1.0, abs_n)
                return apply_neg1_power(k_ok, k_num, k_bi, g_val)
            end
        end

        -- Case 4: (n < 0) and (k < 0)
        if n_num < 0 and k_num < 0 then
            if both_int then
                -- ((-1)^(n+k)) * C(|k| - 1, |n| - 1)
                local abs_k_m1_bi = bi_sub(make_bi(1, k_bi.limbs), make_bi(1, { 1 }))
                local abs_n_m1_bi = bi_sub(make_bi(1, n_bi.limbs), make_bi(1, { 1 }))
                if ul_cmp(abs_k_m1_bi.limbs, abs_n_m1_bi.limbs) < 0 then
                    return 0
                end
                local top_maybe_num = bi_to_py_or_num(abs_k_m1_bi)
                local bot_maybe_num = bi_to_py_or_num(abs_n_m1_bi)
                local c_val = comb_pos_exact(type(top_maybe_num) ~= "number", type(top_maybe_num) == "number" and top_maybe_num or math.huge, abs_k_m1_bi, bot_maybe_num)
                local nk_bi = bi_add(n_bi, k_bi)
                return apply_neg1_power(true, n_num + k_num, nk_bi, c_val)
            else
                -- ((-1)^(n+k)) * Gamma(|k|) / (Gamma(|n|) * Gamma(|k| - |n| + 1))
                local abs_k, abs_n = math.abs(k_num), math.abs(n_num)
                local g_val = gamma_comb_ratio(abs_k, abs_n, abs_k - abs_n + 1.0)
                local nk_ok, nk_num, nk_bi = inspect_int_arg(n_num + k_num)
                return apply_neg1_power(nk_ok, nk_num, nk_bi, g_val)
            end
        end

        -- Case 5: n 或 k 有一項不是整數 (且 n >= k > 0)
        if not both_int then
            return gamma_comb_ratio(n_num + 1.0, k_num + 1.0, n_num - k_num + 1.0)
        end

        -- Case 6: n 與 k 皆為正整數且 n >= k > 0
        return comb_pos_exact(math.abs(n_num) > MAX_SAFE_INT, n_num, n_bi, k_num)
    end
    math_api.perm = function(args)
        local n_ok, n_num, n_bi = inspect_int_arg(args[1])
        local k_arg = (args[2] ~= nil and args[2] ~= Runtime.builtins.None) and args[2] or args[1]
        local k_ok, k_num, _ = inspect_int_arg(k_arg)

        if n_ok and k_ok then
            if n_num < 0 or k_num < 0 then raise_err("ValueError", "perm() not defined for negative values") end
            if k_num > n_num then return 0 end
            if k_num == 0 then return 1 end

            if math.abs(n_num) <= MAX_SAFE_INT then
                local p = 1
                local is_big = false
                local p_bi = nil
                for i = 1, k_num do
                    local factor = n_num - i + 1
                    if not is_big then
                        if p * factor > MAX_SAFE_INT then
                            is_big = true
                            p_bi = bi_mul(float_int_to_bi(p), float_int_to_bi(factor))
                        else
                            p = p * factor
                        end
                    else
                        p_bi = bi_mul(p_bi, float_int_to_bi(factor))
                    end
                end
                return is_big and bi_to_py_or_num(p_bi) or p
            else
                local p_bi = make_bi(1, { 1 })
                local one_bi = make_bi(1, { 1 })
                local cur_n = n_bi
                for _ = 1, k_num do
                    p_bi = bi_mul(p_bi, cur_n)
                    cur_n = bi_sub(cur_n, one_bi)
                end
                return bi_to_py_or_num(p_bi)
            end
        end
        -- 非整數時使用 Gamma(n + 1) / Gamma(n - k + 1)
        return gamma_comb_ratio(n_num + 1.0, 1.0, n_num - k_num + 1.0)
    end

    math_api.isclose = function(args, kwargs)
        local rel_tol = (kwargs and kwargs.rel_tol) or 1e-09; local abs_tol = (kwargs and kwargs.abs_tol) or 0.0
        local function check_close(x, y)
            if is_any_seq(x) and is_any_seq(y) then
                if #x.elements ~= #y.elements then return false end
                for i = 1, #x.elements do
                    if not check_close(x.elements[i], y.elements[i]) then return false end
                end
                return true
            end
            local a, b = unwrap_num(x), unwrap_num(y)
            return math.abs(a - b) <= math.max(rel_tol * math.max(math.abs(a), math.abs(b)), abs_tol)
        end
        return check_close(args[1], args[2])
    end

    local inst = PyClass.make_instance(PyClass.make_class("module", {}, {}))
    inst.__dict["__name__"] = "math"
    for k, v in pairs(math_api) do
        if type(v) == "function" then inst.__dict[k] = { __type = "builtin_method", __call = v } else inst.__dict[k] = v end
    end
    for k, v in pairs(MathCoreMain.linalg) do
        if type(v) == "function" then inst.__dict[k] = { __type = "builtin_method", __call = v } else inst.__dict[k] = v end
    end

    return inst
end

MathCore.MathLib = MathLib

return MathCore