Module:JI ratio finder: Difference between revisions

Ganaram inukshuk (talk | contribs)
Added NEFTH filtering
ArrowHead294 (talk | contribs)
m Alphabetise dependencies
 
(6 intermediate revisions by one other user not shown)
Line 1: Line 1:
local utils = require('Module:Utils')
local interval = require('Module:Interval')
local rat = require('Module:Rational')
local p = {}
local p = {}


-- Finds the Tenney height of a ratio, regardless of inversion; also called
-- local interval = require('Module:Interval')
-- complement-agnostic tenney height (CATH). Equave complement depends on what
local rat = require('Module:Rational')
-- the equivalence interval is (default is 2/1).
local utils = require('Module:Utils')
-- Given a ratio and its equave complement, the CATH is the smaller of the two
-- ratios' Tenney heights.
function p.complement_agnostic_tenney_height(ratio, equave)
local ratio = ratio or rat.new(81, 64)
local equave = equave or rat.new(2)
local comp = p.equave_complement(ratio, equave)
return math.min(rat.tenney_height(ratio), rat.tenney_height(comp))
end


-- Finds the Tenney height of a ratio that ignores equave factors.
-- Finds the Tenney height of a ratio that ignores equave factors.
-- If the equave is 2/1, then this is equivalent to no-2's Tenney Height.
-- If the equave is 2/1, then this is equivalent to no-2's Tenney Height.
-- This is an attempt at generalizing no-2's Tenney height for nonoctave
-- This is an attempt at generalizing no-2's Tenney height for nonoctave
-- equaves, such as 3/1 or 3/2.
-- equaves, such as 3/1 or 3/2, which would be no-2's and no-2's-or-3's.
function p.no_equave_factors_tenney_height(ratio, equave)
function p.no_equave_factors_tenney_height(ratio, equave)
local ratio = ratio or rat.new(81, 64)
local ratio = ratio or rat.new(81, 64)
Line 63: Line 52:
-- Ratios found this way will range from 0 cents to the given cent value.
-- Ratios found this way will range from 0 cents to the given cent value.
-- These ratios should then be filtered as needed.
-- These ratios should then be filtered as needed.
function p.find_candidate_ratios(cents, denominator_limit, prime_limit)
function p.find_candidate_ratios_within_prime_limit(cents, int_limit, prime_limit)
local cents = cents or 1200
local cents = cents or 1200
local denominator_limit = denominator_limit or 99
local int_limit = int_limit or 99
local prime_limit = prime_limit or 97
local prime_limit = prime_limit or 97
local candidate_ratios = {}
local candidate_ratios = {}
for i = 1, denominator_limit do
for i = 1, int_limit do
local denominator = i
for j = i, int_limit do
local numerator = i
local numerator = j
local is_within_cents = true
local denominator = i
repeat
local current_ratio = rat.new(numerator, denominator)
local is_simplified = utils._gcd(numerator, denominator) == 1
local is_within_prime_limit = rat.max_prime(current_ratio) <= prime_limit
is_within_cents = rat.cents(current_ratio) <= cents
if is_simplified and is_within_cents and is_within_prime_limit then
-- Proceed if ratio is simplified
table.insert(candidate_ratios, current_ratio)
if utils._gcd(numerator, denominator) == 1 then
local current_ratio = rat.new(numerator, denominator)
local is_within_prime_limit = rat.max_prime(current_ratio) <= prime_limit
local is_within_cents = rat.cents(current_ratio) <= cents
if is_within_cents and is_within_prime_limit then
table.insert(candidate_ratios, current_ratio)
end
end
end
end
numerator = numerator + 1
until not is_within_cents
end
end
Line 94: Line 82:


