跳转到内容

模組:BigNumber/sandbox

本页使用了标题或全文手工转换
维基百科,自由的百科全书

这是本页的一个历史版本,由A2569875留言 | 贡献2022年10月4日 (二) 07:31编辑。这可能和当前版本存在着巨大的差异。

local p={}
local getArgs = require('Module:Arguments').getArgs
local yesno = require('Module:Yesno')
local lib_calc = require('Module:Complex_Number/Calculate')
local lib_solve = require("Module:Complex_Number/Solver")
local lib_fact ={} --Module:Factorization
local lib_bit=require('bit32');
local bit={lS=lib_bit.lshift,rS=lib_bit.rshift,Or=lib_bit.bor,And=lib_bit.band}
local utils = {}

--大數運算的Metatable
p.bigintMeta = {
	__add = function (op_1, op_2) --大數加法。如被加數或加數有負值則為大數減法
		local op1, op2 = p.bigint(op_1), p.bigint(op_2)
		local result = p.bigint()
		--位數對齊
		if op1.point > op2.point then op2:setpoint(op1.point)
		elseif op1.point < op2.point then op1:setpoint(op2.point)end
		result.point = op1.point
		--計算位數為原始位數+1 (如果進位的話)
		local length = math.max(op1:length(), op2:length()) + 1 
		local carry = 0 --進位/借位
		for i = 1,length-1 do
			--該位數相加
			local digit = op1.sign * op1:atl(i) + op2.sign * op2:atl(i) + carry
			--超過底數代表進位
			if digit >= op1.base then
				carry = 1
				digit = digit - op1.base
			--低於0則需借位
			elseif digit < 0 then
				carry = -1
				digit = digit + op1.base
			else
				carry = 0
			end
			result:setl(i, digit)
		end
		--仍有未處理的進位/借位
		if carry > 0 then
			result:setl(length, carry)
		elseif carry < 0 then
			--最高位仍有借位代表結果為負
			result.sign = -1
			carry = 0
			--全體取補數
			for i = 1,length-1 do
				local digit = op1.base - result:atl(i) + carry
				carry = -1
				result:setl(i, digit)
			end
		end
		--清除前導零
		if not (op1.nodelzero or op2.nodelzero) then result:delzero() end
		result.isNaN = op1.isNaN or op2.isNaN 
		return result
	end,
	__sub = function (op_1, op_2) --大數減法 轉為加法 (減數取相反數)
		local op1, op2 = p.bigint(op_1), p.bigint(op_2)
		op2.sign = op2.sign * -1 --減數取相反數
		return op1 + op2
	end,
	__mul = function (op_1, op_2) --大數乘法 使用FFT加速
		local op1, op2 = p.bigint(op_1), p.bigint(op_2)
		local result = p.bigint()
		local a_sign, b_sign = op1.sign, op2.sign
		local a_is_zero, b_is_zero = true, true
		local res,rea,ina,reb,inb,ret,intt = {0},{0},{0},{0},{0},{0},{0}
		local len1,len2,lent,lenres,_len;
		local s1, s2 = tostring(in1), tostring(in2)
		len1 = op1:length(); len2 = op2:length();
		if len1 > len2 then lent = len1 else lent = len2 end; _len=1
		while _len < lent do _len = bit.lS(_len,1) end _len = bit.lS(_len,1)
		--填入FFT序列
	    for i = 0,_len-1 do
			if i < len1 then rea[i+1] = op1.data[len1-i] end
			if i < len2 then reb[i+1] = op2.data[len2-i] end
			a_is_zero = a_is_zero and (rea[i+1]or 0) < 1e-14
			b_is_zero = b_is_zero and (reb[i+1]or 0) < 1e-14
			ina[i+1],inb[i+1] = 0,0;
	    end
		--乘法正負號結果為兩者正負號相乘
		local res_sign = a_sign * b_sign
		--若被乘數或乘數為零則結果為零
	    if a_is_zero or b_is_zero then
	    	local _zero = p.bigint()
	    	_zero.sign = res_sign < 0 and -1 or 1
	    	return _zero
	    end
	    --執行FFT
		p._FFT(rea,ina,_len,false); p._FFT(reb,inb,_len,false);
		--執行捲積
	    for i=0,_len-1 do
			local rec = rea[i+1] * reb[i+1] - ina[i+1] * inb[i+1];
			local inc = rea[i+1] * inb[i+1] + ina[i+1] * reb[i+1];
			rea[i+1] = rec; ina[i+1] = inc;
	    end
		--執行逆FFT
		p._FFT(rea,ina,_len,true);--ifft
	    for i=0,_len-1 do rea[i+1] = rea[i+1] / _len; ina[i+1] = ina[i+1] / _len end
	
	    for i=0,_len-1 do res[i+1] = math.floor(rea[i+1] + 0.5)end
	    for i=0,_len-1 do res[i+2] = (res[i+2]or 0) + math.floor((res[i+1]or 0) / op1.base) ; res[i+1] = (res[i+1]or 0) % op1.base end
	
	    lenres = len1 + len2 + 2;
	    while (res[lenres+1]or 0) == 0 and lenres > 0 do lenres=lenres-1 end
		local j = 1
		for i=lenres,0,-1 do 
			result.data[j] = (res[i+1]or 0)
			j = j + 1
		end
		result.sign = res_sign < 0 and -1 or 1
		result.point = op1.point + op2.point
		if result.point > result:length() then result:fractionalzero()end
		if not (op1.nodelzero or op2.nodelzero) then result:delzero() end
		result.isNaN = op1.isNaN or op2.isNaN 
		return result
	end,
	__div = function (op_1, op_2) --大數除法 轉為乘法 (除數取倒數)
		local op1, op2 = p.bigint(op_1), p.bigint(op_2)
		local invop2 = op2:inverse(op_1:length() * 2 + 2)
		local result = op1 * invop2 --將除法轉換成乘以倒數
		result:setpoint(op_1:length() + 1)
		local pointfix = p.bigint("1")
		pointfix.point = result.point
		pointfix:fractionalzero()
		result = result + pointfix
		result:setpoint(op_1:length())
		if not (op1.nodelzero or op2.nodelzero) then result:delzero() end
		result.isNaN = op1.isNaN or op2.isNaN 
		return result
	end,
	__mod = function (op_1, op_2)
		local op1, op2 = p.bigint(op_1), p.bigint(op_2)
		local divided = op1 / op2
		divided:setpoint(0)
		local result = op1 - divided * op2
		if not (op1.nodelzero or op2.nodelzero) then result:delzero() end
		result.isNaN = op1.isNaN or op2.isNaN 
		return result
	end,
	__pow = function (op_1, op_2)
		local op1, op2 = p.bigint(op_1), tonumber(tostring(op_2)) or 1
		local this = op1
		if this.isNaN then return this:clone() end
		--非整數的指數不支援,目前僅能計算某數的整數次方。使用一般非高精度的math.pow運算
		if math.abs(utils.myfloor(op2) - op2) > 1e-14 then return p.bigint(math.pow(tonumber(tostring(op_1)) or 1, op2)) end
		if op2 < 0 then --負的次方為倒數自乘
			this = this:inverse(this:length() + 1) 
			op2 = -op2
		end
		if op2 == 0 then return p.bigint(1) end
		if op2 == 1 then return this:clone() end
		if op2 == 2 then return this * this end
		local loge = math.log(op2)
		local log2 = loge / math.log(2)
		if utils.isInt(log2) then --次方為2的冪 直接連續自乘,以減少乘法運算的次數
			local result = this:clone()
			for i=1,log2 do
				result = result * result
			end
			return result
		end
		local log3 = loge / math.log(3)
		if utils.isInt(log3) then --次方為3的冪 直接連續的3次自乘,以減少乘法運算的次數
			local result = this:clone()
			for i=1,log3 do
				result = result * result * result
			end
			return result
		end

		local times_data = {}
		local times_times = {}
		
		local two_time = math.pow(2, math.floor(log2)) --其餘情況轉換成2的冪的組合,以減少乘法運算的次數
		local lose_time = op2 - two_time
		local to_times = this:clone()
		local zero_flag = 0
		repeat
			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)
			two_time = math.pow(2, math.floor(log2))
			lose_time = lose_time - two_time
			to_times = this:clone()
			if lose_time <= 0 then zero_flag = zero_flag + 1 end
		until zero_flag > 1

		local result = p.bigint(1)
		--for i=1,op2 do
		--	result = result * this
		--end
		for i=1,#times_data do
			result = result * times_data[i]
		end
		return result
	end,
	__tostring = function (this)
		local this_length = this:length()
		local result = ''
		for i = 1, this_length do
			if i == this_length - this.point + 1 then
				result = result .. '.'
			end
			result = result .. string.format(string.format("%%0%dd", this.base_pow), this.data[i])
		end
		if result:find("%.") then else
			result = result .. '.'
		end
		result = mw.text.trim(result,"0")
		if result:sub(1,1) == '.' then result = '0' .. result end
		result = mw.text.trim(result,".")
		if mw.text.trim(result) == '' then result = '0' end
		if this.isNaN then result = 'nan' end
		if this.sign < 0 then result = '−' .. result end
		return result
	end,
	__unm = function (this)
		local result = this:clone()
		result.sign = result.sign * -1
		return result
	end,
	__eq = function (op_1, op_2)
		local op1, op2 = p.bigint(op_1), p.bigint(op_2)
		return op1:equal(op2)
	end,
	__lt = function (op_1, op_2)
		return op_1:less(op_2)
	end,
	__le = function (op_1, op_2)
		return op_1:lessequal(op_2)
	end,
}

