Module:MOS: Difference between revisions

Ganaram inukshuk (talk | contribs)
Added (the skeletons for) two more functions: interval() and interval_from_mossteps(); these serve the same role as bright_gen(), dark_gen(), and period(), but apply to all other mos intervals
Ganaram inukshuk (talk | contribs)
Fleshed out module; basically redefining module:mos as a module for doing mos arithmetic, with tamnams-related things being moved to module:tamnams
Line 1: Line 1:
-- Module for working with mosses in code, plus some basic arithmetic functions
local rat = require('Module:Rational')
local rat = require('Module:Rational')
local seq = require('Module:Sequence')
local utils = require('Module:Utils')
local utils = require('Module:Utils')
local et = require('Module:ET')
local et = require('Module:ET')
local p = {}
local p = {}


-- TODO: transfer tamnams-related info to tamnams module
--------------------------------------------------------------------------------
------------------------------- HELPER FUNCTIONS -------------------------------
--------------------------------------------------------------------------------


-- Table of official tamnams names (2/1-equave only)
function round(num, numDecimalPlaces)
p.tamnams_name = { -- Only mosses with 2/1-equave names in TAMNAMS
  local mult = 10^(numDecimalPlaces or 0)
['1L 1s'] = 'monowood',
  return math.floor(num * mult + 0.5) / mult
['2L 2s'] = 'biwood',
['1L 5s'] = 'antimachinoid',
['2L 4s'] = 'malic',
['3L 3s'] = 'triwood',
['4L 2s'] = 'citric',
['5L 1s'] = 'machinoid',
['1L 6s'] = 'onyx',
['2L 5s'] = 'antidiatonic',
['3L 4s'] = 'mosh',
['4L 3s'] = 'smitonic',
['5L 2s'] = 'diatonic',
['6L 1s'] = 'archaeotonic',
['1L 7s'] = 'antipine',
['2L 6s'] = 'subaric',
['3L 5s'] = 'checkertonic',
['4L 4s'] = 'tetrawood',
['5L 3s'] = 'oneirotonic',
['6L 2s'] = 'ekic',
['7L 1s'] = 'pine',
['1L 8s'] = 'antisubneutralic',
['2L 7s'] = 'balzano',
['3L 6s'] = 'tcherepnin',
['4L 5s'] = 'gramitonic',
['5L 4s'] = 'semiquartal',
['6L 3s'] = 'hyrulic',
['7L 2s'] = 'armotonic',
['8L 1s'] = 'subneutralic',
['1L 9s'] = 'antisinatonic',
['2L 8s'] = 'jaric',
['3L 7s'] = 'sephiroid',
['4L 6s'] = 'lime',
['5L 5s'] = 'pentawood',
['6L 4s'] = 'lemon',
['7L 3s'] = 'dicoid',
['8L 2s'] = 'taric',
['9L 1s'] = 'sinatonic'
}
 
-- Prefixes
p.tamnams_prefix = { -- Only mosses with 2/1-equave names in TAMNAMS
['1L 1s'] = 'monwd-',
['2L 2s'] = 'biwd-',
['1L 5s'] = 'amech-',
['2L 4s'] = 'mal-',
['3L 3s'] = 'triwd-',
['4L 2s'] = 'citro-',
['5L 1s'] = 'mech-',
['1L 6s'] = 'on-',
['2L 5s'] = 'pel-',
['3L 4s'] = 'mosh-',
['4L 3s'] = 'smi-',
['5L 2s'] = 'dia-',
['6L 1s'] = 'arch-',
['1L 7s'] = 'apine-',
['2L 6s'] = 'subar-',
['3L 5s'] = 'check-',
['4L 4s'] = 'tetrawd-',
['5L 3s'] = 'oneiro-',
['6L 2s'] = 'ek-',
['7L 1s'] = 'pine-',
['1L 8s'] = 'ablu-',
['2L 7s'] = 'bal-',
['3L 6s'] = 'cher-',
['4L 5s'] = 'gram-',
['5L 4s'] = 'cthon-',
['6L 3s'] = 'hyru-',
['7L 2s'] = 'arm-',
['8L 1s'] = 'blu-',
['1L 9s'] = 'asina-',
['2L 8s'] = 'jara-',
['3L 7s'] = 'seph-',
['4L 6s'] = 'lime-',
['5L 5s'] = 'pentawd-',
['6L 4s'] = 'lem-',
['7L 3s'] = 'dico-',
['8L 2s'] = 'tara-',
['9L 1s'] = 'sina-'
}
 
