Module:JI ratios: Difference between revisions

From Xenharmonic Wiki
Jump to navigation Jump to search
Ganaram inukshuk (talk | contribs)
m todo
Ganaram inukshuk (talk | contribs)
Rename functions; combine tenney height and int limit (plus complements-only, a new unimplemented search arg) into a table of search args; will momentarily break dependent templates
Line 3: Line 3:
local tip = require("Module:Template input parse")
local tip = require("Module:Template input parse")
local med = require("Module:Mediants")
local med = require("Module:Mediants")
local yesno = require("Module:Yesno")
p = {}
p = {}


Line 26: Line 27:
--  optional. If omitted, tenney height defaults to infinity.
--  optional. If omitted, tenney height defaults to infinity.


-- INT_LIMIT_MAX is hardcoded to limit the size of output. This only applies to
local DEFAULT_FINE_SEARCH_ARGS = {
-- int limit search, as other search functions (subgroup, prime-limit) may allow
["Int Limit"]        = 50,
-- higher search maxima. For reference, searching within the octave yields this
["Tenney Height"]    = 1/0,
-- many ratios:
["Complements Only"] = false
-- 400 -> ~24000 ratios
}
-- 300 -> ~14000 ratios
 
-- 250 -> ~9500 ratios
function p.preprocess_fine_search_args(fine_search_args)
-- 200 -> ~6000 ratios
local fine_search_args = fine_search_args or DEFAULT_FINE_SEARCH_ARGS
-- 150 -> ~3400 ratios
local tenney_height = fine_search_args["Tenney Height"] or 1/0
-- 128 -> ~2500 ratios
local comps_only    = fine_search_args["Complements Only"] or false
-- 100 -> ~1500 ratios
local int_limit    = fine_search_args["Int Limit"] or DEFAULT_INT_LIMIT
local INT_LIMIT_MAX = 200
local DEFAULT_INT_LIMIT = 50
return {
["Tenney Height"]    = tenney_height,
["Complements Only"] = comps_only,
["Int Limit"]        = int_limit
}
end


