Module:User:Theknightwho/mn-decl

local export = {} local m_table = require( "Module:table" ) local m_links = require( "Module:links" ) local m_string_utilities = require( "Module:string utilities" ) local lang = require( "Module:languages" ).getByCode( "mn" ) local com = require( "Module:mn-common" ) local iut = require( "Module:inflection utilities" ) local m_para = require( "Module:parameters" )

local char = mw.ustring.char local find = mw.ustring.find local format = mw.ustring.format local len = require( "Module:string" ).ulen local match = mw.ustring.match local gmatch = mw.ustring.gmatch local sub = mw.ustring.sub local gsub = mw.ustring.gsub local lower = mw.ustring.lower local split = mw.text.split local reverse = require( "Module:string" ).reverse local upper = mw.ustring.upper table.append = require( "Module:table" ).append

local output_noun_slots = { nom_sg = "nom|s", gen_sg = "gen|s", datloc_sg = "dat|-|loc|s", acc_sg = "acc|s", abl_sg = "abl|s", ins_sg = "ins|s", com_sg = "com|s", priv_sg = "priv|s", dirc_sg = "dirc|s", nom_pl = "nom|p", gen_pl = "gen|p", datloc_pl = "dat|-|loc|p", acc_pl = "acc|p", abl_pl = "abl|p", ins_pl = "ins|p", com_pl = "com|p", priv_pl = "priv|p", dirc_pl = "dirc|p", nom_sg_refl = "nom|s|refl", gen_sg_refl = "gen|s|refl", datloc_sg_refl = "dat|-|loc|s|refl", acc_sg_refl = "acc|s|refl", abl_sg_refl = "abl|s|refl", ins_sg_refl = "ins|s|refl", com_sg_refl = "com|s|refl", priv_sg_refl = "priv|s|refl", dirc_sg_refl = "dirc|s|refl", nom_pl_refl = "nom|p|refl", gen_pl_refl = "gen|p|refl", datloc_pl_refl = "dat|-|loc|p|refl", acc_pl_refl = "acc|p|refl", abl_pl_refl = "abl|p|refl", ins_pl_refl = "ins|p|refl", com_pl_refl = "com|p|refl", priv_pl_refl = "priv|p|refl", dirc_pl_refl = "dirc|p|refl", }

local output_noun_slots_with_linked = m_table.shallowcopy( output_noun_slots ) output_noun_slots_with_linked["nom_sg_linked"] = "nom|s" output_noun_slots_with_linked["nom_pl_linked"] = "nom|p"

local input_params_to_slots_both = { [1] = "nom_sg", [2] = "nom_pl", [3] = "gen_sg", [4] = "gen_pl", [5] = "datloc_sg", [6] = "datloc_pl", [7] = "acc_sg", [8] = "acc_pl", [7] = "abl_sg", [8] = "abl_pl", [11] = "ins_sg", [12] = "ins_pl", [13] = "com_sg", [14] = "com_pl", [15] = "priv_sg", [16] = "priv_pl", [17] = "dirc_sg", [18] = "dirc_pl", [19] = "nom_sg_refl", [20] = "nom_pl_refl", [21] = "gen_sg_refl", [22] = "gen_pl_refl", [23] = "datloc_sg_refl", [24] = "datloc_pl_refl", [25] = "acc_sg_refl", [26] = "acc_pl_refl", [27] = "abl_sg_refl", [28] = "abl_pl_refl", [29] = "ins_sg_refl", [30] = "ins_pl_refl", [31] = "com_sg_refl", [32] = "com_pl_refl", [33] = "priv_sg_refl", [34] = "priv_pl_refl", [35] = "dirc_sg_refl", [36] = "dirc_pl_refl", }

local input_params_to_slots_sg = { [1] = "nom_sg", [2] = "gen_sg", [3] = "datloc_sg", [4] = "acc_sg", [5] = "abl_sg", [6] = "ins_sg", [7] = "com_sg", [8] = "priv_sg", [9] = "dirc_sg", [10] = "nom_sg_refl", [11] = "gen_sg_refl", [12] = "datloc_sg_refl", [13] = "acc_sg_refl", [14] = "abl_sg_refl", [15] = "ins_sg_refl", [16] = "com_sg_refl", [17] = "priv_sg_refl", [18] = "dirc_sg_refl", }

local input_params_to_slots_pl = { [1] = "nom_pl", [2] = "gen_pl", [3] = "datloc_pl", [4] = "acc_pl", [5] = "abl_pl", [6] = "ins_pl", [7] = "com_pl", [8] = "priv_pl", [9] = "dirc_pl", [10] = "nom_pl_refl", [11] = "gen_pl_refl", [12] = "datloc_pl_refl", [13] = "acc_pl_refl", [14] = "abl_pl_refl", [15] = "ins_pl_refl", [16] = "com_pl_refl", [17] = "priv_pl_refl", [18] = "dirc_pl_refl", }

local cases = { nom = true, gen = true, datloc = true, acc = true, abl = true, ins = true, com = true, priv = true, dirc = true, }

local accented_cases = { ["nóm"] = "nom", ["gén"] = "gen", ["dátloc"] = "datloc", ["ácc"] = "acc", ["ábl"] = "abl", ["íns"] = "ins", ["cóm"] = "com", ["prív"] = "priv", ["dírc"] = "dirc", }

local function skip_slot( number, slot ) return number == "sg" and find( slot, "_p$" ) or		number == "pl" and find( slot, "_s$" ) end

local function add( data, slot, stem_and_ending, footnotes ) local stem local ending if not stem_and_ending then return end if skip_slot( data.number, slot ) then return end if type( stem_and_ending ) == "string" then stem = stem_and_ending ending = "" else stem = stem_and_ending[1] ending = stem_and_ending[2] end iut.add_forms( data.forms, slot, stem, ending, com.combine_stem_ending ) end

local function process_slot_overrides( data, do_slot ) for slot, overrides in pairs( data.overrides ) do		if skip_slot( data.number, slot ) then error( "Override specified for invalid slot '" .. slot .. "' due to '" .. data.number .. "' number restriction" ) end if do_slot( slot ) then data.forms[slot] = nil local slot_is_plural = find( slot, "_p$" ) for _, override in ipairs( overrides ) do				for _, value in ipairs( override.values ) do					local form = value.form local combined_notes = iut.combine_footnotes( data.footnotes, value.footnotes ) end end end end end

