<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ja">
	<id>https://ja.xen.wiki/index.php?action=history&amp;feed=atom&amp;title=%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%3AJI_ratios</id>
	<title>モジュール:JI ratios - 版の履歴</title>
	<link rel="self" type="application/atom+xml" href="https://ja.xen.wiki/index.php?action=history&amp;feed=atom&amp;title=%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%3AJI_ratios"/>
	<link rel="alternate" type="text/html" href="https://ja.xen.wiki/index.php?title=%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB:JI_ratios&amp;action=history"/>
	<updated>2026-07-11T03:52:56Z</updated>
	<subtitle>このウィキのこのページに関する変更履歴</subtitle>
	<generator>MediaWiki 1.43.6</generator>
	<entry>
		<id>https://ja.xen.wiki/index.php?title=%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB:JI_ratios&amp;diff=1379&amp;oldid=prev</id>
		<title>Furcht968: ページの作成:「-- This module follows User:Ganaram inukshuk/Provisional style guide for Lua local getArgs = require(&quot;Module:Arguments&quot;).getArgs local med = require(&quot;Module:Mediants&quot;) local rat = require(&quot;Module:Rational&quot;) local tip = require(&quot;Module:Template input parse&quot;) local utils = require(&quot;Module:Utils&quot;) local yesno = require(&quot;Module:Yesno&quot;)  local p = {}  -- Template for handling multiple entry of JI ratios into a template, and for -- searching for JI ratios if automatic…」</title>
		<link rel="alternate" type="text/html" href="https://ja.xen.wiki/index.php?title=%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB:JI_ratios&amp;diff=1379&amp;oldid=prev"/>
		<updated>2026-04-01T18:54:24Z</updated>

		<summary type="html">&lt;p&gt;ページの作成:「-- This module follows &lt;a href=&quot;/index.php?title=%E5%88%A9%E7%94%A8%E8%80%85:Ganaram_inukshuk/Provisional_style_guide_for_Lua&amp;amp;action=edit&amp;amp;redlink=1&quot; class=&quot;new&quot; title=&quot;利用者:Ganaram inukshuk/Provisional style guide for Lua (存在しないページ)&quot;&gt;User:Ganaram inukshuk/Provisional style guide for Lua&lt;/a&gt; local getArgs = require(&amp;quot;Module:Arguments&amp;quot;).getArgs local med = require(&amp;quot;Module:Mediants&amp;quot;) local rat = require(&amp;quot;Module:Rational&amp;quot;) local tip = require(&amp;quot;Module:Template input parse&amp;quot;) local utils = require(&amp;quot;Module:Utils&amp;quot;) local yesno = require(&amp;quot;Module:Yesno&amp;quot;)  local p = {}  -- Template for handling multiple entry of JI ratios into a template, and for -- searching for JI ratios if automatic…」&lt;/p&gt;