--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
Line 48: Line 54:
-- Int limit is hardcoded to a max size to restrict the size of output, to avoid
-- Int limit is hardcoded to a max size to restrict the size of output, to avoid
-- risk of out-of-memory operations or the like.
-- risk of out-of-memory operations or the like.
function p.search_within_equave(equave, int_limit, tenney_height)
function p.search_within_equave(equave, fine_search_args)
local int_limit = int_limit or DEFAULT_INT_LIMIT
local equave = equave or rat.new(2,1) -- Defualt equave is 2/1.
local equave = equave or rat.new(2,1) -- Defualt equave is 2/1.
local tenney_height = tenney_height or 1/0 -- Defualt tenney height is infinity.
local fine_search_args = p.preprocess_fine_search_args(fine_search_args)
int_limit = math.max(1, math.min(INT_LIMIT_MAX, int_limit))
local init_ratios = {{1,1}, {1,0}}
local init_ratios = {{1,1}, {1,0}}
local search_func = p.int_limit_mediant_search
local search_func = p.int_limit_mediant_search
local search_args = { ["equave"] = equave, ["int_limit"] = int_limit, ["tenney_height"] = tenney_height }
local search_args = {  
["Equave"] = equave,
["Int Limit"]     = fine_search_args["Int Limit"],
["Tenney Height"] = fine_search_args["Tenney Height"]
}
local ratios = med.find_only_mediants_by_search_func(init_ratios, search_func, search_args)
local ratios = med.find_only_mediants_by_search_func(init_ratios, search_func, search_args)
Line 82: Line 89:
local mediant  = mediant_data["mediant"]
local mediant  = mediant_data["mediant"]
local ratio_1  = mediant_data["ratio_1"]
local ratio_1  = mediant_data["ratio_1"]
local equave        = search_args["equave"]
local equave        = search_args["Equave"]
local int_limit    = search_args["int_limit"]
local int_limit    = search_args["Int Limit"]
local tenney_height = search_args["tenney_height"]
local tenney_height = search_args["Tenney Height"]
local equave_as_float = rat.as_float(equave)
local equave_as_float = rat.as_float(equave)
Line 105: Line 112:
-- Note that members in a subgroup need not be prime, as long as the terms are,
-- Note that members in a subgroup need not be prime, as long as the terms are,
-- for the most part, relatively prime.
-- for the most part, relatively prime.
function p.search_by_subgroup_within_equave(subgroup, equave, int_limit, tenney_height)
function p.search_by_subgroup_within_equave(subgroup, equave, fine_search_args)
local subgroup = subgroup or { 2, 3, 7 }
local subgroup = subgroup or { 2, 3, 7 }
local int_limit = int_limit or 50
local equave = equave or rat.new(2,1) -- Defualt equave is 2/1.
local equave = equave or rat.new(2,1) -- Defualt equave is 2/1.
local tenney_height = tenney_height or 1/0 -- Defualt tenney height is infinity.
local fine_search_args = p.preprocess_fine_search_args(fine_search_args)
-- Be absolutely sure the subgroup's members are sorted!
-- Be absolutely sure the subgroup's members are sorted!
table.sort(subgroup)
table.sort(subgroup)
-- Fine search params for ease of access
local int_limit = fine_search_args["Int Limit"]
local tenney_height = fine_search_args["Tenney Height"]
-- Find all possible products given the factors in the subgroup.
-- Find all possible products given the factors in the subgroup.
Line 192: Line 202:
-- Like subgroup search, prime limit search can also allow for very high int
-- Like subgroup search, prime limit search can also allow for very high int
-- limits, as long as the prime is reasonably small.
-- limits, as long as the prime is reasonably small.
function p.search_by_prime_limit_within_equave(prime_limit, equave, int_limit, tenney_height)
function p.search_by_prime_limit_within_equave(prime_limit, equave, fine_search_args)
local prime_limit = prime_limit or 5
local prime_limit = prime_limit or 5
local int_limit = int_limit or 1000
local equave = equave or rat.new(2,1) -- Defualt equave is 2/1.
local equave = equave or rat.new(2,1) -- Defualt equave is 2/1.
local tenney_height = tenney_height or 1/0 -- Defualt tenney height is infinity.
local fine_search_args = p.preprocess_fine_search_args(fine_search_args)
-- Find all primes up to the prime limit.
-- Find all primes up to the prime limit.
Line 215: Line 224:
-- Perform subgroup search on the primes found, as subgroup-search code can
-- Perform subgroup search on the primes found, as subgroup-search code can
-- be reused for prime-limit search.
-- be reused for prime-limit search.
return p.search_by_subgroup_within_equave(primes, equave, int_limit, tenney_height)
return p.search_by_subgroup_within_equave(primes, equave, fine_search_args)
end
end


--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
------------------------- PARAM-BASED SEARCH FUNCTIONS -------------------------
-------------------------- ARG-BASED SEARCH FUNCTIONS --------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------


-- Search for ratios based on params passed in. Certain params have their own
-- Search for ratios based on an array of args passed in. Certain params have
-- function calls.
-- their own function calls.
function p.search_by_params(params, equave)
function p.search_by_args_within_equave(equave, search_args)
local equave = equave or rat.new(2,1)
local equave = equave or rat.new(2,1)
-- First get ratios up to an int limit. If no int limit was passed in, it
-- For each search method, check whether the corresponding search arg and
-- will default to the hardcoded default value.
-- int limit are both present and pass it and the equave to that function.
-- All other search args are used as finer search args.
-- Note that search by int limit alone just has the equave and search args
-- passed in.
local ratios = {}
local ratios = {}
if params["Prime Limit"] ~= nil and params["Int Limit"] ~= nil then
if search_args["Prime Limit"] ~= nil and search_args["Int Limit"] ~= nil then
ratios = p.search_by_prime_limit_within_equave(params["Prime Limit"], equave, params["Int Limit"], params["Tenney Height"])
ratios = p.search_by_prime_limit_within_equave(search_args["Prime Limit"], equave, search_args)
elseif params["Subgroup"] ~= nil and params["Int Limit"] ~= nil then
elseif search_args["Subgroup"] ~= nil and search_args["Int Limit"] ~= nil then
ratios = p.search_by_subgroup_within_equave(params["Subgroup"], equave, params["Int Limit"], params["Tenney Height"])
ratios = p.search_by_subgroup_within_equave(search_args["Subgroup"], equave, search_args)
elseif params["Int Limit"] ~= nil then
elseif search_args["Int Limit"] ~= nil then
ratios = p.search_within_equave(equave, params["Int Limit"], params["Tenney Height"])
ratios = p.search_within_equave(equave, search_args)
end
end
return ratios
return ratios
end
end