-- Finds candidate ratios up to a cent value, up to a denominator limit, and
-- Finds candidate ratios up to a cent value, up to a denominator limit, and
-- within a prime subgroup.
-- with any of the given prime factors
-- These ratios should be filtered as needed.
function p.find_candidate_ratios_within_subgroup(cents, int_limit, primes)
function p.find_candidate_ratios_within_subgroup(cents, denominator_limit, subgroup)
local cents = cents or 1200
local cents = cents or 1200
local denominator_limit = denominator_limit or 99
local int_limit = int_limit or 99
local subgroup = subgroup or { 2, 3, 7, 11 }
local primes = primes or { 2, 3, 7, 11 }
local candidate_ratios = {}
local candidate_ratios = {}
for i = 1, denominator_limit do
for i = 1, int_limit do
local denominator = i
for j = i, int_limit do
local numerator = i
local numerator = j
local is_within_cents = true
local denominator = i
repeat
local current_ratio = rat.new(numerator, denominator)
local is_simplified = utils._gcd(numerator, denominator) == 1
local is_within_subgroup = p.within_subgroup(current_ratio, subgroup)
is_within_cents = rat.cents(current_ratio) <= cents
if is_simplified and is_within_cents and is_within_subgroup then
-- Proceed if ratio is simplified
table.insert(candidate_ratios, current_ratio)
if utils._gcd(numerator, denominator) == 1 then
local current_ratio = rat.new(numerator, denominator)
local is_within_subgroup = p.within_subgroup(current_ratio, primes)
local is_within_cents = rat.cents(current_ratio) <= cents
if is_within_cents and is_within_subgroup then
table.insert(candidate_ratios, current_ratio)
end
end
end
end
numerator = numerator + 1
until not is_within_cents
end
end
Line 140: Line 126:
end
end
return filtered_ratios
end
-- Filter ratios based on whether its equave complement exceeds an int limit
function p.filter_ratios_by_equave_complement_int_limit(ratios, int_limit, equave)
local ratios = ratios or { rat.new(5, 4), rat.new(81, 64), rat.new(9, 7) }
local int_limit = int_limit or 10
local equave = equave or rat.new(2)
local filtered_ratios = {}
for i = 1, #ratios do
local complement = p.equave_complement(ratios[i], equave)
local numerator, denominator = rat.as_pair(complement)
if numerator <= int_limit and denominator <= int_limit then
table.insert(filtered_ratios, ratios[i])
end
end
return filtered_ratios
return filtered_ratios
end
end
Line 199: Line 202:
function p.filter_ratios_by_subgroup(ratios, subgroup)
function p.filter_ratios_by_subgroup(ratios, subgroup)
local ratios = ratios or { rat.new(1), rat.new(5, 4), rat.new(81, 64), rat.new(9, 7) }
local ratios = ratios or { rat.new(1), rat.new(5, 4), rat.new(81, 64), rat.new(9, 7) }
local subgroup = subgroup or { 2, 3, 5 }
local subgroup = subgroup or { 2, 3, 7 }
local candidate_ratios = p.filter_ratios_by_prime_limit(ratios, subgroup[#subgroup])
local candidate_ratios = p.filter_ratios_by_prime_limit(ratios, subgroup[#subgroup])
Line 224: Line 227:
for i = 1, #ratios do
for i = 1, #ratios do
if rat.tenney_height(ratios[i]) <= tenney_height then
if rat.tenney_height(ratios[i]) <= tenney_height then
table.insert(filtered_ratios, ratios[i])
end
end
return filtered_ratios
end
-- Filters ratios by complement-agnostic Tenney height
-- Filters ratios where lg(numerator) + lg(denominator) does not exceed the
-- given height, where lg is log-base-2
-- If the equave complement of that ratio has a lower Tenney height, the ratio
-- is kept instead.
function p.filter_ratios_by_complement_agnostic_tenney_height(ratios, tenney_height, equave)
local ratios = ratios or { rat.new(5, 4), rat.new(81, 64), rat.new(9, 7) }
local tenney_height = tenney_height or 5.0
local equave = equave or rat.new(2)
local filtered_ratios = {}
for i = 1, #ratios do
if p.complement_agnostic_tenney_height(ratios[i], equave) <= tenney_height then
table.insert(filtered_ratios, ratios[i])
table.insert(filtered_ratios, ratios[i])
end
end
Line 253: Line 236:
-- Filters ratios by no-equave-factors Tenney height
-- Filters ratios by no-equave-factors Tenney height
-- Filters ratios where lg(numerator) + lg(denominator) does not exceed the
-- Filters ratios where lg(numerator) + lg(denominator) does not exceed the
-- given height, where lg is log-base-2
-- given height, where lg is log-base-2. If the equave is 2/1, this is the same
-- If the equave complement of that ratio has a lower Tenney height, the ratio
-- as no-2's tenney height.
-- is kept instead.
-- EG, assuming 2/1 equave, 3/2 and 4/3 have the same tenney height of lg(3).
function p.filter_ratios_by_no_equave_factors_tenney_height(ratios, tenney_height, equave)
function p.filter_ratios_by_no_equave_factors_tenney_height(ratios, tenney_height, equave)
local ratios = ratios or { rat.new(5, 4), rat.new(81, 64), rat.new(9, 7) }
local ratios = ratios or { rat.new(5, 4), rat.new(81, 64), rat.new(9, 7) }
Line 329: Line 312:
-- Converts ratios to text, with delimiter
-- Converts ratios to text, with delimiter
-- Default delimiter is a comma followed by a space
-- Default delimiter is a comma followed by a space
function p.ratios_to_text(ratios, delimiter)
function p.ratios_to_text(ratios, delimiter, add_links)
local ratios = ratios or { rat.new(5, 4), rat.new(81, 64), rat.new(9, 7) }
local ratios = ratios or { rat.new(5, 4), rat.new(81, 64), rat.new(9, 7) }
local delimiter = delimiter or ", "
local delimiter = delimiter or ", "
local add_links = add_links == true


local text = ""
local text = ""
for i = 1, #ratios do
if add_links then
text = text .. rat.as_ratio(ratios[i])
for i = 1, #ratios do
if i < #ratios then
text = text .. string.format("[[%s]]", rat.as_ratio(ratios[i]))
text = text .. delimiter
if i < #ratios then
text = text .. delimiter
end
end
else
for i = 1, #ratios do
text = text .. rat.as_ratio(ratios[i])
if i < #ratios then
text = text .. delimiter
end
end
end
end
end