Module:Huge table

From Xenharmonic Wiki
Jump to navigation Jump to search
Test Template Info-Icon - Version (2).svg Module documentation[view] [edit] [history] [purge]
Note: Do not invoke this module directly; use the corresponding template instead: Template:Huge table.
Icon-Todo.png Todo: Documentation

local p = {}
local utils = require("Module:Utils")
local rat = require("Module:Rational")
local ud = require("Module:Ups and downs notation")
local ET = require("Module:ET")

-- Gets mapping of a monzo in a val
local function mapping(monzo, val)
	local result = 0
	for k, _ in pairs(val) do
		result = result + val[k] * (monzo[k] or 0)
	end
	return result
end

-- Generates list of ratios up to a max numerator & denominator, max ratio size, and prime limit
local function get_ratios_list(max_nd, max_size, prime_limit)
    local ratios = {}
    local ratio_set = {}

    for i = 1, max_nd do
        for j = 1, max_nd do
            local gcd = utils._gcd(i, j)
            local reduced_i = i / gcd
            local reduced_j = j / gcd
            local ratio = reduced_i .. "/" .. reduced_j

            if (i / j) >= 1 and (i / j) <= max_size and
               rat.max_prime(rat.new(i, j)) <= prime_limit and
               not ratio_set[ratio] then
                ratio_set[ratio] = true
                table.insert(ratios, { reduced_i, reduced_j })
            end
        end
    end

    return ratios
end

-- Utility function to get specific note name with ud.get_note_names_table
-- (this is essentially what "Template:Ups and downs note name" does)
local function ud_note(et, fifth, step)
	return table.concat(ud.get_note_names_table(et, fifth)[step], ", "):sub(0, -1)
end

-- ???
local function mysplit(inputstr, sep)
	if sep == nil then
		sep = "%s"
	end
	local t = {}
	for str in string.gmatch(inputstr, "([^" .. sep .. "]+)") do
		table.insert(t, str)
	end
	return t
end

function p.huge_table(frame)
	local additional = frame.args["additional"] or ""
	local additional_split = mysplit(additional, "\n")
	for i = 1, #additional_split do
		additional_split[i] = mysplit(additional_split[i], ";")
	end
	local tuning = frame.args["tuning"]
	local et = ET.parse(tuning) or ET.parse("12edo")
	local patent_val = {
		[2] = ET.approximate(et, 2),
		[3] = ET.approximate(et, 3),
		[5] = ET.approximate(et, 5),
		[7] = ET.approximate(et, 7),
		[11] = ET.approximate(et, 11),
		[13] = ET.approximate(et, 13),
		[17] = ET.approximate(et, 17),
		[19] = ET.approximate(et, 19),
		[23] = ET.approximate(et, 23),
		[29] = ET.approximate(et, 29),
		[31] = ET.approximate(et, 31),
		[37] = ET.approximate(et, 37),
		[41] = ET.approximate(et, 41),
		[43] = ET.approximate(et, 43),
		[47] = ET.approximate(et, 47),
	} -- NOTE: indices are prime numbers

	local t_head = "{| class=\"wikitable center-1 right-2\"\n"
	if et.size > 10000 then
		t_head = "{| class=\"wikitable center-1 right-2 mw-collapsible mw-collapsed\"\n"
	end
	local fifth = patent_val[3] - patent_val[2]
	local fifth_error = ET.cents(et, fifth) - rat.cents(rat.new(3, 2))
	local dual_fifth = math.abs(fifth_error) > (400 / et.size)
	local dual_flat_fifth = ET.approximate(et, 3 / 2, -1)
	local dual_sharp_fifth = ET.approximate(et, 3 / 2, 1)

	-- List of all 23-limit ratios with numerator and denominator <= certain cents value and in the range 1/1 - 2/1
	local ratios_list = get_ratios_list(54,2,23)

	wikitext = t_head .. 
	"|-\n" ..
	"! Steps\n" .. 
	"! Cents\n" ..
	"! Approximate Ratios\n"
	if rat.eq(et.equave, 2) then
		if dual_fifth then
			wikitext = wikitext
				.. "! [[Ups and downs notation|Ups and Downs Notation]]<br>(Dual Flat Fifth "
				.. dual_flat_fifth
				.. "\\"
				.. et.size
				.. ")\n"
			wikitext = wikitext
				.. "! [[Ups and downs notation|Ups and Downs Notation]]<br>(Dual Sharp Fifth "
				.. dual_sharp_fifth
				.. "\\"
				.. et.size
				.. ")\n"
		else
			wikitext = wikitext .. "! [[Ups and downs notation|Ups and Downs Notation]]\n"
		end
	end
	
	if #additional_split > 0 then
		for i = 1, #additional_split[1] do
			wikitext = wikitext .. "! " .. additional_split[1][i] .. "\n"
		end
	end

	for i = 0, et.size do
		wikitext = wikitext .. "|-\n"
		wikitext = wikitext .. "| " .. i .. "\n"
		wikitext = wikitext .. "| " .. utils._round_dec(ET.cents(et, i), 3) .. "\n"
		wikitext = wikitext .. "| "
		for j = 1, #ratios_list do
			-- Go through all ratios.
			-- If they are mapped to the current step in the ET's patent val and < specified edostep error,
			-- add to approximate ratios column.
			local t = mapping(rat.new(ratios_list[j][1], ratios_list[j][2]), patent_val)
			if
				t == i
				and math.abs(ET.cents(et, i) - utils._log(ratios_list[j][1] / ratios_list[j][2], 2) * 1200)
					< (1200 / et.size)
			then
				wikitext = wikitext .. "[[" .. ratios_list[j][1] .. "/" .. ratios_list[j][2] .. "]], "
			end
		end
		
		-- ???
		if wikitext:sub(-2, -1) == "| " then
			wikitext = wikitext .. "\n"
		else
			wikitext = wikitext:sub(0, -3) .. "\n"
		end
		
		if rat.eq(et.equave, 2) then
			if dual_fifth then
				wikitext = wikitext .. "| " .. ud_note(et, dual_flat_fifth, i) .. "\n"
				wikitext = wikitext .. "| " .. ud_note(et, dual_sharp_fifth, i) .. "\n"
			else
				wikitext = wikitext .. "| " .. ud_note(et, fifth, i) .. "\n"
			end
		end
		
		if (i + 2) <= #additional_split then
			for j = 1, #additional_split[i + 2] do
				wikitext = wikitext .. "| " .. additional_split[i + 2][j] .. "\n"
			end
		end
	end

	wikitext = wikitext .. "|}"
	return wikitext
end

return p