Module:JI ratios: Difference between revisions

Ganaram inukshuk (talk | contribs)
add search-within-cent-range functions; add todo to convert search-within-equave functions to use the search-within-cent-range functions, since code is near-identical
Ganaram inukshuk (talk | contribs)
cleanup; remove todo
Line 6: Line 6:
local getArgs = require("Module:Arguments").getArgs
local getArgs = require("Module:Arguments").getArgs
p = {}
p = {}
-- TODO
-- - Convert search-within-equave functions to use search-within-cent-range
--  functions, since code is nearly identical.


-- Template for handling multiple entry of JI ratios into a template, and for
-- Template for handling multiple entry of JI ratios into a template, and for
Line 80: Line 76:
-- numerator or denominator don't exceed the int limit.
-- numerator or denominator don't exceed the int limit.
function p.search_by_int_limit(equave, int_limit)
function p.search_by_int_limit(equave, int_limit)
--local equave    = equave or rat.new(2,1) -- Defualt equave is 2/1.
return p.search_by_int_limit_within_cents(0, rat.cents(equave), int_limit)
--local int_limit = int_limit or 50 -- Default is 50
-- Find all ratios from 1/1 to the equave by finding mediants between 1/1
-- and 1/0. Mediants module has a function for this built-in, but includes
-- ratios that exceed the equave (for example, if the equave is 2/1, then
-- the mediants function will include 3/1 and 4/1), which must be removed
-- afterwards.
local init_ratios = {{1,1}, {1,0}}
local ratios = med.find_only_mediants_by_int_limit(init_ratios, int_limit)
-- Convert to ratios that Module:Rational can work with
for i = 1, #ratios do
ratios[i] = rat.new(ratios[i][1], ratios[i][2])
end
-- Remove ratios that exceed the equave.
-- Because the ratios are already sorted by cent value, remove ratios from
-- the end of the table until there are no more ratios to remove.
while rat.gt(ratios[#ratios], equave) do
table.remove(ratios, #ratios)
end
return ratios
end
end


Line 143: Line 116:
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------


-- Prime limit search finds ratios with prime factors that don't exceed some
function p.prime_limit_to_subgroup(prime_limit)
-- prime limit.
local subgroup = {}
-- Upper bounds for searching is the equave and int limit.
function p.search_by_prime_limit(equave, int_limit, prime_limit)
--local equave      = equave or rat.new(2,1) -- Defualt equave is 2/1.
--local int_limit  = int_limit or 50 -- Default is 50
--local prime_limit = prime_limit or 5 -- Default is 5-prime-limit
 
-- Convert prime limit into an equivalent subgroup (EG, 7-limit becomes
-- 2.3.5.7) so that it can be passed into the subgroup search function.
local primes = {}
for i = 2, prime_limit do
for i = 2, prime_limit do
local is_prime = true
local is_prime = true
Line 163: Line 127:
end
end
if is_prime then
if is_prime then
table.insert(primes, rat.new(i))
table.insert(subgroup, rat.new(i))
end
end
end
end
return subgroup
-- Perform subgroup search.
end
return p.search_by_subgroup(equave, int_limit, primes)
 
-- Prime limit search finds ratios with prime factors that don't exceed some
-- prime limit.
-- Upper bounds for searching is the equave and int limit.
function p.search_by_prime_limit(equave, int_limit, prime_limit)
local subgroup = p.prime_limit_to_subgroup(prime_limit)
return p.search_by_subgroup_within_cents(0, rat.cents(equave), int_limit, subgroup)
end
end


Line 174: Line 144:
-- prime limit. Searches within a cent range.
-- prime limit. Searches within a cent range.
function p.search_by_prime_limit_within_cents(min_cents, max_cents, int_limit, prime_limit)
function p.search_by_prime_limit_within_cents(min_cents, max_cents, int_limit, prime_limit)
--local equave      = equave or rat.new(2,1) -- Defualt equave is 2/1.
local subgroup = p.prime_limit_to_subgroup(prime_limit)
--local int_limit  = int_limit or 50 -- Default is 50
local ratios = p.search_by_subgroup_within_cents(min_cents, max_cents, int_limit, subgroup)
--local prime_limit = prime_limit or 5 -- Default is 5-prime-limit
while rat.cents(ratios[1]) <= min_cents do
 
table.remove(ratios, 1)
-- Convert prime limit into an equivalent subgroup (EG, 7-limit becomes
-- 2.3.5.7) so that it can be passed into the subgroup search function.
local primes = {}
for i = 2, prime_limit do
local is_prime = true
for j = 2, math.floor(math.sqrt(i)) do
if i % j == 0 then
is_prime = false
break
end
end
if is_prime then
table.insert(primes, rat.new(i))
end
end
end
return ratios
-- Perform subgroup search.
return p.search_by_subgroup_within_cents(min_cents, max_cents, int_limit, primes)
end
end


Line 202: Line 156:
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------


-- Subgroup search find ratios that are products of at least two (not
-- Subgroup search find ratios that are products of at least two non-unique
-- necessarily unique) elements from the subgroup. EG, for 2.3.7, 2/1, 3/2, 7/4,
-- elements from the subgroup.
-- and 7/6 are within the subgroup, but not 5/4 because there's no 5.
-- Upper bounds for searching is the equave and int limit.
function p.search_by_subgroup(equave, int_limit, subgroup)
function p.search_by_subgroup(equave, int_limit, subgroup)
--local equave    = equave or rat.new(2,1) -- Defualt equave is 2/1.
local ratios = p.search_by_subgroup_within_cents(0, rat.cents(equave), int_limit, subgroup)
--local int_limit = int_limit or 50 -- Default is 50
while rat.cents(ratios[1]) <= min_cents do
--local subgroup  = subgroup or {rat.new(2), rat.new(3), rat.new(7)} -- Default is 2.3.7 subgroup
table.remove(ratios, 1)
-- Find all possible ways to multiply subgroup elements with one another
-- using breadth-first-search. Products found this way should not exceed the
-- int limit, and if a subgroup element is rational, neither its numerator
-- nor denominator should exceed the int limit.
local products = { rat.new(1) }
local i = 1
while i <= #products do
-- Multiply each subgroup element by the current ratio. The table of
-- product ratios created this way is merged with the running table of
-- ratios. This is the Cartesian product of the single ratio as a set,
-- with the subgroup elements as a set, or {p/q} X subgroup.
local new_products = {}
for j = 1, #subgroup do
local new_ratio = rat.mul(products[i], subgroup[j])
if rat.is_within_int_limit(new_ratio, int_limit) and not p.find_ratio_in_table(new_products, new_ratio) then
table.insert(new_products, new_ratio)
end
end
-- Merge new products with the table of products, omitting duplicates.
p.merge_tables(products, new_products)
i = i + 1
end
end
-- Sort for next step
table.sort(products, rat.lt)
-- Use the products found to find all ratios between 1 and the equave.
-- For each ratio in the table of products, create a set of new ratios by
-- having that ratio be the numerator and all successive ratios be possible
-- denominators. Store these new ratios in a table, and repeat with all
-- successive products, omitting duplicats. From earlier testing, this is
-- faster than performing BFS on each ratio, and yields the same results.
local ratios = {}
for i = 1, #products do
local new_ratios = {}
for j = i, #products do
local new_ratio = rat.div(products[j], products[i])
if rat.as_float(new_ratio) > rat.as_float(equave) then break end
if not p.find_ratio_in_table(new_ratios, new_ratio) and rat.is_within_int_limit(new_ratio, int_limit) then
table.insert(new_ratios, new_ratio)
end
end
-- Merge new ratios with the table of ratios, omitting duplicates.
p.merge_tables(ratios, new_ratios)
end
-- Sort
table.sort(ratios, rat.lt)
return ratios
return ratios
end
end
Line 505: Line 405:
function p.tester()
function p.tester()
--return p.ratios_as_string(p._ji_ratios(p.parse_args("Int Limit: 16; Equave: 3/1; Complements Only: 0")))
--return p.ratios_as_string(p._ji_ratios(p.parse_args("Int Limit: 16; Equave: 3/1; Complements Only: 0")))
return p.ratios_as_string(p.search_by_prime_limit_within_cents(372, 440, 17, 30))
--return p.ratios_as_string(p.search_by_prime_limit_within_cents(372, 440, 17, 30))
return p.ratios_as_string(p.search_by_int_limit_within_cents(300,500, 20))
end
end