Module:Q-odd-limit intervals
- This module should not be invoked directly; use its corresponding template instead: Template:Q-odd-limit intervals.
This module automatically calculates a given equal-tempered tuning's approximations of intervals in a given odd limit, and lists them in a table.
Currently, odd limits up to and including 63 and prime limits up to and including 61 are supported.
| Introspection summary for Module:Q-odd-limit intervals | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||
No function descriptions were provided. The Lua code may have further information.
bit32 = require("bit32")
utils = require("Module:Utils")
ET = require("Module:ET")
limits = require("Module:Limits")
local p = {}
local PRIME_LIST = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31}
local function is_in(v, t)
for i = 1, #t do
if v == t[i] then
return true
end
end
return false
end
local function table_filter(t, thres)
for i = 1, #t do
if t[i] > thres then
return {unpack(t, 1, i - 1)}
end
end
return t
end
local function inner_product(val, monzo)
local result = 0
for i = 1, #val do
result = result + val[i] * monzo[i]
end
return result
end
local function monzo2ratio(monzo, subgroup)
local num = 1
local den = 1
for i = 1, #subgroup do
if monzo[i] > 0 then
num = num * subgroup[i]^monzo[i]
elseif monzo[i] < 0 then
den = den * subgroup[i]^(-monzo[i])
end
end
return {num = num, den = den}
end
local function ratio2monzo(ratio, subgroup)
local monzo = {}
for i = 1, #subgroup do
monzo[i] = 0
while ratio.num % subgroup[i] == 0 do
monzo[i] = monzo[i] + 1
ratio.num = ratio.num / subgroup[i]
end while ratio.den % subgroup[i] == 0 do
monzo[i] = monzo[i] - 1
ratio.den = ratio.den / subgroup[i]
end
end
return monzo
end
local function monzo2cent(monzo, subgroup)
local jip = {}
for i = 1, #subgroup do
jip[i] = 1200 * utils._log(subgroup[i], 2)
end
return inner_product(jip, monzo)
end
local function ratio_8ve_reduction(ratio)
local oct = math.floor(utils._log(ratio.num / ratio.den, 2))
if oct > 0 then
ratio.den = ratio.den * 2^oct
elseif oct < 0 then
ratio.num = ratio.num * 2^(-oct)
end
return ratio
end
local function odd_limit_monzo_list_gen(limit)
local monzo_list = {}
local subgroup = table_filter(PRIME_LIST, limit)
for num = 1, limit, 2 do
for den = 1, num, 2 do
if utils._gcd(num, den) == 1 then
ratio = ratio_8ve_reduction({num = num, den = den})
table.insert(monzo_list, ratio2monzo(ratio, subgroup))
end
end
end
return monzo_list
end
local function find_error_direct(val, subgroup, monzo_list)
local step_size = 1200/val[1]
local true_size
local approx_size
local error_list_direct = {}
for i = 1, #monzo_list do
ratio = monzo2ratio(monzo_list[i], subgroup)
comp = {2 * ratio.den, ratio.num}
true_size = monzo2cent(monzo_list[i], subgroup)
nearest_size = math.floor(true_size / step_size + 0.5)
error_abs_direct = math.abs(nearest_size * step_size - true_size)
error_rel_direct = 100 * error_abs_direct / step_size
error_list[i] =
{
ratio = ratio,
comp = comp,
error_abs_direct = error_abs_direct,
error_rel_direct = error_rel_direct,
}
end
table.sort(error_list_direct, function(a, b) return a.error_abs_direct < b.error_abs_direct end)
return error_list_direct
end
local function find_error_val(val, subgroup, monzo_list)
local step_size = 1200/val[1]
local true_size
local approx_size
local error_list_val = {}
for i = 1, #monzo_list do
ratio = monzo2ratio(monzo_list[i], subgroup)
comp = {2 * ratio.den, ratio.num}
true_size = monzo2cent(monzo_list[i], subgroup)
approx_size = step_size * inner_product(val, monzo_list[i])
nearest_size = math.floor(true_size / step_size + 0.5)
error_abs_val = math.abs(approx_size - true_size)
error_rel_val = 100 * error_abs_val / step_size
error_list[i] =
{
ratio = ratio,
comp = comp,
error_abs_val = error_abs_val,
error_rel_val = error_rel_val
}
end
table.sort(error_list_val, function(a, b) return a.error_abs_val < b.error_abs_val end)
return error_list_val
end
local function approx(steps, subgroup, monzo_list, t_title)
local t_body_direct = {}
local t_body_val = {}
local val = {}
for i = 1, #subgroup do
val[i] = utils._round_dec(steps*utils._log(subgroup[i], 2))
end
errlist_direct = find_error_direct(val, subgroup, monzo_list)
errlist_val = find_error_val(val, subgroup, monzo_list)
for i = 1, #errlist_direct do
ratiocomp = string.format("%d/%d, %d/%d", errlist_directt[i].ratio.num, error_list[i].ratio.den, 2 * errlist_direct[i].ratio.den, errlist_direct[i].ratio.num)
error_abs_val = string.format("%.3f", errlist_direct[i].error_abs_val)
error_rel_val = string.format("%.1f", errlist_direct[i].error_rel_val)
error_abs_direct = string.format("%.3f", errlist_direct[i].error_abs_direct)
error_rel_direct = string.format("%.1f", errlist_direct[i].error_rel_direct)
if bit32.band(errlist_direct[i].ratio.den, errlist_direct[i].ratio.den - 1) == 0 and is_in(errlist_direct[i].ratio.num, subgroup) then -- check power of 2 for den and prime for num
ratiocomp = "'''" .. ratiocomp .. "'''"
error_abs_val = "'''" .. error_abs_val .. "'''"
error_rel_val = "'''" .. error_rel_val .. "'''"
error_abs_direct = "'''" .. error_abs_direct .. "'''"
error_rel_direct = "'''" .. error_rel_direct .. "'''"
end
if errlist_direct[i].error_rel_val > 50 then
ratiocomp = "''" .. ratiocomp .. "''"
error_abs_val = "''" .. error_abs_val .. "''"
error_rel_val = "''" .. error_rel_val .. "''"
error_abs_direct = "''" .. error_abs_direct .. "''"
error_rel_direct = "''" .. error_rel_direct .. "''"
end
t_body_direct[i] = string.format("|-\n| %s\n| %s\n| %s", ratiocomp, error_abs_direct, error_rel_direct)
t_body_val[i] = string.format("|-\n| %s\n| %s\n| %s", ratiocomp, error_abs_val, error_rel_val)
end
return "{| class=\"wikitable center-all mw-collapsible mw-collapsed sortable\"\n" ..
"|+ style=\"white-space: nowrap;\" | " .. t_title .. " (direct approximation)\n" ..
"|-\n" ..
"! class=\"unsortable\" | Interval and complement " ..
"!! Error (abs, [[Cent|¢]]) " ..
"!! Error (rel, [[Relative cent|%]])\n" ..
table.concat(t_body_direct, "\n") ..
"\n|}\n\n" ..
"{| class=\"wikitable center-all mw-collapsible mw-collapsed sortable\"\n" ..
"|+ style=\"white-space: nowrap;\" | " .. t_title .. " (patent val mapping)\n" ..
"|-\n" ..
"! class=\"unsortable\" | Interval and complement " ..
"!! Error (abs, [[Cent|¢]]) " ..
"!! Error (rel, [[Relative cent|%]])\n" ..
table.concat(t_body_val, "\n") ..
"\n|}"
end
-- local function prec_by_equal (steps)
-- return math.floor (math.log (steps*1.9)/math.log (10))
-- end
function p.q_odd_limit_intervals(frame)
local steps = tonumber(frame.args["steps"])
local limit = math.max(tonumber(frame.args["limit"]), 2)
local constcy = limits.consistency_limit(ET.parse(steps .. "edo"), false, 43)
-- local prec = tonumber (frame.args['prec']) or prec_by_equal (steps)
local note = frame.args["note"]
local title = frame.args["title"]
if title == nil or #title == 0 then
title = string.format("%d-odd-limit interval mappings in " .. steps .. "edo", limit)
end
local subgroup = table_filter(PRIME_LIST, limit)
local monzo_list = odd_limit_monzo_list_gen(limit)
local out_str = ""
if frame.args["header"] ~= "none" then
out_str = out_str .. "The following table shows how [[" .. limit .. "-odd-limit intervals]] are represented in " .. steps .. "edo. Prime harmonics are in '''bold'''"
if constcy >= limit then
out_str = out_str .. ".\n\nAs " .. steps .. "edo is consistent in the " .. limit .. "-odd-limit, the mappings by direct approximation and through the patent val are identical."
else
out_str = out_str .. "; inconsistent intervals are in ''italic''."
end
out_str = out_str .. "\n\n"
if note ~= "" then
out_str = out_str .. note .. "\n\n"
end
if steps == 12 then
out_str = out_str .. "Note that since the [[cent]] was defined in terms of 12edo, the absolute and relative errors for 12edo are identical.\n\n"
end
end
out_str = out_str .. approx(steps, subgroup, monzo_list, title)
return out_str
end
return p;