Module:Limits: Difference between revisions

Improve documentation and request answers
ArrowHead294 (talk | contribs)
mNo edit summary
 
(2 intermediate revisions by 2 users not shown)
Line 1: Line 1:
local p = {}
local ET = require("Module:ET")
local rat = require("Module:Rational")
local rat = require("Module:Rational")
local ET = require("Module:ET")
local p = {}


-- returns a table of all positive ratios n/m with n and m <= q modulo powers of equave
-- returns a table of all positive q-equave-limit ratios if the equave is provided
--  n/m with n and m <= q modulo powers of equave
-- otherwise q-integer-limit ratios
-- previous: already computed ratios for q - 1
-- previous: already computed ratios for q - 1
function p.limit_modulo_equave(q, equave, previous)
function p.limit_modulo_equave(q, equave, previous)
equave = equave or 2
local ratios = {}
local ratios = {}
if previous then
if previous then
for n = 1, q do
for n = 1, q do
local a = rat.new(n, q)
local a = rat.new(n, q)
a = rat.modulo_mul(a, equave)
local b = rat.new(q, n)
if equave then
a = rat.modulo_mul(a, equave)
b = rat.modulo_mul(b, equave)
end
local a_key = rat.as_ratio(a)
local a_key = rat.as_ratio(a)
local b = rat.new(q, n)
b = rat.modulo_mul(b, equave)
local b_key = rat.as_ratio(b)
local b_key = rat.as_ratio(b)


Line 29: Line 32:
for m = 1, q do
for m = 1, q do
local a = rat.new(n, m)
local a = rat.new(n, m)
a = rat.modulo_mul(a, equave)
if equave then
a = rat.modulo_mul(a, equave)
end
local key = rat.as_ratio(a)
local key = rat.as_ratio(a)
ratios[key] = a
ratios[key] = a
Line 55: Line 60:
end
end


-- check additive consistency for a set of ratios (modulo powers of equave)
-- check additive consistency for a set of ratios of an equal tuning
-- of an equal tuning:
--  approx(a*b) = approx(a) + approx(b) for all a, b: a, b, a*b in ratios
--  approx(a*b) = approx(a) + approx(b) for all a, b: a, b, ab in ratios
-- `use_equave`: whether check consistency modulo powers of the tuning's formal equave
-- - we don't allow arbitrary equaves here
-- - since consistency only makes sense if the equave is pure
-- `distinct`: whether distinct ratios are required to be mapped to distinct approximations
-- `distinct`: whether distinct ratios are required to be mapped to distinct approximations
-- `previous`: already computed ratios for the previous iteraton
-- `previous`: already computed ratios for the previous iteraton
function p.additively_consistent(et, ratios, distinct, previous)
function p.additively_consistent(et, ratios, use_equave, distinct, previous)
distinct = distinct or false
distinct = distinct or false
previous = previous or {}
previous = previous or {}
-- distinction check
-- approx_set stores ratios and their directly approximated number of steps as keys
-- we find the number of steps for every ratio and check if this number is taken
-- if it's taken, we compare the ratio in question with the stored one
-- if they're unequal, it means different ratios are mapped to the same step
-- therefore distinction isn't satisfied
-- otherwise, we add the ratio and step number to approx_set
-- we do this to previous and new ratios alike
if distinct then
if distinct then
local approx_set = {}
local approx_set = {}
for a_key, a in pairs(previous) do
for a_key, a in pairs(previous) do
local a_approx = ET.approximate(et, rat.as_float(a)) % et.size
local a_approx = use_equave and ET.approximate(et, rat.as_float(a)) % et.size
or ET.approximate(et, rat.as_float(a))
if approx_set[a_approx] then
if approx_set[a_approx] then
if not rat.eq(rat.modulo_mul(rat.div(a, approx_set[a_approx]), et.equave), 1) then
if use_equave and not rat.eq(rat.modulo_mul(rat.div(a, approx_set[a_approx]), et.equave), 1)
or not rat.eq(a, approx_set[a_approx]) then
mw.log(a_key .. " -> " .. a_approx .. ": conflict!")
mw.log(a_key .. " -> " .. a_approx .. ": conflict!")
return false
return false
Line 77: Line 95:
end
end
for a_key, a in pairs(ratios) do
for a_key, a in pairs(ratios) do
local a_approx = ET.approximate(et, rat.as_float(a)) % et.size
local a_approx = use_equave and ET.approximate(et, rat.as_float(a)) % et.size
or ET.approximate(et, rat.as_float(a))
if approx_set[a_approx] then
if approx_set[a_approx] then
if not rat.eq(rat.modulo_mul(rat.div(a, approx_set[a_approx]), et.equave), 1) then
if use_equave and not rat.eq(rat.modulo_mul(rat.div(a, approx_set[a_approx]), et.equave), 1)
or not rat.eq(a, approx_set[a_approx]) then
mw.log(a_key .. " -> " .. a_approx .. ": conflict!")
mw.log(a_key .. " -> " .. a_approx .. ": conflict!")
return false
return false
Line 110: Line 130:
local c_approx = ET.approximate(et, rat.as_float(c))
local c_approx = ET.approximate(et, rat.as_float(c))


c = rat.modulo_mul(c, et.equave)
if use_equave then
c = rat.modulo_mul(c, et.equave)
end
local c_key = rat.as_ratio(c)
local c_key = rat.as_ratio(c)
if previous[c_key] or ratios[c_key] then
if previous[c_key] or ratios[c_key] then
Line 126: Line 148:
local c = rat.mul(a, b)
local c = rat.mul(a, b)
local c_approx = ET.approximate(et, rat.as_float(c))
local c_approx = ET.approximate(et, rat.as_float(c))
 
c = rat.modulo_mul(c, et.equave)
if use_equave then
c = rat.modulo_mul(c, et.equave)
end
local c_key = rat.as_ratio(c)
local c_key = rat.as_ratio(c)
if previous[c_key] or ratios[c_key] then
if previous[c_key] or ratios[c_key] then
Line 157: Line 181:
end
end


-- use the equave iff the tuning is an edo
local use_equave = rat.eq (et.equave, rat.new (2, 1))
max_n = max_n or 1 / 0
max_n = max_n or 1 / 0
distinct = distinct or false
distinct = distinct or false
Line 166: Line 192:
return last_n
return last_n
end
end
local ratios = p.limit_modulo_equave(n, et.equave, previous)
local ratios = p.limit_modulo_equave(n, use_equave and et.equave or nil, previous)
for key, _ in pairs(ratios) do
for key, _ in pairs(ratios) do
mw.log("step " .. n .. ": " .. key)
mw.log("step " .. n .. ": " .. key)
end
end
if next(ratios) ~= nil then
if next(ratios) ~= nil then
local consistent = p.additively_consistent(et, ratios, distinct, previous)
local consistent = p.additively_consistent(et, ratios, use_equave, distinct, previous)
if not consistent then
if not consistent then
mw.log("Not consistent at step " .. n .. ", returning " .. last_n)
mw.log("Not consistent at step " .. n .. ", returning " .. last_n)