跳转到内容

模組:BigNumber/utils

本页使用了标题或全文手工转换
被永久保护的模块
维基百科,自由的百科全书

这是本页的一个历史版本,由A2569875留言 | 贡献2022年11月1日 (二) 10:58 建立内容为“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) t…”的新页面)编辑。这可能和当前版本存在着巨大的差异。

(差异) ←上一修订 | 最后版本 (差异) | 下一修订→ (差异)

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