-- Abbreviations (most abbrevs are the same as the prefixes but there are exceptions)
p.tamnams_abbrev = { -- Only mosses with 2/1-equave names in TAMNAMS
['1L 1s'] = 'wood',
['2L 2s'] = 'bw',
['1L 5s'] = 'amech',
['2L 4s'] = 'mal',
['3L 3s'] = 'trw',
['4L 2s'] = 'cit',
['5L 1s'] = 'mech',
['1L 6s'] = 'on',
['2L 5s'] = 'pel',
['3L 4s'] = 'mosh',
['4L 3s'] = 'smi',
['5L 2s'] = 'dia',
['6L 1s'] = 'arch',
['1L 7s'] = 'apine',
['2L 6s'] = 'subar',
['3L 5s'] = 'chk',
['4L 4s'] = 'ttw',
['5L 3s'] = 'onei',
['6L 2s'] = 'ek',
['7L 1s'] = 'pine',
['1L 8s'] = 'ablu',
['2L 7s'] = 'bal',
['3L 6s'] = 'ch',
['4L 5s'] = 'gram',
['5L 4s'] = 'cth',
['6L 3s'] = 'hyru',
['7L 2s'] = 'arm',
['8L 1s'] = 'blu',
['1L 9s'] = 'asi',
['2L 8s'] = 'jar',
['3L 7s'] = 'seph',
['4L 6s'] = 'lime',
['5L 5s'] = 'pw',
['6L 4s'] = 'lem',
['7L 3s'] = 'dico',
['8L 2s'] = 'tar',
['9L 1s'] = 'si'
}
 
 
function table_invert(t)
  local s={}
  for k,v in pairs(t) do
    s[v]=k
  end
  return s
end
end


-- Create a table that parses a mos string from the TAMNAMS name.
--------------------------------------------------------------------------------
p.parse_name = table_invert(p.tamnams_name)
-------------------------------- BASE FUNCTIONS --------------------------------
--------------------------------------------------------------------------------


-- create a MOS structure (nL)L (ns)s <equave>
-- create a MOS structure (nL)L (ns)s <equave>
Line 148: Line 24:
local equave = equave or 2
local equave = equave or 2
return { nL = nL, ns = ns, equave = equave }
return { nL = nL, ns = ns, equave = equave }
end
function round(num, numDecimalPlaces)
  local mult = 10^(numDecimalPlaces or 0)
  return math.floor(num * mult + 0.5) / mult
end
end


Line 167: Line 38:
return p.new(nL, ns, equave)
return p.new(nL, ns, equave)
end
end
--------------------------------------------------------------------------------
------------------------------ SCALESIG FUNCTIONS ------------------------------
--------------------------------------------------------------------------------


-- Construct a string representation (scalesig) for a MOS structure
-- Construct a string representation (scalesig) for a MOS structure
Line 187: Line 62:
return '' .. mos.nL .. 'L ' .. mos.ns .. 's' .. suffix
return '' .. mos.nL .. 'L ' .. mos.ns .. 's' .. suffix
end
end
--------------------------------------------------------------------------------
--------------- INTERVAL FUNCTIONS FOR PERFECTABLE INTERVALS -------------------
------------------ (IE, GENERATORS AND PERIOD INTERVALS) -----------------------
--------------------------------------------------------------------------------


-- Find the brightest mode of a mos (the Christoffel word)
-- Find the brightest mode of a mos (the Christoffel word)
Line 242: Line 122:
['L'] = mos.nL / gcd,
['L'] = mos.nL / gcd,
['s'] = mos.ns / gcd
['s'] = mos.ns / gcd
}
return result
end
-- Compute the equave as a vector of L's and s's.
function p.period(mos)
local result = {
['L'] = mos.nL,
['s'] = mos.ns
}
}
return result
return result
Line 286: Line 175:
return dark_gen
return dark_gen
end
end
--------------------------------------------------------------------------------
---------------- INTERVAL FUNCTIONS FOR ARBITRARY INTERVALS --------------------
--------------------------------------------------------------------------------


