Module:Infobox ET: Difference between revisions

From Xenharmonic Wiki
Jump to navigation Jump to search
Undo revision 184876 by ArrowHead294 (talk). The debug mode is used in several places on the wiki where categories should be disabled
Tag: Undo
Prevent timeout by adopting a lower limit for non-octave equal tunings
Line 29: Line 29:
local converges = rat.converges(ratio, math.log(interval) / math.log(rat.as_float(et.equave)))
local converges = rat.converges(ratio, math.log(interval) / math.log(rat.as_float(et.equave)))
if et.size > 1 and converges then
if et.size > 1 and converges then
convergement_notice = "<br />(" .. converges .. ")"
convergement_notice = "<br>(" .. converges .. ")"
end
end


Line 182: Line 182:
end
end


-- consistency and distinct consistency
-- max_limit is used to prevent timeout
local consistency = tonumber(frame.args["Consistency"])
local consistency = tonumber(frame.args["Consistency"])
local max_limit = rat.eq(et.equave, 2) and 43 or 32
if consistency == nil then
if consistency == nil then
consistency = limits.consistency_limit(et, false, 43)
consistency = limits.consistency_limit(et, false, max_limit)
end
end
if consistency == nil then
if consistency == nil then
consistency = "at least 43"
consistency = "at least " .. max_limit
end
end
if consistency ~= nil then
if consistency ~= nil then
Line 197: Line 200:
local distinct_consistency = tonumber(frame.args["Distinct consistency"])
local distinct_consistency = tonumber(frame.args["Distinct consistency"])
if distinct_consistency == nil then
if distinct_consistency == nil then
distinct_consistency = limits.consistency_limit(et, consistency or true, 43)
distinct_consistency = limits.consistency_limit(et, consistency or true, max_limit)
end
end
if distinct_consistency == nil then
if distinct_consistency == nil then
distinct_consistency = "at least 43"
distinct_consistency = "at least " .. max_limit
end
end
if distinct_consistency ~= nil then
if distinct_consistency ~= nil then
Line 223: Line 226:
if zeta_switch then
if zeta_switch then
if #text > 0 then
if #text > 0 then
text = text .. "<br />"
text = text .. "<br>"
end
end
if not value_provided(zeta_override) then
if not value_provided(zeta_override) then

Revision as of 09:20, 9 March 2025

Module documentation[view] [edit] [history] [purge]
Note: Do not invoke this module directly; use the corresponding template instead: Template:Infobox ET.

This module automatically fills in information about a specified equal temperament tuning.


local p = {}
local utils = require("Module:Utils")
local rat = require("Module:Rational")
local limits = require("Module:Limits")
local ET = require("Module:ET")
local infobox = require("Module:Infobox")
local yesno = require("Module:Yesno")

-- check whether the input is a non-empty string
local function value_provided(s)
	return type(s) == "string" and #s > 0
end

-- towards is one of: -1 (floor), 0 (nearest), 1 (ceil)
local function approximation(et, interval, towards, precomputed_approx)
	local approx = precomputed_approx or ET.approximate(et, interval, towards or 0)

	-- string for backslash notation
	-- "edo" is omitted
	local tuning = et.size
	if not rat.eq(et.equave, 2) then
		tuning = tuning .. et.suffix
	end

	local ratio = rat.new(approx, et.size)

	-- convergence notice, suppressed for 1ed's
	local convergement_notice = ""
	local converges = rat.converges(ratio, math.log(interval) / math.log(rat.as_float(et.equave)))
	if et.size > 1 and converges then
		convergement_notice = "<br>(" .. converges .. ")"
	end

	if rat.as_table(ratio)[1] ~= approx then
		convergement_notice = ""
		local link = rat.as_table(ratio)[2] .. et.suffix
		ratio = " (&rarr;&nbsp;[[" .. link .. "|" .. rat.as_ratio(ratio, "\\")
		if not rat.eq(et.equave, 2) then
			ratio = ratio .. et.suffix
		end
		ratio = ratio .. "]])"
	else
		ratio = ""
	end

	local cents = utils._round(ET.cents(et, approx), 6)

	return approx .. "\\" .. tuning .. " (" .. cents .. "{{c}})" .. ratio .. convergement_notice
end