-- Parse search params.
-- Parse search args.
function p.parse_search_params(search_params)
function p.parse_search_args(search_args)
local parsed = tip.parse_kv_pairs(search_params)
local parsed = tip.parse_kv_pairs(search_args)
if parsed["Int Limit"] ~= nil then
if parsed["Int Limit"] ~= nil then
Line 258: Line 270:
if parsed["Subgroup"] ~= nil then
if parsed["Subgroup"] ~= nil then
parsed["Subgroup"] = tip.parse_numeric_entries(parsed["Subgroup"], ".")
parsed["Subgroup"] = tip.parse_numeric_entries(parsed["Subgroup"], ".")
end
if parsed["Complements Only"] ~= nil then
parsed["Complements Only"] = yesno(parsed["Complements Only"])
end
end
Line 263: Line 279:
end
end


function p.search_param_footnotes(search_params)
function p.search_footnotes(search_args)
local result = "Other interpretations are possible."
local result = "Other interpretations are possible."
local autosearch_text = "Automatic search may include irrelevant ratios and may not include all notable ratios."
local autosearch_text = "Automatic search may include irrelevant ratios and may not include all notable ratios."
local search_text = ""
local search_text = ""
if search_params["Prime Limit"] ~= nil then
if search_args["Prime Limit"] ~= nil then
search_text = string.format("Ratios shown are within the %s-prime limit.", search_params["Prime Limit"])
search_text = string.format("Ratios shown are within the %s-prime limit.", search_args["Prime Limit"])
.. " " .. autosearch_text
.. " " .. autosearch_text
elseif search_params["Subgroup"] ~= nil then
elseif search_args["Subgroup"] ~= nil then
search_text = string.format("Ratios shown are within the %s subgroup.", table.concat(search_params["Subgroup"], "."))
search_text = string.format("Ratios shown are within the %s subgroup.", table.concat(search_args["Subgroup"], "."))
.. " " .. autosearch_text
.. " " .. autosearch_text
elseif search_params["Int Limit"] ~= nil then
elseif search_args["Int Limit"] ~= nil then
search_text = string.format("Ratios shown are within the %s-integer limit.", search_params["Int Limit"])
search_text = string.format("Ratios shown are within the %s-integer limit.", search_args["Int Limit"])
.. " " .. autosearch_text
.. " " .. autosearch_text
end
end
Line 368: Line 384:
--return p.ratios_as_text(ratios)
--return p.ratios_as_text(ratios)
local fine_search_args = {["Complements Only"] = true, ["Int Limit"] = 40, ["Subgroup"] = {2,3,7}}
return p.parse_search_params("Prime Limit: 19")
return p.ratios_as_text(p.search_by_args_within_equave(rat.new(3,2), fine_search_args))
end
end


return p
return p

Revision as of 07:28, 20 September 2024

Module documentation[view] [edit] [history] [purge]
This module may be invoked by templates using its corresponding template Template:JI ratios, or used directly from other modules.
Module:JI ratios is a draft module. It is incomplete and may not be in active development. If possible, editors are encouraged to help with its development. In the meantime, editors should avoid using this module across the Xenharmonic Wiki, except for testing.
Introspection summary for Module:JI ratios 
Functions provided (0)
Line Function Params
Lua modules required (5)
Variable Module Functions used
med Module:Mediants find_only_mediants_by_search_func
rat Module:Rational new
gt
as_float
cents
as_ratio
tip Module:Template input parse parse_kv_pairs
parse_numeric_entries
parse_numeric_pairs
utils Module:Utils _gcd
yesno Module:Yesno yesno

No function descriptions were provided. The Lua code may have further information.


local rat = require("Module:Rational")
local utils = require("Module:Utils")
local tip = require("Module:Template input parse")
local med = require("Module:Mediants")
local yesno = require("Module:Yesno")
p = {}

-- TODO:
-- Add option to filter out ratios whose complement would exceed the int limit
-- Group complements option and tenney height into fine-search params

-- Template for handling multiple entry of JI ratios into a template, and for
-- searching for JI ratios if automatic entry is desired.
-- This is a successor/replacement for JI ratio finder.

