Module:JI ratio finder: Difference between revisions
Jump to navigation
Jump to search
m find_ratio_for_cents now returns ratios (as lua tables), not text |
Added function to find candidate ratios, intended for finding multiple sets of ratios; added filter functions |
||
Line 4: | Line 4: | ||
local p = {} | local p = {} | ||
-- | -- Finds candidate ratios up to a cent value and up to an integer limit | ||
-- Ratios found this way will range from 0 cents to the given cent value | |||
-- These ratios should then be filtered as needed | |||
function p.find_candidate_ratios(cents, int_limit) | |||
local cents = cents or 1200 | |||
local int_limit = int_limit or 99 | |||
local candidate_ratios = {} | |||
for i = 1, int_limit do | |||
local denominator = i | |||
local numerator = i | |||
local is_within_cents = true | |||
repeat | |||
local current_ratio = rat.new(numerator, denominator) | |||
local is_simplified = utils._gcd(numerator, denominator) == 1 | |||
is_within_cents = rat.cents(current_ratio) <= cents | |||
if is_simplified and is_within_cents then | |||
table.insert(candidate_ratios, current_ratio) | |||
end | |||
numerator = numerator + 1 | |||
until not is_within_cents | |||
end | |||
return candidate_ratios | |||
end | |||
-- Filter ratios based on whether they're within a cent range | |||
function p.filter_ratios_by_range(ratios, min_cents, max_cents) | |||
local ratios = ratios or { rat.new(5, 4), rat.new(81, 64), rat.new(9, 7) } | |||
local min_cents = min_cents or 380 | |||
local max_cents = max_cents or 420 | |||
local filtered_ratios = {} | |||
for i = 1, #ratios do | |||
local ratio_in_cents = rat.cents(ratios[i]) | |||
if ratio_in_cents >= min_cents and ratio_in_cents <= max_cents then | |||
table.insert(filtered_ratios, ratios[i]) | |||
end | |||
end | |||
return filtered_ratios | |||
end | |||
-- Filters ratios by prime limit | |||
-- Filters out ratios whose prime factorizations contains primes larger than | |||
-- the prime limit | |||
function p.filter_ratios_by_prime_limit(ratios, prime_limit) | |||
local ratios = ratios or { rat.new(5, 4), rat.new(81, 64), rat.new(9, 7) } | |||
local prime_limit = prime_limit or 5 | |||
local filtered_ratios = {} | |||
for i = 1, #ratios do | |||
if rat.max_prime(ratios[i]) <= prime_limit then | |||
table.insert(filtered_ratios, ratios[i]) | |||
end | |||
end | |||
return filtered_ratios | |||
end | |||
-- Filters ratios by odd limit | |||
-- Filters out ratios where, ignoring powers of 2, either the numerator or | |||
-- denominator exceeds the odd limit | |||
function p.filter_ratios_by_odd_limit(ratios, odd_limit) | |||
local ratios = ratios or { rat.new(5, 4), rat.new(81, 64), rat.new(9, 7) } | |||
local odd_limit = odd_limit or 5 | |||
local filtered_ratios = {} | |||
for i = 1, #ratios do | |||
if rat.odd_limit(ratios[i]) <= odd_limit then | |||
table.insert(filtered_ratios, ratios[i]) | |||
end | |||
end | |||
return filtered_ratios | |||
end | |||
-- Filters ratios by harmonic class | |||
-- Filters out ratios whose largest prime is larger than the given prime; only | |||
-- ratios whose largest prime is the given harmonic class are kept | |||
function p.filter_ratios_by_harmonic_class(ratios, harmonic_class) | |||
local ratios = ratios or { rat.new(5, 4), rat.new(81, 64), rat.new(9, 7) } | |||
local harmonic_class = harmonic_class or 5 | |||
local filtered_ratios = {} | |||
for i = 1, #ratios do | |||
if rat.max_prime(ratios[i]) == harmonic_class then | |||
table.insert(filtered_ratios, ratios[i]) | |||
end | |||
end | |||
return filtered_ratios | |||
end | |||
-- Filters ratios by Tenney height | |||
-- Filters ratios where lg(numerator) + lg(denominator) does not exceed the | |||
-- given height, where lg is log-base-2 | |||
function p.filter_ratios_by_tenney_height(ratios, tenney_height) | |||
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 filtered_ratios = {} | |||
for i = 1, #ratios do | |||
if rat.tenney_height(ratios[i]) <= tenney_height then | |||
table.insert(filtered_ratios, ratios[i]) | |||
end | |||
end | |||
return filtered_ratios | |||
end | |||
-- Finds approximated JI ratios for a cent value for a prime and odd limit | -- Finds approximated JI ratios for a cent value for a prime and odd limit | ||
-- Meant to find ratios within the range of a single cent value | |||
-- TODO: use integer limit instead of odd limit | |||
function p.find_ratios_for_cents(cents, tolerance, prime_limit, odd_limit) | function p.find_ratios_for_cents(cents, tolerance, prime_limit, odd_limit) | ||
local cents = cents or 700 | local cents = cents or 700 | ||
Line 59: | Line 176: | ||
end | end | ||
function p.ratios_to_text(ratios) | -- Converts ratios to text, with delimiter | ||
-- Default delimiter is a comma followed by a space | |||
function p.ratios_to_text(ratios, delimiter) | |||
local ratios = ratios or { rat.new(5, 4), rat.new (81, 64) } | local ratios = ratios or { rat.new(5, 4), rat.new (81, 64) } | ||
local delimiter = delimiter or ", " | |||
local text = "" | local text = "" | ||
Line 66: | Line 186: | ||
text = text .. rat.as_ratio(ratios[i]) | text = text .. rat.as_ratio(ratios[i]) | ||
if i ~= #ratios then | if i ~= #ratios then | ||
text = text .. | text = text .. delimiter | ||
end | end | ||
end | end | ||
return text | return text | ||
end | end | ||
return p | return p |
Revision as of 10:06, 21 January 2024
local utils = require('Module:Utils')
local interval = require('Module:Interval')
local rat = require('Module:Rational')
local p = {}
-- Finds candidate ratios up to a cent value and up to an integer limit
-- Ratios found this way will range from 0 cents to the given cent value
-- These ratios should then be filtered as needed
function p.find_candidate_ratios(cents, int_limit)
local cents = cents or 1200
local int_limit = int_limit or 99
local candidate_ratios = {}
for i = 1, int_limit do
local denominator = i
local numerator = i
local is_within_cents = true
repeat
local current_ratio = rat.new(numerator, denominator)
local is_simplified = utils._gcd(numerator, denominator) == 1
is_within_cents = rat.cents(current_ratio) <= cents
if is_simplified and is_within_cents then
table.insert(candidate_ratios, current_ratio)
end
numerator = numerator + 1
until not is_within_cents
end
return candidate_ratios
end
-- Filter ratios based on whether they're within a cent range
function p.filter_ratios_by_range(ratios, min_cents, max_cents)
local ratios = ratios or { rat.new(5, 4), rat.new(81, 64), rat.new(9, 7) }
local min_cents = min_cents or 380
local max_cents = max_cents or 420
local filtered_ratios = {}
for i = 1, #ratios do
local ratio_in_cents = rat.cents(ratios[i])
if ratio_in_cents >= min_cents and ratio_in_cents <= max_cents then
table.insert(filtered_ratios, ratios[i])
end
end
return filtered_ratios
end
-- Filters ratios by prime limit
-- Filters out ratios whose prime factorizations contains primes larger than
-- the prime limit
function p.filter_ratios_by_prime_limit(ratios, prime_limit)
local ratios = ratios or { rat.new(5, 4), rat.new(81, 64), rat.new(9, 7) }
local prime_limit = prime_limit or 5
local filtered_ratios = {}
for i = 1, #ratios do
if rat.max_prime(ratios[i]) <= prime_limit then
table.insert(filtered_ratios, ratios[i])
end
end
return filtered_ratios
end
-- Filters ratios by odd limit
-- Filters out ratios where, ignoring powers of 2, either the numerator or
-- denominator exceeds the odd limit
function p.filter_ratios_by_odd_limit(ratios, odd_limit)
local ratios = ratios or { rat.new(5, 4), rat.new(81, 64), rat.new(9, 7) }
local odd_limit = odd_limit or 5
local filtered_ratios = {}
for i = 1, #ratios do
if rat.odd_limit(ratios[i]) <= odd_limit then
table.insert(filtered_ratios, ratios[i])
end
end
return filtered_ratios
end
-- Filters ratios by harmonic class
-- Filters out ratios whose largest prime is larger than the given prime; only
-- ratios whose largest prime is the given harmonic class are kept
function p.filter_ratios_by_harmonic_class(ratios, harmonic_class)
local ratios = ratios or { rat.new(5, 4), rat.new(81, 64), rat.new(9, 7) }
local harmonic_class = harmonic_class or 5
local filtered_ratios = {}
for i = 1, #ratios do
if rat.max_prime(ratios[i]) == harmonic_class then
table.insert(filtered_ratios, ratios[i])
end
end
return filtered_ratios
end
-- Filters ratios by Tenney height
-- Filters ratios where lg(numerator) + lg(denominator) does not exceed the
-- given height, where lg is log-base-2
function p.filter_ratios_by_tenney_height(ratios, tenney_height)
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 filtered_ratios = {}
for i = 1, #ratios do
if rat.tenney_height(ratios[i]) <= tenney_height then
table.insert(filtered_ratios, ratios[i])
end
end
return filtered_ratios
end
-- Finds approximated JI ratios for a cent value for a prime and odd limit
-- Meant to find ratios within the range of a single cent value
-- TODO: use integer limit instead of odd limit
function p.find_ratios_for_cents(cents, tolerance, prime_limit, odd_limit)
local cents = cents or 700
local tolerance = tolerance or 20
local prime_limit = prime_limit or 5
local odd_limit = odd_limit or 49
local num = 1
local den = 1
local within_min_tolerance = true
local within_max_tolerance = true
local within_odd_limit = true
local within_prime_limit = true
local is_simplified = true
local found_ratios = {}
-- Algorithm is as follows:
-- Start with a unison p/q, where p=q and check whether it's within the
-- range of the target ratio. If so, record it. Increment p by 1 and repeat
-- until p/q exceeds the target range.
-- Once p/q exceeds the range of the target ratio, increment q and repeat
-- the process described above. End this process once p or q exceed a
-- specified limit (the odd limit, in this case).
repeat
repeat
local current_ratio = rat.new(num, den)
local ratio_in_cents = rat.cents(current_ratio)
-- Check conditions
within_min_tolerance = ratio_in_cents > cents - tolerance
within_max_tolerance = ratio_in_cents < cents + tolerance
within_odd_limit = rat.odd_limit(current_ratio) <= odd_limit
within_prime_limit = rat.max_prime(current_ratio) <= prime_limit
is_simplified = utils._gcd(num, den) == 1
if within_min_tolerance and is_simplified and within_prime_limit and within_max_tolerance and within_odd_limit then
table.insert(found_ratios, current_ratio)
end
-- Increment numerator
num = num + 1
until not within_max_tolerance
den = den + 1
num = den
until not within_odd_limit
return found_ratios
end
-- Converts ratios to text, with delimiter
-- Default delimiter is a comma followed by a space
function p.ratios_to_text(ratios, delimiter)
local ratios = ratios or { rat.new(5, 4), rat.new (81, 64) }
local delimiter = delimiter or ", "
local text = ""
for i = 1, #ratios do
text = text .. rat.as_ratio(ratios[i])
if i ~= #ratios then
text = text .. delimiter
end
end
return text
end
return p