Module:ET: Difference between revisions
Module created |
These all looked too dumb tbh |
||
(58 intermediate revisions by 7 users not shown) | |||
Line 1: | Line 1: | ||
local rat = require( | local rat = require("Module:Rational") | ||
local seq = require("Module:Sequence") | |||
local p = {} | local p = {} | ||
local common_suffix = { | local common_suffix = { | ||
[ | ["3/2"] = "f", | ||
[ | ["2"] = "o", | ||
[ | ["2/1"] = "o", | ||
[ | ["3"] = "t", | ||
[ | ["3/1"] = "t", | ||
} | } | ||
local common_ratio = { | local common_ratio = { | ||
[ | ["f"] = rat.new(3, 2), | ||
[ | ["o"] = 2, | ||
[ | ["t"] = 3, | ||
["ϕ"] = (1 + math.sqrt(5)) / 2, | |||
["n"] = math.exp(1), | |||
["π"] = math.pi | |||
} | } | ||
-- create a ET structure <size>ed<equave> | |||
function p.new(size, equave, suffix) | function p.new(size, equave, suffix) | ||
size = size or 12 | size = size or 12 | ||
Line 21: | Line 27: | ||
local equave_n, equave_m = rat.as_pair(equave) | local equave_n, equave_m = rat.as_pair(equave) | ||
local equave_ratio = rat.as_ratio(equave) | local equave_ratio = rat.as_ratio(equave) | ||
suffix = | equave_ratio = equave_ratio:lower() | ||
suffix = "ed" | |||
if common_suffix[equave_ratio] then | if common_suffix[equave_ratio] then | ||
suffix = suffix .. common_suffix[equave_ratio] | suffix = suffix .. common_suffix[equave_ratio] | ||
Line 33: | Line 40: | ||
end | end | ||
-- parse a ET structure | |||
function p.parse(unparsed) | function p.parse(unparsed) | ||
local size, suffix, equave = unparsed:match( | local size, suffix, equave = unparsed:match("^(%d+%.*%d*)([Ee][Dd](.+))$") | ||
-- local size, suffix, equave = unparsed:match("^(%d+%.*%d*)([Cc]?[Ee][Dd]?[Tt]?(.*))$") | |||
if equave == nil then | if equave == nil then | ||
return nil | return nil | ||
end | end | ||
suffix = suffix:lower() | |||
size = tonumber(size) | size = tonumber(size) | ||
equave = common_ratio[equave] or rat.parse(equave) | equave = common_ratio[equave] or rat.parse(equave) | ||
Line 44: | Line 54: | ||
end | end | ||
return p.new(size, equave, suffix) | return p.new(size, equave, suffix) | ||
end | |||
-- construct a string representation for a ET structure | |||
function p.as_string(et) | |||
return et.size .. et.suffix | |||
end | |||
-- convert steps to a proper ratio (except that it is a float approximation) | |||
function p.backslash_ratio(et, steps) | |||
if et.size == 0 then | |||
return 1 | |||
end | |||
return rat.as_float(et.equave) ^ (steps / et.size) | |||
end | |||
function p.backslash_display(et, steps) | |||
if et.size == 0 then | |||
return 1 | |||
end | |||
return steps .. p.backslash_modifier(et) | |||
end | |||
function p.backslash_modifier(et) | |||
if not rat.eq(et.equave, 2) then | |||
return "\\" .. et.size .. et.suffix | |||
end | |||
return "\\" .. et.size | |||
end | |||
-- convert steps to cents | |||
function p.cents(et, steps) | |||
if et.size == 0 then | |||
return 0 | |||
end | |||
steps = steps or 1 | |||
return 1200 * steps / et.size * math.log(rat.as_float(et.equave)) / math.log(2) | |||
end | |||
-- convert steps to hekts | |||
function p.hekts(et, steps) | |||
if et.size == 0 then | |||
return 0 | |||
end | |||
steps = steps or 1 | |||
return 1300 * steps / et.size * math.log(rat.as_float(et.equave)) / math.log(3) | |||
end | |||
-- convert ratio to steps | |||
-- ratio is a float! | |||
-- towards is one of: -1 (floor), 0 (nearest), 1 (ceil) | |||
function p.approximate(et, ratio, towards) | |||
towards = towards or 0 | |||
if et.size == 0 then | |||
return 0 | |||
end | |||
local exact = math.log(ratio) / math.log(rat.as_float(et.equave)) * et.size | |||
if towards < 0 then | |||
return math.floor(exact) | |||
elseif towards > 0 then | |||
return math.ceil(exact) | |||
else | |||
return math.floor(exact + 0.5) | |||
end | |||
end | |||
-- whether this ET tempers out the provided rational number | |||
function p.tempers_out(et, ratio) | |||
local t = 0 | |||
for factor, power in pairs(ratio) do | |||
if type(factor) == "number" then | |||
t = t + power * p.approximate(et, factor) | |||
end | |||
end | |||
return t == 0 | |||
end | |||
-- determine whether ET is highly composite | |||
function p.is_highly_composite(et) | |||
et.highly_composite = et.highly_composite or rat.is_highly_composite(et.size) | |||
return et.highly_composite | |||
end | |||
-- determine whether ET's size could be within one of zeta function-related sequences | |||
function p.is_zeta(et) | |||
return seq.contains(seq.zeta_peak, et.size) | |||
or seq.contains(seq.zeta_peak_integer, et.size) | |||
or seq.contains(seq.zeta_integral, et.size) | |||
or seq.contains(seq.zeta_gap, et.size) | |||
end | |||
-- describe why | |||
function p.why_zeta(et) | |||
local zeta_peak = seq.contains(seq.zeta_peak, et.size) | |||
local zeta_peak_integer = seq.contains(seq.zeta_peak_integer, et.size) | |||
local zeta_integral = seq.contains(seq.zeta_integral, et.size) | |||
local zeta_gap = seq.contains(seq.zeta_gap, et.size) | |||
local z = "The Riemann zeta function and tuning#Zeta EDO lists" | |||
local markers = {} | |||
if zeta_peak then | |||
table.insert(markers, string.format("[[%s|Zeta peak]]", z)) | |||
elseif zeta_peak == nil then | |||
table.insert(markers, string.format("[[%s|Zeta peak?]]", z)) | |||
end | |||
if zeta_peak_integer then | |||
table.insert(markers, string.format("[[%s|Zeta peak integer]]", z)) | |||
elseif zeta_peak_integer == nil then | |||
table.insert(markers, string.format("[[%s|Zeta peak integer?]]", z)) | |||
end | |||
if zeta_integral then | |||
table.insert(markers, string.format("[[%s|Zeta integral]]", z)) | |||
elseif zeta_integral == nil then | |||
table.insert(markers, string.format("[[%s|Zeta integral?]]", z)) | |||
end | |||
if zeta_gap then | |||
table.insert(markers, string.format("[[%s|Zeta gap]]", z)) | |||
elseif zeta_gap == nil then | |||
table.insert(markers, string.format("[[%s|Zeta gap?]]", z)) | |||
end | |||
return table.concat(markers, "<br />") | |||
end | end | ||
return p | return p |