-- Compute an arbitrary mos interval as a vector of L's and s's.
-- Compute an arbitrary mos interval as a vector of L's and s's.
-- The mossteps param is the number of mossteps in the interval. EG, a 2-mosstep
-- The mossteps param is the number of mossteps in the interval. EG, in 5L 2s,
-- should return a vector whose L and s values add up to 2.
-- the large 2-mosstep is "LL", so the corresponding vector has L=2, s=0.
-- Mossteps larger than the equave (analogous to the minor 9th in standard
-- notation) are allowed.
-- The size param is a value that denotes whether the interval is the large size
-- The size param is a value that denotes whether the interval is the large size
-- (0) or the small size (-1). This can exceed the range of [-1, 0] to represent
-- (0) or the small size (-1). This can exceed the range of [-1, 0] to represent
Line 296: Line 191:
-- size (0 = perfect), so -1 is diminished and 1 is augmented.
-- size (0 = perfect), so -1 is diminished and 1 is augmented.
function p.interval(mos, mossteps, size)
function p.interval(mos, mossteps, size)
local step_sequence = p.brightest_mode(mos)
step_sequence = string.rep(step_sequence, math.ceil(mossteps/(mos.nL + mos.ns)))
step_sequence = string.sub(step_sequence, 1, mossteps)
local interval_vector = p.interval_from_step_sequence(mos, step_sequence)
interval_vector['L'] = interval_vector['L'] + size
interval_vector['s'] = interval_vector['s'] - size
return interval_vector
end
end


-- Compute an arbitrary mos interval (as a string of steps) as a vector of L's
-- Compute an arbitrary mos interval (as a string of steps) as a vector of L's
-- and s's.
-- and s's. This also serves as a helper function for p.interval().
-- This has the same output as the previous function, except arbitrary strings
-- This has the same output as the above function, except arbitrary strings can
-- can be entered, such as with step sequences for certain modmosses. The step
-- be entered, such as with step sequences for certain modmosses. The step types
-- types are as follows:
-- are as follows:
-- - L: large step.
-- - L: large step.
-- - s: small step.
-- - s: small step.
Line 308: Line 211:
-- - A: an augmented step; a large step plus a chroma.
-- - A: an augmented step; a large step plus a chroma.
-- - d: a diminished step, or diesis; a small step minus a chroma.
-- - d: a diminished step, or diesis; a small step minus a chroma.
function p.interval_from_mossteps(mos, step_sequence)
function p.interval_from_step_sequence(mos, step_sequence)
local mossteps = #step_sequence
local interval_vector = {
['L'] = 0,
['s'] = 0
}
for i = 1, mossteps do
local step = string.sub(step_sequence, i, i)
if step == "L" then
interval_vector['L'] = interval_vector['L'] + 1
elseif step == "s" or step == "S" then
interval_vector['s'] = interval_vector['s'] + 1
elseif step == "c" then
interval_vector['L'] = interval_vector['L'] + 1
interval_vector['s'] = interval_vector['s'] - 1
elseif step == "A" then
interval_vector['L'] = interval_vector['L'] + 2
interval_vector['s'] = interval_vector['s'] - 1
elseif step == "d" then
interval_vector['L'] = interval_vector['L'] - 1
interval_vector['s'] = interval_vector['s'] + 2
end
end
return interval_vector
end
 
--------------------------------------------------------------------------------
---------------------- INTERVAL ARITHMETIC FUNCTIONS ---------------------------
--------------------------------------------------------------------------------
 
-- Add two intervals together by adding their respective vectors
function p.add_intervals(v1, v2)
local interval_vector = {
['L'] = v1['L'] + v2['L'],
['s'] = v1['s'] + v2['s']
}
return interval_vector
end
-- Subtract two intervals together by subtracting their respective vectors
function p.subtract_intervals(v1, v2)
local interval_vector = {
['L'] = v1['L'] - v2['L'],
['s'] = v1['s'] - v2['s']
}
return interval_vector
end
 