--大數帶餘長除法 / 可作為進制轉換用的帶餘長除法
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 * destination_base + carry
			carry = math.floor(d / original_base)
			d = utils.mod(d, original_base)
		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
	return result, carry --回傳商, 餘數
end

function p.bigint(input_data, base)
	local _base_pow = 6
	local _base = 10 ^ _base_pow
	if type(input_data) == type({}) and (input_data or {})['type'] == 'bigint' then return input_data end
	local _bigint = { --大數資料結構
		data = {0}, --大數的位數
		sign = 1, --大數的正負號
		point = 0, --小數位數
		base = _base, --運算的底數
		base_pow = _base_pow, --該底數是10的多少次方,用於處理輸出
		['type'] = 'bigint', --標記type為bigint
		numberType = 'bigint'
	}
	function _bigint:length() --取得大數的位數
		return #(self.data)
	end
	function _bigint:atl(dig) --取得從右起算的第n位數
		local idx = self:length() - dig + 1
		if idx <= 0 then
			for i = 1,1-idx do
				table.insert(self.data, 1, 0)
			end
		end
		return self.data[self:length() - dig + 1] or 0
	end
	function _bigint:setl(dig, value) --設定從右起算的第n位數
		local idx = self:length() - dig + 1
		if idx <= 0 then
			for i = 1,1-idx do
				table.insert(self.data, 1, 0)
			end
		end
		self.data[self:length() - dig + 1] = value
		return self
	end
	function _bigint:nan() --標記為NaN
		self.isNaN = true
		return self
	end
	function _bigint:setpoint(point) --設定小數位數
		local point_diff = point - self.point
		if point_diff > 0 then
			for i=1,point_diff do
				self.data[self:length() + 1] = 0
			end
		elseif point_diff < 0 then
			for i=1,-point_diff do
				table.remove( self.data, self:length())
			end
		end
		self.point = point
		return self
	end
	function _bigint:fractionalzero() --依據小數位數補齊零至個位數
		if self.point >= self:length() then
			local lost_digs = self.point - self:length()
			for i=1,lost_digs+1 do
				table.insert(self.data, 1, 0)
			end
		end
	end
	function _bigint:delzero() --移除前導零
		for i=1,self:length()-self.point do
			if math.abs(self.data[1]) > 1e-14 then break
			else
				table.remove( self.data, 1)
			end
		end
		local self_point = self.point
		for i=1,self_point do
			if math.abs(self:atl(1)) > 1e-14 then break
			else
				table.remove( self.data, self:length())
				self.point = self.point - 1
			end
		end
		return self
	end
	function _bigint:equal(op) --大數相等判斷
		local other = p.bigint(op):clone()
		if self.isNaN or other.isNaN then return false end
		if self.sign ~= other.sign then
			if utils.is_zero(self.data) and utils.is_zero(other.data) then
				return true
			end
			return false 
		end
		local myself = self:clone()
		myself:delzero()
		other:delzero()
		local max_point = math.max(myself.point, other.point)
		myself:setpoint(max_point + 1)
		other:setpoint(max_point + 1)
		local max_digs = math.max(myself:length(), other:length())
		for i = 1, max_digs do
			if myself:atl(i) ~= other:atl(i) then return false end
		end
		return true
	end
	function _bigint:clone() --複製一份大數物件
		local result = p.bigint()
		for i=1,self:length() do result.data[i] = self.data[i]end
		for k,v in pairs(self) do
			if k~="data" and type(v) ~= type({}) and type(v) ~= type(function()end) then
				result[k] = v
			end
		end
		return result
	end
	function _bigint:intlength() --取得整數部分的位數
		local length = self:length() - self.point
		if length == 1 and math.abs(self.data[length]) < 1e-14 then return 0 end
		return length
	end
	function _bigint:divsmall(other) --大數除一般的數 (長除法)
		local num = (type(other) == type(0)) and other or (tonumber(tostring(other)) or 1)
		local result = self:clone()
		result.data = utils.modulo_div(result.data, result.base, num)
		return result
	end
	function _bigint:divdigits(op_2, digit) --大數除法指定計算位數
		local op1, op2 = self, p.bigint(op_2)
		local invop2 = op2:inverse(digit * 2 + 2)
		local result = op1 * invop2
		result:setpoint(digit + 1)
		local pointfix = p.bigint("1")
		pointfix.point = result.point
		pointfix:fractionalzero()
		result = result + pointfix
		result:setpoint(digit)
		if not (op1.nodelzero or op2.nodelzero) then result:delzero() end
		result.isNaN = op1.isNaN or op2.isNaN 
		return result
	end
	function _bigint:inverse(_digs) --大數倒數 (牛頓法)
		local digs = (_digs or (self:length() * 2)) + 1
		if self:equal(p.bigint("0")) then error("嘗試除以零") end
		--計算牛頓法迭代起始值
		local init = p.bigint("1")
		local intlength = self:intlength()
		for i=1,digs - intlength + 1 do
			init.data[init:length() + 1] = 0
		end
		local myself = self:clone()
		myself:delzero()
		local first_non_zero, pre_point = myself.data[1], 0
		--若要計算的數絕對值小於1需要補齊位數
		for i = 2, myself:length() do
			if math.abs(first_non_zero) > 1e-14 then break end
			pre_point = pre_point + 1
			first_non_zero = myself.data[i]
		end
		--以1除以最高位數作為起始值,使用長除法
		init.data = utils.modulo_div(init.data, myself.base, first_non_zero)
		init.nodelzero = true
		for i=1,intlength - pre_point - 1 do
			table.insert(init.data, 1, 0)
		end
		for i=1,pre_point do
			init.data[#init.data + 1] = 0
		end
		init.point = digs
		--設定牛頓法起始值
		local x0 = (2 - init * self) * init
		x0:fractionalzero()
		x0.nodelzero = true
		x0:setpoint(digs)

		local x1 = x0
		x0 = init

		local i = 0
		--迭代,當各個位數值不再改變則結束計算
		while not x0:equal(x1) do
			--x1 = (2 - x0 * num) * x0
			local new_x1 = (2 - x0 * self) * x0
			new_x1:fractionalzero()
			new_x1.nodelzero = true
			new_x1:setpoint(digs)
			x1 = x0
			x0 = new_x1
			--避免無窮迴圈,設定最高迭代次數
			if i > 20 then break end
			i = i + 1
		end
		return x0
	end
	function _bigint:less(other)
		local op1, op2 = p.bigint(self), p.bigint(other)
		if op1.point > op2.point then op2:setpoint(op1.point)
		elseif op1.point < op2.point then op1:setpoint(op2.point)end
		local total_len = math.max(op1:length(), op2:length())
		for i=1,total_len do
			local j = total_len - i + 1
			local a, b = op1:atl(j) * op1.sign, op2:atl(j) * op2.sign
			if a ~= b then
				return a < b
			end
		end
		return false
	end
	function _bigint:lessequal(other)
		local op1, op2 = p.bigint(self), p.bigint(other)
		if op1.point > op2.point then op2:setpoint(op1.point)
		elseif op1.point < op2.point then op1:setpoint(op2.point)end
		local total_len = math.max(op1:length(), op2:length())
		for i=1,total_len do
			local j = total_len - i + 1
			local a, b = op1:atl(j) * op1.sign, op2:atl(j) * op2.sign
			if a ~= b then
				return a < b
			end
		end
		return true
	end
	setmetatable(_bigint, p.bigintMeta) 
	if input_data == nil then return _bigint end
	local in_str = tostring(input_data)
	in_str = mw.text.trim(in_str)
	--取得第一個字元判斷正負號
	local first_sign = mw.ustring.sub(in_str,1,1)
	if first_sign == '-' or first_sign == '−' then _bigint.sign = -1 end
	--從字串讀取位數
	local int_digits, fractional_digits = lib_calc._getNumString(in_str)
	local src_base = tonumber(base) or 10
	if math.abs(src_base) <= 1 then src_base = 10 end
	--轉換為大數運算的目標進位制
	_bigint.data = utils._convertBase(int_digits, src_base, _base, false)
	fractional_digits = utils._convertBase(fractional_digits, src_base, _base, true)
	--將位數存入大數物件
	for i=1,#fractional_digits do
		_bigint.data[_bigint:length() + 1] = fractional_digits[i]
	end
	_bigint.point = #fractional_digits
	return _bigint
end

p.bigintmath = {
	abs=function(op)
		local num = p.bigint(op):clone()
		num.sign = 1
		return num
	end,
	floor=function(op)
		local num = p.bigint(op):clone()
		if num.sign < 0 then
			num.sign = 1
			num = p.bigintmath.ceil(num)
			num.sign = -1
			return num
		end
		num:setpoint(0)
		return num
	end,
	ceil=function(op)
		local num = p.bigint(op):clone()
		if num.sign < 0 then
			num.sign = 1
			num = p.bigintmath.floor(num)
			num.sign = -1
			return num
		end
		num:delzero()
		if num.point > 0 then
			num:setpoint(0)
			num = num + 1
		end
		return num
	end,
	div=function(op1,op2)
		return op1 / op2
	end,
	re=function(z)return p.bigint(z) end,
	nonRealPart=function(z) return p.bigint(0) end,
	inverse=function(op)
		local num = p.bigint(op):clone()
		return num:inverse(16)
	end,
	digits=function(op)
		local num = p.bigint(op)
		return p.bigint(num:intlength())
	end,
	sqrt=function(op) --計算平方根,牛頓法
		local num = p.bigint(op)
		if num:less(0) then error('不支援計算負值的平方根') end
		local i = 0
		
		--先用一般的math.sqrt計算
		local init_sqrt = math.sqrt(tonumber(tostring(op)))
		local x0 = p.bigint(-1)
		local x1 = p.bigint(init_sqrt)
		local check_sqrt = tostring(init_sqrt)
		if check_sqrt:find("[Ee]") then else
			local strlen = check_sqrt:gsub("%.",''):len()
			--若結果位於有效數字內,則直接回傳運算結果
			if strlen < 13 then
				return x1
			end
		end
		--若計算的數字大小超過math.sqrt能計算的範圍及精度,則開始調用牛頓法
		local i = 0
		local digits = x1:length()+num:length()
		--計算至各個位數不變時則停止
		while not x0:equal(x1) and not x0:equal(x1 - 1) do
			x0 = x1
			--牛頓法迭代
			-- x1 = (num / x0 + x0) / 2
			x1 = (num:divdigits(x0,digits+3) + x0):divsmall(2)
			if x0.point > 0 or x1.point > 0 then
				x0:setpoint(digits+2)
				x1:setpoint(digits+2)
			end
			x0:delzero()
			x1:delzero()
			--避免無窮迴圈,設定最高迭代次數
			if i > 20 then break end
			i = i + 1
		end
		if x0.point > 0 then x0:setpoint(digits)end
		return x0
	end,
	coterminal_angle=function(op)
		local num = p.bigint(op)
		local twopi = p.bigintmath.pi * 2
		return num - p.bigintmath.floor(num / twopi) * twopi
	end,
	sin=function(op_1)
		local op1 = tonumber(tostring(p.bigintmath.coterminal_angle(p.bigint(op_1)))) or 0
		return p.bigint(math.sin(op1))
	end,
	cos=function(op_1)
		local op1 = tonumber(tostring(p.bigintmath.coterminal_angle(p.bigint(op_1)))) or p.bigintmath.pi
		return p.bigint(math.cos(op1))
	end,
	tan=function(op_1)
		local op1 = tonumber(tostring(p.bigintmath.coterminal_angle(p.bigint(op_1)))) or 0
		return p.bigint(math.tan(op1))
	end,
	cot=function(op_1)
		local op1 = tonumber(tostring(p.bigintmath.coterminal_angle(p.bigint(op_1)))) or 0
		return p.bigint(1/math.tan(op1))
	end,
	asin=function(op_1)
		local op1 = tonumber(tostring(op_1)) or 0
		return p.bigint(math.asin(op1))
	end,
	acos=function(op_1)
		local op1 = tonumber(tostring(op_1)) or 1
		return p.bigint(math.acos(op1))
	end,
	atan=function(op_1)
		local op1 = tonumber(tostring(op_1)) or 1
		return p.bigint(math.atan(op1))
	end,
	acot=function(op_1)
		local op1 = tonumber(tostring(op_1)) or 1
		return p.bigint(math.atan(1/op1))
	end,
	sinh=function(op_1)
		local op1 = tonumber(tostring(op_1)) or 0
		return p.bigint(math.sinh(op1))
	end,
	cosh=function(op_1)
		local op1 = tonumber(tostring(op_1)) or 1
		return p.bigint(math.cosh(op1))
	end,
	tanh=function(op_1)
		local op1 = tonumber(tostring(op_1)) or 1
		return p.bigint(math.tanh(op1))
	end,
	coth=function(op_1)
		local op1 = tonumber(tostring(op_1)) or 1
		return p.bigint(math.cosh(op1) / math.sinh(op1))
	end,
	asinh=function(op_1)
		local op1 = tonumber(tostring(op_1)) or 0
		return p.bigint(math.log( op1 + math.sqrt( op1 * op1 + 1 ) ))
	end,
	acosh=function(op_1)
		local op1 = tonumber(tostring(op_1)) or 1
		return p.bigint(math.log( op1 + math.sqrt( op1 * op1 - 1 ) ))
	end,
	atanh=function(op_1)
		local op1 = tonumber(tostring(op_1)) or 1
		return p.bigint(0.5 * math.log((1+op1)/(1-op1)))
	end,
	acoth=function(op_1)
		local op1 = tonumber(tostring(op_1)) or 1
		return p.bigint(0.5 * math.log((op1+1)/(op1-1)))
	end,
	sgn=function(op)
		local num = p.bigint(op)
		return p.bigint(num.sign)
	end,
	exp=function(op)
		return p.bigintmath.e ^ tonumber(tostring(op))
	end,	
	pow=function(op_1, op_2)
		local op1, op2 = p.bigint(op_1), tonumber(tostring(op_2)) or 1
		return op1 ^ op2
	end,
	elog=function(op_1)
		local op1 = tonumber(tostring(op_1)) or 1
		return p.bigint(math.log(op1))
	end,
	log=function(_z,_basez)
		local z = tonumber(tostring(_z)) or 1
		local basez = tonumber(tostring(_basez))
		if basez~=nil then return math.log(basez) / math.log(z) end
		return p.bigint(math.log(z))
	end,

	factorial=function(op)
		local num = tonumber(tostring(op)) or 1
		local result = p.bigint(1)
		for i=1,num do
			result = result * i
		end
		return result
	end,
	bigint=function()
		return 1
	end,
	init = function(base)
		p.bigintmath.base = tonumber(base) or 10
		p.bigintmath.e  = p.bigint("2.718281828459045235360287471352662497")
		p.bigintmath.pi = p.bigint("3.141592653589793238462643383279502884")
		p.bigintmath["π"] = p.bigintmath.pi
		p.bigintmath["°"] = p.bigint(p.bigintmath.pi/180)
		p.bigintmath.nan = p.bigint():nan()
		p.bigintmath.zero = p.bigint(0) 
		p.bigintmath.one = p.bigint(1) 
		p.bigintmath[-1] = p.bigint(-1) 
		p.bigintmath[0],p.bigintmath[1] = p.bigint(0),p.bigint(1)
		p.bigintmath.elements = {p.bigint(1)}
		p.bigintmath.numberType = lib_solve._numberType
		p.bigintmath.constructor = function(x)
			if type(x) == type({}) and (x or {})['type'] == 'bigint' then return x end
			if tonumber(tostring(x), p.bigintmath.base) then
				return p.bigint(x)
			end
			return nil
		end
		return p.bigintmath
	end
}

function utils._convertBase(n, _original_base, _destination_base, fractional_flag)
	local digits = {}
	if utils.isInf(_original_base) then
		error(string.format("底數不能為 '%s'", tostring(_original_base)))
	end
	local original_base = utils.getBaseValue(_original_base, 0)
	local destination_base = utils.getBaseValue(_destination_base, 0)
	local dn, digit = n
	local max_digit = #n + 1
	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) --加成正的
			--商+1
			if fractional_flag then
				for j=1,#dn do
					dn[j] = original_base - dn[j]
				end
			else
				dn[#dn] = dn[#dn] + 1
			end
		end
		--計算小數時,位數加在末尾
		if fractional_flag then
			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 p.convertBase(_num, _to, _from, _digs, _precision, _sub)
	local num, from, to, digs, subarg, precision = tostring(_num) or "0", _from or 10, _to or 10, _digs or 0, _sub or 0, _precision or -1
	local from_str, to_str = tostring(_from or ''), tostring(_to or '')
	local default, prefix, suffix = '', '', ''
	local no_from = not _from
	local is_template = false
	--從模板讀取參數
	if type(_num) == type({}) then
		local frame = _num
		local success, args = false, frame
		is_template = true
		if type((((type(_num) == type(0)) and {} or _num) or {}).args) == type({}) then
			success, args = pcall(getArgs, frame, {
	        	parentFirst=true
	        }) --frame.args
			if not success then args = frame.args or frame end
		end
		local arg1 = mw.ustring.gsub(mw.text.trim(args[1] or args['1'] or ''), "[-−]+", "-")
		local arg2 = mw.text.trim(args[2] or args['2'] or args.number or args.Number or args.num or args.Num or args.n or args.N or '')
		local arg3 = mw.text.trim(args[3] or args['3'] or args.width or args.Width or '')
		local argTo = mw.ustring.gsub(mw.text.trim(args.to or args.To or args.base or args.Base or ''), "[-−]+", "-")
		local argFrom = mw.ustring.gsub(mw.text.trim(args.from or args.From or ''), "[-−]+", "-")
		local argSub = mw.text.trim(args['sub'] or args.Sub or '')
		local argDefault = args.default or args.Default or ''
		local argPrecision = mw.text.trim(args.precision or args.Precision or '')
		local argPrefix = args.prefix or args.Prefix or ''
		local argSuffix = args.suffix or args.Suffix or ''
		if arg1 ~= '' then 
			success, to = pcall(utils.checkSpecialBase, arg1)
			to = to or arg1
			to_str = tostring(arg1)
		elseif argTo ~= '' then 
			success, to = pcall(utils.checkSpecialBase, argTo)
			to = to or argTo
			to_str = tostring(argTo)
		end
		if arg2 ~= '' then num = tostring(arg2)end
		if arg3 ~= '' then digs = tonumber(arg3) or 0 end
		if argFrom ~= '' then 
			success, from = pcall(utils.checkSpecialBase, argFrom)
			no_from = not from
			from = from or argFrom
			from_str = tostring(argFrom)
		else no_from = true end
		if argSub ~= '' then subarg = tonumber(argSub) or 0 end
		if argDefault ~= '' then default = argDefault end
		if argPrefix ~= '' then prefix = argPrefix end
		if argSuffix ~= '' then suffix = argSuffix end
		if argPrecision ~= '' then precision = tonumber(argPrecision) or -1 end
		if yesno(args.error or args.Error or '') then is_template = false end
	end
	
	---------------- 例外處理 ----------------
	--無限大判斷 (若放任無限大進去計算會無窮迴圈而超時)
	if utils.isInf(from) then
		if mw.ustring.find(num, "[,:]") then --不只一個位數的無限大進制無法轉換
			if not is_template then error(string.format("底數不能為 '%s'", from_str)) end
			return utils.print_base_string('∞', --硬要說的話其值就是無限大
				"", {tonumber("inf")}, {},({mw.ustring.find(num, "[-−]")})[1] and -1 or 1, from, to, subarg, prefix, suffix)
		else
			--只有一個位數的無限大進制就原數輸出
			return utils.print_base_string(num, "", {utils.tonumber(num) or 0}, {},({mw.ustring.find(num, "[-−]")})[1] and -1 or 1, from, to, subarg, prefix, suffix)
		end
	end
	
	--NaN判斷 (若放任NaN進去計算會無窮迴圈而超時)
	if utils.isNaN(from) then --並非有效的底數,無法轉換
		if not is_template then error(string.format("底數不能為 '%s'", from_str)) end
		return default
	end
	if utils.isNaN(to) then --並非有效的底數,無法轉換
		if not is_template then error(string.format("底數不能為 '%s'", to_str)) end
		return default
	end
	
	--大過整數運算範圍的底數無法計算 (誤差導致運算結果不準確)
	if math.abs(utils.tonumber(from)or 0) > 9007199254740991 and not utils.isInf(from) then
		if not is_template then error(string.format("底數 '%s' 過大", from_str)) end
		return default
	end
	if math.abs(utils.tonumber(to)or 0) > 9007199254740991 and not utils.isInf(to) then
		if not is_template then error(string.format("底數 '%s' 過大", to_str)) end
		return default
	end
	
	--判斷是否為特殊進制
	local special_base_data_from = utils.getSpecialBase(from)
	local special_base_data_to = utils.getSpecialBase(to)
	
	--並非能夠運算的底數
	if not utils.tonumber(from) and not special_base_data_from then
		if not is_template then error(string.format("'%s' 不是有效的底數", from_str)) end
		return default
	end
	if not utils.tonumber(to) and not special_base_data_to then
		if not is_template then error(string.format("'%s' 不是有效的底數", to_str)) end
		return default
	end
	---------------- 例外處理結束,開始轉換進制 ----------------
	
	local to_num = utils.tonumber(to) or 10
	local from_num = utils.tonumber(from) or 10
	if math.abs(to_num) < 1 then --base can not less then 1
		if not is_template then error("底數不能小於1") end 
		return default 
	end
	local sign = 1
	num = mw.text.trim(num)
	if _num == nil or num == '' then return default end
	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
	--當輸入開頭為'0x'時視為16進制
	if no_from or from == 16 then
		local chex_hex = mw.ustring.sub(num,1,2)
		if chex_hex == '0x' or chex_hex == '0X' then
			from = 16
			num = mw.ustring.sub(num,3,-1)
		end
	end
	local ori_int_digits, ori_fractional_digits = lib_calc._getNumString(num)
	--負進制、非整數進制等無法經由長除法整數進制轉整數進制的Case先轉為十進制再做處理
	if from_num < -1 or math.abs(utils.myfloor(from_num) - from_num) > 1e-14 or (special_base_data_from or{}).needtoDecimal then
		if (special_base_data_from or{}).name == 'ContinuedFraction' then
			local dec_string = tostring(utils.fromContinuedFraction(ori_int_digits, ori_fractional_digits))
			ori_int_digits, ori_fractional_digits = lib_calc._getNumString(dec_string)
		else
			local dec_number = utils.toDecimal(ori_int_digits, ori_fractional_digits, from) * sign
			if dec_number < 0 then
				sign = -1
				dec_number = -dec_number
			else
				sign = 1
			end
			ori_int_digits, ori_fractional_digits = lib_calc._getNumString(tostring(dec_number))
		end
		from = 10
	end
	--負底數進制在轉換過程中要連正負號一同考量
	if to_num < 0 and sign < 0 then
		local check = math.abs(to_num)
		if math.abs(utils.myfloor(check)-check)>1e-14 then
			if not is_template then error(string.format("不支援底數 '%s' 的進制", to_str)) end 
			return default
		end
		if sign < 0 then
			sign = 1
			for i=1,#ori_int_digits do ori_int_digits[i] = -ori_int_digits[i]end
			for i=1,#ori_fractional_digits do ori_fractional_digits[i] = -ori_fractional_digits[i]end
		end
	end
	local int_digits, fractional_digits = ori_int_digits, ori_fractional_digits
	if math.abs(utils.myfloor(to_num) - to_num) > 1e-14 or math.abs(from_num + 1) < 1e-14 then --非整數進制的處理
		int_digits, fractional_digits, sign = utils.non_integer_base(int_digits, fractional_digits, from, to, sign)
		for i = #fractional_digits, 1, -1 do
			if fractional_digits[i] ~= 0 then break
			else
				table.remove(fractional_digits, i)
			end
		end
	elseif math.abs(math.abs(to_num) - 1) < 1e-14 then --一進制,自然數中最簡單的進制,輸出跟數字相同數量的1即可
		local number = utils.myfloor(utils.toDecimal(ori_int_digits, {}, from))
		if math.abs(number) <= 9007199254740991 then --Lua整數運算上限
			local base1 = (math.abs(number) > 0) 
				and ((to_num < 0) 
					and ((number < 0) 
						and string.rep('10', math.abs(number)) 
						or ('1' .. ((math.abs(number - 1) < 1e-14) and '' or string.rep('01', number - 1) )) )
					or string.rep('1', math.abs(number)))
				or '0'
			if base1:len() < digs then --補齊位數
				local lose_digs = digs - base1:len()
				base1 = string.rep('0', lose_digs) .. base1
			end
			return utils.print_base_string(base1, "", ori_int_digits, {},(to_num < 0) and 1 or sign, from, to, subarg, prefix, suffix)
		end
		if not is_template then error(string.format("無法將 '%s' 轉換成底數為 '%s' 的進制", num, to_str)) end 
		return default
	else --其餘情況即一般情況,使用整數進制轉整數進制的長除法演算法
		int_digits = ((special_base_data_to or{}).convertBase or utils._convertBase)(int_digits, from, to, false)
		fractional_digits = ((special_base_data_to or{}).convertBase or utils._convertBase)(fractional_digits, from, to, true)
	end
	---------------- 進制轉換完成,準備輸出數字 ----------------
	local int_result, fractional_result = '', ''

	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
	
	int_result = utils.printAllDigit(int_digits, to, -1, subarg, false)
	fractional_result = utils.printAllDigit(fractional_digits, to, precision, subarg, true)

	local result = utils.print_base_string(int_result, fractional_result, ori_int_digits, ori_fractional_digits, sign, from, to, subarg, prefix, suffix)
	return result
end

function p._FFT(reA, inA, num, flag)
	local lgn = math.floor(math.log(num) / math.log(2))
	for i=0,num-1 do
		local j = bit.rev(i,lgn)
		if j > i then utils._swap(reA, i+1, reA, j+1); utils._swap(inA, i+1, inA, j+1) end
	end
	for s=1,lgn do
		local m = bit.lS(1,s)
		local reWm, inWm = math.cos(2*math.pi/m), math.sin(2*math.pi/m)
		if flag==true then inWm = -inWm end
		local k = 0 while k < num do
			local reW, inW = 1.0, 0.0
			for j=0,math.floor(m/2)-1 do
				local tag = k + j + math.floor(m / 2);
                local reT = reW * (reA[tag+1]or 0) - inW * (inA[tag+1]or 0);
                local inT = reW * (inA[tag+1]or 0) + inW * (reA[tag+1]or 0);
                local reU, inU = (reA[k+j+1]or 0), (inA[k+j+1]or 0);
                reA[k+j+1] = reU + reT; inA[k+j+1] = inU + inT;
                reA[tag+1] = reU - reT; inA[tag+1] = inU - inT;
                local reWt = reW * reWm - inW * inWm;
                local inWt = reW * inWm + inW * reWm;
                reW = reWt; inW = inWt;
			end
		k=k+m end
	end
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.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 bit.rev(x,_len)
	local ans = 0
	for i=1,_len do ans=bit.lS(ans,1);ans=bit.Or(ans,bit.And(x,1));x=bit.rS(x,1) end
	return ans
end

function utils.fibonacci_coding(int_digits, original_base, _destination_base, fractional_flag)
	if fractional_flag then return {} end
	local n = utils.myfloor(utils.toDecimal(int_digits, {}, original_base))
	if math.abs(tonumber(table.concat(int_digits,''))-1)<1e-14 then return {1} end
	local sqrt5 = math.sqrt(5)
	local phi = (sqrt5 + 1) * 0.5
	local logphi = math.log(phi)
	local k = utils.myceil(math.log(n*sqrt5-0.5)/logphi)
	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
	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,1,-1 do
		result[#result + 1] = vaild_fib[i] or 0
	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.fromContinuedFraction(int_digits, fractional_digits)
	local result = p.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)
	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 = p.ContinuedFraction(decimal_string, math.ceil(decimal_string:len() * 0.8))
	return result_digits
end

--以大數運算計算連分數
function p.ContinuedFraction(input_str, _length)
	local num = input_str
	local length = tonumber(_length) or 10
	local is_template = false
	local suffix = _suffix or ''
	if type(input_str) == type({"table"}) then
		num = (input_str.args or {})[1] or input_str[1] or ''
		length = tonumber((input_str.args or {})[2] or input_str[2] or '') or 4
		if input_str.args then is_template = true end
	elseif type(input_str) ~= type("string") then
		num = tostring(input_str)
	end

	local input_num = p.bigint(num)
	local sign = input_num.sign
	input_num.sign = 1
	local calclen = (input_num:length() < 4) and (input_num:length() * 2) or (input_num:length() + 2)
	local int_part = input_num:clone():setpoint(0)
	local int_digits, fractional_digits = {tonumber(tostring(int_part))}, {}
	local it = input_num - int_part
	local i = 1
	while not it:equal(0) do
		it = it:inverse(calclen)
		int_part = it:clone():setpoint(0)
		fractional_digits[#fractional_digits + 1] = tonumber(tostring(int_part))
		it = it - int_part
		if i >= length then break end
		i = i + 1
	end
	if is_template then
		return table.concat(int_digits,',') .. ';' .. table.concat(fractional_digits,',')
	end
	return int_digits, fractional_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
			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 = utils.fibonacci_coding,
	},
	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.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)) 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 load_bases = mw.text.split(base_name, ',')
		local bases = {}
		local max_base = 0
		local repeat_base = false
		for i=1,#load_bases do
			local unit_base = utils.tonumber(load_bases[i]) or 10
			if 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
				table.insert(bases, 1, unit_base)
			end
		end
		local new_base_data = {
			name = base_string,
			display = base_name,
			page = "混合进制",
			digitsplit = split,
			point = point,
			base_list = 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,digit_id do
					result = result * (this.base_list[this.repeat_base and utils.loop_mod(i, #(this.base_list)) or i] or this.base_list[#(this.base_list)] or 10)
				end
				return result
			end,
			baseValue = function(this, digit_id)
				return (digit_id < 0) 
					and (this.base_list[this.repeat_base and utils.loop_mod(-digit_id, #(this.base_list)) or (-digit_id)] or this.base_list[#(this.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 max_base > 36) and (not this.az_digits) then
					return 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)) 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 load_bases = mw.text.split(base_string, ',')
		for i=1,#load_bases do if not utils.tonumber(load_bases[i]) 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 (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 (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 = ((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 p