local function vowelharmony( infl, data ) return com.vowelharmony( infl, data )[#com.vowelharmony( infl, data )] end

local function syllables( infl ) return #com.syllables( infl ) end

local function propernoun( infl, data ) if sub( infl, 1, 1 ) ~= lower( sub( infl, 1, 1 ) ) then return true else return false end end

local function voweldeletion( infl, data ) return com.voweldeletion( infl, data ) end

local function attributive( infl, data ) end

local function plural( infl, data ) local vh = vowelharmony( infl, data ) local reduced = voweldeletion( infl, data ) if data.decl == "g" then return infl .. "г" .. vh.uu .. "д" elseif data.decl == "n" then elseif data.decl == "r" then local matches = { { {				match( infl, "ч.н$" ), match( infl, "ён$" ) },				"д" },			{ { match( infl, "[йы]$" ) }, "н" .. vh.uu .. "д" },			{ {				match( infl, "[аеёийоөуүэюя][аоөуүэ]$" ), match( reduced, "[бвгджзклмнпрстфхцчшщ][аоөуүэ]$" ), -- C[уү], and also loanwords with a word-final long vowel written as a single vowel match( infl, "[еёюя]$" ) and data.bor },				"г" .. vh.uu .. "д" },			{ { match( infl, "[иь]$" ) }, "и" .. vh.u .. "д" },			{ { match( infl, "[еёюя]$" ) }, vh.u .. "д" },			{ { match( infl, "[бвгджзклмнпрстфхцчшщ][аоөэ]?$" ) }, vh.uu .. "д" }		}		for s,t in ipairs( matches ) do			for _,m in pairs( t[1] ) do				if match( infl, "^хүн$" ) then return "хүмүүс" elseif t[2] == "д" then return sub( infl, 1, len( infl ) - 1 ) .. t[2] else return reduced .. t[2] end end end end end

local function genitive( infl, data ) local vh = vowelharmony( infl, data ) local reduced = voweldeletion( infl, data )

if data.decl == "g" then return infl .. "гийн" elseif data.decl == "n" then elseif data.decl == "r" then local matches = { { { match( infl, "н$" ) }, vh.ii			}, { {				match( infl, "[йы]$" ), match( reduced, "и$" ) },				"н" },			{ {				match( infl, "[аеёийоөуүэюя][аоөуүэ]$" ), match( reduced, "[бвгджзклмнпрстфхцчшщ][аоөуүэ]$" ), -- C[уү], and also loanwords with a word-final long vowel written as a single vowel match( infl, "[еёюя]$" ) and data.bor },				"гийн" },			{ { match( infl, "[гжикчшщь]$" ) }, "ийн" },			{ {				match( infl, "[бвдзлмпрстфхц]$" ), match( infl, "[еёюя]$" ), match( infl, "[бвгджзклмнпрстфхцчшщ][аоөэ]$" ) -- not C[уү] },				vh.ii .. "н" }		}		for s,t in ipairs( matches ) do			for _,m in pairs( t[1] ) do				if m then return { reduced, t[2] } end end end end end

local function dativelocative( infl, data ) local vh = vowelharmony( infl, data ) local syllables = syllables( infl ) local reduced = voweldeletion( infl, data )

if data.decl == "g" then return infl .. "d" elseif data.decl == "n" then elseif data.decl == "r" then local vowel = "[аеёийоөуүэюяы]" local consonant = "[бвгджзклмнпрстфхцчшщ]" local matches = { { {				match( infl, "[гкф]$" ) or match( infl, "[^бвгклмнф]п$" ), ( match( infl, "в$" ) or match( infl, "[^вгклмнпф]б$" ) ) and ( data.syllables ~= 1 or match( infl, "^" .. consonant .. consonant ) ), match( infl, vowel .. vowel .. "[рс]$" ) or match( infl, consonant .. "р$" ), ( match( infl, "р$" ) or match( infl, vowel .. "с$" ) ) and ( data.syllables ~= 1 or data.proper == true ), match( infl, vowel .. "[бгкпсф]ь?с$" ), match( infl, "[влмнр]ьс$" ), match( infl, "и[влмнр]с$" ), },				"т" },			{ {				match( infl, vowel .. "$" ), match( infl, "[^вгклмнпф]б$" ), match( infl, "[влмнр]$" ), match( infl, "[бвгклмнпрф]ь$" ), },				"д" },			{ {				match( infl, "[жчшщ]ь?$" ), match( infl, "[дзстфхц]ь$" ) },				"ид" },			{ {				match( infl, ".$" ), },				vh.a .. "д" }		}		for s,t in ipairs( matches ) do			for _,m in pairs( t[1] ) do				if m then if match( infl, "[джзстхцчшщ]ь$" ) then return { sub( infl, 1, len( infl ) - 1 ), t[2] } elseif match( infl, "[джзтхцчш]$" ) then return { reduced, t[2] } else return { infl, t[2] } end end end end return infl .. "д" end end

local function accusative( infl, data ) local vh = vowelharmony( infl, data ) local reduced = voweldeletion( infl, data )

if data.decl == "g" then return infl .. "г" elseif data.decl == "n" then elseif data.decl == "r" then local matches = { { {				match( infl, "[йы]$" ), match( infl, "[аеёийоөуүэюя][аоөуүэ]$" ), match( reduced, "[бвгджзклмнпрстфхцчшщ][аоөуүэ]$" ), -- C[уү], and also loanwords with a word-final long vowel written as a single vowel match( reduced, "и$" ), match( infl, "[еёюя]$" ) and data.bor },				"г" },			{ { match( infl, "[гжикчшщь]$" ) }, "ийг" },			{ {				match( infl, "[бвдзлмнпрстфхц]$" ), match( infl, "[еёюя]$" ), match( infl, "[бвгджзклмнпрстфхцчшщ][аоөэ]$" ) -- not C[уү] },				vh.ii .. "г" }		}		for s,t in ipairs( matches ) do			for _,m in pairs( t[1] ) do				if m then return { reduced, t[2] } end end end end end

local function aa( infl, data, decl ) local vh = vowelharmony( infl, data ) local reduced = voweldeletion( infl, data )

if decl == "g" then return infl .. "г" .. vh.aa	elseif decl == "n" then elseif decl == "r" then local matches = { { {				match( infl, "[йы]$" ), match( infl, "[аеёийоөуүэюя][аоөуүэ]$" ), match( reduced, "[бвгджзклмнпрстфхцчшщ][аоөуүэ]$" ), -- C[уү], and also loanwords with a word-final long vowel written as a single vowel match( reduced, "и$" ), match( infl, "[еёюя]$" ) and data.bor },				"г" .. vh.aa			}, { { match( infl, "[еёюя]$" ) }, vh.a			}, { { match( infl, "[иь]$" ) }, "и" .. vh.a			}, { {				match( infl, "[бвгджзклмнпрстфхцчшщ]$" ), match( infl, "[бвгджзклмнпрстфхцчшщ][аоөэ]$" ) -- not C[уү] },				vh.aa			} }		for s,t in ipairs( matches ) do			for _,m in pairs( t[1] ) do				if m then return reduced, t[2] end end end end end

local function ablative( infl, data ) local stem, ending = aa( infl, data, data.decl ) return { stem, ending .. "с" } end

local function instrumental( infl, data ) if match( data.decl, "[rnm]" ) then local stem, ending = aa( infl, data, "r" ) return { stem, ending .. "р" } elseif match( data.decl, "g" ) then local stem, ending = aa( infl, data, "g" ) return { stem, ending .. "р" } end end

local function reflexive( infl, data ) if match( data.decl, "[rnm]" ) then local stem, ending = aa( infl, data, "r" ) return { stem, ending } elseif match( data.decl, "g" ) then local stem, ending = aa( infl, data, "g" ) return { stem, ending } end end

local function comitative( infl, data )

local vh = vowelharmony( infl, data ) if match( infl, "[джзстхцчшщ]ь$" ) then return { sub( infl, 1, len( infl ) - 1 ), "ит" .. vh.ai, } else return { infl, "т" .. vh.ai, } end end

local function privative( infl, data ) return { infl, "гүй" } end

local function directional( infl, data )

local vh = vowelharmony( infl, data )

if match( infl, "рь?$" ) then return { infl, " л" .. vh.uu, } else return { infl, " р" .. vh.uu, } end end

local function equative( infl, data ) end

local function handle_derived_slots_and_overrides( data )

add( data, "nom_sg", data.lemma ) add( data, "gen_sg", genitive( data.lemma, data ) ) add( data, "datloc_sg", dativelocative( data.lemma, data ) ) add( data, "acc_sg", accusative( data.lemma, data ) ) add( data, "abl_sg", ablative( data.lemma, data ) ) add( data, "ins_sg", instrumental( data.lemma, data ) ) add( data, "com_sg", comitative( data.lemma, data ) ) add( data, "priv_sg", privative( data.lemma, data ) ) add( data, "dirc_sg", directional( data.lemma, data ) ) add( data, "nom_pl", plural( data.lemma, data ) ) add( data, "gen_pl", genitive( data.forms["nom_pl"][1].form, data ) ) add( data, "datloc_pl", dativelocative( data.forms["nom_pl"][1].form, data ) ) add( data, "acc_pl", accusative( data.forms["nom_pl"][1].form, data ) ) add( data, "abl_pl", ablative( data.forms["nom_pl"][1].form, data ) ) add( data, "ins_pl", instrumental( data.forms["nom_pl"][1].form, data ) ) add( data, "com_pl", comitative( data.forms["nom_pl"][1].form, data ) ) add( data, "priv_pl", privative( data.forms["nom_pl"][1].form, data ) ) add( data, "dirc_pl", directional( data.forms["nom_pl"][1].form, data ) ) add( data, "nom_sg_refl", reflexive( data.lemma, data ) ) add( data, "gen_sg_refl", reflexive( data.forms["gen_sg"][1].form, data ) ) add( data, "datloc_sg_refl", reflexive( data.forms["datloc_sg"][1].form, data ) ) add( data, "acc_sg_refl", reflexive( data.forms["acc_sg"][1].form, data ) ) add( data, "abl_sg_refl", reflexive( data.forms["abl_sg"][1].form, data ) ) add( data, "ins_sg_refl", reflexive( data.forms["ins_sg"][1].form, data ) ) add( data, "com_sg_refl", reflexive( data.forms["com_sg"][1].form, data ) ) add( data, "priv_sg_refl", reflexive( data.forms["priv_sg"][1].form, data ) ) add( data, "dirc_sg_refl", reflexive( data.forms["dirc_sg"][1].form, data ) ) add( data, "nom_pl_refl", reflexive( data.forms["nom_pl"][1].form, data ) ) add( data, "gen_pl_refl", reflexive( data.forms["gen_pl"][1].form, data ) ) add( data, "datloc_pl_refl", reflexive( data.forms["datloc_pl"][1].form, data ) ) add( data, "acc_pl_refl", reflexive( data.forms["acc_pl"][1].form, data ) ) add( data, "abl_pl_refl", reflexive( data.forms["abl_pl"][1].form, data ) ) add( data, "ins_pl_refl", reflexive( data.forms["ins_pl"][1].form, data ) ) add( data, "com_pl_refl", reflexive( data.forms["com_pl"][1].form, data ) ) add( data, "priv_pl_refl", reflexive( data.forms["priv_pl"][1].form, data ) ) add( data, "dirc_pl_refl", reflexive( data.forms["dirc_pl"][1].form, data ) )

-- Compute linked versions of potential lemma slots, for use in. -- We substitute the original lemma (before removing links) for forms that -- are the same as the lemma, if the original lemma has links. for _, slot in ipairs( { "nom_sg", "nom_pl" } ) do iut.insert_forms( data.forms, slot .. "_linked", iut.map_forms( data.forms[slot], function( form ) if form == data.orig_lemma_no_links and find( data.orig_lemma, "%[%[" ) then return data.orig_lemma else return form end end ) ) end end

--[=[ function export.loanworddiagnostic( text ) local consonant = "[БВГДЖЗКЛМНПРСТФХЦЧШЩбвгджзклмнпрстфхцчшщ]" local vconsonant = "[БВГЛМНРбвглмнр]" local vowel = "[АЕЁИЙОӨУҮЭЮЯаеёийоөуүэюя]" local cvowel = "[АИЙОӨЭаийоөэ]" local ivowel = "[ЕЁЮЯеёюя]" local sign = "[ЪЬъь]" local soft = "[Ьь]" local hard = "[Ъъ]" local prefixes = { "аб", "авто", "агро", "алко", "анарх", "анти", "архео", "астро", "аудио", "афро", "аэро", "баро", "био", "видео", "внутр", "эко", "гекто", "гео", "геронто", "гимна", "гига", "гидро", "гипер", "дека", "демо", "деци", "диа", "евро", "зепто", "изо", "интер", "интра", "инфра", "йокто", "квази", "кибер", "кило", "кино", "ко", "контра", "лабо", "либер", "макро", "мега", "мета", "метро", "микро", "милли", "мини", "мото", "мульти", "нано", "нео", "об", "олимп", "орга", "пано", "панто", "пара", "поли", "полу", "пост", "пре", "про", "псевдо", "радио", "суб", "супер", "теле", "тетр", "техн", "три", "турбо", "ультра", "фото", "цис", "четвер", "четыр", "экс", "электр" }

local suffixes = { "атор", "атори", "еон", "етик", "жер", "изм", "ист", "логи", "мент", "метр", "оми", "пол", "порт", "сер", "тер", "техник", "ятор", "ятори" }	local loanword local matches = { match( text, "[КФЩкфщ]" ), -- always used in loanwords match( text, consonant .. vconsonant .. consonant ), -- voweled consonant in the middle of a cluster match( text, consonant .. vconsonant .. sign .. consonant ), match( text, "[Бб]" .. consonant ), -- б followed by consonant match( text, consonant .. "[Йй]" ), -- consonant followed by й match( text, cvowel .. cvowel .. cvowel ), -- triple common vowel match( text, "и[ЁӨҮЭёөүэ]" ), match( text, hard .. consonant ), match( text, hard .. "[АЕИЙОӨЭЮаеийоөэю]" ), match( text, vowel .. sign ), match( text, "[жчш]э" .. consonant ), match( text, consonant .. vconsonant .. "$" ), -- word-final voweled consonant after consonant match( text, consonant .. sign .. vconsonant .. "$" ), match( text, "[БДЖЗТЦЧШбджзтцчш]" .. consonant .. "$" ), match( text, "[БДЖЗТЦЧШбджзтцчш]" .. sign .. consonant .. "$" ), match( text, "[СХсх][БВГДЖЗЛМНПРСХЦШбвгджзлмнпрсхцш]" .. consonant .. "$" ), match( text, "[СХсх][БВГДЖЗЛМНПРСХЦШбвгджзлмнпрсхцш]" .. sign .. consonant .. "$" ), match( text, "[ЖЧШжчш]э$" ), match( text, "^[ЙЪЬйъь]" ), -- starting with й, ь or ъ match( text, "^" .. consonant .. consonant ) -- starting with consonant cluster }	for _,v in pairs( matches ) do		if v then loanword = true break end end if loanword ~= true then for _,prefix in pairs( prefixes ) do if match( text, "^" .. prefix ) then loanword = true break end end end if loanword ~= true then for _,suffix in pairs( suffixes ) do if match( text, suffix .. "$" ) then loanword = true break end end end if loanword == true then return true else return false end end ]=]--

local function parse_indicator_spec( angle_bracket_spec ) local inside = match( angle_bracket_spec, "^<(.*)>$" ) local data = { overrides = {}, forms = {} } if inside ~= "" then local segments = iut.parse_balanced_segment_run( inside, "[", "]" ) local dot_separated_groups = iut.split_alternating_runs( segments, "%." ) for i, dot_separated_group in ipairs( dot_separated_groups ) do			local part = dot_separated_group[1] local case_prefix = sub( part, 1, 3 ) if cases[case_prefix] or accented_cases[case_prefix] then local slot, override = parse_override( dot_separated_group ) if data.overrides[slot] then table.insert( data.overrides[slot], override ) else data.overrides[slot] = { override } end elseif part == "" then if #dot_separated_group == 1 then error( "Blank indicator: '" .. inside .. "'" ) end data.footnotes = fetch_footnotes( dot_separated_group ) elseif part == "r" or part == "n" or part == "g" then if data.decl then error( "Can't specify declension twice: '" .. inside .. "'" ) end data.decl = part elseif part == "sg" or part == "pl" then if data.number then error( "Can't specify number twice: '" .. inside .. "'" ) end data.number = part elseif part == "а" or part == "о" or part == "ө" or part == "э" then if data.vh_override then error( "Can't specify vowel harmony twice: '" .. inside .. "'" ) end data.vh_override = part elseif part == "bor" or part == "Russian" then if data.bor then error( "Can't specify borrowing twice: '" .. inside .. "'" ) end data.bor = part elseif part == "proper" then if data.proper then error( "Can't specify proper noun twice: '" .. inside .. "'" ) end data.proper = true else error( "Unrecognized indicator '" .. part .. "': '" .. inside .. "'" ) end end else error( "Blank indicator: '" .. inside .. "'" ) end return data end

local function set_defaults_and_check_bad_indicators( data ) -- Set default values. if not data.adj then if data.proper then data.number = data.number or "sg" else data.number = data.number or "both" end end end

local function check_indicators_match_lemma( data ) -- Check for indicators that don't make sense given the context. if data.decl == "n" and match( data.lemma, "н$" ) then error( "Hidden-n declension cannot be specified with a lemma ending in н" ) end if data.decl == "g" and not match( data.lemma, "н$" ) then error( "Hidden-g declension can only be specified with a lemma ending in н" ) end end

local function detect_indicator_spec( data ) if propernoun( data.lemma, data ) then data.proper = true end set_defaults_and_check_bad_indicators( data ) check_indicators_match_lemma( data ) end

local function detect_all_indicator_specs( alternant_multiword_spec ) local is_multiword = #alternant_multiword_spec.alternant_or_word_specs > 1 iut.map_word_specs( alternant_multiword_spec, function( data )		detect_indicator_spec( data )		data.multiword = is_multiword	end ) end

local propagate_multiword_properties

local function propagate_alternant_properties( alternant_spec, property, mixed_value, nouns_only ) local seen_property for _, multiword_spec in ipairs( alternant_spec.alternants ) do		propagate_multiword_properties( multiword_spec, property, mixed_value, nouns_only ) if seen_property == nil then seen_property = multiword_spec[property] elseif multiword_spec[property] and seen_property ~= multiword_spec[property] then seen_property = mixed_value end end alternant_spec[property] = seen_property end

propagate_multiword_properties = function( multiword_spec, property, mixed_value, nouns_only ) local seen_property = nil local last_seen_nounal_pos = 0 local word_specs = multiword_spec.alternant_or_word_specs or multiword_spec.word_specs for i = 1, #word_specs do		local is_nounal if word_specs[i].alternants then propagate_alternant_properties( word_specs[i], property, mixed_value ) is_nounal = not not word_specs[i][property] elseif nouns_only then is_nounal = not word_specs[i].adj else is_nounal = not not word_specs[i][property] end if is_nounal then if not word_specs[i][property] then error( "Internal error: noun-type word spec without " .. property .. " set" ) end for j = last_seen_nounal_pos + 1, i - 1 do				word_specs[j][property] = word_specs[j][property] or word_specs[i][property] end last_seen_nounal_pos = i			if seen_property == nil then seen_property = word_specs[i][property] elseif seen_property ~= word_specs[i][property] then seen_property = mixed_value end end end if last_seen_nounal_pos > 0 then for i = last_seen_nounal_pos + 1, #word_specs do			word_specs[i][property] = word_specs[i][property] or word_specs[last_seen_nounal_pos][property] end end multiword_spec[property] = seen_property end

local function propagate_properties_downward( alternant_multiword_spec, property, default_propval ) local propval1 = alternant_multiword_spec[property] or default_propval for _, alternant_or_word_spec in ipairs( alternant_multiword_spec.alternant_or_word_specs ) do		local propval2 = alternant_or_word_spec[property] or propval1 if alternant_or_word_spec.alternants then for _, multiword_spec in ipairs( alternant_or_word_spec.alternants ) do				local propval3 = multiword_spec[property] or propval2 for _, word_spec in ipairs( multiword_spec.word_specs ) do					local propval4 = word_spec[property] or propval3 if propval4 == "mixed" then error( "Attempt to assign mixed " .. property .. " to word" ) end word_spec[property] = propval4 end end else if propval2 == "mixed" then error( "Attempt to assign mixed " .. property .. " to word" ) end alternant_or_word_spec[property] = propval2 end end end

local function propagate_properties( alternant_multiword_spec, property, default_propval, mixed_value ) propagate_multiword_properties( alternant_multiword_spec, property, mixed_value, "nouns only" ) propagate_multiword_properties( alternant_multiword_spec, property, mixed_value, false ) propagate_properties_downward( alternant_multiword_spec, property, default_propval ) end

local function normalize_all_lemmas( alternant_multiword_spec ) iut.map_word_specs( alternant_multiword_spec, function( data )		data.orig_lemma = data.lemma		data.orig_lemma_no_links = m_links.remove_links( data.lemma )		data.lemma = data.orig_lemma_no_links	end ) end

local function compute_categories_and_annotation( alternant_multiword_spec ) local cats = {} local function insert( cattype ) m_table.insertIfNot( cats, "Mongolian " .. cattype ) end if alternant_multiword_spec.pos == "noun" then if alternant_multiword_spec.number == "sg" then insert( "uncountable nouns" ) elseif alternant_multiword_spec.number == "pl" then insert( "pluralia tantum" ) end end local annotation if alternant_multiword_spec.manual then alternant_multiword_spec.annotation = alternant_multiword_spec.number == "sg" and "sg-only" or			alternant_multiword_spec.number == "pl" and "pl-only" or			"" else local annparts = {} local bor = nil local decl = {} local irregs = {} local stems = {} local reducible = nil local vh = {} local function do_word_spec( data ) m_table.insertIfNot( vh, vowelharmony( data.lemma, data ).a ) insert( vowelharmony( data.lemma, data ).a .. "-harmonic nouns" ) if data.decl == "r" then m_table.insertIfNot( decl, "reg" ) insert( "regular declension " .. alternant_multiword_spec.pos .. "s" ) elseif data.decl == "n" then m_table.insertIfNot( decl, "hidden-n" ) insert( "hidden-n declension " .. alternant_multiword_spec.pos .. "s" ) elseif data.decl == "g" then m_table.insertIfNot( decl, "hidden-g" ) insert( "hidden-g declension " .. alternant_multiword_spec.pos .. "s" ) end if data.bor then bor = true end if voweldeletion( data.lemma, data ) ~= data.lemma then reducible = true end end local key_entry = alternant_multiword_spec.first_noun or 1 if #alternant_multiword_spec.alternant_or_word_specs >= key_entry then local alternant_or_word_spec = alternant_multiword_spec.alternant_or_word_specs[key_entry] if alternant_or_word_spec.alternants then for _, multiword_spec in ipairs( alternant_or_word_spec.alternants ) do					key_entry = multiword_spec.first_noun or 1 if #multiword_spec.word_specs >= key_entry then do_word_spec( multiword_spec.word_specs[key_entry] ) end end else do_word_spec( alternant_or_word_spec ) end end if alternant_multiword_spec.pos == "proper noun" then table.insert( annparts, "proper" ) elseif alternant_multiword_spec.number ~= "both" then table.insert( annparts, alternant_multiword_spec.number == "sg" and "sg-only" or "pl-only" ) end if #vh > 0 then table.insert( annparts, table.concat( vh, "/" ) .. "-harmonic" ) end if #decl > 0 then table.insert( annparts, table.concat( decl, "/" ) .. " decl" ) end if bor then table.insert( annparts, "borr" ) end if reducible then table.insert( annparts, "reduc" ) end if #irregs > 0 then table.insert( annparts, table.concat( irregs, " // " ) ) end alternant_multiword_spec.annotation = table.concat( annparts, " " ) if #stems > 1 then insert( alternant_multiword_spec.pos .. "s with multiple stems" ) end end alternant_multiword_spec.categories = cats end

local function combine_stem_ending( stem, ending ) return stem .. ending end

local function show_forms( alternant_multiword_spec ) local lemmas = {} local props = { lemmas = lemmas, slot_table = output_noun_slots_with_linked, lang = lang, canonicalize = form, include_translit = true, footnotes = alternant_multiword_spec.footnotes, allow_footnote_symbols = not not alternant_multiword_spec.footnotes, }	iut.show_forms( alternant_multiword_spec.forms, props ) end

local function make_table( alternant_multiword_spec )

local forms = alternant_multiword_spec.forms local function header( min_width ) min_width = min_width or "70" return gsub( [===[ {title}{annotation} {\op}| style="background:#F9F9F9; text-align:center; min-width:MINWIDTHem; width:100%;" class="inflection-table" ]===], "MINWIDTH", min_width ) end local function reflexive( min_width ) min_width = min_width or "70" return gsub( [===[|- Reflexive forms {\op}| style="background:#F9F9F9; text-align:center; min-width:MINWIDTHem; width:100%;" class="inflection-table" ]===], "MINWIDTH", min_width ) end local function template_footer return [===[|- |{\cl}{notes_clause}   ]===] end local table_spec_both = header( "45" ) .. [===[ ! style="width:10em;background:#d9ebff" | ! style="background:#d9ebff;width:20em;" | singular / indefinite ! style="background:#d9ebff;width:20em;" | definite plural ! style="background:#eff7ff" | nominative ! style="background:#eff7ff" | genitive ! style="background:#eff7ff" | dative-locative ! style="background:#eff7ff" | accusative ! style="background:#eff7ff" | ablative ! style="background:#eff7ff" | instrumental ! style="background:#eff7ff" | comitative ! style="background:#eff7ff" | privative ! style="background:#eff7ff" | directional ]===] .. reflexive( "45" ) .. [===[ ! style="width:10em;background:#d9ebff" | ! style="background:#d9ebff;width:20em;" | singular / indefinite ! style="background:#d9ebff;width:20em;" | definite plural ! style="background:#eff7ff" | nominative ! style="background:#eff7ff" | genitive ! style="background:#eff7ff" | dative-locative ! style="background:#eff7ff" | accusative ! style="background:#eff7ff" | ablative ! style="background:#eff7ff" | instrumental ! style="background:#eff7ff" | comitative ! style="background:#eff7ff" | privative ! style="background:#eff7ff" | directional ]===] .. template_footer local table_spec_sg = header( "30" ) .. [===[ ! style="width:10em;background:#d9ebff" | ! style="background:#d9ebff" | singular / indefinite ! style="background:#eff7ff" | nominative ! style="background:#eff7ff" | genitive ! style="background:#eff7ff" | dative-locative ! style="background:#eff7ff" | accusative ! style="background:#eff7ff" | ablative ! style="background:#eff7ff" | instrumental ! style="background:#eff7ff" | comitative ! style="background:#eff7ff" | privative ! style="background:#eff7ff" | directional ]===] .. reflexive( "30" ) .. [===[ ! style="width:10em;background:#d9ebff" | ! style="background:#d9ebff" | singular / indefinite ! style="background:#eff7ff" | nominative ! style="background:#eff7ff" | genitive ! style="background:#eff7ff" | dative-locative ! style="background:#eff7ff" | accusative ! style="background:#eff7ff" | ablative ! style="background:#eff7ff" | instrumental ! style="background:#eff7ff" | comitative ! style="background:#eff7ff" | privative ! style="background:#eff7ff" | directional ]===] .. template_footer
 * {\cl}
 * {nom_sg}
 * {nom_pl}
 * {gen_sg}
 * {gen_pl}
 * {datloc_sg}
 * {datloc_pl}
 * {acc_sg}
 * {acc_pl}
 * {abl_sg}
 * {abl_pl}
 * {ins_sg}
 * {ins_pl}
 * {com_sg}
 * {com_pl}
 * {priv_sg}
 * {priv_pl}
 * {dirc_sg}
 * {dirc_pl}
 * {nom_sg_refl}
 * {nom_pl_refl}
 * {gen_sg_refl}
 * {gen_pl_refl}
 * {datloc_sg_refl}
 * {datloc_pl_refl}
 * {acc_sg_refl}
 * {acc_pl_refl}
 * {abl_sg_refl}
 * {abl_pl_refl}
 * {ins_sg_refl}
 * {ins_pl_refl}
 * {com_sg_refl}
 * {com_pl_refl}
 * {priv_sg_refl}
 * {priv_pl_refl}
 * {dirc_sg_refl}
 * {dirc_pl_refl}
 * {nom_sg}
 * {gen_sg}
 * {datloc_sg}
 * {acc_sg}
 * {abl_sg}
 * {ins_sg}
 * {com_sg}
 * {priv_sg}
 * {dirc_sg}
 * {nom_sg_refl}
 * {gen_sg_refl}
 * {datloc_sg_refl}
 * {acc_sg_refl}
 * {abl_sg_refl}
 * {ins_sg_refl}
 * {com_sg_refl}
 * {priv_sg_refl}
 * {dirc_sg_refl}

local table_spec_pl = header( "30" ) .. [===[ ! style="width:10em;background:#d9ebff" | ! style="background:#d9ebff" | definite plural ! style="background:#eff7ff" | nominative ! style="background:#eff7ff" | genitive ! style="background:#eff7ff" | dative-locative ! style="background:#eff7ff" | accusative ! style="background:#eff7ff" | ablative ! style="background:#eff7ff" | instrumental ! style="background:#eff7ff" | comitative ! style="background:#eff7ff" | privative ! style="background:#eff7ff" | directional ]===] .. reflexive( "30" ) .. [===[ ! style="width:10em;background:#d9ebff" | ! style="background:#d9ebff" | definite plural ! style="background:#eff7ff" | nominative ! style="background:#eff7ff" | genitive ! style="background:#eff7ff" | dative-locative ! style="background:#eff7ff" | accusative ! style="background:#eff7ff" | ablative ! style="background:#eff7ff" | instrumental ! style="background:#eff7ff" | comitative ! style="background:#eff7ff" | privative ! style="background:#eff7ff" | directional ]===] .. template_footer local notes_template = [===[ {footnote} ]===]	if alternant_multiword_spec.title then forms.title = alternant_multiword_spec.title else forms.title = "Declension of " .. forms.lemma .. "" end local annotation = alternant_multiword_spec.annotation or "" if annotation == "" then forms.annotation = "" else forms.annotation = " (" .. annotation .. " )" end local table_spec = alternant_multiword_spec.number == "sg" and table_spec_sg or		alternant_multiword_spec.number == "pl" and table_spec_pl or		table_spec_both forms.notes_clause = forms.footnote ~= "" and m_string_utilities.format( notes_template, forms ) or "" return m_string_utilities.format( table_spec, forms ) end
 * {nom_pl}
 * {gen_pl}
 * {datloc_pl}
 * {acc_pl}
 * {abl_pl}
 * {ins_pl}
 * {com_pl}
 * {priv_pl}
 * {dirc_pl}
 * {nom_pl_refl}
 * {gen_pl_refl}
 * {datloc_pl_refl}
 * {acc_pl_refl}
 * {abl_pl_refl}
 * {ins_pl_refl}
 * {com_pl_refl}
 * {priv_pl_refl}
 * {dirc_pl_refl}

function create_reflexives( infl, data )

data[infl].syllables = #com.syllables( data[infl].text ) vowelharmony( infl, data ) data[infl].vh = data[infl].vh[#data[infl].vh] data[infl].reduced = voweldeletion( infl, data ) data[infl].text_nostress = lang:makeEntryName( data[infl].text ) data[infl].reduced_nostress = lang:makeEntryName( data[infl].reduced ) return reflexive( infl, data ) end

local function create_caseforms( infl, data ) data[infl].syllables = #com.syllables( data[infl].text ) vowelharmony( infl, data ) data[infl].vh = data[infl].vh[#data[infl].vh] data[infl].reduced = voweldeletion( infl, data ) data[infl].text_nostress = lang:makeEntryName( data[infl].text ) data[infl].reduced_nostress = lang:makeEntryName( data[infl].reduced ) iut.add_forms( data.cases, "nom_" .. infl, data[infl].text, "", combine_stem_ending, lang ) genitive( infl, data ) dativelocative( infl, data ) accusative( infl, data ) ablative( infl, data ) instrumental( infl, data ) comitative( infl, data ) privative( infl, data ) directional( infl, data ) return data end

function export.do_generate_forms( parent_args, pos, from_headword, def )

local params = { [1] = { required = true, default = "хаан" }, footnote = { list = true }, title = {}, pos = { default = "noun" } }

local args = m_para.process( parent_args, params ) if not match( args[1], "<.*>" ) then error( "Please specify declension" ) end local parse_props = { parse_indicator_spec = parse_indicator_spec }	local alternant_multiword_spec = iut.parse_inflected_text( args[1], parse_props ) alternant_multiword_spec.title = args.title alternant_multiword_spec.pos = args.pos or pos alternant_multiword_spec.footnotes = args.footnote alternant_multiword_spec.args = args normalize_all_lemmas( alternant_multiword_spec ) detect_all_indicator_specs( alternant_multiword_spec ) propagate_properties( alternant_multiword_spec, "proper", "", true ) propagate_properties( alternant_multiword_spec, "number", "both", "both" ) local inflect_props = { skip_slot = function( slot ) return skip_slot( alternant_multiword_spec.number, slot ) end, slot_table = output_noun_slots_with_linked, get_variants = get_variants, inflect_word_spec = handle_derived_slots_and_overrides, }	iut.inflect_multiword_or_alternant_multiword_spec( alternant_multiword_spec, inflect_props ) compute_categories_and_annotation( alternant_multiword_spec ) return alternant_multiword_spec end

function export.show( frame ) local parent_args = frame:getParent.args or frame.args local alternant_multiword_spec = export.do_generate_forms( parent_args ) show_forms( alternant_multiword_spec ) return make_table( alternant_multiword_spec ) .. require( "Module:utilities" ).format_categories( alternant_multiword_spec.categories, lang ) end

local function lb( total ) return ( ( total + 1 ) / 14 ) * ( tonumber( mw.title.getCurrentTitle.subpageText ) - 1 ) end

local function ub( total ) return ( ( total + 1 ) / 14 ) * tonumber( mw.title.getCurrentTitle.subpageText ) end

function export.testpreprocess( rawtext ) local data = {}

data.text, data.spec = match( rawtext, "^(.*)<(.-)>$" ) data.text_nostress = lang:makeEntryName( data.text ) data.spec = split( data.spec, ".", true ) data.decl = data.spec[1] data.bor = data.spec[2] or nil data.syllables = #com.syllables( data.text ) vowelharmony( data ) propernoun( data ) data.vh = data.vh[#data.vh] data.reduced = export.voweldeletion( data ) data.reduced_nostress = lang:makeEntryName( data.reduced ) return data end

local function testheader( section ) local title = mw.title.getCurrentTitle.subpageText local function subpages( inflection ) local subpages = {} for i=1,14 do table.insert( subpages, "" .. i .. " " ) end return table.concat( subpages ) end if section == 1 then return "  " .. testheader( 2 ) .." " end

function export.vowelharmonytest( frame ) local lemmas = require( "Module:User:Theknightwho/mn-lemmas" ).group1 local terms = {} local correct = 0 local wrong_native = 0 local wrong_loan = 0 local predicted_vh for i,v in pairs( lemmas ) do		if i >= ( tonumber( frame.args[1] ) or lb( #lemmas ) ) and i <= ( tonumber( frame.args[2] ) or ub( #lemmas ) ) and not v.bor then local vharm = vowelharmony( v.lemma ) vharm = vharm[#vharm] if vharm.position == "back" and vharm.quality == "unrounded" then predicted_vh = 1 elseif vharm.position == "back" and vharm.quality == "rounded" then predicted_vh = 2 elseif vharm.position == "front" and vharm.quality == "rounded" then predicted_vh = 3 else predicted_vh = 4 end if predicted_vh == v.vh then correct = correct + 1 colour = "#BBFFBB" elseif v.bor then wrong_loan = wrong_loan + 1 colour = "#FFFFBB" else wrong_native = wrong_native + 1 colour = "#FFBBBB" end local term = " " .. v.class .. " " .. v.lemma .. " " .. v.vh .. " " .. predicted_vh .. " "			if colour == "#BBFFBB" then term = term .. "Correct " elseif colour == "#FFFFBB" then term = term .. "Incorrect loan " else term = term .. "Incorrect " end table.insert( terms, term ) end end return testheader( 1 ) .. " " .. testheader( 2 ) .." " end

function export.loanwordtest( frame ) local lemmas = require( "Module:User:Theknightwho/mn-lemmas" ).group1 local terms = {} local correct = 0 local wrong = 0 for i,v in pairs( lemmas ) do		if i >= ( tonumber( frame.args[1] ) or lb( #lemmas ) ) and i <= ( tonumber( frame.args[2] ) or ub( #lemmas ) ) then if v.bor then if ( export.loanworddiagnostic( v.lemma ) == true ) then correct = correct + 1 colour = "#BBFFBB" else wrong = wrong + 1 colour = "#FFBBBB" end else if ( export.loanworddiagnostic( v.lemma ) == false ) then correct = correct + 1 colour = "#BBFFBB" else wrong = wrong + 1 colour = "#FFBBBB" end end local term = " " .. v.class .. " " .. v.lemma .. " "			if v.bor then if colour == "#BBFFBB" then term = term .. "Loanword Loanword  Correct  " else term = term .. "Loanword Native word  Incorrect  " end else if colour == "#BBFFBB" then term = term .. "Native word Native word  Correct  " else term = term .. "Native word Loanword  Incorrect  " end end table.insert( terms, term ) end end return testheader( 1 ) .. " " .. testheader( 2 ) .. " " end

function export.genitivetest( frame ) local lemmas = require( "Module:User:Theknightwho/mn-lemmas" ).group1 local terms = {} local correct = 0 local wrong = 0 local data = {} local gen local stem local known local iin = { 1, 3, 5, 7, 14, 17, 18, 19, 21 } local iiin = { 4, 6, 8, 22, 23, 24, 31, 32 } local ii = { 2, 20 } local n = { 15 } local giiin = { 16, 30 } local nii = { 9, 10, 11, 12, 12.1, 25, 26, 27 } local inii = { 13, 28, 29 } for i,v in pairs( lemmas ) do		if i >= ( tonumber( frame.args[1] ) or 0 ) and i <= ( tonumber( frame.args[2] ) or 15000 ) and ( v.class <= 8 or ( v.class >= 14 and v.class <= 24 ) or v.class >= 30 ) then data = export.testpreprocess( v.lemma ) gen = genitive( data ) stem = data.text if v.class == 17 or ( v.class >= 19 and v.class <= 22 ) or v.class == 26 or v.class == 27 or v.class == 31 then stem = sub( stem, 1, len( stem ) - 2 ) .. sub( stem, -1 ) elseif v.class == 8 or v.class == 18 or v.class == 23 or v.class == 24 or v.class == 28 or v.class == 29 then stem = sub( stem, 1, len( stem ) - 1 ) end for _,class in pairs( iin ) do				if v.class == class and v.vh <= 2 then known = stem .. "ын" elseif v.class == class and v.vh >= 3 then known = stem .. "ийн" end end for _,class in pairs( iiin ) do if v.class == class then known = stem .. "ийн" end end for _,class in pairs( ii ) do				if v.class == class and v.vh <= 2 then known = stem .. "ы" elseif v.class == class and v.vh >= 3 then known = stem .. "ий" end end for _,class in pairs( n ) do if v.class == class then known = stem .. "н" end end for _,class in pairs( giiin ) do if v.class == class then known = stem .. "гийн" end end for _,class in pairs( nii ) do				if v.class == class and v.vh <= 2 then known = stem .. "ны" elseif v.class == class and v.vh >= 3 then known = stem .. "ний" end end for _,class in pairs( inii ) do				if v.class == class and v.vh <= 2 then known = stem .. "ины" elseif v.class == class and v.vh >= 3 then known = stem .. "иний" end end if gen == known then correct = correct + 1 colour = "#BBFFBB" else wrong = wrong + 1 colour = "#FFBBBB" end local term = " " .. v.class .. " " .. data.text .. " " .. known .. " " .. gen .. " "			if colour == "#BBFFBB" then term = term .. "Correct " else term = term .. "Incorrect " end table.insert( terms, term ) end end return testheader( 1 ) .. " " .. testheader( 2 ) .." " end

function export.dativelocativetest( frame ) local lemmas = require( "Module:User:Theknightwho/mn-lemmas" ).group1 local terms = {} local correct = 0 local wrong = 0 local data = {} local datloc local stem local d = { 1, 2, 7, 8, 15, 16, 17, 18, 20, 23, 30 } local ad = { 3, 19 } local id = { 4, 24, 31, 32 } local t = { 5, 6, 21, 22 } local nd = { 12, 12.1, 13, 25 } local _and = { 9, 10, 14, 26 } local ind = { 11, 27, 28, 29 } for i,v in pairs( lemmas ) do		if i >= ( tonumber( frame.args[1] ) or 0 ) and i <= ( tonumber( frame.args[2] ) or 15000 ) and ( v.class <= 8 or ( v.class >= 14 and v.class <= 24 ) or v.class >= 30 ) then data = export.testpreprocess( v.lemma ) datloc = dativelocative( data ) stem = data.text for j,vh in pairs( { "а", "о", "ө", "э" } ) do				if j == v.vh then vowel = vh end end if v.class == 19 or v.class == 26 or v.class == 27 or v.class == 31 then stem = sub( stem, 1, len( stem ) - 2 ) .. sub( stem, -1 ) elseif v.class == 24 or v.class == 28 or v.class == 29 then stem = sub( stem, 1, len( stem ) - 1 ) end for _,class in pairs( d ) do if v.class == class then known = stem .. "д" end end for _,class in pairs( ad ) do if v.class == class then known = stem .. vowel .. "д" end end for _,class in pairs( id ) do if v.class == class then known = stem .. "ид" end end for _,class in pairs( t ) do if v.class == class then known = stem .. "т" end end for _,class in pairs( nd ) do if v.class == class then known = stem .. "нд" end end for _,class in pairs( _and ) do if v.class == class then known = stem .. vowel .. "нд" end end for _,class in pairs( ind ) do if v.class == class then known = stem .. "инд" end end if datloc == known then correct = correct + 1 colour = "#BBFFBB" else wrong = wrong + 1 colour = "#FFBBBB" end local term = " " .. v.class .. " " .. data.text .. " " .. known .. " " .. datloc .. " "			if colour == "#BBFFBB" then term = term .. "Correct " else term = term .. "Incorrect " end table.insert( terms, term ) end end return testheader( 1 ) .. " " .. testheader( 2 ) .." " end

function export.accusativetest( frame ) local lemmas = require( "Module:User:Theknightwho/mn-lemmas" ).group1 local terms = {} local correct = 0 local wrong = 0 local data = {} local acc local stem local known local iig = { 1, 2, 3, 5, 7, 9, 12.1, 14, 17, 18, 19, 20, 21, 25, 26 } local iiig = { 4, 6, 8, 10, 11, 13, 22, 23, 24, 27, 28, 29, 31, 32 } local g = { 12, 15, 16, 30 } for i,v in pairs( lemmas ) do		if i >= ( tonumber( frame.args[1] ) or 0 ) and i <= ( tonumber( frame.args[2] ) or 15000 ) and ( v.class <= 8 or ( v.class >= 14 and v.class <= 24 ) or v.class >= 30 ) then data = export.testpreprocess( v.lemma ) acc = accusative( data ) stem = data.text if v.class == 17 or ( v.class >= 19 and v.class <= 22 ) or v.class == 26 or v.class == 27 or v.class == 31 then stem = sub( stem, 1, len( stem ) - 2 ) .. sub( stem, -1 ) elseif v.class == 8 or v.class == 18 or v.class == 23 or v.class == 24 or v.class == 28 or v.class == 29 then stem = sub( stem, 1, len( stem ) - 1 ) end for _,class in pairs( iig ) do				if v.class == class and v.vh <= 2 then known = stem .. "ыг" elseif v.class == class and v.vh >= 3 then known = stem .. "ийг" end end for _,class in pairs( iiig ) do if v.class == class then known = stem .. "ийг" end end for _,class in pairs( g ) do if v.class == class then known = stem .. "г" end end if acc == known then correct = correct + 1 colour = "#BBFFBB" else wrong = wrong + 1 colour = "#FFBBBB" end local term = " " .. v.class .. " " .. data.text .. " " .. known .. " " .. acc .. " "			if colour == "#BBFFBB" then term = term .. "Correct " else term = term .. "Incorrect " end table.insert( terms, term ) end end return testheader( 1 ) .. " Correct:  " .. correct .. "   Incorrect:  " .. wrong .. "   Accuracy:  " .. ( correct / ( correct + wrong ) ) * 100 .. "%  " .. testheader( 2 ) .." " end

function export.ablativetest( frame ) local lemmas = require( "Module:User:Theknightwho/mn-lemmas" ).group1 local terms = {} local correct = 0 local wrong = 0 local data = {} local abl local stem local vowel local known local aas = { 1, 2, 3, 4, 5, 6, 14, 17, 18, 19, 20, 21, 22, 31, 32 } local as = { 7 } local ias = { 8, 23, 24 } local gaas = { 15, 16, 30 } local naas = { 9, 10, 11, 12, 12.1, 13, 25, 26, 27 } local inaas = { 28, 29 } for i,v in pairs( lemmas ) do		if i >= ( tonumber( frame.args[1] ) or 0 ) and i <= ( tonumber( frame.args[2] ) or 15000 ) and ( v.class <= 8 or ( v.class >= 14 and v.class <= 24 ) or v.class >= 30 ) then data = export.testpreprocess( v.lemma ) abl = ablative( data ) stem = data.text for j,vh in pairs( { "а", "о", "ө", "э" } ) do				if j == v.vh then vowel = vh end end if v.class == 17 or ( v.class >= 19 and v.class <= 22 ) or v.class == 26 or v.class == 27 or v.class == 31 then stem = sub( stem, 1, len( stem ) - 2 ) .. sub( stem, -1 ) elseif v.class == 8 or v.class == 18 or v.class == 23 or v.class == 24 then stem = sub( stem, 1, len( stem ) - 1 ) end for _,class in pairs( aas ) do				if v.class == class then known = stem .. vowel .. vowel .. "с" end end for _,class in pairs( as ) do				if v.class == class then known = stem .. vowel .. "с" end end for _,class in pairs( ias ) do				if v.class == class then known = stem .. "и" .. vowel .. "с" end end for _,class in pairs( gaas ) do				if v.class == class then known = stem .. "г" .. vowel .. vowel .. "с" end end for _,class in pairs( naas ) do				if v.class == class then known = stem .. "н" .. vowel .. vowel .. "с" end end for _,class in pairs( inaas ) do				if v.class == class then known = stem .. "ин" .. vowel .. vowel .. "с" end end if abl == known then correct = correct + 1 colour = "#BBFFBB" else wrong = wrong + 1 colour = "#FFBBBB" end local term = " " .. v.class .. " " .. data.text .. " " .. known .. " " .. abl .. " "			if colour == "#BBFFBB" then term = term .. "Correct " else term = term .. "Incorrect " end table.insert( terms, term ) end end return testheader( 1 ) .. " Correct:  " .. correct .. "   Incorrect:  " .. wrong .. "   Accuracy:  " .. ( correct / ( correct + wrong ) ) * 100 .. "%  " .. testheader( 2 ) .." " end

function export.instrumentaltest( frame ) local lemmas = require( "Module:User:Theknightwho/mn-lemmas" ).group1 local terms = {} local correct = 0 local wrong = 0 local data = {} local ins local stem local vowel local known local aar = { 1, 2, 3, 4, 5, 6, 9, 10, 11, 14, 17, 18, 19, 20, 21, 22, 25, 26, 27, 31, 32 } local ar = { 7, 12.1 } local iar = { 8, 13, 23, 24, 28, 29 } local gaar = { 12, 15, 16, 30 } for i,v in pairs( lemmas ) do		if i >= ( tonumber( frame.args[1] ) or 0 ) and i <= ( tonumber( frame.args[2] ) or 15000 ) then data = export.testpreprocess( v.lemma ) ins = instrumental( data ) stem = data.text for j,vh in pairs( { "а", "о", "ө", "э" } ) do				if j == v.vh then vowel = vh end end if v.class == 17 or ( v.class >= 19 and v.class <= 22 ) or v.class == 26 or v.class == 27 or v.class == 31 then stem = sub( stem, 1, len( stem ) - 2 ) .. sub( stem, -1 ) elseif v.class == 8 or v.class == 13 or v.class == 18 or ( v.class >= 23 and v.class <= 25 ) or v.class == 28 or v.class == 29 then stem = sub( stem, 1, len( stem ) - 1 ) end for _,class in pairs( aar ) do				if v.class == class then known = stem .. vowel .. vowel .. "р" end end for _,class in pairs( ar ) do				if v.class == class then known = stem .. vowel .. "р" end end for _,class in pairs( iar ) do				if v.class == class then known = stem .. "и" .. vowel .. "р" end end for _,class in pairs( gaar ) do				if v.class == class then known = stem .. "г" .. vowel .. vowel .. "р" end end if ins == known then correct = correct + 1 colour = "#BBFFBB" else wrong = wrong + 1 colour = "#FFBBBB" end local term = " " .. v.class .. " " .. data.text .. " " .. known .. " " .. ins .. " "			if colour == "#BBFFBB" then term = term .. "Correct " else term = term .. "Incorrect " end table.insert( terms, term ) end end return testheader( 1 ) .. " Correct:  " .. correct .. "   Incorrect:  " .. wrong .. "   Accuracy:  " .. ( correct / ( correct + wrong ) ) * 100 .. "%  " .. testheader( 2 ) .." " end

return export