function p.infobox_ET(frame)
	-- debug mode will disable the categories
	local debug_mode = yesno(frame.args["debug"])
	local categories = ""

	local tuning = frame.args["tuning"]
	local et = ET.parse(tuning) or ET.parse("12edo")

	-- category of the main article
	categories = categories .. "[[Category:" .. tuning .. "| ]]"
	-- category of the equal division
	if rat.eq(et.equave, 2) then
		categories = categories
			.. "[[Category:Equal divisions of the octave|"
			.. string.rep("#", string.len(et.size))
			.. "]]"
	else
		categories = categories .. "[[Category:" .. et.suffix .. "|" .. string.rep("#", string.len(et.size)) .. "]]"
	end

	-- prime factorization
	local prime_factorization_override = frame.args["Prime factorization"]
	local prime_factorization
	if not value_provided(prime_factorization_override) then
		prime_factorization = utils._prime_factorization(et.size)
		if utils.is_prime(et.size) then
			prime_factorization = prime_factorization .. " (prime)"
			if rat.eq(et.equave, 2) then
				categories = categories .. "[[Category:Prime EDOs|" .. string.rep("#", string.len(et.size)) .. "]]"
			end
		end
	else
		prime_factorization = prime_factorization_override
	end

	-- zeta test
	local zeta_override = frame.args["Zeta"]
	local zeta_switch
	if value_provided(zeta_override) then
		zeta_switch = not zeta_override:match("^[Nn][Oo]$")
	else
		zeta_switch = rat.eq(et.equave, 2) and ET.is_zeta(et)
	end

	-- navigation arrows
	local increment = 1
	if rat.eq(et.equave, rat.new(9, 4)) or rat.eq(et.equave, 4) or rat.eq(et.equave, 9) then
		increment = 2
	end
	local prev_one = ""
	if et.size >= increment then
		prev_one = "[[" .. (et.size - increment) .. et.suffix .. "|&larr;&nbsp;" .. (et.size - increment) .. et.suffix .. "]]"
	end
	local next_one = "[[" .. (et.size + increment) .. et.suffix .. "|" .. (et.size + increment) .. et.suffix .. "&nbsp;&rarr;]]"

	-- step size in cents
	local step_size = ET.cents(et, 1)
	if step_size > 100 then
		categories = categories .. "[[Category:Macrotonal|" .. string.rep("#", string.len(et.size)) .. "]]"
	end
	local note_12edo = ""
	if rat.eq(et.equave, 2) and et.size == 12 then
		note_12edo = " (by&nbsp;definition)"
	end

	-- octave, twelfth, and fifth in steps
	local octave = ET.approximate(et, 2)
	local twelfth = ET.approximate(et, 3)

	local fifth = -octave + twelfth -- 3/2 = [-1 1>
	local fifth_error = ET.cents(et, fifth) - rat.cents(rat.new(3, 2))
	local is_dual_fifth = math.abs(fifth_error) > step_size / 3

	local A1 = -11 * octave + 7 * twelfth -- 2187/2048 = [-11 7>
	local m2 = 8 * octave - 5 * twelfth -- 256/243 = [8 -5>
	local A1_cents = utils._round(ET.cents(et, A1), 4)
	local m2_cents = utils._round(ET.cents(et, m2), 4)

	-- display
	local infobox_data = {}

	table.insert(infobox_data, {
		"Prime factorization",
		prime_factorization,
	})

	table.insert(infobox_data, {
		"Step size",
		utils._round(step_size, 6) .. "{{c}}" .. note_12edo .. "&nbsp;",
	})

	if not rat.eq(et.equave, 2) then
		table.insert(infobox_data, {
			"Octave",
			approximation(et, 2),
		})
		if not rat.eq(et.equave, 3) then
			table.insert(infobox_data, {
				"Twelfth",
				approximation(et, 3),
			})
		end
	else
		table.insert(infobox_data, {
			"Fifth",
			approximation(et, 3 / 2),
		})
		table.insert(infobox_data, {
			"Semitones (A1:m2)",
			A1 .. ":" .. m2 .. " (" .. A1_cents .. "{{c}} : " .. m2_cents .. "{{c}})",
		})
		if is_dual_fifth and et.size > 0 then
			table.insert(infobox_data, {
				"Dual sharp fifth",
				approximation(et, 3 / 2, 1),
			})
			table.insert(infobox_data, {
				"Dual flat fifth",
				approximation(et, 3 / 2, -1),
			})
			local sharp = ET.approximate(et, 3 / 2, 1)
			local flat = ET.approximate(et, 3 / 2, -1)
			table.insert(infobox_data, {
				"Dual major 2nd",
				approximation(et, 9 / 8, 0, sharp + flat - octave),
			})
			categories = categories
				.. "[[Category:Dual-fifth temperaments|"
				.. string.rep("#", string.len(et.size))
				.. "]]"
		end
	end

	-- consistency and distinct consistency
	-- max_limit is used to prevent timeout
	local consistency = tonumber(frame.args["Consistency"])
	local max_limit = rat.eq(et.equave, 2) and 43 or 32
	if consistency == nil then
		consistency = limits.consistency_limit(et, false, max_limit)
	end
	if consistency == nil then
		consistency = "at least " .. max_limit
	end
	if consistency ~= nil then
		table.insert(infobox_data, {
			"Consistency limit",
			consistency,
		})
	end
	local distinct_consistency = tonumber(frame.args["Distinct consistency"])
	if distinct_consistency == nil then
		distinct_consistency = limits.consistency_limit(et, consistency or true, max_limit)
	end
	if distinct_consistency == nil then
		distinct_consistency = "at least " .. max_limit
	end
	if distinct_consistency ~= nil then
		table.insert(infobox_data, {
			"Distinct consistency limit",
			distinct_consistency,
		})
	end

	-- special properties
	if ET.is_highly_composite(et) or zeta_switch then
		local text = ""
		if ET.is_highly_composite(et) then
			text = text .. "[[Highly composite equal division|Highly composite]]"
			if rat.eq(et.equave, 2) then
				categories = categories
					.. "[[Category:Highly composite EDOs|"
					.. string.rep("#", string.len(et.size))
					.. "]]"
			end
		end
		if zeta_switch then
			if #text > 0 then
				text = text .. "<br>"
			end
			if not value_provided(zeta_override) then
				text = text .. ET.why_zeta(et)
			else
				text = text .. zeta_override
			end
			if rat.eq(et.equave, 2) then
				categories = categories
					.. "[[Category:Zeta record EDOs|" 
					.. string.rep("#", string.len(et.size)) 
					.. "]]"
			end
		end
		table.insert(infobox_data, {
			"Special properties",
			"<div style=\"max-width: 300px;\">" .. text .. "</div>",
		})
	end

	local result = infobox.build("[[" .. et.suffix .. "|" .. tuning .. "]]", infobox_data, prev_one, next_one)
	
	if not value_provided(debug_mode) then
		result = result .. categories
	end
	
	return frame:preprocess(result)
end

return p