-- Repeatedly add the same interval to itself
function p.multiply_interval(v1, amt)
local interval_vector = {
['L'] = v1['L'] * amt,
['s'] = v1['s'] * amt
}
return interval_vector
end
end
--------------------------------------------------------------------------------
--------- INTERVAL STEP COUNT FUNCTIONS FOR PERFECTABLE INTERVALS --------------
--------------------------------------------------------------------------------


-- Compute the size of the period in mossteps (L's plus s's)
-- Compute the size of the period in mossteps (L's plus s's)
function p.period_step_count(mos)
function p.period_step_count(mos)
local period = p.period(mos)
local interval = p.period(mos)
return period['L'] + period['s']
return interval['L'] + interval['s']
end
 
-- Compute the size of the equave in mossteps (L's plus s's)
function p.equave_step_count(mos)
local interval = p.equave(mos)
return interval['L'] + interval['s']
end
end


-- Compute the size of the bright gen in mossteps (L's plus s's)
-- Compute the size of the bright gen in mossteps (L's plus s's)
function p.bright_gen_step_count(mos)
function p.bright_gen_step_count(mos)
local bright_gen = p.bright_gen(mos)
local interval = p.bright_gen(mos)
return bright_gen['L'] + bright_gen['s']
return interval['L'] + interval['s']
end
end


-- Compute the size of the dark gen in mossteps (L's plus s's)
-- Compute the size of the dark gen in mossteps (L's plus s's)
function p.dark_gen_step_count(mos)
function p.dark_gen_step_count(mos)
local dark_gen = p.dark_gen(mos)
local interval = p.dark_gen(mos)
return dark_gen['L'] + dark_gen['s']
return interval['L'] + interval['s']
end
end
--------------------------------------------------------------------------------
------------ UNUSED FUNCTIONS OR FUNCTIONS TO MOVE TO OTHER MODULES ------------
--------------------------------------------------------------------------------