-- JI ratios are searched by the following params in a hierarchy:
-- - Search by prime limit. Int limit is used to limit the num/den of ratios.
--   Prime limit takes precedence over subgroup.
-- - Search by subgroup. (Subgroup may contain nonprime numbers, but ratios are
--   currently not supported.) Int limit is used to limit the num/den of ratios.
-- - If neither prime limit or subgroup is present, search by int limit. This
--   is considered the absolute minimum requirement for ratio searching.
-- NOTES:
-- - Prime limits are infinite sets, so int limit is used to restrain the set
--   to a finite size. The same is true for subgroup.
-- - Tenney height is used for further filtering of ratios, and is considered
--   optional. If omitted, tenney height defaults to infinity.

local DEFAULT_FINE_SEARCH_ARGS = {
	["Int Limit"]        = 50,
	["Tenney Height"]    = 1/0,
	["Complements Only"] = false
}

function p.preprocess_fine_search_args(fine_search_args)
	local fine_search_args = fine_search_args or DEFAULT_FINE_SEARCH_ARGS
	local tenney_height = fine_search_args["Tenney Height"] or 1/0
	local comps_only    = fine_search_args["Complements Only"] or false
	local int_limit     = fine_search_args["Int Limit"] or DEFAULT_INT_LIMIT
	
	return {
		["Tenney Height"]    = tenney_height,
		["Complements Only"] = comps_only,
		["Int Limit"]        = int_limit
	}
end

--------------------------------------------------------------------------------
----------------------- INT-LIMIT-BASED SEARCH FUNCTION ------------------------
--------------------------------------------------------------------------------

