模組:BigNumber/utils

local main_module = "Module:BigNumber"
local utils = {}
local cmath = {}
local bignum = {}
local lib_calc = {}

--大數帶餘長除法 / 可作為進制轉換用的帶餘長除法
function utils.modulo_div(n, _original_base, destination_base, fractional_flag)
	local original_base = tonumber(_original_base)
	local non_number_base = not original_base --確認是否為非數字進制
	if non_number_base then original_base = 1 end
	local carry = 0
	local result = {0} --儲存商的陣列
	for i =fractional_flag and #n or 1,fractional_flag and 1 or #n, fractional_flag and -1 or 1 do
		if non_number_base then 
			original_base = utils.getBaseValue(_original_base, fractional_flag and (-i) or (#n - i))
			if utils.isInf(original_base) then original_base = 10 end --若混合底數出現無限大作為十進制計算
		end
		local d = n[i]
		if fractional_flag then --進制轉換時,小數點以後用乘的
			d = d * math.abs(destination_base) + carry
			carry = math.floor(d / original_base)
			d = utils.mod(d, original_base)
			if destination_base < 0 then d = -d end
		else --對每個位數進行帶餘長除法
			d = d + original_base * carry --上一位的餘數,取下一位
			carry = utils.mod(d, destination_base) --新的餘數 
			d = math.floor(d / destination_base) --商
		end
		result[i] = math.floor(d) --紀錄商
	end
	if fractional_flag and destination_base < 0 then carry = -carry end
	return result, carry --回傳商, 餘數
end

function utils._convertBase(n, _original_base, _destination_base, fractional_flag, total_digit)
	local digits = {}
	if utils.isInf(_original_base) then
		error(string.format("底數不能為 '%s'", tostring(_original_base)),2)
	end
	if fractional_flag and utils.isInf(_destination_base) then --無限大進制的小數位數位置值為 1/無限大 是零,無法表示小數
		return {}
	end
	local original_base = utils.getBaseValue(_original_base, 0)
	local destination_base = utils.getBaseValue(_destination_base, 0)
	local dn, digit = n
	local max_digit = total_digit or math.max(#n+1, 20) --未指定時不超過20位
	local i = 0
	local src_max_digit = #dn

	--進位轉換為不斷帶餘除法直到商為0
	while not utils.is_zero(dn) do
		destination_base = utils.getBaseValue(_destination_base, fractional_flag and (-i - 1) or i)
		--以帶餘長除法計算各個位數 (回傳值 => 商, 位數)
		dn, digit = utils.modulo_div(dn, _original_base, destination_base, fractional_flag)
		if digit < 0 then --負底數進位會遇到餘數小於0的情況
			digit = digit + math.abs(destination_base) --加成正的
			if fractional_flag then
			else --商+1 (因為等於多減一次除數)
				dn[#dn] = dn[#dn] + 1
			end
		end
		--計算小數時,位數加在末尾
		if fractional_flag then
			if utils.isNaN(digit) then break end
			digits[#digits + 1] = digit
			if i >= max_digit then break end
		--計算整數時,位數加在開頭
		else
			table.insert(digits, 1, digit)
		end
		i = i + 1
	end
	return digits
end

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.round(n)
	return utils.myfloor(n + 0.5)
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.cintpow(num, power)
	if type(cmath.constructor) ~= type(tostring) then cmath = require("Module:Complex Number").cmath.init()end
	power = utils.round(power)
	local this = cmath[0] + cmath.constructor(num)
	if power < 0 then --負的次方為倒數自乘
		this = cmath.inverse(this) 
		power = -power
	end
	if power == 0 then return cmath[0] + 1 end
	if power == 1 then return cmath[0] + this end
	if power == 2 then return this * this end
	local loge = math.log(power)
	local log2 = loge / math.log(2)
	if utils.isInt(log2) then --次方為2的冪 直接連續自乘,以減少乘法運算的次數
		local result = this
		for i=1,log2 do result = result * result end
		return result
	end
	local times_data = {}
	local times_times = {}
	local two_time = utils.round(math.pow(2, math.floor(log2))) --其餘情況轉換成2的冪的組合,以減少乘法運算的次數
	local lose_time = power - two_time
	local to_times = cmath[0] + this
	local zero_flag = 0
	repeat --重複分成2的冪的組合
		for i=1,log2 do to_times = to_times * to_times end
		times_data[#times_data+1] = to_times --紀錄本次自乘次數的結果
		times_times[#times_times+1] = two_time
		log2 = math.log(lose_time) / math.log(2) --計算剩餘數字的2的冪的組合
		two_time = math.pow(2, math.floor(log2))
		lose_time = lose_time - two_time --計算扣除本次的2的冪的次數後剩下多少次要乘
		to_times = cmath[0] + this
		if lose_time <= 0 then zero_flag = zero_flag + 1 end --剩餘次數為0為迴圈結束條件
	until zero_flag > 1
	local result = cmath[1]
	for i=1,#times_data do result = result * times_data[i]end --將所有自乘次數的結果相乘
	return result
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.real_negabase(int_digits, fractional_digits, original_base, destination_base, _sign)
	local num = utils.toDecimal(int_digits, fractional_digits, original_base)
	local sign = _sign
	if _sign < 0 then 
		sign = sign * -1
		num = -num
	end
	local b = destination_base
	local sb = math.abs(b)
	local mb, pb = -sb / (sb + 1), 1 / (sb + 1)
	local lb, rb = mb, pb
	if lb > rb then lb, rb = pb, mb end
	local p_chack = math.log((num/lb > 0) and num/lb or num/rb)/math.log(sb)
	local fp, cp = math.floor(p_chack), math.ceil(p_chack)
	local lp, rp = fp, cp
	if lp > rp then lp, rp = cp, fp end
	local p_list = {lp-1, lp, rp, rp+1}
	local p = lp
	local min_x = math.huge
	for i=1,#p_list do
		local p0 = num / math.pow(b, p_list[i])
		if p0 >= lb and p0 <= rb then
			if p0 < min_x then
				min_x = p0
				p = p_list[i]
			end
		end
	end
	local Tb = function(x) return b*x - math.floor(b*x-lb) end
	local Tbj = min_x
	if p<0 then Tbj = num end
	local k = p
	local int_result, fractional_result = {}, {}
	for i=k-1,0,-1 do
		local digit = math.floor(b * Tbj - lb)
		Tbj = Tb(Tbj)
		int_result[#int_result + 1] = digit
	end
	if #int_result == 0 then int_result[1] = 0 end
	if math.abs(Tbj) > 0 then
		for i=1,20 do
			local digit = math.floor(b * Tbj - lb)
			Tbj = Tb(Tbj)
			fractional_result[#fractional_result + 1] = digit
			if math.abs(Tbj) <= 0 then break end
		end
	end
	return int_result, fractional_result, sign
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
	local abs_base = utils.round(math.abs(destination_base))
	for i=#fractional_digits,1,-1 do
		local d = utils.round(fractional_digits[i]) + carry
		carry = 0
		if d >= abs_base then
			carry = i%2 and -1 or 1
			d = d - abs_base
		end
		if d < 0 then
			carry = i%2 and 1 or -1
			d = d + abs_base
		end
		fractional_digits[i] = utils.round(d)
	end
	local j = 0
	for i=#int_digits,1,-1 do
		j = #int_digits - i
		local d = utils.round(int_digits[i]) + carry
		carry = 0
		if d >= abs_base then
			carry = j%2 and -1 or 1
			d = d - abs_base
		end
		if d < 0 then
			carry = j%2 and 1 or -1
			d = d + abs_base
		end
		int_digits[i] = utils.round(d)
	end
	while math.abs(carry) > 0 do
		j = j + 1
		local d = utils.round(carry)
		carry = 0
		if d >= abs_base then
			carry = j%2 and -1 or 1
			d = d - abs_base
		end
		if d < 0 then
			carry = j%2 and 1 or -1
			d = d + abs_base
		end
		table.insert(int_digits, 1, utils.round(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

local function complexBase2Decimal(num, imag, base)
	if type(cmath.constructor) ~= type(tostring) then cmath = require("Module:Complex Number").cmath.init()end
	local base = cmath.constructor(base)
	if not base then return nil end
	num = mw.text.trim(num)
	local sign = 1
	local first_sign = mw.ustring.sub(num,1,1)
	--讀取正負號
	if first_sign == '+' or first_sign == '-' or first_sign == '−' then 
		num = mw.ustring.sub(num,2,-1)
		if first_sign == '-' or first_sign == '−' then 
			sign = -1 
		end
	end
	if type(lib_calc._getNumString) ~= type(tostring) then lib_calc = require('Module:Complex_Number/Calculate')end
	local int_digits, fractional_digits = lib_calc._getNumString(num, true)
	local result = cmath.constructor(utils.toDecimalComplex(int_digits, fractional_digits, imag, base))
	if sign < 0 then result = result * -1 end
	return result
end

function utils.complexBaseConvert(num, to, from, digs, precision)
	if mw.ustring.find(to..' '..from, "[%+%-−%d]i") then
		if type(cmath.constructor) ~= type(tostring) then cmath = require("Module:Complex Number").cmath.init()end
		num = mw.ustring.gsub(num, '−', '-')
		to = mw.ustring.gsub(to, '−', '-')
		from = mw.ustring.gsub(from, '−', '-')
		local cto, cfrom = cmath.constructor(tostring(to)), cmath.constructor(tostring(from))
		cfrom = cfrom or cmath.constructor(10)
		
		if cto and cfrom then
			local number = tostring(num)
			number = mw.text.trim(number)
			--轉換為十進制
			if cmath.abs(cmath.im(cfrom)) < 1e-14 and cmath.abs(cmath.re(cfrom)) > 0 then
				local check_number = num
				if not mw.ustring.find(check_number,"^[%+%-−]") then check_number = '+'..num end
				number = mw.ustring.gsub(check_number, "[%+%-−][%a%d%.,;:]+", function(number_part)
					if type(bignum.convertBase) ~= type(tostring)then bignum = require(main_module)end
					local is_imag = (mw.ustring.sub(number_part,-1,-1) == 'i')
					local convert_number = is_imag and mw.ustring.sub(number_part,1,-2) or number_part
					if convert_number == '' or convert_number == '+' or convert_number == '-' or convert_number == '−' then convert_number = convert_number..'1' end
					convert_number = bignum.convertBase(convert_number, 10, from)..(is_imag and 'i' or '')
					if not mw.ustring.find(convert_number,"^[%+%-−]") then convert_number = '+'..convert_number end
					return mw.ustring.gsub(convert_number, '−', '-')
				end)
			else
				local check_number = num
				if not mw.ustring.find(check_number,"^[%+%-−]") then check_number = '+'..num end
				number = mw.ustring.gsub(check_number, "[%+%-−][%a%d%.,;:]+", function(number_part)
					if type(lib_calc.scientific2number) ~= type(tostring) then lib_calc = require('Module:Complex_Number/Calculate')end
					local is_imag = (mw.ustring.sub(number_part,-1,-1) == 'i')
					local convert_number = is_imag and mw.ustring.sub(number_part,1,-2) or number_part
					convert_number = mw.ustring.gsub(convert_number, '−', '-')
					if convert_number == '' or convert_number == '+' or convert_number == '-' or convert_number == '−' then convert_number = convert_number..'1' end
					convert_number = complexBase2Decimal(convert_number, is_imag, cfrom)
					convert_number = tostring(convert_number)
					--跳脫科學記號,避免加與減和科學記號混淆
					convert_number = mw.ustring.gsub(convert_number, "[Ee]%+?(%d)", "F%1")
					convert_number = mw.ustring.gsub(convert_number, "[Ee]%-(%d)", "D%1")
					if not mw.ustring.find(convert_number,"^[%+%-−]") then convert_number = '+'..convert_number end
					--還原科學記號為一般數字
					convert_number = mw.ustring.gsub(convert_number, "([%+%-−])([%a%d%.,;:]+)", function(number_sign_part,number_value_part)
						local is_part_imag = (mw.ustring.sub(number_value_part,-1,-1) == 'i')
						local number_value_part_real = is_part_imag and mw.ustring.sub(number_value_part,1,-2) or number_value_part
						number_value_part_real = mw.ustring.gsub(number_value_part_real, "[DdFf]", function(Evalue) return ({D='e-',d='e-',F='e+',f='e+'})[Evalue] end)
						return number_sign_part..lib_calc.scientific2number(number_value_part_real, true)..(is_part_imag and 'i' or '')
					end)
					return mw.ustring.gsub(convert_number, '−', '-')
				end)
			end
			number = cmath.constructor(number)
			if not number then
				error(mw.ustring.format("轉換失敗:'%s' 不是有效的數字。",tostring(num)), 2)
				return nil
			end
			local int_digits, fractional_digits = {}, {}
			if cmath.abs(cto) <= 1 then
				return nil
			elseif cmath.abs(cmath.re(cto)) < 1e-14 then
				local to_im = cmath.im(cto)
				local to_im_sq = to_im * to_im
				if to_im_sq > 1 then
					local real, imag = cmath.re(number), cmath.i * cmath.im(number)
					imag = imag * cmath.i * to_im
					local function convert_part(num_val, base, frac_precision)
						local num_str = tostring(num_val)
						local sign = 1
						local first_sign = mw.ustring.sub(num_str,1,1)
						--讀取正負號
						if first_sign == '+' or first_sign == '-' or first_sign == '−' then 
							num_str = mw.ustring.sub(num_str,2,-1)
							if first_sign == '-' or first_sign == '−' then 
								sign = -1 
							end
						end
						if type(lib_calc._getNumString) ~= type(tostring) then lib_calc = require('Module:Complex_Number/Calculate')end
						local int_digits, fractional_digits = lib_calc._getNumString(num_str, true)
						if base < 0 and sign < 0 then
							for i=1,#int_digits do int_digits[i] = -int_digits[i]end
							for i=1,#fractional_digits do fractional_digits[i] = -fractional_digits[i]end
						end
						if utils.isInt(base) then
							int_digits = utils._convertBase(int_digits, 10, base, false)
							fractional_digits = utils._convertBase(fractional_digits, 10, base, true, tonumber(frac_precision<0 and '' or frac_precision))
							if #int_digits == 0 then int_digits[1] = 0 end
							if base < 0 then int_digits, fractional_digits = utils.negabaseCarry(int_digits, fractional_digits, base, sign)end
						else
							int_digits, fractional_digits = utils.real_negabase(int_digits, fractional_digits, 10, base, 1)
						end
						return int_digits, fractional_digits
					end
					local function revert(x)
						rev = {}
						for i=#x, 1, -1 do table.insert(rev, x[i])end
						return rev
					end
					local real_int, real_frac = convert_part(cmath.re(real), -to_im_sq, precision)
					local imag_int, imag_frac = convert_part(cmath.re(imag), -to_im_sq, precision)
					table.insert(imag_frac, 1, imag_int[#imag_int])
					table.remove(imag_int, #imag_int)
					if utils.is_zero(imag_frac) then imag_frac = {} end
					real_int, imag_int = revert(real_int), revert(imag_int)
					for i=1,math.max(#real_int, #imag_int) do
						table.insert(int_digits,1,real_int[i] or 0)
						table.insert(int_digits,1,imag_int[i] or 0)
					end
					if int_digits[1] == 0 then table.remove(int_digits,1) end
					for i=1,math.max(#real_frac, #imag_frac) do
						table.insert(fractional_digits,imag_frac[i] or 0)
						table.insert(fractional_digits,real_frac[i] or 0)
					end
					if fractional_digits[#fractional_digits] == 0 then table.remove(fractional_digits,#fractional_digits) end
				end
			elseif cmath.abs(cmath.re(cto)) >= 1 and cmath.abs(cmath.im(cto)) < 1e-14 then
				local real, imag = cmath.re(number), cmath.im(number)
				if type(bignum.convertBase) ~= type(tostring)then bignum = require(main_module)end
				local real_result, imag_result = bignum.convertBase(real, cmath.re(cto), 10, digs, precision), bignum.convertBase(imag, cmath.re(cto), 10, digs, precision)
				local real_is0, imag_is0 = ((tonumber(real_result)or 1) == 0), ((tonumber(imag_result)or 1) == 0)
				if imag_is0 then return real_result, '' 
				elseif real_is0 and not imag_is0 then return ((math.abs(tonumber(({mw.ustring.gsub(imag_result, '−', '-')})[1]) or 0) ~= 1) and imag_result or ({mw.ustring.gsub(imag_result,'1','')})[1])..'i', ''end
				local check_sign = mw.ustring.sub(imag_result,1,1)
				return real_result .. ((check_sign == '+' or check_sign == '-' or check_sign == '−') and '' or '+') .. ((math.abs(tonumber(({mw.ustring.gsub(imag_result, '−', '-')})[1]) or 0) ~= 1) and imag_result or ({mw.ustring.gsub(imag_result,'1','')})[1]) ..'i', ''
			elseif utils.isInt(cmath.re(cto)) and utils.isInt(cmath.im(cto)) and cmath.abs(cto) > 1 then
				--尚不知道其他高斯整數底數的非整數轉換演算法。部分高斯整數底數無法轉換
				local real, imag = cmath.re(number), cmath.im(number)
				local function complex_div_quotient_remainder(input_number, base)
					local base_real, base_imag = cmath.re(base), cmath.im(base)
					local max_remainder = base_real * base_real + base_imag * base_imag
					for i=0,max_remainder-1 do
						local check_quotient = (input_number-i) / base
						if utils.isInt(cmath.re(check_quotient)) and utils.isInt(cmath.im(check_quotient)) then
							return check_quotient, i
						end
					end
				end
				local int_number = cmath.ceil(number)
				local frac_number = number - int_number
				if cmath.abs(frac_number) > 1e-14 then --阻止非高斯整數進行轉換,因為目前演算法不支援
					error(mw.ustring.format("轉換失敗:底數 '%s' 不支援非高斯整數 '%s' 的轉換。",tostring(to), tostring(num)), 2)
					return nil 
				end
				local tk = (utils.myceil(math.log(cmath.abs(number))/math.log(cmath.abs(cto))) + 1) * 4 + 4
				local do_number = int_number
				local counter = 0
				while cmath.abs(do_number) > 0 do
					local quotient, remainder = complex_div_quotient_remainder(do_number, cto)
					do_number = quotient
					table.insert(int_digits,1,remainder)
					
					counter = counter + 1
					if counter > tk then --轉換到變成無窮迴圈,視為失敗
						error(mw.ustring.format("轉換失敗:無法將 '%s' 轉換為底數 '%s' 的進制。", tostring(num), tostring(to)), 2)
						return nil 
					end
				end
				if #int_digits == 0 then int_digits[1] = 0 end
			else
				return nil 
			end
			local max_digs = 0
			for i=1,#int_digits do if int_digits[i] > max_digs then max_digs = int_digits[i] end end
			for i=1,#fractional_digits do if fractional_digits[i] > max_digs then max_digs = fractional_digits[i] end end
			
			if #int_digits < digs then --補齊整數位數
				local lose_digs = digs - #int_digits
				for i=1,lose_digs do
					table.insert(int_digits, 1, 0)
				end
			end
			if #fractional_digits < precision then --補齊小數位數
				local lose_digs = precision - #fractional_digits
				for i=1,lose_digs do
					fractional_digits[#fractional_digits+1] = 0
				end
			end
			local int_result, fractional_result = utils.printAllDigit(int_digits, max_digs + 1, -1, 0, false), utils.printAllDigit(fractional_digits, max_digs + 1, precision, 0, true)
			return int_result, fractional_result
		end
	end
	return nil
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 digit = utils.round(digit)
	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] or 0
	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.toDecimalComplex(int_digits, fractional_digits, imag, base)
	if type(cmath.constructor) ~= type(tostring) then cmath = require("Module:Complex Number").cmath.init()end
	base = cmath.constructor(base)
	if not base then return nil end
	local n = cmath.constructor(int_digits[#int_digits])
	local j = 1
	for i = #int_digits-1, 1, -1 do
		n = n + int_digits[i] * utils.cintpow(base, j)
		j = j + 1
	end
	for i = 1, #fractional_digits do
		n = n + fractional_digits[i] * utils.cintpow(base, -i)
	end
	n = (imag and cmath.i or cmath[1]) * n
	n:clean()
	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 '.')
	if tostring(to):sub(-1,-1) == 'i' and (int_result:find(',') or (fractional_result or''):find(',')) then point_string = ';'end
	local number_result = ((sign < 0) and '−' or '') .. (int_result == '' and '0' or int_result) .. (fractional_result == '' and '' or (point_string..fractional_result))
	local result = (prefix or '') .. number_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 success, num = pcall(utils.toDecimal, ori_int_digits, ori_fractional_digits, from)
		if not success then
			--轉換失敗時
			if type(bignum.convertBase) ~= type(tostring)then bignum = require(main_module)end
			if tonumber(to) then
				num = (tonumber(to) == 10) and number_result or "數字" --不支援的部分
			else
				success, num = pcall(bignum.convertBase, number_result, 10, to)
				if not success then num = "數字" end --不支援的部分
			end
		end
		local base_name = lib_num2zh._numberToChinese(utils.tostring(to))
		if base_name == lib_num2zh.NotANumber() then base_name = utils.tostring(to) end
		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 (base_name..'進制'), 
			(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