&lt;p&gt;&lt;b&gt;新規ページ&lt;/b&gt;&lt;/p&gt;&lt;div&gt;-- This module follows [[User:Ganaram inukshuk/Provisional style guide for Lua]]&lt;br /&gt;
local getArgs = require(&amp;quot;Module:Arguments&amp;quot;).getArgs&lt;br /&gt;
local med = require(&amp;quot;Module:Mediants&amp;quot;)&lt;br /&gt;
local rat = require(&amp;quot;Module:Rational&amp;quot;)&lt;br /&gt;
local tip = require(&amp;quot;Module:Template input parse&amp;quot;)&lt;br /&gt;
local utils = require(&amp;quot;Module:Utils&amp;quot;)&lt;br /&gt;
local yesno = require(&amp;quot;Module:Yesno&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
-- Template for handling multiple entry of JI ratios into a template, and for&lt;br /&gt;
-- searching for JI ratios if automatic entry is desired.&lt;br /&gt;
-- This is a successor/replacement for JI ratio finder.&lt;br /&gt;
&lt;br /&gt;
-- TODO: Refactor code such that:&lt;br /&gt;
-- - For int-limit search, int limit is the first arg, and equave and min/max&lt;br /&gt;
--   cents default to 2/1, 0c, and 1200c respectively.&lt;br /&gt;
--   (int_limit, equave)&lt;br /&gt;
--   (int_limit, min_cents, max_cents)&lt;br /&gt;
-- - For odd-limit search, odd limit is the first arg, int limit defaults to&lt;br /&gt;
--   twice the odd limit, and equave and min/max cents default to 2/1, 0c, and &lt;br /&gt;
--   1200c respectively.&lt;br /&gt;
--   (odd_limit, int_limit, equave)&lt;br /&gt;
--   (odd_limit, int_limit, min_cents, max_cents)&lt;br /&gt;
-- - For prime-limit search, prime-limit is the first arg, int limit defaults to&lt;br /&gt;
--   twice the largest prime, and equave and min/max cents default to 2/1, 0c,&lt;br /&gt;
--   and 1200c respectively.&lt;br /&gt;
--   (prime_limit, int_limit, equave)&lt;br /&gt;
--   (prime_limit, int_limit, min_cents, max_cents)&lt;br /&gt;
-- - For subgroup search, subgroup is the first arg, there&amp;#039;s no default value&lt;br /&gt;
--   for int limit (due to complexity of subgroups), and equave and min/max&lt;br /&gt;
--   cents default to 2/1, 0c, and 1200c respectively.&lt;br /&gt;
--   (subgroup, int_limit, equave)&lt;br /&gt;
--   (subgroup, int_limit, min_cents, max_cents)&lt;br /&gt;
-- - Filter ratios function is split into two:&lt;br /&gt;
--   - Filter ratios by complement removes ratios from a table if its complement&lt;br /&gt;
--     is missing. Complements are octave-complements by default.&lt;br /&gt;
--   - Filter ratios by tenney height removes ratios from a table if its tenney&lt;br /&gt;
--     height exceeds a passed-in value.&lt;br /&gt;
&lt;br /&gt;
-- TODO: write filter function for cent range&lt;br /&gt;
&lt;br /&gt;
-- Module searches for ratios that are, at the minimum, up to an equave and are&lt;br /&gt;
-- up to some integer limit. Search hierarchy is as follows:&lt;br /&gt;
-- - Search by subgroup (subgroup elements may be nonprime or rational)&lt;br /&gt;
-- - Then search by prime limit&lt;br /&gt;
-- - Then search by odd limit&lt;br /&gt;
-- - Then search by int limit&lt;br /&gt;
&lt;br /&gt;
-- Optional args omit ratios that don&amp;#039;t meet certain conditions, and are used&lt;br /&gt;
-- to further limit the number of ratios found. Current options include:&lt;br /&gt;
-- - Tenney Height: omits ratios that exceed some max Tenney height. Has no&lt;br /&gt;
--   effect if no Tenney height is passed in.&lt;br /&gt;
-- - Complements Only: omits ratios and their equave complements if either would&lt;br /&gt;
--   be omitted by Tenney height, or if no Tenney height is entered, omits&lt;br /&gt;
--   ratios whose complements are missing.&lt;br /&gt;
&lt;br /&gt;
local DEFAULT_EQUAVE = rat.new(2)&lt;br /&gt;
local DEFAULT_INT_LIMIT = 30&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
------------------------------- FILTER FUNCTIONS -------------------------------&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- Filter function removes certain ratios that don&amp;#039;t meet some requirement.&lt;br /&gt;
-- Filters currently include:&lt;br /&gt;
-- - Removing ratios that exceed a max Tenney height.&lt;br /&gt;
-- - Removing ratios whose complement would exceed a max Tenney height or int limit&lt;br /&gt;
function p.filter_ratios(ratios, equave, int_limit, tenney_height, complements_only)&lt;br /&gt;
	&lt;br /&gt;
	local filtered_ratios = {}&lt;br /&gt;
	for i = 1, #ratios do&lt;br /&gt;
		local complement = rat.mul(rat.inv(ratios[i]), equave)&lt;br /&gt;
		local ratio_th   = rat.tenney_height(ratios[i])&lt;br /&gt;
		local compl_th   = rat.tenney_height(complement)&lt;br /&gt;
		&lt;br /&gt;
		-- Are the ratios within the Tenney height?&lt;br /&gt;
		-- Has no effect (defaults to TRUE) if Tenney height is infinity.&lt;br /&gt;
		local ratio_within_th = ratio_th &amp;lt;= tenney_height&lt;br /&gt;
		local compl_within_th = compl_th &amp;lt;= tenney_height&lt;br /&gt;
		&lt;br /&gt;
		-- Is the ratio&amp;#039;s complement within the int limit?&lt;br /&gt;
		local compl_within_int_limit = rat.is_within_int_limit(complement, int_limit)&lt;br /&gt;
		&lt;br /&gt;
		if complements_only then&lt;br /&gt;
			if ratio_within_th and compl_within_th and compl_within_int_limit then&lt;br /&gt;
				table.insert(filtered_ratios, ratios[i])&lt;br /&gt;
			end&lt;br /&gt;
		else&lt;br /&gt;
			if ratio_within_th then&lt;br /&gt;
				table.insert(filtered_ratios, ratios[i])&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return filtered_ratios&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Filters ratios from a table of ratios, returning an array of ratios within&lt;br /&gt;
-- the cent range and preserving the original table. Meant for searching for&lt;br /&gt;
-- multiple ranges. TODO: write&lt;br /&gt;
function p.filter_ratios_within_cent_range(ratios, min_cents, max_cents)&lt;br /&gt;
	&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-------------------------- INT-LIMIT SEARCH FUNCTION ---------------------------&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- Int limit search finds ratios from 1/1 to an equave, where each ratio&amp;#039;s&lt;br /&gt;
-- numerator or denominator don&amp;#039;t exceed the int limit.&lt;br /&gt;
function p.search_by_int_limit(equave, int_limit)&lt;br /&gt;
	return p.search_by_int_limit_within_cents(0, rat.cents(equave), int_limit)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Cent range search finds ratios within a cent range. Meant for searching for&lt;br /&gt;
-- ratios within a single interval range. If searching for ratios within many&lt;br /&gt;
-- interval ranges, then try a broad search first.&lt;br /&gt;
function p.search_by_int_limit_within_cents(min_cents, max_cents, int_limit)&lt;br /&gt;
	&lt;br /&gt;
	local init_ratios = {{1,1}, {1,0}}&lt;br /&gt;
	local ratios = med.find_only_mediants(init_ratios, 2)&lt;br /&gt;
	for i = 3, int_limit do&lt;br /&gt;
		ratios = med.find_mediants_by_int_limit(ratios, i)&lt;br /&gt;
		&lt;br /&gt;
		-- Purge ratios from the beginning.&lt;br /&gt;
		-- If the first and second ratio are smaller than min_cents, and smaller&lt;br /&gt;
		-- than max_cents, then remove the first ratio. Keeping the first ratio&lt;br /&gt;
		-- would add mediants outside the cent range.&lt;br /&gt;
		local cents_1 = utils.log2(ratios[1][1] / ratios[1][2]) * 1200&lt;br /&gt;
		local cents_2 = utils.log2(ratios[2][1] / ratios[2][2]) * 1200&lt;br /&gt;
		if cents_1 &amp;lt; min_cents and cents_2 &amp;lt;= min_cents and cents_1 &amp;lt; max_cents and cents_2 &amp;lt; max_cents then&lt;br /&gt;
			table.remove(ratios, 1)&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		-- Purge ratios from the end.&lt;br /&gt;
		-- If the 2nd-last ratio and last ratio are greater than max_cents, and&lt;br /&gt;
		-- larger than min_cents, then remove the last ratio. Keeping the last&lt;br /&gt;
		-- ratio would add mediants outside the cent range.&lt;br /&gt;
		local cents_3 = utils.log2(ratios[#ratios-1][1] / ratios[#ratios-1][2]) * 1200&lt;br /&gt;
		local cents_4 = utils.log2(ratios[#ratios  ][1] / ratios[#ratios  ][2]) * 1200&lt;br /&gt;
		if cents_3 &amp;gt; max_cents and cents_4 &amp;gt;= max_cents and cents_3 &amp;gt; min_cents and cents_4 &amp;gt; min_cents then&lt;br /&gt;
			table.remove(ratios, #ratios)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Convert to ratios that Module:Rational can work with&lt;br /&gt;
	for i = 1, #ratios do&lt;br /&gt;
		ratios[i] = rat.new(ratios[i][1], ratios[i][2])&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Remove any remaining ratios that fall outside the cent range.&lt;br /&gt;
	while rat.cents(ratios[1]) &amp;lt; min_cents do&lt;br /&gt;
		table.remove(ratios, 1)&lt;br /&gt;
	end&lt;br /&gt;
	while rat.cents(ratios[#ratios]) &amp;gt; max_cents do&lt;br /&gt;
		table.remove(ratios, #ratios)&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return ratios&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-------------------------- ODD-LIMIT SEARCH FUNCTION ---------------------------&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- Convert odd limit into equivalent subgroup.&lt;br /&gt;
-- EG, 11-odd-limit becomes 2.3.5.7.9.11&lt;br /&gt;
-- 2 is part of the subgroup by definition.&lt;br /&gt;
function p.odd_limit_to_subgroup(odd_limit)&lt;br /&gt;
	local subgroup = { rat.new(2) }&lt;br /&gt;
	for i = 3, odd_limit, 2 do&lt;br /&gt;
		table.insert(subgroup, rat.new(i))&lt;br /&gt;
	end&lt;br /&gt;
	return subgroup&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.search_by_odd_limit(equave, int_limit, odd_limit)&lt;br /&gt;
	local subgroup = p.odd_limit_to_subgroup(odd_limit)&lt;br /&gt;
	return p.search_by_subgroup_within_cents(0, rat.cents(equave), int_limit, subgroup)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.search_by_odd_limit_within_cents(min_cents, max_cents, odd_limit)&lt;br /&gt;
	local subgroup = p.odd_limit_to_subgroup(odd_limit)&lt;br /&gt;
	return p.search_by_subgroup_within_cents(min_cents, max_cents, int_limit, subgroup)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
------------------------- PRIME-LIMIT SEARCH FUNCTION --------------------------&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- Convert prime limit into equivalent subgroup.&lt;br /&gt;
-- EG, 11-prime-limit becomes 2.3.5.7.11&lt;br /&gt;
function p.prime_limit_to_subgroup(prime_limit)&lt;br /&gt;
	local subgroup = {}&lt;br /&gt;
	for i = 3, prime_limit do&lt;br /&gt;
		local is_prime = true&lt;br /&gt;
		for j = 2, math.floor(math.sqrt(i)) do&lt;br /&gt;
			if i % j == 0 then&lt;br /&gt;
				is_prime = false&lt;br /&gt;
				break&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		if is_prime then&lt;br /&gt;
			table.insert(subgroup, rat.new(i))&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return subgroup&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Prime limit search finds ratios with prime factors that don&amp;#039;t exceed some&lt;br /&gt;
-- prime limit.&lt;br /&gt;
-- Upper bounds for searching is the equave and int limit.&lt;br /&gt;
function p.search_by_prime_limit(equave, int_limit, prime_limit)&lt;br /&gt;
	local subgroup = p.prime_limit_to_subgroup(prime_limit)&lt;br /&gt;
	return p.search_by_subgroup_within_cents(0, rat.cents(equave), int_limit, subgroup)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Prime limit search finds ratios with prime factors that don&amp;#039;t exceed some&lt;br /&gt;
-- prime limit. Searches within a cent range.&lt;br /&gt;
function p.search_by_prime_limit_within_cents(min_cents, max_cents, int_limit, prime_limit)&lt;br /&gt;
	local subgroup = p.prime_limit_to_subgroup(prime_limit)&lt;br /&gt;
	local ratios = p.search_by_subgroup_within_cents(min_cents, max_cents, int_limit, subgroup)&lt;br /&gt;
	while rat.cents(ratios[1]) &amp;lt; min_cents do&lt;br /&gt;
		table.remove(ratios, 1)&lt;br /&gt;
	end&lt;br /&gt;
	return ratios&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
---------------------------- SUBGROUP SEARCH FUNCTION --------------------------&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- Subgroup search find ratios that are products of at least two non-unique&lt;br /&gt;
-- elements from the subgroup.&lt;br /&gt;
function p.search_by_subgroup(equave, int_limit, subgroup)&lt;br /&gt;
	local ratios = p.search_by_subgroup_within_cents(0, rat.cents(equave), int_limit, subgroup)&lt;br /&gt;
	return ratios&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.search_by_subgroup_within_cents(min_cents, max_cents, int_limit, subgroup)&lt;br /&gt;
	--local equave    = equave or rat.new(2,1)	-- Defualt equave is 2/1.&lt;br /&gt;
	--local int_limit = int_limit or 50			-- Default is 50&lt;br /&gt;
	--local subgroup  = subgroup or {rat.new(2), rat.new(3), rat.new(7)}		-- Default is 2.3.7 subgroup&lt;br /&gt;
	&lt;br /&gt;
	-- Find all possible ways to multiply subgroup elements with one another&lt;br /&gt;
	-- using breadth-first-search. Products found this way should not exceed the&lt;br /&gt;
	-- int limit, and if a subgroup element is rational, neither its numerator&lt;br /&gt;
	-- nor denominator should exceed the int limit.&lt;br /&gt;
	local products = { rat.new(1) }&lt;br /&gt;
	local i = 1&lt;br /&gt;
	while i &amp;lt;= #products do&lt;br /&gt;
		-- Multiply each subgroup element by the current ratio. The table of&lt;br /&gt;
		-- product ratios created this way is merged with the running table of&lt;br /&gt;
		-- ratios. This is the Cartesian product of the single ratio as a set,&lt;br /&gt;
		-- with the subgroup elements as a set, or {p/q} X subgroup.&lt;br /&gt;
		local new_products = {}&lt;br /&gt;
		for j = 1, #subgroup do&lt;br /&gt;
			local new_ratio = rat.mul(products[i], subgroup[j])&lt;br /&gt;
			if rat.is_within_int_limit(new_ratio, int_limit) and not p.find_ratio_in_table(new_products, new_ratio) then&lt;br /&gt;
				table.insert(new_products, new_ratio)&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		-- Merge new products with the table of products, omitting duplicates.&lt;br /&gt;
		p.merge_tables(products, new_products)&lt;br /&gt;
		i = i + 1&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Sort for next step&lt;br /&gt;
	table.sort(products, rat.lt)&lt;br /&gt;
	&lt;br /&gt;
	-- Use the products found to find all ratios between 1 and the equave.&lt;br /&gt;
	-- For each ratio in the table of products, create a set of new ratios by&lt;br /&gt;
	-- having that ratio be the numerator and all successive ratios be possible&lt;br /&gt;
	-- denominators. Store these new ratios in a table, and repeat with all&lt;br /&gt;
	-- successive products, omitting duplicats. From earlier testing, this is&lt;br /&gt;
	-- faster than performing BFS on each ratio, and yields the same results.&lt;br /&gt;
	local ratios = {}&lt;br /&gt;
	for i = 1, #products do&lt;br /&gt;
		local new_ratios = {}&lt;br /&gt;
		for j = i, #products do&lt;br /&gt;
			local new_ratio = rat.div(products[j], products[i])	&lt;br /&gt;
			if rat.cents(new_ratio) &amp;gt; max_cents then break end&lt;br /&gt;
			&lt;br /&gt;
			if not p.find_ratio_in_table(new_ratios, new_ratio) and rat.is_within_int_limit(new_ratio, int_limit) then&lt;br /&gt;
				table.insert(new_ratios, new_ratio)&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		-- Merge new ratios with the table of ratios, omitting duplicates.&lt;br /&gt;
		p.merge_tables(ratios, new_ratios)&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Sort&lt;br /&gt;
	table.sort(ratios, rat.lt)&lt;br /&gt;
	&lt;br /&gt;
	-- Remove ratios less than minimum&lt;br /&gt;
	while rat.cents(ratios[1]) &amp;lt; min_cents do&lt;br /&gt;
		table.remove(ratios, 1)&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return ratios&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
------------------------------- HELPER FUNCTIONS -------------------------------&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- Heleper function; merges elements from source table with destination table&lt;br /&gt;
-- while disallowing duplicates.&lt;br /&gt;
function p.merge_tables(dest_table, source_table)&lt;br /&gt;
	for i = 1, #source_table do&lt;br /&gt;
		if not p.find_ratio_in_table(dest_table, source_table[i]) then&lt;br /&gt;
			table.insert(dest_table, source_table[i])&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Helper function for merge function.&lt;br /&gt;
function p.find_ratio_in_table(table_, ratio)&lt;br /&gt;
	local found = false&lt;br /&gt;
	for i = 1, #table_ do&lt;br /&gt;
		if rat.as_float(table_[i]) == rat.as_float(ratio) then&lt;br /&gt;
			found = true&lt;br /&gt;
			break&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return found&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
---------------------------- RATIO STRING FUNCTIONS ----------------------------&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- Convert a table of ratios into a string, with options for links and delimiter&lt;br /&gt;
function p.ratios_as_string(ratios, add_links, delimiter)&lt;br /&gt;
	local add_links = add_links == true&lt;br /&gt;
	local delimiter = delimiter or &amp;quot;, &amp;quot;&lt;br /&gt;
	&lt;br /&gt;
	local text = &amp;quot;&amp;quot;&lt;br /&gt;
	if #ratios ~= 0 then&lt;br /&gt;
		text = add_links and string.format(&amp;quot;[[%s]]&amp;quot;, rat.as_ratio(ratios[1])) or rat.as_ratio(ratios[1])&lt;br /&gt;
		for i = 2, #ratios do&lt;br /&gt;
			text = text .. (add_links and string.format(&amp;quot;%s[[%s]]&amp;quot;, delimiter, rat.as_ratio(ratios[i])) or string.format(&amp;quot;%s%s&amp;quot;, delimiter, rat.as_ratio(ratios[i])))&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return text&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Convert a jagged array of ratios into an array of strings&lt;br /&gt;
function p.ratios_as_strings(ratios, add_links, delimiter)&lt;br /&gt;
	local add_links = add_links == true&lt;br /&gt;
	local delimiter = delimiter or &amp;quot;, &amp;quot;&lt;br /&gt;
	&lt;br /&gt;
	local texts = {}&lt;br /&gt;
	for i = 1, #ratios do&lt;br /&gt;
		local text = p.ratios_as_string(ratios[i], add_links, delimiter)&lt;br /&gt;
		table.insert(texts, text)&lt;br /&gt;
	end&lt;br /&gt;
	return texts&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
---------------------------- ARG-PARSING FUNCTION ------------------------------&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- Parse search args if entered as one string. Use is to be determined.&lt;br /&gt;
function p.parse_args(search_args)&lt;br /&gt;
	local parsed = tip.parse_kv_pairs(search_args)&lt;br /&gt;
	&lt;br /&gt;
	if parsed[&amp;quot;Equave&amp;quot;] ~= nil then&lt;br /&gt;
		parsed[&amp;quot;Equave&amp;quot;] = rat.parse(parsed[&amp;quot;Equave&amp;quot;])&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if parsed[&amp;quot;Int Limit&amp;quot;] ~= nil then&lt;br /&gt;
		parsed[&amp;quot;Int Limit&amp;quot;] = tonumber(parsed[&amp;quot;Int Limit&amp;quot;])&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if parsed[&amp;quot;Tenney Height&amp;quot;] ~= nil then&lt;br /&gt;
		parsed[&amp;quot;Tenney Height&amp;quot;] = tonumber(parsed[&amp;quot;Tenney Height&amp;quot;])&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if parsed[&amp;quot;Prime Limit&amp;quot;] ~= nil then&lt;br /&gt;
		parsed[&amp;quot;Prime Limit&amp;quot;] = tonumber(parsed[&amp;quot;Prime Limit&amp;quot;])&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if parsed[&amp;quot;Subgroup&amp;quot;] ~= nil then&lt;br /&gt;
		local subgroup_elements = tip.parse_numeric_pairs(parsed[&amp;quot;Subgroup&amp;quot;], &amp;quot;.&amp;quot;, &amp;quot;/&amp;quot;, true)&lt;br /&gt;
		for i = 1, #subgroup_elements do&lt;br /&gt;
			subgroup_elements[i] = rat.new(subgroup_elements[i][1], subgroup_elements[i][2])&lt;br /&gt;
		end&lt;br /&gt;
		parsed[&amp;quot;Subgroup&amp;quot;] = subgroup_elements&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if parsed[&amp;quot;Complements Only&amp;quot;] ~= nil then&lt;br /&gt;
		parsed[&amp;quot;Complements Only&amp;quot;] = yesno(parsed[&amp;quot;Complements Only&amp;quot;])&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return parsed&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
----------------------------- INVOKABLE FUNCTIONS ------------------------------&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- Function callable by other modules&lt;br /&gt;
-- Ratios are returned as a table, for use with other modules.&lt;br /&gt;
function p._ji_ratios(args)&lt;br /&gt;
	-- Args for ease of access&lt;br /&gt;
	equave      = args[&amp;quot;Equave&amp;quot;     ]	or DEFAULT_EQUAVE&lt;br /&gt;
	int_limit   = args[&amp;quot;Int Limit&amp;quot;  ]	or DEFAULT_INT_LIMIT&lt;br /&gt;
	odd_limit   = args[&amp;quot;Odd Limit&amp;quot;  ]&lt;br /&gt;
	prime_limit = args[&amp;quot;Prime Limit&amp;quot;]&lt;br /&gt;
	subgroup    = args[&amp;quot;Subgroup&amp;quot;   ]&lt;br /&gt;
	&lt;br /&gt;
	-- Filtering args&lt;br /&gt;
	tenney_height    = args[&amp;quot;Tenney Height&amp;quot;   ] or 1/0		-- Default Tenney height is infinity&lt;br /&gt;
	complements_only = args[&amp;quot;Complements Only&amp;quot;] or false	-- Default is to include all ratios&lt;br /&gt;
	&lt;br /&gt;
	local ratios = {}&lt;br /&gt;
	if subgroup ~= nil then&lt;br /&gt;
		ratios = p.search_by_subgroup(equave, int_limit, subgroup)&lt;br /&gt;
	elseif prime_limit ~= nil then&lt;br /&gt;
		ratios = p.search_by_prime_limit(equave, int_limit, prime_limit)&lt;br /&gt;
	elseif int_limit ~= nil then&lt;br /&gt;
		ratios = p.search_by_int_limit(equave, int_limit)&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Filter ratios&lt;br /&gt;
	ratios = p.filter_ratios(ratios, equave, int_limit, tenney_height, complements_only)&lt;br /&gt;
	&lt;br /&gt;
	return ratios&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Invokable function; for templates&lt;br /&gt;
-- Ratios are returned as a comma-delimited list. For finer control, it&amp;#039;s&lt;br /&gt;
-- necessary to call the &amp;quot;main&amp;quot; function, then further process the results.&lt;br /&gt;
function p.ji_ratios(frame)&lt;br /&gt;
	args = getArgs(frame)&lt;br /&gt;
	&lt;br /&gt;
	-- Preprocess equave&lt;br /&gt;
	-- Ratios are searched from 1/1 to some equave (default 2/1), so an equave&lt;br /&gt;
	-- must be passed in.&lt;br /&gt;
	args[&amp;quot;Equave&amp;quot;] = args[&amp;quot;Equave&amp;quot;] ~= nil and rat.parse(args[&amp;quot;Equave&amp;quot;])&lt;br /&gt;
	&lt;br /&gt;
	-- Preprocess int limit&lt;br /&gt;
	-- Ratios are searched up to some int limit (default 50), so an int limit&lt;br /&gt;
	-- must be passed in.&lt;br /&gt;
	args[&amp;quot;Int Limit&amp;quot;] = args[&amp;quot;Int Limit&amp;quot;] ~= nil and tonumber(args[&amp;quot;Int Limit&amp;quot;])&lt;br /&gt;
&lt;br /&gt;
	-- Preprocess Tenney height&lt;br /&gt;
	if args[&amp;quot;Tenney Height&amp;quot;] ~= nil then&lt;br /&gt;
		args[&amp;quot;Tenney Height&amp;quot;] = tonumber(args[&amp;quot;Tenney Height&amp;quot;])&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Preprocess prime limit&lt;br /&gt;
	if args[&amp;quot;Prime Limit&amp;quot;] ~= nil then&lt;br /&gt;
		args[&amp;quot;Prime Limit&amp;quot;] = tonumber(args[&amp;quot;Prime Limit&amp;quot;])&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Preprocess subgroup&lt;br /&gt;
	if args[&amp;quot;Subgroup&amp;quot;] ~= nil then&lt;br /&gt;
		local subgroup_elements = tip.parse_numeric_pairs(args[&amp;quot;Subgroup&amp;quot;], &amp;quot;.&amp;quot;, &amp;quot;/&amp;quot;, true)&lt;br /&gt;
		for i = 1, #subgroup_elements do&lt;br /&gt;
			subgroup_elements[i] = rat.new(subgroup_elements[i][1], subgroup_elements[i][2])&lt;br /&gt;
		end&lt;br /&gt;
		args[&amp;quot;Subgroup&amp;quot;] = subgroup_elements&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if args[&amp;quot;Complements Only&amp;quot;] ~= nil then&lt;br /&gt;
		args[&amp;quot;Complements Only&amp;quot;] = yesno(args[&amp;quot;Complements Only&amp;quot;], false)&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Find and return ratios&lt;br /&gt;
	local result = p.ratios_as_string(p._ji_ratios(args))&lt;br /&gt;
	local debugg = yesno(frame.args[&amp;quot;debug&amp;quot;])&lt;br /&gt;
	&lt;br /&gt;
	if debugg == true then&lt;br /&gt;
		result = &amp;quot;&amp;lt;syntaxhighlight lang=\&amp;quot;wikitext\&amp;quot;&amp;gt;&amp;quot; .. result .. &amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return frame:preprocess(result)&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.tester()&lt;br /&gt;
	--return p.ratios_as_string(p._ji_ratios(p.parse_args(&amp;quot;Int Limit: 16; Equave: 3/1; Complements Only: 0&amp;quot;)))&lt;br /&gt;
	--return p.ratios_as_string(p.search_by_prime_limit_within_cents(372, 440, 17, 30))&lt;br /&gt;
	return p.ratios_as_string(p.search_by_odd_limit(rat.new(2), 15, 15*2))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
---------------------------- FUNCTIONS TO BE MOVED -----------------------------&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- Parse a list of ratios from a string. String is formatted as follows:&lt;br /&gt;
-- &amp;quot;a/b; c/d; e/f; g/h&amp;quot;&lt;br /&gt;
function p.parse_ratios(unparsed)&lt;br /&gt;
	local parsed = tip.parse_numeric_pairs(unparsed)&lt;br /&gt;
	for i = 1, #parsed do&lt;br /&gt;
		parsed[i] = rat.new(parsed[i][1], parsed[i][2])&lt;br /&gt;
	end&lt;br /&gt;
	return parsed&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Sorts ratios by closeness to cent values. Move to new module?&lt;br /&gt;
function p.sort_by_closeness_to_cent_values(ratios, cent_values, tolerance)&lt;br /&gt;
	local tolerance = tolerance or 30&lt;br /&gt;
	&lt;br /&gt;
	local sorted_ratios = {}&lt;br /&gt;
	local curr_index = 1		-- Index of current_ratio&lt;br /&gt;
	for i = 1, #cent_values do&lt;br /&gt;
		local lower_bound = cent_values[i] - tolerance&lt;br /&gt;
		local upper_bound = cent_values[i] + tolerance&lt;br /&gt;
		local cents_within_range = true&lt;br /&gt;
		local curr_ratios = {}&lt;br /&gt;
		&lt;br /&gt;
		for j = curr_index, #ratios do&lt;br /&gt;
			local curr_ratio = ratios[j]&lt;br /&gt;
			local curr_cents = rat.cents(curr_ratio)&lt;br /&gt;
			&lt;br /&gt;
			if lower_bound &amp;lt; curr_cents and curr_cents &amp;lt; upper_bound then&lt;br /&gt;
				table.insert(curr_ratios, curr_ratio)&lt;br /&gt;
			--elseif curr_cents &amp;gt; upper_bound then&lt;br /&gt;
			--	curr_index = j&lt;br /&gt;
			--	break&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		table.insert(sorted_ratios, curr_ratios)&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return sorted_ratios&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Furcht968</name></author>
	</entry>
</feed>