-- Given mos a MOS structure, hardness = L/s a rational number,
-- Given mos a MOS structure, hardness = L/s a rational number,
-- return the et and the bright MOS generator corresponding to the hardness.
-- return the et and the bright MOS generator corresponding to the hardness.
-- Apparently unused?
-- Currently unused
function p.et_tuning_by_hardness(mos, hardness)
function p.et_tuning_by_hardness(mos, hardness)
local nL, ns, equave = mos.nL, mos.ns, mos.equave
local nL, ns, equave = mos.nL, mos.ns, mos.equave
Line 368: Line 341:
return p.new(z, w, mos.equave)
return p.new(z, w, mos.equave)
end
end
-- Table of official tamnams names (2/1-equave only)
p.tamnams_name = { -- Only mosses with 2/1-equave names in TAMNAMS
['1L 1s'] = 'monowood',
['2L 2s'] = 'biwood',
['1L 5s'] = 'antimachinoid',
['2L 4s'] = 'malic',
['3L 3s'] = 'triwood',
['4L 2s'] = 'citric',
['5L 1s'] = 'machinoid',
['1L 6s'] = 'onyx',
['2L 5s'] = 'antidiatonic',
['3L 4s'] = 'mosh',
['4L 3s'] = 'smitonic',
['5L 2s'] = 'diatonic',
['6L 1s'] = 'archaeotonic',
['1L 7s'] = 'antipine',
['2L 6s'] = 'subaric',
['3L 5s'] = 'checkertonic',
['4L 4s'] = 'tetrawood',
['5L 3s'] = 'oneirotonic',
['6L 2s'] = 'ekic',
['7L 1s'] = 'pine',
['1L 8s'] = 'antisubneutralic',
['2L 7s'] = 'balzano',
['3L 6s'] = 'tcherepnin',
['4L 5s'] = 'gramitonic',
['5L 4s'] = 'semiquartal',
['6L 3s'] = 'hyrulic',
['7L 2s'] = 'armotonic',
['8L 1s'] = 'subneutralic',
['1L 9s'] = 'antisinatonic',
['2L 8s'] = 'jaric',
['3L 7s'] = 'sephiroid',
['4L 6s'] = 'lime',
['5L 5s'] = 'pentawood',
['6L 4s'] = 'lemon',
['7L 3s'] = 'dicoid',
['8L 2s'] = 'taric',
['9L 1s'] = 'sinatonic'
}
-- Prefixes
p.tamnams_prefix = { -- Only mosses with 2/1-equave names in TAMNAMS
['1L 1s'] = 'monwd-',
['2L 2s'] = 'biwd-',
['1L 5s'] = 'amech-',
['2L 4s'] = 'mal-',
['3L 3s'] = 'triwd-',
['4L 2s'] = 'citro-',
['5L 1s'] = 'mech-',
['1L 6s'] = 'on-',
['2L 5s'] = 'pel-',
['3L 4s'] = 'mosh-',
['4L 3s'] = 'smi-',
['5L 2s'] = 'dia-',
['6L 1s'] = 'arch-',
['1L 7s'] = 'apine-',
['2L 6s'] = 'subar-',
['3L 5s'] = 'check-',
['4L 4s'] = 'tetrawd-',
['5L 3s'] = 'oneiro-',
['6L 2s'] = 'ek-',
['7L 1s'] = 'pine-',
['1L 8s'] = 'ablu-',
['2L 7s'] = 'bal-',
['3L 6s'] = 'cher-',
['4L 5s'] = 'gram-',
['5L 4s'] = 'cthon-',
['6L 3s'] = 'hyru-',
['7L 2s'] = 'arm-',
['8L 1s'] = 'blu-',
['1L 9s'] = 'asina-',
['2L 8s'] = 'jara-',
['3L 7s'] = 'seph-',
['4L 6s'] = 'lime-',
['5L 5s'] = 'pentawd-',
['6L 4s'] = 'lem-',
['7L 3s'] = 'dico-',
['8L 2s'] = 'tara-',
['9L 1s'] = 'sina-'
}
-- Abbreviations (most abbrevs are the same as the prefixes but there are exceptions)
p.tamnams_abbrev = { -- Only mosses with 2/1-equave names in TAMNAMS
['1L 1s'] = 'wood',
['2L 2s'] = 'bw',
['1L 5s'] = 'amech',
['2L 4s'] = 'mal',
['3L 3s'] = 'trw',
['4L 2s'] = 'cit',
['5L 1s'] = 'mech',
['1L 6s'] = 'on',
['2L 5s'] = 'pel',
['3L 4s'] = 'mosh',
['4L 3s'] = 'smi',
['5L 2s'] = 'dia',
['6L 1s'] = 'arch',
['1L 7s'] = 'apine',
['2L 6s'] = 'subar',
['3L 5s'] = 'chk',
['4L 4s'] = 'ttw',
['5L 3s'] = 'onei',
['6L 2s'] = 'ek',
['7L 1s'] = 'pine',
['1L 8s'] = 'ablu',
['2L 7s'] = 'bal',
['3L 6s'] = 'ch',
['4L 5s'] = 'gram',
['5L 4s'] = 'cth',
['6L 3s'] = 'hyru',
['7L 2s'] = 'arm',
['8L 1s'] = 'blu',
['1L 9s'] = 'asi',
['2L 8s'] = 'jar',
['3L 7s'] = 'seph',
['4L 6s'] = 'lime',
['5L 5s'] = 'pw',
['6L 4s'] = 'lem',
['7L 3s'] = 'dico',
['8L 2s'] = 'tar',
['9L 1s'] = 'si'
}
function table_invert(t)
  local s={}
  for k,v in pairs(t) do
    s[v]=k
  end
  return s
end
-- Create a table that parses a mos string from the TAMNAMS name.
p.parse_name = table_invert(p.tamnams_name)
--------------------------------------------------------------------------------
----------------------------------- TESTER -------------------------------------
--------------------------------------------------------------------------------


-- Tester function
-- Tester function
function p.tester()
function p.tester()
return p.period_length(p.new(21, 8))
return p.add_intervals({ ['L'] = 3, ['s'] = 1}, { ['L'] = 3, ['s'] = 1})
end
end


return p
return p