-- Int-limit-based search; finds ratios between 1/1 and an equave, within an int
-- limit. An optional tenney height can be passed in.
-- Int limit is hardcoded to a max size to restrict the size of output, to avoid
-- risk of out-of-memory operations or the like.
function p.search_within_equave(equave, fine_search_args)
	local equave = equave or rat.new(2,1)			-- Defualt equave is 2/1.
	local fine_search_args = p.preprocess_fine_search_args(fine_search_args)
	
	local init_ratios = {{1,1}, {1,0}}
	local search_func = p.int_limit_mediant_search
	local search_args = { 
		["Equave"] = equave,
		["Int Limit"]     = fine_search_args["Int Limit"],
		["Tenney Height"] = fine_search_args["Tenney Height"]
	}
	local ratios = med.find_only_mediants_by_search_func(init_ratios, search_func, search_args)
	
	-- 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.
	-- Note that mediant search results in sorted ratios, so remove them from
	-- the end until there's no more to remove.
	while rat.gt(ratios[#ratios], equave) do
		table.remove(ratios, #ratios)
	end
	
	return ratios
end

-- Int limit search function, with equave and tenney height cutoffs.
-- If nil is passed in for the tenney height, it will defualt to infinity.
-- To be passed into mediant-search function, as part of int-limit-search
-- function call.
function p.int_limit_mediant_search(mediant_data, search_args)
	local mediant   = mediant_data["mediant"]
	local ratio_1   = mediant_data["ratio_1"]
	local equave        = search_args["Equave"]
	local int_limit     = search_args["Int Limit"]
	local tenney_height = search_args["Tenney Height"]
	
	local equave_as_float = rat.as_float(equave)
	local rat_1_as_float = ratio_1[1] / ratio_1[2]
	local mediant_th = math.log(mediant[1] * mediant[2]) / math.log(2)
	
	return math.max(mediant[1], mediant[2]) <= int_limit and rat_1_as_float < equave_as_float and mediant_th <= tenney_height
end

--------------------------------------------------------------------------------
------------------------ SUBGROUP-BASED SEARCH FUNCTION ------------------------
--------------------------------------------------------------------------------

-- Subgroup-based search; finds ratios between 1/1 and an equave, within a sub-
-- group. An int limit is passed in to limit the size of output, since subgroups
-- are infinite sets. An optional tenney height can be passed in to further
-- limit output.
-- Unlike int limit search, subgroup search can allow for very high int limits,
-- as long as the subgroup is reasonably small and has reasonably small terms.
-- Note that members in a subgroup need not be prime, as long as the terms are,
-- for the most part, relatively prime.
function p.search_by_subgroup_within_equave(subgroup, equave, fine_search_args)
	local subgroup = subgroup or { 2, 3, 7 }
	local equave = equave or rat.new(2,1)			-- Defualt equave is 2/1.
	local fine_search_args = p.preprocess_fine_search_args(fine_search_args)
	
	-- Be absolutely sure the subgroup's members are sorted!
	table.sort(subgroup)
	
	-- Fine search params for ease of access
	local int_limit = fine_search_args["Int Limit"]
	local tenney_height = fine_search_args["Tenney Height"]
	
	-- Find all possible products given the factors in the subgroup.
	-- These will be used to find all possible ratios.
	local products = {{1}}
	local new_products_found = true
	while new_products_found do
		local new_products = {}
		for i = 1, #subgroup do
			for j = 1, #products[#products] do
				local new_product = products[#products][j] * subgroup[i]
				if new_product <= int_limit then
					local product_already_added = false
					for k = 1, #new_products do
						product_already_added = product_already_added or new_product == new_products[k]
						if product_already_added then break end
					end
					if not product_already_added then
						table.insert(new_products, new_product)
					end
				end
			end
		end
		if #new_products == 0 then
			new_products_found = false
		else
			table.insert(products, new_products)
		end
	end
	
	-- Consolidate and sort products
	local consolidated_products = {}
	for i = 1, #products do
		for j = 1, #products[i] do
			table.insert(consolidated_products, products[i][j])
		end
	end
	products = consolidated_products
	table.sort(products)

	-- Using the products produced earlier, combine them to make all possible
	-- ratios from 1/1 to the equave. Ratios with non-coprime numerator and
	-- denominator, or exceed the tenney height, are omitted.
	local ratios = {}
	local equave_as_float = rat.as_float(equave)
	for i = 1, #products do
		local denominator = products[i]
		for j = i, #products do
			local numerator = products[j]
			local gcd = utils._gcd(numerator, denominator)
			if gcd == 1 then
				local within_equave = numerator / denominator <= equave_as_float
				local within_tenney_height = math.log(numerator * denominator) / math.log(2) <= tenney_height
				if within_equave and within_tenney_height then
					table.insert(ratios, {numerator, denominator})
				else
					break
				end
			end
		end
	end

	-- 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
	
	return ratios
end

--------------------------------------------------------------------------------
---------------------- PRIME-LIMIT-BASED SEARCH FUNCTION -----------------------
--------------------------------------------------------------------------------

-- Prime-limit-based search; finds ratios between 1/1 and an equave, within a
-- prime limit. An int limit is passed in to limit the size of output, since
-- prime limits are inifinite sets. An optional tenney height can be passed in
-- to further limit output.
-- Like subgroup search, prime limit search can also allow for very high int
-- limits, as long as the prime is reasonably small.
function p.search_by_prime_limit_within_equave(prime_limit, equave, fine_search_args)
	local prime_limit = prime_limit or 5
	local equave = equave or rat.new(2,1)			-- Defualt equave is 2/1.
	local fine_search_args = p.preprocess_fine_search_args(fine_search_args)
	
	-- Find all primes up to the prime limit.
	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, i)
		end
	end
	
	-- Perform subgroup search on the primes found, as subgroup-search code can
	-- be reused for prime-limit search.
	return p.search_by_subgroup_within_equave(primes, equave, fine_search_args)
end

--------------------------------------------------------------------------------
-------------------------- ARG-BASED SEARCH FUNCTIONS --------------------------
--------------------------------------------------------------------------------

-- Search for ratios based on an array of args passed in. Certain params have
-- their own function calls.
function p.search_by_args_within_equave(equave, search_args)
	local equave = equave or rat.new(2,1)
	
	-- For each search method, check whether the corresponding search arg and
	-- int limit are both present and pass it and the equave to that function.
	-- All other search args are used as finer search args.
	-- Note that search by int limit alone just has the equave and search args
	-- passed in.
	local ratios = {}
	if search_args["Prime Limit"] ~= nil and search_args["Int Limit"] ~= nil then
		ratios = p.search_by_prime_limit_within_equave(search_args["Prime Limit"], equave, search_args)
	elseif search_args["Subgroup"] ~= nil and search_args["Int Limit"] ~= nil then
		ratios = p.search_by_subgroup_within_equave(search_args["Subgroup"], equave, search_args)
	elseif search_args["Int Limit"] ~= nil then
		ratios = p.search_within_equave(equave, search_args)
	end
	return ratios
end

-- Parse search args.
function p.parse_search_args(search_args)
	local parsed = tip.parse_kv_pairs(search_args)
	
	if parsed["Int Limit"] ~= nil then
		parsed["Int Limit"] = tonumber(parsed["Int Limit"])
	end
	
	if parsed["Tenney Height"] ~= nil then
		parsed["Tenney Height"] = tonumber(parsed["Tenney Height"])
	end
	
	if parsed["Prime Limit"] ~= nil then
		parsed["Prime Limit"] = tonumber(parsed["Prime Limit"])
	end
	
	if parsed["Subgroup"] ~= nil then
		parsed["Subgroup"] = tip.parse_numeric_entries(parsed["Subgroup"], ".")
	end
	
	if parsed["Complements Only"] ~= nil then
		parsed["Complements Only"] = yesno(parsed["Complements Only"])
	end
	
	return parsed
end

function p.search_footnotes(search_args)
	local result = "Other interpretations are possible."
	local autosearch_text = "Automatic search may include irrelevant ratios and may not include all notable ratios."
	local search_text = ""
	
	if search_args["Prime Limit"] ~= nil then
		search_text = string.format("Ratios shown are within the %s-prime limit.", search_args["Prime Limit"])
			.. " " .. autosearch_text
	elseif search_args["Subgroup"] ~= nil then
		search_text = string.format("Ratios shown are within the %s subgroup.", table.concat(search_args["Subgroup"], "."))
			.. " " .. autosearch_text
	elseif search_args["Int Limit"] ~= nil then
		search_text = string.format("Ratios shown are within the %s-integer limit.", search_args["Int Limit"])
			.. " " .. autosearch_text
	end
	
	result = search_text .. " " .. result
	return result
end

--------------------------------------------------------------------------------
--------------------------- RATIO SORTING FUNCTIONS ----------------------------
--------------------------------------------------------------------------------

-- Sorts ratios by closeness to cent values.
function p.sort_by_closeness_to_cent_values(ratios, cent_values, tolerance)
	local tolerance = tolerance or 30
	
	local sorted_ratios = {}
	local curr_index = 1		-- Index of current_ratio
	for i = 1, #cent_values do
		local lower_bound = cent_values[i] - tolerance
		local upper_bound = cent_values[i] + tolerance
		local cents_within_range = true
		local curr_ratios = {}
		
		for j = curr_index, #ratios do
			local curr_ratio = ratios[j]
			local curr_cents = rat.cents(curr_ratio)
			
			if lower_bound < curr_cents and curr_cents < upper_bound then
				table.insert(curr_ratios, curr_ratio)
			--elseif curr_cents > upper_bound then
			--	curr_index = j
			--	break
			end
		end
		
		table.insert(sorted_ratios, curr_ratios)
	end
	
	return sorted_ratios
end

--------------------------------------------------------------------------------
------------------------ RATIO PARSING/INPUT FUNCTIONS -------------------------
--------------------------------------------------------------------------------

-- Parse a list of ratios from a string. String is formatted as follows:
-- "a/b; c/d; e/f; g/h"
function p.parse_ratios(unparsed)
	local parsed = tip.parse_numeric_pairs(unparsed)
	for i = 1, #parsed do
		parsed[i] = rat.new(parsed[i][1], parsed[i][2])
	end
	return parsed
end

--------------------------------------------------------------------------------
---------------------------- RATIO STRING FUNCTIONS ----------------------------
--------------------------------------------------------------------------------

-- Convert a table of ratios into a string, with options for links and delimiter
function p.ratios_as_text(ratios, add_links, delimiter)
	local add_links = add_links == true
	local delimiter = delimiter or ", "
	
	local text = ""
	if #ratios ~= 0 then
		text = add_links and string.format("[[%s]]", rat.as_ratio(ratios[1])) or rat.as_ratio(ratios[1])
		for i = 2, #ratios do
			text = text .. (add_links and string.format("%s[[%s]]", delimiter, rat.as_ratio(ratios[i])) or string.format("%s%s", delimiter, rat.as_ratio(ratios[i])))
		end
	end
	return text
end

-- Convert a table of ratios (tables, as defined by rational module) into a
-- line of text, with options for delimiters.
function p.ratios_as_texts(ratios, add_links, delimiter)
	local add_links = add_links == true
	local delimiter = delimiter or ", "
	
	local texts = {}
	for i = 1, #ratios do
		local text = p.ratios_as_text(ratios[i], add_links, delimiter)
		table.insert(texts, text)
	end
	return texts
end

function p.tester()

	
	--return p.ratios_as_text(ratios)
	local fine_search_args = {["Complements Only"] = true, ["Int Limit"] = 40, ["Subgroup"] = {2,3,7}}
	
	return p.ratios_as_text(p.search_by_args_within_equave(rat.new(3,2), fine_search_args))
end

return p