模組:沙盒/a2569875/PythonScript/Librarys/StdAlgorithms
外观
--Module:PythonScript/Librarys/StdAlgorithms
-- 1. base64
-- 2. fractions
-- 3. graphlib
-- 4. hash
-- 塞不下了。
local StdAlgorithms = {}
-- ==========================================
-- 1. base64 演算法
-- ==========================================
function StdAlgorithms.base64()
local b64_helper = {}
-- ==========================================
-- 1. 字元集定義與反向索引快取
-- ==========================================
b64_helper.chars = {
b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
b64url = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
b32 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567",
b32hex = "0123456789ABCDEFGHIJKLMNOPQRSTUV",
b16 = "0123456789ABCDEF",
a85 = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstu",
b85 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~",
z85 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-:+=^!/*?&<>()[]{}@%$#"
}
b64_helper.inv_maps = {}
-- 延遲建立字元集的 O(1) 反向查找表 (byte -> 0-based index)
function b64_helper.get_inv_map(key, alphabet)
local map = b64_helper.inv_maps[key]
if not map then
map = {}
local alpha = alphabet or b64_helper.chars[key]
for i = 1, #alpha do
map[string.byte(alpha, i)] = i - 1
end
b64_helper.inv_maps[key] = map
end
return map
end
-- ==========================================
-- 2. 共通檢查、型別轉換與例外拋出輔助函式
-- ==========================================
function b64_helper.is_none(v)
return v == nil or (type(v) == "table" and v.__class and v.__class.__name == "NoneType")
end
function b64_helper.ensure_exceptions()
if not b64_helper.ErrorClass then
local PyClass = require("Module:沙盒/a2569875/PythonScript/Class")
local Runtime = require("Module:沙盒/a2569875/PythonScript/Runtime")
b64_helper.ErrorClass = PyClass.make_class("Error", { Runtime.builtins.ValueError }, {})
b64_helper.ErrorClass.__dict["__module__"] = "binascii"
b64_helper.IncompleteClass = PyClass.make_class("Incomplete", { Runtime.builtins.Exception }, {})
b64_helper.IncompleteClass.__dict["__module__"] = "binascii"
end
end
function b64_helper.raise_error(msg)
b64_helper.ensure_exceptions()
require("Module:沙盒/a2569875/PythonScript/Runtime").raise(b64_helper.ErrorClass, msg)
end
function b64_helper.make_bytes(raw_str)
return { __type = "bytes", _data = raw_str or "" }
end
-- 驗證並提取底層二進位字串:
-- allow_ascii = false -> 僅接受 bytes / bytearray / memoryview (供 encode 使用)
-- allow_ascii = true -> 額外接受純 ASCII str (供 decode 使用)
function b64_helper.extract_bytes(obj, allow_ascii)
local Runtime = require("Module:沙盒/a2569875/PythonScript/Runtime")
if type(obj) == "table" and obj.__type == "instance" and obj.__builtin_value ~= nil then
obj = obj.__builtin_value
end
if type(obj) == "table" then
local pt = obj.__type
if pt == "bytes" or pt == "bytearray" then
return obj._data
elseif pt == "memoryview" then
if obj._released then
Runtime.raise("ValueError", "operation forbidden on released memoryview object")
end
return obj._data
end
elseif type(obj) == "string" then
-- 維基特化語法糖:encode 時 (allow_ascii=false) 亦無條件放行字串
-- decode 時 (allow_ascii=true) 則維持原本的純 ASCII 安全檢查
if allow_ascii and obj:find("[\128-\255]") then
Runtime.raise("ValueError", "string argument should contain only ASCII characters")
end
return obj
end
local tname = type(obj)
if tname == "string" then tname = "str"
elseif tname == "boolean" then tname = "bool"
elseif tname == "number" then tname = (math.floor(obj) == obj) and "int" or "float"
elseif tname == "table" then tname = (obj.__class and obj.__class.__name) or obj.__type or "object" end
Runtime.raise("TypeError", "a bytes-like object is required, not '" .. tostring(tname) .. "'")
end
-- ==========================================
-- 3. Base64 核心編解碼 (含 altchars / urlsafe / validate)
-- ==========================================
function b64_helper.raw_b64encode(s, alphabet)
local alpha = alphabet or b64_helper.chars.b64
local out, n = {}, #s
local i = 1
while i <= n - 2 do
local b1, b2, b3 = string.byte(s, i, i + 2)
local v = b1 * 65536 + b2 * 256 + b3
local c1 = math.floor(v / 262144) + 1
local c2 = (math.floor(v / 4096) % 64) + 1
local c3 = (math.floor(v / 64) % 64) + 1
local c4 = (v % 64) + 1
out[#out + 1] = alpha:sub(c1, c1) .. alpha:sub(c2, c2) .. alpha:sub(c3, c3) .. alpha:sub(c4, c4)
i = i + 3
end
local rem = n - i + 1
if rem == 1 then
local b1 = string.byte(s, i)
local c1 = math.floor(b1 / 4) + 1
local c2 = ((b1 % 4) * 16) + 1
out[#out + 1] = alpha:sub(c1, c1) .. alpha:sub(c2, c2) .. "=="
elseif rem == 2 then
local b1, b2 = string.byte(s, i, i + 1)
local v = b1 * 256 + b2
local c1 = math.floor(v / 1024) + 1
local c2 = (math.floor(v / 16) % 64) + 1
local c3 = ((v % 16) * 4) + 1
out[#out + 1] = alpha:sub(c1, c1) .. alpha:sub(c2, c2) .. alpha:sub(c3, c3) .. "="
end
return table.concat(out)
end
function b64_helper.raw_b64decode(s, altchars, validate)
local alpha = b64_helper.chars.b64
if altchars then
if #altchars ~= 2 then
require("Module:沙盒/a2569875/PythonScript/Runtime").raise("ValueError", "altchars must be a length-2 bytes-like object")
end
alpha = alpha:sub(1, 62) .. altchars
end
local inv = (alpha == b64_helper.chars.b64) and b64_helper.get_inv_map("b64")
or (alpha == b64_helper.chars.b64url and b64_helper.get_inv_map("b64url"))
or b64_helper.get_inv_map(alpha, alpha)
local clean = {}
for i = 1, #s do
local b = string.byte(s, i)
if inv[b] ~= nil or b == 61 then -- 61 is '='
clean[#clean + 1] = string.char(b)
elseif validate then
b64_helper.raise_error("Non-base64 digit found")
end
end
local cs = table.concat(clean)
local clen = #cs
if clen == 0 then return "" end
if validate and (clen % 4 ~= 0) then
b64_helper.raise_error("Incorrect padding")
end
-- 剝除尾端 '=' 並檢查有效資料字元數
local data_str = cs:match("^(.-)=*$") or cs
if data_str:find("=", 1, true) then
if validate then b64_helper.raise_error("Discontinuous padding not allowed") end
data_str = data_str:gsub("=", "")
end
local dlen = #data_str
local rem = dlen % 4
if rem == 1 then
b64_helper.raise_error("Invalid base64-encoded string: number of data characters (" .. dlen .. ") cannot be 1 more than a multiple of 4")
end
if rem > 0 and (#cs - dlen) < (4 - rem) and validate then
b64_helper.raise_error("Incorrect padding")
end
local out = {}
local i = 1
while i <= dlen - 3 do
local v1, v2, v3, v4 = inv[string.byte(data_str, i)], inv[string.byte(data_str, i+1)], inv[string.byte(data_str, i+2)], inv[string.byte(data_str, i+3)]
local n = v1 * 262144 + v2 * 4096 + v3 * 64 + v4
out[#out + 1] = string.char(math.floor(n / 65536), math.floor(n / 256) % 256, n % 256)
i = i + 4
end
if rem == 2 then
local v1, v2 = inv[string.byte(data_str, i)], inv[string.byte(data_str, i+1)]
local n = v1 * 64 + v2
out[#out + 1] = string.char(math.floor(n / 16))
elseif rem == 3 then
local v1, v2, v3 = inv[string.byte(data_str, i)], inv[string.byte(data_str, i+1)], inv[string.byte(data_str, i+2)]
local n = v1 * 4096 + v2 * 64 + v3
out[#out + 1] = string.char(math.floor(n / 1024), math.floor(n / 4) % 256)
end
return table.concat(out)
end
-- ==========================================
-- 4. Base32 / Base32hex 核心編解碼
-- ==========================================
function b64_helper.raw_b32encode(s, is_hex)
local alpha = is_hex and b64_helper.chars.b32hex or b64_helper.chars.b32
local out, n = {}, #s
local i = 1
while i <= n do
local rem = n - i + 1
local b1 = string.byte(s, i) or 0
local b2 = (rem >= 2) and string.byte(s, i + 1) or 0
local b3 = (rem >= 3) and string.byte(s, i + 2) or 0
local b4 = (rem >= 4) and string.byte(s, i + 3) or 0
local b5 = (rem >= 5) and string.byte(s, i + 4) or 0
-- 40-bit 精確整數運算
local v = (((b1 * 256 + b2) * 256 + b3) * 256 + b4) * 256 + b5
local c = {}
for k = 8, 1, -1 do
local idx = (v % 32) + 1
c[k] = alpha:sub(idx, idx)
v = math.floor(v / 32)
end
if rem == 1 then
out[#out + 1] = c[1] .. c[2] .. "======"
elseif rem == 2 then
out[#out + 1] = c[1] .. c[2] .. c[3] .. c[4] .. "===="
elseif rem == 3 then
out[#out + 1] = c[1] .. c[2] .. c[3] .. c[4] .. c[5] .. "==="
elseif rem == 4 then
out[#out + 1] = c[1] .. c[2] .. c[3] .. c[4] .. c[5] .. c[6] .. c[7] .. "="
else
out[#out + 1] = table.concat(c)
end
i = i + 5
end
return table.concat(out)
end
function b64_helper.raw_b32decode(s, is_hex, casefold, map01)
if (#s % 8) ~= 0 then
b64_helper.raise_error("Incorrect padding")
end
if casefold then s = s:upper() end
if not is_hex and map01 then
if #map01 ~= 1 then
require("Module:沙盒/a2569875/PythonScript/Runtime").raise("ValueError", "map01 must be a 1-character string or bytes")
end
local repl1 = casefold and map01:upper() or map01
s = s:gsub("0", "O"):gsub("1", repl1)
end
local inv = b64_helper.get_inv_map(is_hex and "b32hex" or "b32")
local n = #s
if n == 0 then return "" end
local out = {}
for i = 1, n, 8 do
local chunk = s:sub(i, i + 7)
local pad_start = chunk:find("=", 1, true)
local pad_len = 0
if pad_start then
if i + 7 < n or not chunk:sub(pad_start):match("^=+$") then
b64_helper.raise_error("Incorrect padding")
end
pad_len = 8 - pad_start + 1
if pad_len ~= 1 and pad_len ~= 3 and pad_len ~= 4 and pad_len ~= 6 then
b64_helper.raise_error("Incorrect padding")
end
end
local v = 0
for k = 1, 8 do
if pad_start and k >= pad_start then
v = v * 32
else
local d = inv[string.byte(chunk, k)]
if d == nil then b64_helper.raise_error("Non-base32 digit found") end
v = v * 32 + d
end
end
local bytes = {}
for k = 5, 1, -1 do
bytes[k] = string.char(v % 256)
v = math.floor(v / 256)
end
local keep = (pad_len == 0 and 5) or (pad_len == 1 and 4) or (pad_len == 3 and 3) or (pad_len == 4 and 2) or 1
out[#out + 1] = table.concat(bytes, "", 1, keep)
end
return table.concat(out)
end
-- ==========================================
-- 5. Base16 核心編解碼
-- ==========================================
function b64_helper.raw_b16encode(s)
local alpha = b64_helper.chars.b16
local out = {}
for i = 1, #s do
local b = string.byte(s, i)
local hi = math.floor(b / 16) + 1
local lo = (b % 16) + 1
out[i] = alpha:sub(hi, hi) .. alpha:sub(lo, lo)
end
return table.concat(out)
end
function b64_helper.raw_b16decode(s, casefold)
if (#s % 2) ~= 0 then
b64_helper.raise_error("Odd-length string")
end
if casefold then s = s:upper() end
local inv = b64_helper.get_inv_map("b16")
local out = {}
for i = 1, #s, 2 do
local hi = inv[string.byte(s, i)]
local lo = inv[string.byte(s, i + 1)]
if hi == nil or lo == nil then
b64_helper.raise_error("Non-base16 digit found")
end
out[#out + 1] = string.char(hi * 16 + lo)
end
return table.concat(out)
end
-- ==========================================
-- 6. Ascii85 / Base85 / Z85 核心編解碼
-- ==========================================
-- 將 32-bit 無號整數轉為 5 個 85 進位字元
function b64_helper.u32_to_85(v, alpha)
local c5 = (v % 85) + 1; v = math.floor(v / 85)
local c4 = (v % 85) + 1; v = math.floor(v / 85)
local c3 = (v % 85) + 1; v = math.floor(v / 85)
local c2 = (v % 85) + 1
local c1 = math.floor(v / 85) + 1
return alpha:sub(c1, c1) .. alpha:sub(c2, c2) .. alpha:sub(c3, c3) .. alpha:sub(c4, c4) .. alpha:sub(c5, c5)
end
function b64_helper.raw_a85encode(s, foldspaces, wrapcol, pad, adobe)
local alpha = b64_helper.chars.a85
local out, n = {}, #s
local i = 1
while i <= n - 3 do
local b1, b2, b3, b4 = string.byte(s, i, i + 3)
local v = ((b1 * 256 + b2) * 256 + b3) * 256 + b4
if v == 0 then
out[#out + 1] = "z"
elseif foldspaces and v == 0x20202020 then
out[#out + 1] = "y"
else
out[#out + 1] = b64_helper.u32_to_85(v, alpha)
end
i = i + 4
end
local rem = n - i + 1
if rem > 0 then
local b1 = string.byte(s, i) or 0
local b2 = (rem >= 2) and string.byte(s, i + 1) or 0
local b3 = (rem >= 3) and string.byte(s, i + 2) or 0
local v = ((b1 * 256 + b2) * 256 + b3) * 256
local block = b64_helper.u32_to_85(v, alpha)
out[#out + 1] = pad and block or block:sub(1, rem + 1)
end
local res = table.concat(out)
if adobe then res = "<~" .. res .. "~>" end
if wrapcol and wrapcol > 0 then
local lines = {}
for j = 1, #res, wrapcol do
lines[#lines + 1] = res:sub(j, j + wrapcol - 1)
end
res = table.concat(lines, "\n")
end
return res
end
function b64_helper.raw_a85decode(s, foldspaces, adobe, ignorechars)
local Runtime = require("Module:沙盒/a2569875/PythonScript/Runtime")
if adobe then
if s:sub(-2) ~= "~>" then
Runtime.raise("ValueError", "Ascii85 encoded byte sequence must end with '~>'")
end
if s:sub(1, 2) == "<~" then s = s:sub(3, -3) else s = s:sub(1, -3) end
end
local ign = {}
local ign_str = ignorechars or " \t\n\r\v"
for i = 1, #ign_str do ign[string.byte(ign_str, i)] = true end
local inv = b64_helper.get_inv_map("a85")
local out, group = {}, {}
for i = 1, #s do
local b = string.byte(s, i)
if not ign[b] then
if b == 122 then -- 'z'
if #group > 0 then Runtime.raise("ValueError", "'z' inside Ascii85 5-tuple") end
out[#out + 1] = "\0\0\0\0"
elseif foldspaces and b == 121 then -- 'y'
if #group > 0 then Runtime.raise("ValueError", "'y' inside Ascii85 5-tuple") end
out[#out + 1] = " "
else
local d = inv[b]
if d == nil then
Runtime.raise("ValueError", "Non-Ascii85 digit found: " .. string.char(b))
end
group[#group + 1] = d
if #group == 5 then
local v = (((group[1] * 85 + group[2]) * 85 + group[3]) * 85 + group[4]) * 85 + group[5]
if v > 4294967295 then Runtime.raise("ValueError", "Ascii85 overflow") end
out[#out + 1] = string.char(
math.floor(v / 16777216) % 256,
math.floor(v / 65536) % 256,
math.floor(v / 256) % 256,
v % 256
)
group = {}
end
end
end
end
local rem = #group
if rem == 1 then
Runtime.raise("ValueError", "Final Ascii85 group cannot have length 1")
elseif rem > 1 then
for k = rem + 1, 5 do group[k] = 84 end
local v = (((group[1] * 85 + group[2]) * 85 + group[3]) * 85 + group[4]) * 85 + group[5]
if v > 4294967295 then Runtime.raise("ValueError", "Ascii85 overflow") end
local b4 = string.char(
math.floor(v / 16777216) % 256,
math.floor(v / 65536) % 256,
math.floor(v / 256) % 256,
v % 256
)
out[#out + 1] = b4:sub(1, rem - 1)
end
return table.concat(out)
end
function b64_helper.raw_b85_or_z85_encode(s, mode, pad)
local alpha = b64_helper.chars[mode]
local out, n = {}, #s
local i = 1
while i <= n - 3 do
local b1, b2, b3, b4 = string.byte(s, i, i + 3)
local v = ((b1 * 256 + b2) * 256 + b3) * 256 + b4
out[#out + 1] = b64_helper.u32_to_85(v, alpha)
i = i + 4
end
local rem = n - i + 1
if rem > 0 then
local b1 = string.byte(s, i) or 0
local b2 = (rem >= 2) and string.byte(s, i + 1) or 0
local b3 = (rem >= 3) and string.byte(s, i + 2) or 0
local v = ((b1 * 256 + b2) * 256 + b3) * 256
local block = b64_helper.u32_to_85(v, alpha)
out[#out + 1] = pad and block or block:sub(1, rem + 1)
end
return table.concat(out)
end
function b64_helper.raw_b85_or_z85_decode(s, mode)
local Runtime = require("Module:沙盒/a2569875/PythonScript/Runtime")
local inv = b64_helper.get_inv_map(mode)
local out, n = {}, #s
local rem = n % 5
if rem == 1 then
Runtime.raise("ValueError", "Invalid " .. mode .. " string length: cannot be 1 more than a multiple of 5")
end
local i = 1
while i <= n do
local chunk_len = math.min(5, n - i + 1)
local v = 0
for k = 0, 4 do
local d
if k < chunk_len then
d = inv[string.byte(s, i + k)]
if d == nil then
Runtime.raise("ValueError", "Bad " .. mode .. " character at position " .. (i + k - 1))
end
else
d = 84
end
v = v * 85 + d
end
if v > 4294967295 then
Runtime.raise("ValueError", mode .. " overflow in hunk starting at byte " .. (i - 1))
end
local b4 = string.char(
math.floor(v / 16777216) % 256,
math.floor(v / 65536) % 256,
math.floor(v / 256) % 256,
v % 256
)
out[#out + 1] = (chunk_len == 5) and b4 or b4:sub(1, chunk_len - 1)
i = i + 5
end
return table.concat(out)
end
-- ==========================================
-- 7. Python base64 與 binascii 模組工廠
-- ==========================================
function b64_helper.get_base64_module()
b64_helper.ensure_exceptions()
local PyClass = require("Module:沙盒/a2569875/PythonScript/Class")
local B = require("Module:沙盒/a2569875/PythonScript/Runtime").builtins
local function fn(name, f)
return { __type = "builtin_method", __name = name, __call = f }
end
local dict = {
b64encode = fn("b64encode", function(a, kw)
local s = b64_helper.extract_bytes(a[1], false)
local alt = (a[2] ~= nil and a[2]) or (kw and kw.altchars)
local alpha = b64_helper.chars.b64
if not b64_helper.is_none(alt) then
local alt_s = b64_helper.extract_bytes(alt, false)
if #alt_s ~= 2 then require("Module:沙盒/a2569875/PythonScript/Runtime").raise("ValueError", "altchars must be a length-2 bytes-like object") end
alpha = alpha:sub(1, 62) .. alt_s
end
return b64_helper.make_bytes(b64_helper.raw_b64encode(s, alpha))
end),
b64decode = fn("b64decode", function(a, kw)
local s = b64_helper.extract_bytes(a[1], true)
local alt = (a[2] ~= nil and a[2]) or (kw and kw.altchars)
local val_flag = (a[3] ~= nil and a[3]) or (kw and kw.validate) or false
local alt_s = not b64_helper.is_none(alt) and b64_helper.extract_bytes(alt, true) or nil
return b64_helper.make_bytes(b64_helper.raw_b64decode(s, alt_s, B.bool.__call({val_flag})))
end),
standard_b64encode = fn("standard_b64encode", function(a)
local s = b64_helper.extract_bytes(a[1], false)
return b64_helper.make_bytes(b64_helper.raw_b64encode(s, b64_helper.chars.b64))
end),
standard_b64decode = fn("standard_b64decode", function(a)
local s = b64_helper.extract_bytes(a[1], true)
return b64_helper.make_bytes(b64_helper.raw_b64decode(s, nil, false))
end),
urlsafe_b64encode = fn("urlsafe_b64encode", function(a)
local s = b64_helper.extract_bytes(a[1], false)
return b64_helper.make_bytes(b64_helper.raw_b64encode(s, b64_helper.chars.b64url))
end),
urlsafe_b64decode = fn("urlsafe_b64decode", function(a)
local s = b64_helper.extract_bytes(a[1], true)
return b64_helper.make_bytes(b64_helper.raw_b64decode(s, "-_", false))
end),
b32encode = fn("b32encode", function(a)
local s = b64_helper.extract_bytes(a[1], false)
return b64_helper.make_bytes(b64_helper.raw_b32encode(s, false))
end),
b32decode = fn("b32decode", function(a, kw)
local s = b64_helper.extract_bytes(a[1], true)
local cf = (a[2] ~= nil and a[2]) or (kw and kw.casefold) or false
local m01 = (a[3] ~= nil and a[3]) or (kw and kw.map01)
local m01_s = not b64_helper.is_none(m01) and b64_helper.extract_bytes(m01, true) or nil
return b64_helper.make_bytes(b64_helper.raw_b32decode(s, false, B.bool.__call({cf}), m01_s))
end),
b32hexencode = fn("b32hexencode", function(a)
local s = b64_helper.extract_bytes(a[1], false)
return b64_helper.make_bytes(b64_helper.raw_b32encode(s, true))
end),
b32hexdecode = fn("b32hexdecode", function(a, kw)
local s = b64_helper.extract_bytes(a[1], true)
local cf = (a[2] ~= nil and a[2]) or (kw and kw.casefold) or false
return b64_helper.make_bytes(b64_helper.raw_b32decode(s, true, B.bool.__call({cf}), nil))
end),
b16encode = fn("b16encode", function(a)
local s = b64_helper.extract_bytes(a[1], false)
return b64_helper.make_bytes(b64_helper.raw_b16encode(s))
end),
b16decode = fn("b16decode", function(a, kw)
local s = b64_helper.extract_bytes(a[1], true)
local cf = (a[2] ~= nil and a[2]) or (kw and kw.casefold) or false
return b64_helper.make_bytes(b64_helper.raw_b16decode(s, B.bool.__call({cf})))
end),
a85encode = fn("a85encode", function(a, kw)
local s = b64_helper.extract_bytes(a[1], false)
kw = kw or {}
local fs = B.bool.__call({kw.foldspaces or false})
local wc = tonumber(kw.wrapcol) or 0
local pad = B.bool.__call({kw.pad or false})
local adobe = B.bool.__call({kw.adobe or false})
return b64_helper.make_bytes(b64_helper.raw_a85encode(s, fs, wc, pad, adobe))
end),
a85decode = fn("a85decode", function(a, kw)
local s = b64_helper.extract_bytes(a[1], true)
kw = kw or {}
local fs = B.bool.__call({kw.foldspaces or false})
local adobe = B.bool.__call({kw.adobe or false})
local ign = not b64_helper.is_none(kw.ignorechars) and b64_helper.extract_bytes(kw.ignorechars, false) or " \t\n\r\v"
return b64_helper.make_bytes(b64_helper.raw_a85decode(s, fs, adobe, ign))
end),
b85encode = fn("b85encode", function(a, kw)
local s = b64_helper.extract_bytes(a[1], false)
local pad = (a[2] ~= nil and a[2]) or (kw and kw.pad) or false
return b64_helper.make_bytes(b64_helper.raw_b85_or_z85_encode(s, "b85", B.bool.__call({pad})))
end),
b85decode = fn("b85decode", function(a)
local s = b64_helper.extract_bytes(a[1], true)
return b64_helper.make_bytes(b64_helper.raw_b85_or_z85_decode(s, "b85"))
end),
z85encode = fn("z85encode", function(a)
local s = b64_helper.extract_bytes(a[1], false)
return b64_helper.make_bytes(b64_helper.raw_b85_or_z85_encode(s, "z85", false))
end),
z85decode = fn("z85decode", function(a)
local s = b64_helper.extract_bytes(a[1], true)
return b64_helper.make_bytes(b64_helper.raw_b85_or_z85_decode(s, "z85"))
end),
encodebytes = fn("encodebytes", function(a)
local s = b64_helper.extract_bytes(a[1], false)
if #s == 0 then return b64_helper.make_bytes("") end
local chunks = {}
for i = 1, #s, 57 do
chunks[#chunks + 1] = b64_helper.raw_b64encode(s:sub(i, i + 56), b64_helper.chars.b64) .. "\n"
end
return b64_helper.make_bytes(table.concat(chunks))
end),
decodebytes = fn("decodebytes", function(a)
local s = b64_helper.extract_bytes(a[1], false)
return b64_helper.make_bytes(b64_helper.raw_b64decode(s, nil, false))
end)
}
local mod = PyClass.make_instance(PyClass.make_class("module", {}, {}))
for k, v in pairs(dict) do mod.__dict[k] = v end
mod.__dict["__name__"] = "base64"
return mod
end
function b64_helper.get_binascii_module()
b64_helper.ensure_exceptions()
local PyClass = require("Module:沙盒/a2569875/PythonScript/Class")
local B = require("Module:沙盒/a2569875/PythonScript/Runtime").builtins
local bit32 = require("bit32")
local function fn(name, f)
return { __type = "builtin_method", __name = name, __call = f }
end
local hexlify_fn = fn("hexlify", function(a, kw)
local s = b64_helper.extract_bytes(a[1], false)
local sep = (a[2] ~= nil and a[2]) or (kw and kw.sep)
local hex_lower = b64_helper.raw_b16encode(s):lower()
if not b64_helper.is_none(sep) then
local sep_s = (type(sep) == "string") and sep or b64_helper.extract_bytes(sep, true)
if #sep_s ~= 1 then require("Module:沙盒/a2569875/PythonScript/Runtime").raise("ValueError", "sep must be length 1") end
local parts = {}
for i = 1, #hex_lower, 2 do parts[#parts + 1] = hex_lower:sub(i, i + 1) end
hex_lower = table.concat(parts, sep_s)
end
return b64_helper.make_bytes(hex_lower)
end)
local unhexlify_fn = fn("unhexlify", function(a)
local s = b64_helper.extract_bytes(a[1], true)
return b64_helper.make_bytes(b64_helper.raw_b16decode(s, true))
end)
local b2a_base64_fn = fn("b2a_base64", function(a, kw)
local s = b64_helper.extract_bytes(a[1], false)
local nl = true
if kw and kw.newline ~= nil then nl = B.bool.__call({kw.newline}) end
local res = b64_helper.raw_b64encode(s, b64_helper.chars.b64)
return b64_helper.make_bytes(nl and (res .. "\n") or res)
end)
local a2b_base64_fn = fn("a2b_base64", function(a)
local s = b64_helper.extract_bytes(a[1], true)
return b64_helper.make_bytes(b64_helper.raw_b64decode(s, nil, false))
end)
local crc32_fn = fn("crc32", function(a)
local s = b64_helper.extract_bytes(a[1], false)
local crc = bit32.bnot(tonumber(a[2]) or 0)
for i = 1, #s do
crc = bit32.bxor(crc, string.byte(s, i))
for _ = 1, 8 do
local mask = -(bit32.band(crc, 1))
crc = bit32.bxor(bit32.rshift(crc, 1), bit32.band(0xEDB88320, mask))
end
end
return bit32.bnot(crc)
end)
local dict = {
Error = b64_helper.ErrorClass,
Incomplete = b64_helper.IncompleteClass,
hexlify = hexlify_fn,
b2a_hex = hexlify_fn,
unhexlify = unhexlify_fn,
a2b_hex = unhexlify_fn,
b2a_base64 = b2a_base64_fn,
a2b_base64 = a2b_base64_fn,
crc32 = crc32_fn
}
local mod = PyClass.make_instance(PyClass.make_class("module", {}, {}))
for k, v in pairs(dict) do mod.__dict[k] = v end
mod.__dict["__name__"] = "binascii"
return mod
end
return b64_helper
end
-- ==========================================
-- 2. fractions 有理數演算法
-- ==========================================
function StdAlgorithms.fractions()
local fractions_helper = {
initialized = false,
cache = {}
}
-- ==========================================
-- 1. 大數 (BigInt) 與基礎整數運算橋接器
-- ==========================================
function fractions_helper.is_none(v)
return v == nil or (type(v) == "table" and v.__class and v.__class.__name == "NoneType")
end
function fractions_helper.is_fraction(v)
return type(v) == "table" and v.__type == "instance" and v.__dict and v.__dict._is_fraction == true
end
function fractions_helper.is_decimal(v)
return type(v) == "table" and v.__type == "instance" and v.__dict and v.__dict._is_decimal == true
end
function fractions_helper.is_int(v)
if type(v) == "boolean" then return true end
if type(v) == "number" then return math.floor(v) == v and math.abs(v) <= 9007199254740990 end
if type(v) == "table" and v.__type == "int" then return true end
return false
end
function fractions_helper.int_zero(a)
if type(a) == "boolean" then return not a end
if type(a) == "number" then return a == 0 end
if type(a) == "table" and a.__type == "int" then
return #a.limbs == 1 and a.limbs[1] == 0
end
return false
end
function fractions_helper.int_sign(a)
if fractions_helper.int_zero(a) then return 0 end
if type(a) == "boolean" then return a and 1 or 0 end
if type(a) == "number" then return a < 0 and -1 or 1 end
if type(a) == "table" and a.__type == "int" then return a.sign end
return 1
end
function fractions_helper.int_neg(a)
if type(a) == "boolean" then a = a and 1 or 0 end
if fractions_helper.int_zero(a) then return 0 end
if type(a) == "number" then return -a end
if type(a) == "table" and a.__type == "int" then
return { __type = "int", is_big = true, value = a.value and -a.value or nil, sign = -a.sign, limbs = a.limbs }
end
return 0
end
function fractions_helper.int_abs(a)
if type(a) == "boolean" then return a and 1 or 0 end
if fractions_helper.int_sign(a) < 0 then return fractions_helper.int_neg(a) end
return a
end
function fractions_helper.int_op(a, b, op)
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
local res
if op == "+" then res = a + b
elseif op == "-" then res = a - b
elseif op == "*" then res = a * b
elseif op == "//" then res = math.floor(a / b)
elseif op == "%" then res = a % b end
if res and math.abs(res) <= 9007199254740990 then return res end
end
local Evaluator = require("Module:沙盒/a2569875/PythonScript/Evaluator")
return Evaluator.bigint_op(a, b, op)
end
function fractions_helper.int_cmp(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
if a < b then return -1 elseif a > b then return 1 else return 0 end
end
return require("Module:沙盒/a2569875/PythonScript/Evaluator/Utils").py_cmp(a, b)
end
-- 支援任意精度 BigInt 的最大公因數 (GCD)
function fractions_helper.int_gcd(a, b)
a = fractions_helper.int_abs(a)
b = fractions_helper.int_abs(b)
if type(a) == "number" and type(b) == "number" then
while b ~= 0 do a, b = b, a % b end
return a
end
local BigInt = require("Module:沙盒/a2569875/PythonScript/Runtime/Cmath").BigInt
local la, lb = BigInt.to_limbs(a), BigInt.to_limbs(b)
while not (#lb == 1 and lb[1] == 0) do
local _, r = BigInt.divmod_limbs(la, lb)
la, lb = lb, r
end
if #la == 1 and la[1] == 0 then return 0 end
local s = BigInt.to_string(1, la)
local n = tonumber(s)
if n and n <= BigInt.SAFE_LIMIT then return n end
return { __type = "int", is_big = true, value = n, sign = 1, limbs = la }
end
function fractions_helper.pow10(exp)
if exp <= 0 then return 1 end
if exp <= 15 then return 10 ^ exp end
return require("Module:沙盒/a2569875/PythonScript/Runtime").builtins.int.__call({ "1" .. string.rep("0", exp) })
end
-- 將分子與分母約分並正規化分母為正整數
function fractions_helper.normalize_nd(num, den)
local Runtime = require("Module:沙盒/a2569875/PythonScript/Runtime")
if fractions_helper.int_zero(den) then
Runtime.raise("ZeroDivisionError", "Fraction(" .. tostring(num) .. ", 0)")
end
if fractions_helper.int_zero(num) then
return 0, 1
end
if fractions_helper.int_sign(den) < 0 then
num = fractions_helper.int_neg(num)
den = fractions_helper.int_neg(den)
end
local g = fractions_helper.int_gcd(num, den)
if g ~= 1 and not (type(g) == "table" and #g.limbs == 1 and g.limbs[1] == 1) then
num = fractions_helper.int_op(num, g, "//")
den = fractions_helper.int_op(den, g, "//")
end
return num, den
end
-- ==========================================
-- 2. 循環小數自動識別與字串/數值轉分數核心
-- ==========================================
-- 偵測小數點後字串 frac 是否含有循環節 (回傳 prefix_str, period_str)
function fractions_helper.detect_repeating_period(frac, is_from_float)
local len = #frac
if len < 6 then return nil, nil end
-- 若末尾全為 0,屬於有限小數
if frac:match("^0+$") then return nil, nil end
local max_prefix = math.min(4, len - 6)
for a = 0, max_prefix do
local sub = frac:sub(a + 1)
local slen = #sub
local max_p = math.floor(slen / 2)
for p = 1, max_p do
local pat = sub:sub(1, p)
-- 排除全為 0 的偽週期,以及對 float 結尾全為 9 的誤差進行處理
if not pat:match("^0+$") then
local min_reps = (p == 1 and 6) or (p == 2 and 3) or 2
local reps = math.floor(slen / p)
local rem = slen % p
-- 字串輸入要求完整重複 (rem == 0),或是總長 >= 10 時允許尾端截斷匹配循環節前綴
if reps >= min_reps and (rem == 0 or is_from_float or slen >= 10) then
local ok = true
for r = 1, reps - 1 do
if sub:sub(r * p + 1, (r + 1) * p) ~= pat then
ok = false; break
end
end
if ok and rem > 0 then
local tail = sub:sub(reps * p + 1)
local expected_tail = pat:sub(1, rem)
if tail ~= expected_tail then
-- 容許 float 最末位因四捨五入差 1 (例如 0.16666666666666666 或 0.6666667)
local t_num, e_num = tonumber(tail), tonumber(expected_tail)
if not (is_from_float and t_num and e_num and math.abs(t_num - e_num) <= 1) then
ok = false
end
end
end
if ok then
return frac:sub(1, a), pat
end
end
end
end
end
return nil, nil
end
-- 將整數部分、小數部分與指數轉為 (num, den),支援自動識別循環小數
function fractions_helper.parse_decimal_to_nd(sign, int_part, frac_part, exp_val, is_from_float)
local B = require("Module:沙盒/a2569875/PythonScript/Runtime").builtins
int_part = (int_part == "" or not int_part) and "0" or int_part
frac_part = frac_part or ""
exp_val = exp_val or 0
-- 1. 檢查顯式括號循環節語法,如 0.(142857) 或 0.1(6)
local pre_paren, in_paren = frac_part:match("^(%d*)%((%d+)%)$")
-- 2. 若無括號,啟動智慧循環節偵測 (如 0.142857142857 -> prefix="", period="142857")
if not in_paren then
pre_paren, in_paren = fractions_helper.detect_repeating_period(frac_part, is_from_float)
end
local num, den
if in_paren and #in_paren > 0 then
local a_len = #pre_paren
local p_len = #in_paren
local ap_int = B.int.__call({ (pre_paren .. in_paren == "") and "0" or (pre_paren .. in_paren) })
local a_int = (a_len > 0) and B.int.__call({ pre_paren }) or 0
local frac_num = fractions_helper.int_op(ap_int, a_int, "-")
local nines = B.int.__call({ string.rep("9", p_len) })
local frac_den = fractions_helper.int_op(fractions_helper.pow10(a_len), nines, "*")
local i_int = B.int.__call({ int_part })
num = fractions_helper.int_op(fractions_helper.int_op(i_int, frac_den, "*"), frac_num, "+")
den = frac_den
else
-- 一般有限小數
local combined = int_part .. frac_part
combined = combined:gsub("^0+", "")
if combined == "" then return 0, 1 end
num = B.int.__call({ combined })
den = fractions_helper.pow10(#frac_part)
end
if exp_val > 0 then
num = fractions_helper.int_op(num, fractions_helper.pow10(exp_val), "*")
elseif exp_val < 0 then
den = fractions_helper.int_op(den, fractions_helper.pow10(-exp_val), "*")
end
if sign < 0 then num = fractions_helper.int_neg(num) end
return fractions_helper.normalize_nd(num, den)
end
-- 解析字串為 (num, den)
function fractions_helper.parse_string(str)
local Runtime = require("Module:沙盒/a2569875/PythonScript/Runtime")
local B = Runtime.builtins
local s = str:match("^%s*(.-)%s*$")
if s == "" then
Runtime.raise("ValueError", "Invalid literal for Fraction: '" .. str .. "'")
end
if s:find("__", 1, true) or s:sub(1, 1) == "_" or s:sub(-1) == "_" then
Runtime.raise("ValueError", "Invalid literal for Fraction: '" .. str .. "'")
end
s = s:gsub("(%d)_(%d)", "%1%2"):gsub("(%d)_(%d)", "%1%2")
-- 分數格式: "a/b"
local num_s, den_s = s:match("^([%+%-]?%d+)%s*/%s*([%+%-]?%d+)$")
if num_s and den_s then
local n = B.int.__call({ num_s })
local d = B.int.__call({ den_s })
return fractions_helper.normalize_nd(n, d)
end
local sign = 1
if s:sub(1, 1) == "-" then sign = -1; s = s:sub(2)
elseif s:sub(1, 1) == "+" then s = s:sub(2) end
-- 支援結尾省略號如 "0.142857142857..."
s = s:gsub("%.%.%.$", "")
local mantissa, exp_part = s:match("^([^eE]+)[eE]([%.%+%-]?%d+)$")
local exp_val = 0
if mantissa then
exp_val = tonumber(exp_part)
if not exp_val or math.floor(exp_val) ~= exp_val then
Runtime.raise("ValueError", "Invalid literal for Fraction: '" .. str .. "'")
end
s = mantissa
end
local int_p, frac_p
if s:find("%.", 1, false) then
int_p, frac_p = s:match("^(%d*)%.([%d%(%)]*)$")
if not int_p or (int_p == "" and frac_p == "") then
Runtime.raise("ValueError", "Invalid literal for Fraction: '" .. str .. "'")
end
else
if not s:match("^%d+$") then
Runtime.raise("ValueError", "Invalid literal for Fraction: '" .. str .. "'")
end
int_p, frac_p = s, ""
end
return fractions_helper.parse_decimal_to_nd(sign, int_p, frac_p, exp_val, false)
end
-- 解析任意單一物件為 (num, den)
function fractions_helper.coerce_single(val)
local Runtime = require("Module:沙盒/a2569875/PythonScript/Runtime")
if fractions_helper.is_none(val) then return 0, 1 end
if fractions_helper.is_fraction(val) then
return val.__dict._num, val.__dict._den
end
if fractions_helper.is_int(val) then
if type(val) == "boolean" then return val and 1 or 0, 1 end
return val, 1
end
if type(val) == "number" then
if val ~= val then Runtime.raise("ValueError", "Cannot convert NaN to Fraction.") end
if val == math.huge or val == -math.huge then Runtime.raise("OverflowError", "Cannot convert Infinity to Fraction.") end
-- 將 float 轉為十進位字串並交由循環小數偵測引擎處理
local sign = val < 0 and -1 or 1
local s = string.format("%.14g", math.abs(val))
return fractions_helper.parse_string((sign < 0 and "-" or "") .. s)
end
if fractions_helper.is_decimal(val) then
local d = val.__dict
if d.special == "Inf" then Runtime.raise("OverflowError", "Cannot convert Infinity to Fraction.") end
if d.special then Runtime.raise("ValueError", "Cannot convert NaN to Fraction.") end
local s = require("Module:沙盒/a2569875/PythonScript/Runtime").builtins.str.__call({val})
return fractions_helper.parse_string(s)
end
if type(val) == "string" then
return fractions_helper.parse_string(val)
end
Runtime.raise("TypeError", "argument should be a string or a Rational instance")
end
-- ==========================================
-- 3. 連分數收斂與浮點數轉換輔助
-- ==========================================
function fractions_helper.limit_denominator_raw(num, den, max_den)
local Runtime = require("Module:沙盒/a2569875/PythonScript/Runtime")
if fractions_helper.int_cmp(max_den, 1) < 0 then
Runtime.raise("ValueError", "max_denominator should be at least 1")
end
if fractions_helper.int_cmp(den, max_den) <= 0 then
return num, den
end
local p0, q0, p1, q1 = 0, 1, 1, 0
local n, d = num, den
while true do
local a = fractions_helper.int_op(n, d, "//")
local q2 = fractions_helper.int_op(q0, fractions_helper.int_op(a, q1, "*"), "+")
if fractions_helper.int_cmp(q2, max_den) > 0 then break end
local p2 = fractions_helper.int_op(p0, fractions_helper.int_op(a, p1, "*"), "+")
p0, q0, p1, q1 = p1, q1, p2, q2
local r = fractions_helper.int_op(n, fractions_helper.int_op(a, d, "*"), "-")
n, d = d, r
if fractions_helper.int_zero(d) then break end
end
local k = fractions_helper.int_op(fractions_helper.int_op(max_den, q0, "-"), q1, "//")
local bound1_n = fractions_helper.int_op(p0, fractions_helper.int_op(k, p1, "*"), "+")
local bound1_d = fractions_helper.int_op(q0, fractions_helper.int_op(k, q1, "*"), "+")
-- 比較 |bound1 - self| 與 |p1/q1 - self|
local diff1_num = fractions_helper.int_abs(fractions_helper.int_op(
fractions_helper.int_op(bound1_n, den, "*"),
fractions_helper.int_op(num, bound1_d, "*"), "-"
))
local diff2_num = fractions_helper.int_abs(fractions_helper.int_op(
fractions_helper.int_op(p1, den, "*"),
fractions_helper.int_op(num, q1, "*"), "-"
))
local lhs = fractions_helper.int_op(diff2_num, bound1_d, "*")
local rhs = fractions_helper.int_op(diff1_num, q1, "*")
if fractions_helper.int_cmp(lhs, rhs) <= 0 then
return p1, q1
else
return bound1_n, bound1_d
end
end
function fractions_helper.to_float(self)
local n, d = self.__dict._num, self.__dict._den
if type(n) == "number" and type(d) == "number" then
return n / d
end
return require("Module:沙盒/a2569875/PythonScript/Evaluator").bigint_op(n, d, "/")
end
function fractions_helper.make_fraction(num, den)
fractions_helper.ensure_init()
local n, d = fractions_helper.normalize_nd(num, den)
local inst = require("Module:沙盒/a2569875/PythonScript/Class").make_instance(fractions_helper.cache.Fraction)
inst.__dict._is_fraction = true
inst.__dict._num = n
inst.__dict._den = d
return inst
end
-- ==========================================
-- 4. 供 math 模組呼叫的高精度輔助函式
-- ==========================================
function fractions_helper.math_floor(frac)
return fractions_helper.int_op(frac.__dict._num, frac.__dict._den, "//")
end
function fractions_helper.math_ceil(frac)
local neg_n = fractions_helper.int_neg(frac.__dict._num)
local q = fractions_helper.int_op(neg_n, frac.__dict._den, "//")
return fractions_helper.int_neg(q)
end
function fractions_helper.math_trunc(frac)
local n, d = frac.__dict._num, frac.__dict._den
if fractions_helper.int_sign(n) < 0 then
local pos_q = fractions_helper.int_op(fractions_helper.int_neg(n), d, "//")
return fractions_helper.int_neg(pos_q)
end
return fractions_helper.int_op(n, d, "//")
end
local function int_ln(val)
if type(val) == "number" then return math.log(val) end
local BigInt = require("Module:沙盒/a2569875/PythonScript/Runtime/Cmath").BigInt
local s = BigInt.to_string(1, val.limbs)
local k = #s - 1
local m = tonumber(s:sub(1, 1) .. "." .. s:sub(2, 17)) or 1.0
return math.log(m) + k * math.log(10)
end
function fractions_helper.math_log(frac, base)
local Runtime = require("Module:沙盒/a2569875/PythonScript/Runtime")
local n, d = frac.__dict._num, frac.__dict._den
if fractions_helper.int_sign(n) <= 0 then
Runtime.raise("ValueError", "math domain error")
end
local ln_val = int_ln(n) - int_ln(d)
if base ~= nil and not fractions_helper.is_none(base) then
local b_ln
if fractions_helper.is_fraction(base) then
b_ln = fractions_helper.math_log(base)
else
local bf = tonumber(tostring(base))
if not bf or bf <= 0 then Runtime.raise("ValueError", "math domain error") end
b_ln = math.log(bf)
end
return ln_val / b_ln
end
return ln_val
end
function fractions_helper.math_log10(frac)
return fractions_helper.math_log(frac) / math.log(10)
end
function fractions_helper.math_log2(frac)
return fractions_helper.math_log(frac) / math.log(2)
end
-- ==========================================
-- 5. 延遲初始化 Fraction 類別與所有魔術方法
-- ==========================================
function fractions_helper.ensure_init()
if fractions_helper.initialized then return end
fractions_helper.initialized = true
local PyClass = require("Module:沙盒/a2569875/PythonScript/Class")
local Runtime = require("Module:沙盒/a2569875/PythonScript/Runtime")
local B = Runtime.builtins
local C = fractions_helper.cache
local function to_nd_or_float(other)
if fractions_helper.is_fraction(other) then return "frac", other.__dict._num, other.__dict._den end
if fractions_helper.is_int(other) then
return "frac", (type(other) == "boolean" and (other and 1 or 0) or other), 1
end
if type(other) == "number" then return "float", other end
return nil
end
local frac_dict
frac_dict = {
__new__ = { __type = "staticmethod", __func = { __type = "builtin_method", __name = "__new__", __call = function(a, kw)
local cls = a[1]
local num_arg = (a[2] ~= nil) and a[2] or (kw and kw.numerator) or 0
local den_arg = (a[3] ~= nil) and a[3] or (kw and kw.denominator) or B.None
local n, d
if fractions_helper.is_none(den_arg) then
n, d = fractions_helper.coerce_single(num_arg)
else
local n1, d1
if fractions_helper.is_int(num_arg) then
n1, d1 = (type(num_arg) == "boolean" and (num_arg and 1 or 0) or num_arg), 1
elseif fractions_helper.is_fraction(num_arg) then
n1, d1 = num_arg.__dict._num, num_arg.__dict._den
else
Runtime.raise("TypeError", "both arguments should be Rational instances")
end
local n2, d2
if fractions_helper.is_int(den_arg) then
n2, d2 = (type(den_arg) == "boolean" and (den_arg and 1 or 0) or den_arg), 1
elseif fractions_helper.is_fraction(den_arg) then
n2, d2 = den_arg.__dict._num, den_arg.__dict._den
else
Runtime.raise("TypeError", "both arguments should be Rational instances")
end
if fractions_helper.int_zero(n2) then
Runtime.raise("ZeroDivisionError", "Fraction(" .. tostring(n1) .. ", 0)")
end
n = fractions_helper.int_op(n1, d2, "*")
d = fractions_helper.int_op(d1, n2, "*")
n, d = fractions_helper.normalize_nd(n, d)
end
local inst = PyClass.make_instance(cls)
inst.__dict._is_fraction = true
inst.__dict._num = n
inst.__dict._den = d
return inst
end } },
__init__ = { __type = "builtin_method", __name = "__init__", __call = function() return nil end },
from_float = { __type = "classmethod", __func = { __type = "builtin_method", __name = "from_float", __call = function(a)
local cls, f = a[1], a[2]
if not fractions_helper.is_int(f) and type(f) ~= "number" then
Runtime.raise("TypeError", "from_float() only takes floats or ints")
end
local n, d = fractions_helper.coerce_single(f)
local inst = fractions_helper.make_fraction(n, d)
inst.__class = cls
return inst
end } },
from_decimal = { __type = "classmethod", __func = { __type = "builtin_method", __name = "from_decimal", __call = function(a)
local cls, dec = a[1], a[2]
if not fractions_helper.is_decimal(dec) and not fractions_helper.is_int(dec) then
Runtime.raise("TypeError", "from_decimal() only takes Decimals or ints")
end
local n, d = fractions_helper.coerce_single(dec)
local inst = fractions_helper.make_fraction(n, d)
inst.__class = cls
return inst
end } },
-- 屬性與基礎方法
numerator = { __type = "property", fget = { __type = "builtin_method", __call = function(a) return a[1].__dict._num end } },
denominator = { __type = "property", fget = { __type = "builtin_method", __call = function(a) return a[1].__dict._den end } },
real = { __type = "property", fget = { __type = "builtin_method", __call = function(a) return a[1] end } },
imag = { __type = "property", fget = { __type = "builtin_method", __call = function() return 0 end } },
conjugate = { __type = "builtin_method", __call = function(a) return a[1] end },
as_integer_ratio = { __type = "builtin_method", __call = function(a)
return { __type = "tuple", elements = { a[1].__dict._num, a[1].__dict._den } }
end },
is_integer = { __type = "builtin_method", __call = function(a)
return fractions_helper.int_cmp(a[1].__dict._den, 1) == 0
end },
limit_denominator = { __type = "builtin_method", __call = function(a, kw)
local self = a[1]
local max_d = (a[2] ~= nil and a[2]) or (kw and kw.max_denominator) or 1000000
local n, d = fractions_helper.limit_denominator_raw(self.__dict._num, self.__dict._den, max_d)
return fractions_helper.make_fraction(n, d)
end },
-- 字串與格式化
__str__ = { __type = "builtin_method", __call = function(a)
local n, d = a[1].__dict._num, a[1].__dict._den
local ns = B.str.__call({n})
if fractions_helper.int_cmp(d, 1) == 0 then return ns end
return ns .. "/" .. B.str.__call({d})
end },
__repr__ = { __type = "builtin_method", __call = function(a)
local n, d = a[1].__dict._num, a[1].__dict._den
return "Fraction(" .. B.str.__call({n}) .. ", " .. B.str.__call({d}) .. ")"
end },
__format__ = { __type = "builtin_method", __call = function(a)
local self, spec = a[1], a[2] or ""
if spec == "" then return B.str.__call({self}) end
if spec:match("[eEfFgG%%]$") then
return B.format({ fractions_helper.to_float(self), spec })
end
return B.format({ B.str.__call({self}), spec })
end },
-- 型別轉換與捨入
__bool__ = { __type = "builtin_method", __call = function(a) return not fractions_helper.int_zero(a[1].__dict._num) end },
__float__ = { __type = "builtin_method", __call = function(a) return fractions_helper.to_float(a[1]) end },
__int__ = { __type = "builtin_method", __call = function(a) return fractions_helper.math_trunc(a[1]) end },
__trunc__ = { __type = "builtin_method", __call = function(a) return fractions_helper.math_trunc(a[1]) end },
__floor__ = { __type = "builtin_method", __call = function(a) return fractions_helper.math_floor(a[1]) end },
__ceil__ = { __type = "builtin_method", __call = function(a) return fractions_helper.math_ceil(a[1]) end },
__hash__ = { __type = "builtin_method", __call = function(a)
return B.hash({ fractions_helper.to_float(a[1]) })
end },
__round__ = { __type = "builtin_method", __call = function(a)
local self, ndigits = a[1], a[2]
local shift = 1
local has_nd = not fractions_helper.is_none(ndigits)
local n_digits = has_nd and tonumber(ndigits) or 0
local n, d = self.__dict._num, self.__dict._den
if n_digits > 0 then
shift = fractions_helper.pow10(n_digits)
n = fractions_helper.int_op(n, shift, "*")
elseif n_digits < 0 then
shift = fractions_helper.pow10(-n_digits)
d = fractions_helper.int_op(d, shift, "*")
end
local q = fractions_helper.int_op(n, d, "//")
local r = fractions_helper.int_op(n, d, "%")
local two_r = fractions_helper.int_op(r, 2, "*")
local cmp = fractions_helper.int_cmp(two_r, d)
if cmp > 0 or (cmp == 0 and not fractions_helper.int_zero(fractions_helper.int_op(q, 2, "%"))) then
q = fractions_helper.int_op(q, 1, "+")
end
if not has_nd then return q end
if n_digits >= 0 then return fractions_helper.make_fraction(q, shift)
else return fractions_helper.make_fraction(fractions_helper.int_op(q, shift, "*"), 1) end
end },
-- 一元與二元算術運算
__pos__ = { __type = "builtin_method", __call = function(a) return fractions_helper.make_fraction(a[1].__dict._num, a[1].__dict._den) end },
__neg__ = { __type = "builtin_method", __call = function(a)
return fractions_helper.make_fraction(fractions_helper.int_neg(a[1].__dict._num), a[1].__dict._den)
end },
__abs__ = { __type = "builtin_method", __call = function(a)
return fractions_helper.make_fraction(fractions_helper.int_abs(a[1].__dict._num), a[1].__dict._den)
end },
__add__ = { __type = "builtin_method", __call = function(a)
local self, other = a[1], a[2]
local kind, bn, bd = to_nd_or_float(other)
if kind == "frac" then
local an, ad = self.__dict._num, self.__dict._den
local num = fractions_helper.int_op(fractions_helper.int_op(an, bd, "*"), fractions_helper.int_op(bn, ad, "*"), "+")
local den = fractions_helper.int_op(ad, bd, "*")
return fractions_helper.make_fraction(num, den)
elseif kind == "float" then
return fractions_helper.to_float(self) + bn
end
return B.NotImplemented
end },
__radd__ = { __type = "builtin_method", __call = function(a)
return frac_dict.__add__.__call({ a[1], a[2] })
end },
__sub__ = { __type = "builtin_method", __call = function(a)
local self, other = a[1], a[2]
local kind, bn, bd = to_nd_or_float(other)
if kind == "frac" then
local an, ad = self.__dict._num, self.__dict._den
local num = fractions_helper.int_op(fractions_helper.int_op(an, bd, "*"), fractions_helper.int_op(bn, ad, "*"), "-")
local den = fractions_helper.int_op(ad, bd, "*")
return fractions_helper.make_fraction(num, den)
elseif kind == "float" then
return fractions_helper.to_float(self) - bn
end
return B.NotImplemented
end },
__rsub__ = { __type = "builtin_method", __call = function(a)
local self, left = a[1], a[2]
local kind, an, ad = to_nd_or_float(left)
if kind == "frac" then
local bn, bd = self.__dict._num, self.__dict._den
local num = fractions_helper.int_op(fractions_helper.int_op(an, bd, "*"), fractions_helper.int_op(bn, ad, "*"), "-")
local den = fractions_helper.int_op(ad, bd, "*")
return fractions_helper.make_fraction(num, den)
elseif kind == "float" then
return an - fractions_helper.to_float(self)
end
return B.NotImplemented
end },
__mul__ = { __type = "builtin_method", __call = function(a)
local self, other = a[1], a[2]
local kind, bn, bd = to_nd_or_float(other)
if kind == "frac" then
return fractions_helper.make_fraction(
fractions_helper.int_op(self.__dict._num, bn, "*"),
fractions_helper.int_op(self.__dict._den, bd, "*")
)
elseif kind == "float" then
return fractions_helper.to_float(self) * bn
end
return B.NotImplemented
end },
__rmul__ = { __type = "builtin_method", __call = function(a)
return frac_dict.__mul__.__call({ a[1], a[2] })
end },
__truediv__ = { __type = "builtin_method", __call = function(a)
local self, other = a[1], a[2]
local kind, bn, bd = to_nd_or_float(other)
if kind == "frac" then
if fractions_helper.int_zero(bn) then Runtime.raise("ZeroDivisionError", "Fraction division by zero") end
return fractions_helper.make_fraction(
fractions_helper.int_op(self.__dict._num, bd, "*"),
fractions_helper.int_op(self.__dict._den, bn, "*")
)
elseif kind == "float" then
if bn == 0.0 then Runtime.raise("ZeroDivisionError", "float division by zero") end
return fractions_helper.to_float(self) / bn
end
return B.NotImplemented
end },
__rtruediv__ = { __type = "builtin_method", __call = function(a)
local self, left = a[1], a[2]
local kind, an, ad = to_nd_or_float(left)
if kind == "frac" then
if fractions_helper.int_zero(self.__dict._num) then Runtime.raise("ZeroDivisionError", "Fraction division by zero") end
return fractions_helper.make_fraction(
fractions_helper.int_op(an, self.__dict._den, "*"),
fractions_helper.int_op(ad, self.__dict._num, "*")
)
elseif kind == "float" then
local sf = fractions_helper.to_float(self)
if sf == 0.0 then Runtime.raise("ZeroDivisionError", "float division by zero") end
return an / sf
end
return B.NotImplemented
end },
__floordiv__ = { __type = "builtin_method", __call = function(a)
local div_res = frac_dict.__truediv__.__call(a)
if div_res == B.NotImplemented then return B.NotImplemented end
if fractions_helper.is_fraction(div_res) then return fractions_helper.math_floor(div_res) end
return math.floor(div_res)
end },
__rfloordiv__ = { __type = "builtin_method", __call = function(a)
local div_res = frac_dict.__rtruediv__.__call(a)
if div_res == B.NotImplemented then return B.NotImplemented end
if fractions_helper.is_fraction(div_res) then return fractions_helper.math_floor(div_res) end
return math.floor(div_res)
end },
__mod__ = { __type = "builtin_method", __call = function(a)
local q = frac_dict.__floordiv__.__call(a)
if q == B.NotImplemented then return B.NotImplemented end
local prod = frac_dict.__mul__.__call({ a[1], q })
-- self - other * (self // other)
local step = frac_dict.__mul__.__call({ a[1], 1 })
if type(a[2]) == "number" and not fractions_helper.is_int(a[2]) then
return fractions_helper.to_float(a[1]) % a[2]
end
local _, bn, bd = to_nd_or_float(a[2])
local rhs = fractions_helper.make_fraction(fractions_helper.int_op(bn, q, "*"), bd)
return frac_dict.__sub__.__call({ step, rhs })
end },
__rmod__ = { __type = "builtin_method", __call = function(a)
local q = frac_dict.__rfloordiv__.__call(a)
if q == B.NotImplemented then return B.NotImplemented end
if type(a[2]) == "number" and not fractions_helper.is_int(a[2]) then
return a[2] % fractions_helper.to_float(a[1])
end
local prod = frac_dict.__mul__.__call({ a[1], q })
return frac_dict.__rsub__.__call({ prod, a[2] })
end },
__divmod__ = { __type = "builtin_method", __call = function(a)
local q = frac_dict.__floordiv__.__call(a)
if q == B.NotImplemented then return B.NotImplemented end
local r = frac_dict.__mod__.__call(a)
return { __type = "tuple", elements = { q, r } }
end },
__rdivmod__ = { __type = "builtin_method", __call = function(a)
local q = frac_dict.__rfloordiv__.__call(a)
if q == B.NotImplemented then return B.NotImplemented end
local r = frac_dict.__rmod__.__call(a)
return { __type = "tuple", elements = { q, r } }
end },
__pow__ = { __type = "builtin_method", __call = function(a)
local self, other = a[1], a[2]
local kind, bn, bd = to_nd_or_float(other)
if kind == "frac" and fractions_helper.int_cmp(bd, 1) == 0 then
local exp = bn
local n, d = self.__dict._num, self.__dict._den
if fractions_helper.int_sign(exp) >= 0 then
return fractions_helper.make_fraction(B.pow({n, exp}), B.pow({d, exp}))
else
if fractions_helper.int_zero(n) then Runtime.raise("ZeroDivisionError", "Fraction(0, 1) cannot be raised to a negative power") end
local pos_e = fractions_helper.int_neg(exp)
return fractions_helper.make_fraction(B.pow({d, pos_e}), B.pow({n, pos_e}))
end
elseif kind then
local exp_f = (kind == "frac") and (bn / bd) or bn
return fractions_helper.to_float(self) ^ exp_f
end
return B.NotImplemented
end },
__rpow__ = { __type = "builtin_method", __call = function(a)
local self, left = a[1], a[2]
if fractions_helper.int_cmp(self.__dict._den, 1) == 0 and fractions_helper.is_int(left) then
local exp = self.__dict._num
if fractions_helper.int_sign(exp) >= 0 then
return fractions_helper.make_fraction(B.pow({left, exp}), 1)
else
if fractions_helper.int_zero(left) then Runtime.raise("ZeroDivisionError", "0.0 cannot be raised to a negative power") end
return fractions_helper.make_fraction(1, B.pow({left, fractions_helper.int_neg(exp)}))
end
end
local lf = tonumber(tostring(left))
if lf then return lf ^ fractions_helper.to_float(self) end
return B.NotImplemented
end },
-- 比較運算 (支援與 int / BigInt / Fraction / Decimal / float 精確比較)
__eq__ = { __type = "builtin_method", __call = function(a)
local self, other = a[1], a[2]
local kind, bn, bd = to_nd_or_float(other)
if not kind and fractions_helper.is_decimal(other) then
local ok, dn, dd = pcall(fractions_helper.coerce_single, other)
if ok then kind, bn, bd = "frac", dn, dd else return false end
end
if kind == "frac" then
return fractions_helper.int_cmp(self.__dict._num, bn) == 0
and fractions_helper.int_cmp(self.__dict._den, bd) == 0
elseif kind == "float" then
if bn ~= bn then return false end
if bn == math.huge or bn == -math.huge then return false end
return fractions_helper.to_float(self) == bn
end
return B.NotImplemented
end },
__ne__ = { __type = "builtin_method", __call = function(a)
local eq = frac_dict.__eq__.__call(a)
if eq == B.NotImplemented then return B.NotImplemented end
return not eq
end }
}
local function make_cmp_method(op_fn)
return { __type = "builtin_method", __call = function(a)
local self, other = a[1], a[2]
local kind, bn, bd = to_nd_or_float(other)
if not kind and fractions_helper.is_decimal(other) then
local ok, dn, dd = pcall(fractions_helper.coerce_single, other)
if ok then kind, bn, bd = "frac", dn, dd end
end
if kind == "frac" then
local lhs = fractions_helper.int_op(self.__dict._num, bd, "*")
local rhs = fractions_helper.int_op(bn, self.__dict._den, "*")
return op_fn(fractions_helper.int_cmp(lhs, rhs), 0)
elseif kind == "float" then
if bn ~= bn then return false end
return op_fn(fractions_helper.to_float(self), bn)
end
return B.NotImplemented
end }
end
frac_dict.__lt__ = make_cmp_method(function(x, y) return x < y end)
frac_dict.__le__ = make_cmp_method(function(x, y) return x <= y end)
frac_dict.__gt__ = make_cmp_method(function(x, y) return x > y end)
frac_dict.__ge__ = make_cmp_method(function(x, y) return x >= y end)
frac_dict.__copy__ = frac_dict.__pos__
frac_dict.__deepcopy__ = frac_dict.__pos__
C.Fraction = PyClass.make_class("Fraction", {}, frac_dict)
C.Fraction.__dict["__module__"] = "fractions"
C.gcd = { __type = "builtin_method", __name = "gcd", __call = function(a)
return fractions_helper.int_gcd(a[1] or 0, a[2] or 0)
end }
end
-- ==========================================
-- 6. 模組匯出工廠
-- ==========================================
function fractions_helper.get_module()
local PyClass = require("Module:沙盒/a2569875/PythonScript/Class")
local Runtime = require("Module:沙盒/a2569875/PythonScript/Runtime")
local mod_dict = {
__getattr__ = {
__type = "builtin_method",
__call = function(args)
local self, attr = args[1], args[2]
fractions_helper.ensure_init()
local val = fractions_helper.cache[attr]
if val ~= nil then
self.__dict[attr] = val
return val
end
Runtime.raise("AttributeError", "module 'fractions' has no attribute '" .. tostring(attr) .. "'")
end
}
}
local mod = PyClass.make_instance(PyClass.make_class("module", {}, mod_dict))
mod.__dict["__name__"] = "fractions"
mod.__dict["__all__"] = { __type = "list", elements = { "Fraction", "gcd" } }
return mod
end
return fractions_helper
end
-- ==========================================
-- 3. graphlib 圖論演算法
-- ==========================================
function StdAlgorithms.graphlib()
local graphlib_helper = {
initialized = false,
cache = {}
}
function graphlib_helper.ensure_init()
if graphlib_helper.initialized then return end
graphlib_helper.initialized = true
local PyClass = require("Module:沙盒/a2569875/PythonScript/Class")
local Runtime = require("Module:沙盒/a2569875/PythonScript/Runtime")
local B = Runtime.builtins
local EvalUtils = require("Module:沙盒/a2569875/PythonScript/Evaluator/Utils")
local Evaluator = require("Module:沙盒/a2569875/PythonScript/Evaluator")
-- 1. CycleError 例外類別
local CycleError = PyClass.make_class("CycleError", { B.ValueError }, {})
CycleError.__dict["__module__"] = "graphlib"
graphlib_helper.cache.CycleError = CycleError
-- 2. TopologicalSorter 內部輔助函式 (節點映射)
local function get_node_id(d, node)
for id, n in ipairs(d._id2node) do
if EvalUtils.py_eq(n, node) then return id end
end
local new_id = #d._id2node + 1
d._id2node[new_id] = node
d._preds[new_id] = {}
return new_id
end
local function get_existing_node_id(d, node)
for id, n in ipairs(d._id2node) do
if EvalUtils.py_eq(n, node) then return id end
end
Runtime.raise("ValueError", "node was not added using add()")
end
-- 3. TopologicalSorter 核心類別字典
local ts_dict = {
__init__ = { __type = "builtin_method", __call = function(a, kw)
local self = a[1]
local d = self.__dict
d._id2node = {}
d._preds = {}
d._succs = {}
d._in_degree = {}
d._ready_nodes = {}
d._passed_out = {}
d._finished = {}
d._n_passed_out = 0
d._n_finished = 0
d._prepared = false
local graph = (a[2] ~= nil) and a[2] or (kw and kw.graph) or B.None
if graph ~= B.None then
local items_fn = PyClass.get_attr(graph, "items")
local iter_state = EvalUtils.make_iter_state(Evaluator.invoke(items_fn, {}, {}, graph))
while true do
local has_next, pair = EvalUtils.get_next_item(iter_state)
if not has_next then break end
local node = pair.elements[1]
local preds = pair.elements[2]
local p_state = EvalUtils.make_iter_state(preds)
local add_args = {self, node}
while true do
local hn, p = EvalUtils.get_next_item(p_state)
if not hn then break end
table.insert(add_args, p)
end
-- 利用 self.add 方法寫入圖
self.__class.__dict.add.__call(add_args)
end
end
return nil
end },
add = { __type = "builtin_method", __call = function(a)
local self, node = a[1], a[2]
local d = self.__dict
if d._prepared then Runtime.raise("ValueError", "Nodes cannot be added after a call to prepare()") end
local node_id = get_node_id(d, node)
for i = 3, #a do
local pred_id = get_node_id(d, a[i])
table.insert(d._preds[node_id], pred_id)
end
return B.None
end },
prepare = { __type = "builtin_method", __call = function(a)
local self = a[1]
local d = self.__dict
if d._prepared then Runtime.raise("ValueError", "cannot prepare() more than once") end
d._prepared = true
-- 建立後繼者字典 (successors) 與初始入度
for id = 1, #d._id2node do d._succs[id] = {} end
for node_id, preds in pairs(d._preds) do
local unique_preds = {}
local seen = {}
for _, pred_id in ipairs(preds) do
if not seen[pred_id] then
seen[pred_id] = true
table.insert(unique_preds, pred_id)
table.insert(d._succs[pred_id], node_id)
end
end
d._preds[node_id] = unique_preds
d._in_degree[node_id] = #unique_preds
end
-- DFS 演算法:偵測循環依賴 (Cycle Detection)
local state = {}
local path = {}
local function dfs(u)
state[u] = 1
table.insert(path, u)
for _, v in ipairs(d._succs[u]) do
if state[v] == 1 then
local cycle_nodes = {}
local in_cycle = false
for _, pid in ipairs(path) do
if pid == v then in_cycle = true end
if in_cycle then
table.insert(cycle_nodes, d._id2node[pid])
end
end
table.insert(cycle_nodes, d._id2node[v])
return cycle_nodes
elseif not state[v] then
local c = dfs(v)
if c then return c end
end
end
state[u] = 2
table.remove(path)
return nil
end
for id = 1, #d._id2node do
if not state[id] then
local cycle = dfs(id)
if cycle then
local tup = { __type = "tuple", elements = cycle }
local exc = PyClass.make_instance(CycleError)
exc.__dict.args = { __type = "tuple", elements = { "nodes are in a cycle", tup } }
exc.__dict.msg = "nodes are in a cycle"
Runtime.raise(exc)
end
end
end
-- 蒐集初始狀態下就緒 (無依賴) 的節點
for id = 1, #d._id2node do
if d._in_degree[id] == 0 then
table.insert(d._ready_nodes, id)
end
end
return B.None
end },
is_active = { __type = "builtin_method", __call = function(a)
local d = a[1].__dict
if not d._prepared then Runtime.raise("ValueError", "prepare() must be called first") end
-- 只要還有就緒的節點,或是有已經釋出但尚未標記為 done 的節點,代表進度仍能推進
return (d._n_passed_out > d._n_finished) or (#d._ready_nodes > 0)
end },
done = { __type = "builtin_method", __call = function(a)
local self = a[1]
local d = self.__dict
if not d._prepared then Runtime.raise("ValueError", "prepare() must be called first") end
for i = 2, #a do
local node = a[i]
local id = get_existing_node_id(d, node)
if not d._passed_out[id] then
Runtime.raise("ValueError", "node was not passed out (not ready yet)")
end
if d._finished[id] then
Runtime.raise("ValueError", "node was already marked done")
end
d._finished[id] = true
d._n_finished = d._n_finished + 1
for _, succ_id in ipairs(d._succs[id]) do
d._in_degree[succ_id] = d._in_degree[succ_id] - 1
if d._in_degree[succ_id] == 0 then
table.insert(d._ready_nodes, succ_id)
end
end
end
return B.None
end },
get_ready = { __type = "builtin_method", __call = function(a)
local self = a[1]
local d = self.__dict
if not d._prepared then Runtime.raise("ValueError", "prepare() must be called first") end
local res = {}
for _, id in ipairs(d._ready_nodes) do
table.insert(res, d._id2node[id])
d._passed_out[id] = true
d._n_passed_out = d._n_passed_out + 1
end
d._ready_nodes = {}
return { __type = "tuple", elements = res }
end },
static_order = { __type = "builtin_method", __call = function(a)
local self = a[1]
local d = self.__dict
if not d._prepared then
self.__class.__dict.prepare.__call({self})
end
local out = {}
while self.__class.__dict.is_active.__call({self}) do
local ready = self.__class.__dict.get_ready.__call({self})
if #ready.elements == 0 then
-- 靜態解析下如果發生中斷,即代表非預期狀態 (循環已在 prepare 階段被攔截)
break
end
local done_args = {self}
for _, node in ipairs(ready.elements) do
table.insert(out, node)
table.insert(done_args, node)
end
self.__class.__dict.done.__call(done_args)
end
return B.iter({ { __type = "list", elements = out } })
end }
}
local TopologicalSorter = PyClass.make_class("TopologicalSorter", {}, ts_dict)
TopologicalSorter.__dict["__module__"] = "graphlib"
graphlib_helper.cache.TopologicalSorter = TopologicalSorter
end
function graphlib_helper.get_module()
graphlib_helper.ensure_init()
local PyClass = require("Module:沙盒/a2569875/PythonScript/Class")
local Runtime = require("Module:沙盒/a2569875/PythonScript/Runtime")
local mod_dict = {
__getattr__ = {
__type = "builtin_method",
__call = function(args)
local self, attr = args[1], args[2]
local val = graphlib_helper.cache[attr]
if val ~= nil then
self.__dict[attr] = val
return val
end
Runtime.raise("AttributeError", "module 'graphlib' has no attribute '" .. tostring(attr) .. "'")
end
}
}
local mod = PyClass.make_instance(PyClass.make_class("module", {}, mod_dict))
mod.__dict["__name__"] = "graphlib"
mod.__dict["__all__"] = {
__type = "list",
elements = { "TopologicalSorter", "CycleError" }
}
return mod
end
return graphlib_helper
end
-- ==========================================
-- 4. hashlib 雜湊演算法
-- ==========================================
function StdAlgorithms.hash()
local Hashlib = {}
local PyClass = require("Module:沙盒/a2569875/PythonScript/Class")
local Runtime = require("Module:沙盒/a2569875/PythonScript/Runtime")
local RUtils = require("Module:沙盒/a2569875/PythonScript/Runtime/Utils")
local bit32 = require("bit32")
local B = Runtime.builtins
-- 維基百科 mw.hash 支援的演算法映射
local AVAILABLE_ALGORITHMS = {
["md5"] = true, ["sha1"] = true, ["sha224"] = true,
["sha256"] = true, ["sha384"] = true, ["sha512"] = true
}
local function fn(name, func) return { __type = "builtin_method", __name = name, __call = func } end
local function hex_to_bytes(hex)
return (hex:gsub("%x%x", function(h) return string.char(tonumber(h, 16)) end))
end
local function extract_bytes(obj)
if type(obj) == "table" and (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
return obj._data
end
if type(obj) == "string" then return obj end
Runtime.raise("TypeError", "a bytes-like object is required, not '" .. RUtils.get_type_str(obj) .. "'")
end
-- ==========================================
-- 1. 雜湊物件 (Hash Object) 類別實作
-- ==========================================
local hash_dict = {
__module__ = "hashlib",
__init__ = fn("__init__", function(a, kw)
local self, name = a[1], a[2]
self.__dict._name = tostring(name):lower()
if not AVAILABLE_ALGORITHMS[self.__dict._name] then
Runtime.raise("ValueError", "unsupported hash type " .. self.__dict._name)
end
self.__dict._buffer = {}
local data = (a[3] ~= nil) and a[3] or (kw and kw.data)
if data then
table.insert(self.__dict._buffer, extract_bytes(data))
end
return nil
end),
update = fn("update", function(a)
local self, data = a[1], a[2]
table.insert(self.__dict._buffer, extract_bytes(data))
return B.None
end),
digest = fn("digest", function(a)
local self = a[1]
local raw_data = table.concat(self.__dict._buffer)
local hex_str = mw.hash.hashValue(self.__dict._name, raw_data)
return { __type = "bytes", _data = hex_to_bytes(hex_str) }
end),
hexdigest = fn("hexdigest", function(a)
local self = a[1]
local raw_data = table.concat(self.__dict._buffer)
return mw.hash.hashValue(self.__dict._name, raw_data)
end),
copy = fn("copy", function(a)
local self = a[1]
local new_inst = PyClass.make_instance(self.__class)
new_inst.__dict._name = self.__dict._name
new_inst.__dict._buffer = {}
for _, v in ipairs(self.__dict._buffer) do table.insert(new_inst.__dict._buffer, v) end
return new_inst
end),
name = {
__type = "property",
fget = fn("name", function(a) return a[1].__dict._name end)
},
digest_size = {
__type = "property",
fget = fn("digest_size", function(a)
local sizes = { md5 = 16, sha1 = 20, sha224 = 28, sha256 = 32, sha384 = 48, sha512 = 64 }
return sizes[a[1].__dict._name] or 32
end)
},
block_size = {
__type = "property",
fget = fn("block_size", function(a)
local sizes = { md5 = 64, sha1 = 64, sha224 = 64, sha256 = 64, sha384 = 128, sha512 = 128 }
return sizes[a[1].__dict._name] or 64
end)
}
}
local HashClass = PyClass.make_class("HASH", { B.object }, hash_dict)
local function make_hash_wrapper(algo_name)
return fn(algo_name, function(a)
local data = a[1]
local inst = PyClass.make_instance(HashClass)
inst.__dict._name = algo_name
inst.__dict._buffer = {}
if data ~= nil then table.insert(inst.__dict._buffer, extract_bytes(data)) end
return inst
end)
end
-- ==========================================
-- 2. PBKDF2 HMAC 純 Lua 輕量級實作
-- ==========================================
local function bxor_bytes(b1, b2)
local res = {}
for i = 1, #b1 do res[i] = string.char(bit32.bxor(string.byte(b1, i), string.byte(b2, i))) end
return table.concat(res)
end
local function native_hmac(algo, key, msg)
local block_size = (algo == "sha384" or algo == "sha512") and 128 or 64
if #key > block_size then key = hex_to_bytes(mw.hash.hashValue(algo, key)) end
if #key < block_size then key = key .. string.rep("\0", block_size - #key) end
local opad = bxor_bytes(key, string.rep(string.char(0x5c), block_size))
local ipad = bxor_bytes(key, string.rep(string.char(0x36), block_size))
local inner = hex_to_bytes(mw.hash.hashValue(algo, ipad .. msg))
return hex_to_bytes(mw.hash.hashValue(algo, opad .. inner))
end
local function pbkdf2_hmac(a, kw)
local hash_name = tostring(a[1] or (kw and kw.hash_name)):lower()
local password = extract_bytes(a[2] or (kw and kw.password))
local salt = extract_bytes(a[3] or (kw and kw.salt))
local iterations = tonumber(RUtils.unwrap_num(a[4] or (kw and kw.iterations)))
local dklen = a[5] or (kw and kw.dklen)
if dklen == nil or dklen == B.None then
local sizes = { md5=16, sha1=20, sha224=28, sha256=32, sha384=48, sha512=64 }
dklen = sizes[hash_name] or 32
else
dklen = tonumber(RUtils.unwrap_num(dklen))
end
if not AVAILABLE_ALGORITHMS[hash_name] then Runtime.raise("ValueError", "unsupported hash type") end
if type(iterations) ~= "number" or iterations < 1 then Runtime.raise("ValueError", "iteration value must be >= 1") end
local out = {}
local blocks_needed = math.ceil(dklen / (hash_name == "sha384" and 48 or (hash_name == "sha512" and 64 or (hash_name == "sha224" and 28 or (hash_name == "sha1" and 20 or (hash_name == "md5" and 16 or 32))))))
local hash_len = (hash_name == "sha384" and 48 or (hash_name == "sha512" and 64 or (hash_name == "sha224" and 28 or (hash_name == "sha1" and 20 or (hash_name == "md5" and 16 or 32)))))
for i = 1, blocks_needed do
local block_idx = string.char(bit32.band(bit32.rshift(i, 24), 0xFF), bit32.band(bit32.rshift(i, 16), 0xFF), bit32.band(bit32.rshift(i, 8), 0xFF), bit32.band(i, 0xFF))
local U = native_hmac(hash_name, password, salt .. block_idx)
local T = U
for j = 2, iterations do
U = native_hmac(hash_name, password, U)
T = bxor_bytes(T, U)
end
table.insert(out, T)
end
local final_bytes = string.sub(table.concat(out), 1, dklen)
return { __type = "bytes", _data = final_bytes }
end
-- ==========================================
-- 3. 模組註冊與匯出
-- ==========================================
function Hashlib.get_module()
local mod = PyClass.make_instance(PyClass.make_class("module", {}, {}))
local d = mod.__dict
d["__name__"] = "hashlib"
-- 註冊新建立介面與建構器
d.new = fn("new", function(a, kw)
local inst = PyClass.make_instance(HashClass)
HashClass.__dict.__init__.__call({inst, unpack(a)}, kw)
return inst
end)
d.md5 = make_hash_wrapper("md5")
d.sha1 = make_hash_wrapper("sha1")
d.sha224 = make_hash_wrapper("sha224")
d.sha256 = make_hash_wrapper("sha256")
d.sha384 = make_hash_wrapper("sha384")
d.sha512 = make_hash_wrapper("sha512")
d.pbkdf2_hmac = fn("pbkdf2_hmac", pbkdf2_hmac)
-- 註冊清單 (algorithms_guaranteed / algorithms_available)
local algo_list = { "md5", "sha1", "sha224", "sha256", "sha384", "sha512" }
local algo_set = B.set.__call({})
for _, name in ipairs(algo_list) do
table.insert(algo_set.elements, name)
end
d.algorithms_guaranteed = algo_set
d.algorithms_available = algo_set
return mod
end
return Hashlib
end
return StdAlgorithms