模組:BigNumber/utils
外观
local bignum = require("Module:BigNumber")
local utils = {}
function utils.is_zero(n)
for _,d in ipairs(n) do
if d ~= 0 then return false end
end
return true
end
function utils.myfloor(n)
if math.abs(math.ceil(n)-n)<1e-12 then return math.ceil(n) end
return math.floor(n)
end
function utils.myceil(n)
if math.abs(math.floor(n)-n)<1e-12 then return math.floor(n) end
return math.ceil(n)
end
function utils.mod(n, m)
if utils.isInf(m) then return n end
return n % m
end
function utils.loop_mod(n, m)
if utils.isInf(m) then return n end
local result = n % m
return (result == 0) and m or result
end
function utils.tonumber(n)
local check = tostring(n)
check = mw.ustring.gsub(check, '∞', 'inf')
return tonumber(check)
end
function utils.tostring(n)
local result = tostring(n)
result = mw.ustring.gsub(result, '^%s*([+-]?)inf%s*$', '%1∞')
result = mw.ustring.gsub(result, '%-', '−')
return result
end
function utils.isInt(n)
return math.abs(utils.myfloor(n)-n)<1e-12
end
function utils.isInf(num)
local num_str = tostring(num):lower()
return num_str=='inf' or num_str=='-inf' or num_str=='−inf' or num_str=='+inf'
end
function utils.isNaN(num)
local num_str = tostring(num):lower()
return num_str=='nan' or num_str=='-nan' or num_str=='−nan' or num_str=='+nan'
end
function utils._swap(x,i,y,j) local t = x[i]; x[i] = y[j]; y[j] = t end
function utils.fibonacci_coding(int_digits, original_base, _destination_base, fractional_flag, total_digit, fib_code)
local sqrt5 = math.sqrt(5)
local phi = (sqrt5 + 1) * 0.5
local logphi = math.log(phi)
local function fibonacciId(x)return utils.myceil(math.log(x*sqrt5-0.5)/logphi)end
local function fibonacci(an)return math.floor(math.pow(phi, an) / sqrt5 + 0.5)end
if fractional_flag then
if fib_code then return {} end
local number = utils.toDecimal({0}, int_digits, original_base)
if number == 0 then return {} end
local max_digs = total_digit or math.max(#int_digits + 1,20)
local result = {}
for i=1,max_digs do
local place_value = 1 / fibonacci(i+2)
if place_value > number then
result[#result + 1] = 0
else
result[#result + 1] = 1
number = number - place_value
end
if number == 0 then break end
end
return result
end
local n = utils.myfloor(utils.toDecimal(int_digits, {}, original_base))
if math.abs(tonumber(table.concat(int_digits,''))-1)<1e-14 then
return fib_code and {1,1} or {1,0}
end
local k = utils.myceil(math.log(n*sqrt5-0.5)/logphi)
local vaild_fib = {}
local max_id = 0
local x0 = n
while x0 > 0 do
local id = fibonacciId(x0)
local digval = fibonacci(id)
if digval > x0 then
id = id - 1
digval = fibonacci(id)
end
x0 = x0 - digval
if id > max_id then max_id = id end
vaild_fib[id] = 1
end
local result = {}
for i=max_id,(fib_code and 2 or 1),-1 do
result[#result + 1] = vaild_fib[i] or 0
end
if fib_code then
local the_code = {1}
for i=1,#result do
table.insert(the_code, 1, result[i])
end
return the_code
end
return result
end
function utils.non_integer_base(int_digits, fractional_digits, original_base, destination_base, _sign)
local n = utils.toDecimal(int_digits, fractional_digits, original_base)
local sign = _sign
if n < 0 then
sign = sign * -1
n = -n
end
local k = utils.myfloor(math.log(n)/math.log(destination_base)) + 1
local int_result, fractional_result = {}, {}
for i=k-1,0,-1 do
local digit = utils.myfloor((n / utils.getPlaceValue(destination_base, i)) % destination_base)
n = n - digit * utils.getPlaceValue(destination_base, i)
int_result[#int_result + 1] = digit
end
if n > 0 then
for i=1,20 do
local digit = utils.myfloor((n / utils.getPlaceValue(destination_base, -i)) % destination_base)
n = n - digit * utils.getPlaceValue(destination_base, -i)
fractional_result[#fractional_result + 1] = digit
if n <= 0 then break end
end
end
return int_result, fractional_result, sign
end
function utils.negabaseCarry(int_digits, fractional_digits, destination_base, sign)
if math.abs(fractional_digits[1]or 0) > 0 and (sign or 0) > 0 then
int_digits[#int_digits] = int_digits[#int_digits] + 1
end
local carry = 0
for i=#fractional_digits,1,-1 do
local d = fractional_digits[i] + carry
carry = 0
if d >= math.abs(destination_base) then
carry = i%2 and -1 or 1
d = d - math.abs(destination_base)
end
if d < 0 then
carry = i%2 and 1 or -1
d = d + math.abs(destination_base)
end
fractional_digits[i] = d
end
local j = 0
for i=#int_digits,1,-1 do
j = #int_digits - i
local d = int_digits[i] + carry
carry = 0
if d >= math.abs(destination_base) then
carry = j%2 and -1 or 1
d = d - math.abs(destination_base)
end
if d < 0 then
carry = j%2 and 1 or -1
d = d + math.abs(destination_base)
end
int_digits[i] = d
end
while math.abs(carry) > 0 do
j = j + 1
local d = carry
carry = 0
if d >= math.abs(destination_base) then
carry = j%2 and -1 or 1
d = d - math.abs(destination_base)
end
if d < 0 then
carry = j%2 and 1 or -1
d = d + math.abs(destination_base)
end
table.insert(int_digits, 1, d)
end
for i=1,#int_digits-1 do if int_digits[1] == 0 then table.remove(int_digits, 1) end end
return int_digits, fractional_digits
end
function utils.fromContinuedFraction(int_digits, fractional_digits)
local result = bignum.bigint(0)
local calclen = math.floor(#fractional_digits / 6) + 1
for i=#fractional_digits,1,-1 do
result = (result + fractional_digits[i]):inverse(calclen + 2)
end
result:setpoint(calclen)
local result = ''..(int_digits[1] or 0)..'.'..tostring(result):gsub("^%d+%.","")
return result
end
function utils.ContinuedFraction(digits, original_base, _destination_base, fractional_flag, total_digit)
local result_digits, zero_digits = {}, {}
local decimal_string = ''
if not fractional_flag then
result_digits = utils._convertBase(digits, original_base, 10, false)
for i=1,#result_digits do
if math.abs(tonumber(result_digits[1]) or 1) < 1e-14 then
table.remove(result_digits, 1)
end
end
decimal_string = table.concat(result_digits, "")
if decimal_string == '' then decimal_string = 0 end
return {decimal_string}
end
result_digits = utils._convertBase(digits, original_base, 10, true)
decimal_string = '0.'..table.concat(result_digits, "")
zero_digits, result_digits = bignum.ContinuedFraction(decimal_string, math.ceil(total_digit or (decimal_string:len() * 0.8)))
return result_digits
end
function utils.factorial(n)
local result = 1
for i=1,n do result = result * i end
if n<0 then for i=1,-n do result = result / i end end --提供給阶乘进制 負的用除的
return result
end
utils.specialBaseData = {
['!']={
name = "!",
display = "!",
page = "阶乘进制",
digitsplit = ":",
point = ".",
placeValue = function(this, digit_id) return utils.factorial(digit_id)end,
baseValue = function(this, digit_id) return (digit_id < 0) and -digit_id or (digit_id + 1) end,
printDigit = function(this, digit)return tostring(digit) end,
needtoDecimal = false,
convertBase = utils._convertBase,
},
['#']={
name = "#",
display = "#",
page = "质数阶乘进制",
digitsplit = ":",
point = ".",
placeValue = function(this, digit_id)
if not lib_fact.primeIndex then lib_fact = require("Module:Factorization") end
local result = 1
for i=1,digit_id do result = result * ((i-1<=0) and 1 or lib_fact.primeIndex(i-1)) end
if digit_id<0 then for i=1,-digit_id do result = result / ((i-1<=0) and 1 or lib_fact.primeIndex(i-1)) end end
return result
end,
baseValue = function(this, digit_id)
if not lib_fact.primeIndex then lib_fact = require("Module:Factorization") end
return (digit_id < 0) and ((-digit_id<=1) and 1 or lib_fact.primeIndex(-digit_id-1)) or ((digit_id<=0) and 1 or lib_fact.primeIndex(digit_id))
end,
printDigit = function(this, digit)return tostring(digit) end,
needtoDecimal = false,
convertBase = utils._convertBase,
},
['!0']={ --以0-9A-Z表達的阶乘进制 (遇到超過36位數可能會出現亂碼)
name = "!0",
display = "!",
page = "阶乘进制",
digitsplit = "",
point = ".",
placeValue = function(this, digit_id) return utils.factorial(digit_id)end,
baseValue = function(this, digit_id)return (digit_id < 0) and -digit_id or (digit_id + 1) end,
printDigit = function(this, digit)
return utils.printAZ(digit)
end,
needtoDecimal = false,
convertBase = utils._convertBase,
},
['!-']={ --以0-9A-Z表達的阶乘进制並省略個位數 (遇到超過36位數可能會出現亂碼)
name = "!-", --[[oeis:A007623]]
display = "!",
page = "阶乘进制",
digitsplit = "",
point = ".",
placeValue = function(this, digit_id) return utils.factorial((digit_id < 0) and digit_id or (digit_id + 1))end,
baseValue = function(this, digit_id)return (digit_id < 0) and -digit_id or (digit_id + 2) end,
printDigit = function(this, digit)
return utils.printAZ(digit)
end,
needtoDecimal = false,
convertBase = utils._convertBase,
},
fib={
name = "fib",
display = "fib",
page = "斐波那契进制",
digitsplit = "",
point = ".",
placeValue = function(this, an)
local sqrt5 = math.sqrt(5)
local phi = (sqrt5 + 1) * 0.5
local fib_val = math.floor(math.pow(phi, math.abs(an)+((an < 0) and 2 or 1)) / sqrt5 + 0.5)
return (an < 0) and (1/fib_val) or fib_val
end,
baseValue = function(this, digit_id)return 1 end,
printDigit = function(this, digit)return tostring(digit) end,
needtoDecimal = true,
convertBase = utils.fibonacci_coding,
},
["fib-"]={
name = "fib-",
display = "fib",
page = "斐波那契进制",
digitsplit = "",
point = ".",
placeValue = function(this, an)
local sqrt5 = math.sqrt(5)
local phi = (sqrt5 + 1) * 0.5
local fib_val = math.floor(math.pow(phi, math.abs(an)+2) / sqrt5 + 0.5)
return (an < 0) and (1/fib_val) or fib_val
end,
baseValue = function(this, digit_id)return 1 end,
printDigit = function(this, digit)return tostring(digit) end,
needtoDecimal = true,
convertBase = function(int_digits, original_base, _destination_base, fractional_flag, total_digit)
local result = utils.fibonacci_coding(int_digits, original_base, _destination_base, fractional_flag, total_digit)
if fractional_flag then return result end
table.remove(result,#result)
return result
end,
},
fibcode={
name = "fibcode",
display = "fib code",
page = "斐波那契编码",
digitsplit = "",
point = ".",
placeValue = function(this, an)
local sqrt5 = math.sqrt(5)
local phi = (sqrt5 + 1) * 0.5
return math.floor(math.pow(phi, an+1) / sqrt5 + 0.5)
end,
baseValue = function(this, digit_id)return 1 end,
printDigit = function(this, digit)return tostring(digit) end,
needtoDecimal = true,
convertBase = function(int_digits, original_base, _destination_base, fractional_flag, total_digit)
return utils.fibonacci_coding(int_digits, original_base, _destination_base, fractional_flag, total_digit, true)
end,
},
ContinuedFraction={
name = "ContinuedFraction",
display = "ContinuedFraction",
page = "连分数",
digitsplit = ",",
point = ";",
placeValue = function(this, an)return 1 end,
baseValue = function(this, digit_id)return 1 end,
printDigit = function(this, digit)return tostring(digit) end,
needtoDecimal = true,
convertBase = utils.ContinuedFraction,
},
}
utils.specialBaseData['!'] = utils.specialBaseData['!']
utils.specialBaseData['阶乘'] = utils.specialBaseData['!']
utils.specialBaseData['阶乘进制'] = utils.specialBaseData['!']
utils.specialBaseData['階乘'] = utils.specialBaseData['!']
utils.specialBaseData['階乘進制'] = utils.specialBaseData['!']
utils.specialBaseData['階乘進位'] = utils.specialBaseData['!']
utils.specialBaseData['斐波那契进制'] = utils.specialBaseData.fib
utils.specialBaseData['斐波那契進制'] = utils.specialBaseData.fib
utils.specialBaseData['斐波那契编码'] = utils.specialBaseData.fibcode
utils.specialBaseData['斐波那契編碼'] = utils.specialBaseData.fibcode
utils.specialBaseData['fib code'] = utils.specialBaseData.fibcode
utils.specialBaseData['斐波那契'] = utils.specialBaseData.fib
utils.specialBaseData['連分數'] = utils.specialBaseData.ContinuedFraction
utils.specialBaseData['连分数'] = utils.specialBaseData.ContinuedFraction
utils.specialBaseData['continued fraction'] = utils.specialBaseData.ContinuedFraction
utils.specialBaseData['Continued Fraction'] = utils.specialBaseData.ContinuedFraction
function utils.getPlaceValue(base, digit_id)
local special_base_data = utils.getSpecialBase(base)
if special_base_data then
return special_base_data:placeValue(digit_id)
else
return math.pow(base, digit_id)
end
end
function utils.getBaseValue(base, digit_id)
local special_base_data = utils.getSpecialBase(base)
if special_base_data then
return special_base_data:baseValue(digit_id)
else
return base
end
end
function utils.getSpecialBase(base)
local base_string = mw.text.trim(tostring(base))
if utils.isNaN(base) then error(string.format("底數不能為 '%s'", base_string),2) end
local num_base = utils.tonumber(base_string)
if num_base then return nil end
local special_base_data = utils.specialBaseData[base]
if special_base_data then return special_base_data end
if mw.ustring.find(base_string, '[,;]') then
local point, split = '.', ''
local point_loaded = false
base_string = mw.ustring.gsub(base_string, ' +', '')
local base_name = base_string
local az_digits = false
if not mw.ustring.match(mw.ustring.sub(base_string, -2),'[%d∞]+') then
split = mw.ustring.sub(base_string, -2, -2)
point = mw.ustring.sub(base_string, -1, -1)
base_name = mw.ustring.sub(base_string, 1, -3)
if split == point and split == '.' then
split = ''
az_digits = true
end
point_loaded = true
end
local int_frac, int_base = mw.text.split(base_name..';', ';')
int_base = mw.text.split(int_frac[1], ',')
local int_base_count = #int_base
local load_bases = mw.text.split(base_name, '[,;]')
local bases, frac_bases = {}, {}
local max_base = 0
local repeat_base = false
for i=1,#load_bases do
local unit_base = utils.tonumber(load_bases[i])
if not unit_base then return nil end
if (i > 1 and unit_base == 0) or unit_base < 0 or (not utils.isInf(unit_base) and not utils.isInt(unit_base)) then return nil, string.format("不支援非正整數的混合底數 '%s' 進制", base_name) end
if math.abs(unit_base) > 36 and not point_loaded then
point, split = ';', ','
end
if unit_base > max_base then max_base = unit_base end
if i==1 and math.abs(unit_base) < 1e-14 then
repeat_base = true
else
if i > int_base_count then
frac_bases[#frac_bases + 1] = unit_base
else
table.insert(bases, 1, unit_base)
end
end
end
if #bases <= 0 then bases[1] = frac_bases[1]end
if #frac_bases <= 0 then
frac_bases = mw.clone(bases)
if repeat_base and #frac_bases > 1 then
frac_bases[#frac_bases + 1] = frac_bases[1]
table.remove(frac_bases, 1)
end
end
local new_base_data = {
name = base_string,
display = base_name,
page = "混合进制",
digitsplit = split,
point = point,
base_list = bases,
frac_base_list = frac_bases,
max_base = max_base,
repeat_base = repeat_base,
point_loaded = point_loaded,
az_digits = az_digits,
placeValue = function(this, digit_id)
local result = 1
for i=1,math.abs(digit_id) do
local using_list = digit_id < 0 and this.frac_base_list or this.base_list
local doing_index = this.repeat_base and utils.loop_mod(i, #using_list) or i
result = result * (using_list[doing_index] or using_list[#using_list] or 10)
end
return digit_id < 0 and (1/result) or result
end,
baseValue = function(this, digit_id)
return (digit_id < 0)
and (this.frac_base_list[this.repeat_base and utils.loop_mod(-digit_id, #(this.frac_base_list)) or (-digit_id)] or this.frac_base_list[#(this.frac_base_list)] or 10)
or (this.base_list[this.repeat_base and utils.loop_mod(digit_id + 1, #(this.base_list)) or (digit_id + 1)] or this.base_list[#(this.base_list)] or 10)
end,
printDigit = function(this, digit)
if (this.point_loaded or (math.abs(max_base) > 36)) and (not this.az_digits) then
return utils.tostring(digit)
else
return utils.printAZ(digit)
end
end,
needtoDecimal = false,
convertBase = utils._convertBase,
}
return new_base_data
end
return nil
end
function utils.checkSpecialBase(base)
local base_string = mw.text.trim(tostring(base))
if utils.isNaN(base) then error(string.format("底數不能為 '%s'", base_string),2) end
local num_base = utils.tonumber(base_string)
if num_base then return num_base end
local special_base_data = utils.specialBaseData[base]
if special_base_data then return special_base_data.name end
if mw.ustring.find(base_string, '[,;]') then
base_string = mw.ustring.gsub(base_string, ' +', '')
local check_string = base_string
if not mw.ustring.match(mw.ustring.sub(check_string, -2),'[%d∞]+') then
check_string = mw.ustring.sub(check_string, 1, -3)
end
local load_bases = mw.text.split(check_string, '[,;]')
for i=1,#load_bases do
local check_number = utils.tonumber(load_bases[i])
if not check_number then return nil end
if (i > 1 and check_number == 0) or check_number < 0 or (not utils.isInf(unit_base) and not utils.isInt(check_number)) then return nil end
end
return base_string
end
return nil
end
function utils.printAZ(digit)
local char_0 = mw.ustring.codepoint('0')
local char_A = mw.ustring.codepoint('A') - 10
local char_a = mw.ustring.codepoint('a') - 36
local char_100 = mw.ustring.codepoint('¡') - 62
local codepoint = digit + ((digit >= 10) and ((digit >= 36) and ((digit >= 62) and char_100 or char_a) or char_A) or char_0)
return mw.ustring.char(codepoint)
end
function utils.print_digit(digit, base)
local special_base_data = utils.getSpecialBase(base)
if special_base_data then
return special_base_data:printDigit(digit)
elseif math.abs(tonumber(base)or 10) <= 36 then
return utils.printAZ(digit)
else
return tostring(digit)
end
end
function utils.printAllDigit(digits, base, precision, _subarg, fractional_flag)
local subarg = tonumber(_subarg) or 0
local special_base_data = utils.getSpecialBase(base)
local result, digitcomma = '', ''
if special_base_data then
digitcomma = special_base_data.digitsplit
elseif math.abs(tonumber(base) or 10) > 36 then
digitcomma = ','
end
for i=1,#digits do
if precision >= 0 and i == precision + 1 then break end
if subarg < 6 then
if result ~= '' then result = result .. digitcomma end
end
result = result .. utils.print_digit(digits[i], base)
if subarg >= 6 and subarg < 12 then
result = string.format("%s<sub>%s</sub>", result,
utils.tostring(special_base_data and special_base_data:baseValue(fractional_flag and -i or (#digits-i)) or base)
)
elseif subarg >= 12 and subarg < 18 then
result = string.format("%s<sub>%s</sub>", result,
utils.tostring(((not fractional_flag) and (#digits-i == 0)) and ((tostring(base):sub(1,1)=='!')and 0 or 1)
or (special_base_data and special_base_data:baseValue(fractional_flag and -i or (#digits-i-1))) or base)
)
end
end
return result
end
function utils.toDecimal(int_digits, fractional_digits, base)
local n = int_digits[#int_digits]
local j = 1
for i = #int_digits-1, 1, -1 do
n = n + int_digits[i] * utils.getPlaceValue(base, j)
j = j + 1
end
for i = 1, #fractional_digits do
n = n + fractional_digits[i] * utils.getPlaceValue(base, -i)
end
return n
end
function utils.print_base_string(int_result, fractional_result, ori_int_digits, ori_fractional_digits, sign, from, to, _subarg, prefix, suffix)
local subarg = tonumber(_subarg) or 0
local special_base_data_from = utils.getSpecialBase(from)
local special_base_data_to = utils.getSpecialBase(to)
local point_string = (math.abs(tonumber(to) or 10) > 36) and ';' or ((special_base_data_to or{}).point or '.')
local result = (prefix or '') .. ((sign < 0) and '−' or '') .. (int_result == '' and '0' or int_result) .. (fractional_result == '' and '' or (point_string..fractional_result)) .. (suffix or '')
if subarg % 6 == 1 then
result = string.format("%s<sub>(%s)</sub>", result, utils.tostring((special_base_data_to or{}).display or to))
elseif subarg % 6 == 2 then
result = string.format("(%s)<sub>%s</sub>", result, utils.tostring((special_base_data_to or{}).display or to))
elseif subarg % 6 == 3 or subarg % 6 == 5 then
local lib_num2zh = require('Module:NumberToChinese')
local num = utils.toDecimal(ori_int_digits, ori_fractional_digits, from)
result = mw.ustring.format("[[%s|%s]]<sub>[[%s|%s%s%s]]</sub>",
utils.tostring(num), result,
special_base_data_to and (special_base_data_to.page) or (lib_num2zh._numberToChinese(utils.tostring(to))..'進制'),
(subarg % 6 == 5) and '' or '(',
utils.tostring((special_base_data_to or{}).display or to),
(subarg % 6 == 5) and '' or ')')
elseif subarg % 6 == 4 then
result = string.format("%s<sub>%s</sub>", result, utils.tostring((special_base_data_to or{}).display or to))
end
return result
end
return utils