Module:User:Theknightwho/mn-decl

Hello, you have come here looking for the meaning of the word Module:User:Theknightwho/mn-decl. In DICTIOUS you will not only get to know all the dictionary meanings for the word Module:User:Theknightwho/mn-decl, but we will also tell you about its etymology, its characteristics and you will know how to say Module:User:Theknightwho/mn-decl in singular and plural. Everything you need to know about the word Module:User:Theknightwho/mn-decl you have here. The definition of the word Module:User:Theknightwho/mn-decl will help you to be more precise and correct when speaking or writing your texts. Knowing the definition ofModule:User:Theknightwho/mn-decl, as well as those of other words, enriches your vocabulary and provides you with more and better linguistic resources.


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 = m_string_utilities.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 = m_string_utilities.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|s"
output_noun_slots_with_linked = "nom|p"

local input_params_to_slots_both = {
	 = "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",
}

local input_params_to_slots_sg = {
	 = "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 input_params_to_slots_pl = {
	 = "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",
}

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

local accented_cases = {
	 = "nom",
	 = "gen",
	 = "datloc",
	 = "acc",
	 = "abl",
	 = "ins",
	 = "com",
	 = "priv",
	 = "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
		ending = stem_and_ending
	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 = 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 )
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 ) do
				if match( infl, "^хүн$" ) then
					return "хүмүүс"
				elseif t == "д" then
					return sub( infl, 1, len( infl ) - 1 ) .. t
				else
					return reduced .. t
				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 ) do
				if m then return { reduced, t } 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 ) do
				if m then
					if match( infl, "ь$" ) then
						return { sub( infl, 1, len( infl ) - 1 ), t }
					elseif match( infl, "$" ) then
						return { reduced, t }
					else
						return { infl, t }
					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 ) do
				if m then return { reduced, t } 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 ) do
				if m then return reduced, t 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, "" ) 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, "" ) 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.form, data ) )
	add( data, "datloc_pl", dativelocative( data.forms.form, data ) )
	add( data, "acc_pl", accusative( data.forms.form, data ) )
	add( data, "abl_pl", ablative( data.forms.form, data ) )
	add( data, "ins_pl", instrumental( data.forms.form, data ) )
	add( data, "com_pl", comitative( data.forms.form, data ) )
	add( data, "priv_pl", privative( data.forms.form, data ) )
	add( data, "dirc_pl", directional( data.forms.form, data ) )
	add( data, "nom_sg_refl", reflexive( data.lemma, data ) )
	add( data, "gen_sg_refl", reflexive( data.forms.form, data ) )
	add( data, "datloc_sg_refl", reflexive( data.forms.form, data ) )
	add( data, "acc_sg_refl", reflexive( data.forms.form, data ) )
	add( data, "abl_sg_refl", reflexive( data.forms.form, data ) )
	add( data, "ins_sg_refl", reflexive( data.forms.form, data ) )
	add( data, "com_sg_refl", reflexive( data.forms.form, data ) )
	add( data, "priv_sg_refl", reflexive( data.forms.form, data ) )
	add( data, "dirc_sg_refl", reflexive( data.forms.form, data ) )
	add( data, "nom_pl_refl", reflexive( data.forms.form, data ) )
	add( data, "gen_pl_refl", reflexive( data.forms.form, data ) )
	add( data, "datloc_pl_refl", reflexive( data.forms.form, data ) )
	add( data, "acc_pl_refl", reflexive( data.forms.form, data ) )
	add( data, "abl_pl_refl", reflexive( data.forms.form, data ) )
	add( data, "ins_pl_refl", reflexive( data.forms.form, data ) )
	add( data, "com_pl_refl", reflexive( data.forms.form, data ) )
	add( data, "priv_pl_refl", reflexive( data.forms.form, data ) )
	add( data, "dirc_pl_refl", reflexive( data.forms.form, data ) )

	-- Compute linked versions of potential lemma slots, for use in {{mn-noun}}.
	-- 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, 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
			local case_prefix = sub( part, 1, 3 )
			if cases or accented_cases then
				local slot, override = parse_override( dot_separated_group )
				if data.overrides then
					table.insert( data.overrides, override )
				else
					data.overrides = { 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
		elseif multiword_spec and seen_property ~= multiword_spec then
			seen_property = mixed_value
		end
	end
	alternant_spec = 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.alternants then
			propagate_alternant_properties( word_specs, property, mixed_value )
			is_nounal = not not word_specs
		elseif nouns_only then
			is_nounal = not word_specs.adj
		else
			is_nounal = not not word_specs
		end
		if is_nounal then
			if not word_specs then
				error( "Internal error: noun-type word spec without " .. property .. " set" )
			end
			for j = last_seen_nounal_pos + 1, i - 1 do
				word_specs = word_specs or word_specs
			end
			last_seen_nounal_pos = i
			if seen_property == nil then
				seen_property = word_specs
			elseif seen_property ~= word_specs 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 = word_specs or word_specs
		end
	end
	multiword_spec = seen_property
end


local function propagate_properties_downward( alternant_multiword_spec, property, default_propval )
	local propval1 = alternant_multiword_spec or default_propval
	for _, alternant_or_word_spec in ipairs( alternant_multiword_spec.alternant_or_word_specs ) do
		local propval2 = alternant_or_word_spec or propval1
		if alternant_or_word_spec.alternants then
			for _, multiword_spec in ipairs( alternant_or_word_spec.alternants ) do
				local propval3 = multiword_spec or propval2
				for _, word_spec in ipairs( multiword_spec.word_specs ) do
					local propval4 = word_spec or propval3
					if propval4 == "mixed" then
						error( "Attempt to assign mixed " .. property .. " to word" )
					end
					word_spec = propval4
				end
			end
		else
			if propval2 == "mixed" then
				error( "Attempt to assign mixed " .. property .. " to word" )
			end
			alternant_or_word_spec = 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
			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 )
					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( [===[
<div>
<div class="NavFrame" style="display:inline-block; min-width:MINWIDTHem">
<div class="NavHead" style="background:#eff7ff;">{title}{annotation}&nbsp;</div>
<div class="NavContent">
{\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( [===[|-
|{\cl}
<div class="NavFrame" min-width:MINWIDTHem">
<div class="NavHead" style="background:#eff7ff;">Reflexive forms&nbsp;</div>
<div class="NavContent">
{\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}</div></div></div></div>]===]
	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
| {nom_sg}
| {nom_pl}
|-
! style="background:#eff7ff" | genitive
| {gen_sg}
| {gen_pl}
|-
! style="background:#eff7ff" | dative-locative
| {datloc_sg}
| {datloc_pl}
|-
! style="background:#eff7ff" | accusative
| {acc_sg}
| {acc_pl}
|-
! style="background:#eff7ff" | ablative
| {abl_sg}
| {abl_pl}
|-
! style="background:#eff7ff" | instrumental
| {ins_sg}
| {ins_pl}
|-
! style="background:#eff7ff" | comitative
| {com_sg}
| {com_pl}
|-
! style="background:#eff7ff" | privative
| {priv_sg}
| {priv_pl}
|-
! style="background:#eff7ff" | directional
| {dirc_sg}
| {dirc_pl}
]===] .. 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
| {nom_sg_refl}
| {nom_pl_refl}
|-
! style="background:#eff7ff" | genitive
| {gen_sg_refl}
| {gen_pl_refl}
|-
! style="background:#eff7ff" | dative-locative
| {datloc_sg_refl}
| {datloc_pl_refl}
|-
! style="background:#eff7ff" | accusative
| {acc_sg_refl}
| {acc_pl_refl}
|-
! style="background:#eff7ff" | ablative
| {abl_sg_refl}
| {abl_pl_refl}
|-
! style="background:#eff7ff" | instrumental
| {ins_sg_refl}
| {ins_pl_refl}
|-
! style="background:#eff7ff" | comitative
| {com_sg_refl}
| {com_pl_refl}
|-
! style="background:#eff7ff" | privative
| {priv_sg_refl}
| {priv_pl_refl}
|-
! style="background:#eff7ff" | directional
| {dirc_sg_refl}
| {dirc_pl_refl}
]===] .. template_footer()
	
	local table_spec_sg = header( "30" ) .. [===[
! style="width:10em;background:#d9ebff" |
! style="background:#d9ebff" | singular / indefinite
|-
! style="background:#eff7ff" | nominative
| {nom_sg}
|-
! style="background:#eff7ff" | genitive
| {gen_sg}
|-
! style="background:#eff7ff" | dative-locative
| {datloc_sg}
|-
! style="background:#eff7ff" | accusative
| {acc_sg}
|-
! style="background:#eff7ff" | ablative
| {abl_sg}
|-
! style="background:#eff7ff" | instrumental
| {ins_sg}
|-
! style="background:#eff7ff" | comitative
| {com_sg}
|-
! style="background:#eff7ff" | privative
| {priv_sg}
|-
! style="background:#eff7ff" | directional
| {dirc_sg}
]===] .. reflexive( "30" ) .. [===[
! style="width:10em;background:#d9ebff" |
! style="background:#d9ebff" | singular / indefinite
|-
! style="background:#eff7ff" | nominative
| {nom_sg_refl}
|-
! style="background:#eff7ff" | genitive
| {gen_sg_refl}
|-
! style="background:#eff7ff" | dative-locative
| {datloc_sg_refl}
|-
! style="background:#eff7ff" | accusative
| {acc_sg_refl}
|-
! style="background:#eff7ff" | ablative
| {abl_sg_refl}
|-
! style="background:#eff7ff" | instrumental
| {ins_sg_refl}
|-
! style="background:#eff7ff" | comitative
| {com_sg_refl}
|-
! style="background:#eff7ff" | privative
| {priv_sg_refl}
|-
! style="background:#eff7ff" | directional
| {dirc_sg_refl}
]===] .. template_footer()

	local table_spec_pl = header( "30" ) .. [===[
! style="width:10em;background:#d9ebff" |
! style="background:#d9ebff" | definite plural
|-
! style="background:#eff7ff" | nominative
| {nom_pl}
|-
! style="background:#eff7ff" | genitive
| {gen_pl}
|-
! style="background:#eff7ff" | dative-locative
| {datloc_pl}
|-
! style="background:#eff7ff" | accusative
| {acc_pl}
|-
! style="background:#eff7ff" | ablative
| {abl_pl}
|-
! style="background:#eff7ff" | instrumental
| {ins_pl}
|-
! style="background:#eff7ff" | comitative
| {com_pl}
|-
! style="background:#eff7ff" | privative
| {priv_pl}
|-
! style="background:#eff7ff" | directional
| {dirc_pl}
]===] .. reflexive( "30" ) .. [===[
! style="width:10em;background:#d9ebff" |
! style="background:#d9ebff" | definite plural
|-
! style="background:#eff7ff" | nominative
| {nom_pl_refl}
|-
! style="background:#eff7ff" | genitive
| {gen_pl_refl}
|-
! style="background:#eff7ff" | dative-locative
| {datloc_pl_refl}
|-
! style="background:#eff7ff" | accusative
| {acc_pl_refl}
|-
! style="background:#eff7ff" | ablative
| {abl_pl_refl}
|-
! style="background:#eff7ff" | instrumental
| {ins_pl_refl}
|-
! style="background:#eff7ff" | comitative
| {com_pl_refl}
|-
! style="background:#eff7ff" | privative
| {priv_pl_refl}
|-
! style="background:#eff7ff" | directional
| {dirc_pl_refl}
]===] .. template_footer()
	
	local notes_template = [===[
<div style="width:100%;text-align:left;background:#d9ebff">
<div style="display:inline-block;text-align:left;padding-left:1em;padding-right:1em">
{footnote}
</div></div>
]===]
	
	if alternant_multiword_spec.title then
		forms.title = alternant_multiword_spec.title
	else
		forms.title = "Declension of <i lang=\"mn\" class=\"Cyrl\">" .. forms.lemma .. "</i>"
	end
	
	local annotation = alternant_multiword_spec.annotation or ""
	if annotation == "" then
		forms.annotation = ""
	else
		forms.annotation = " (<span style=\"font-weight:normal;\">" .. annotation .. "</span>)"
	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

function create_reflexives( infl, data )

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

local function create_caseforms( infl, data )
	
	data.syllables = #com.syllables( data.text )
	vowelharmony( infl, data )
	data.vh = data.vh.vh]
	data.reduced = voweldeletion( infl, data )
	data.text_nostress = lang:makeEntryName( data.text )
	data.reduced_nostress = lang:makeEntryName( data.reduced )
	
	iut.add_forms( data.cases, "nom_" .. infl, data.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 = {
		 = { required = true, default = "хаан<r>" },
		footnote = { list = true },
		title = {},
		pos = { default = "noun" }
	}

	local args = m_para.process( parent_args, params )
	if not match( args, "<.*>" ) then error( "Please specify declension" ) end
	local parse_props = {
		parse_indicator_spec = parse_indicator_spec
	}
	
	local alternant_multiword_spec = iut.parse_inflected_text( args, 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
	data.bor = data.spec or nil
	data.syllables = #com.syllables( data.text )
	vowelharmony( data )
	propernoun( data )
	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, "] " )
		end
		return table.concat( subpages )
	end
	if section == 1 then
	return "<table style=\"float:right;\"><tr><td>]</td><td>" .. subpages( "vowel-harmony" ) .. "<tr><tr><td>]</td><td>" .. subpages( "vowel-deletion" ) .. "<tr><tr><td>]</td><td>" .. subpages( "loanword" ) .. "<tr></table><table>"
	elseif section == 2 then
		return "<table style=\"float:right;padding-top:1em;\"><tr><td>]</td><td>" .. subpages( "genitive" ) .. "<tr><tr><td>]</td><td>" .. subpages( "dative-locative" ) .. "<tr><tr><td>]</td><td>" .. subpages( "accusative" ) .. "<tr><tr><td>]</td><td>" .. subpages( "ablative" ) .. "<tr><tr><td>]</td><td>" .. subpages( "instrumental" ) .. "<tr></table><table>"
	end
end

function export.voweldeletiontest( frame )
	
	local function deletevowel( text ) -- deletes the second last character of the string
		return sub( text, 1, len( text ) - 2 ) .. sub( text, len( text ), len( text ) )
	end
	
	local lemmas  = require( "Module:User:Theknightwho/mn-lemmas" ).group1
	local terms = {}
	
	local correct = 0
	local wrong_native = 0
	local wrong_loan = 0
	for i,v in pairs( lemmas ) do
		if i >= ( tonumber( frame.args ) or lb( #lemmas ) ) and i <= ( tonumber( frame.args ) or ub( #lemmas ) ) and match( sub( v.lemma, -1 ), "" ) and v.class ~= 16 then
			local reduced = export.voweldeletion( v.lemma ) 
			if v.class <= 15 or v.class == 18 or ( v.class >= 23 and v.class <= 25 ) or ( v.class >= 28 and v.class <= 30 ) or v.class == 32 then
				if reduced == v.lemma 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
			else
				if reduced == deletevowel( v.lemma ) then
					correct = correct + 1
					colour = "#BBFFBB"
				else
					wrong_native = wrong_native + 1
					colour = "#FFBBBB"
				end
			end
			local term = "<tr " .. "style=\"background:" .. colour .. ";\"" .. "><td>" .. v.class .. "</td><td>" .. v.lemma .. "</td><td>" .. reduced .. "</td><td>"
			if colour == "#BBFFBB" then term = term .. "Correct</td></tr>" elseif colour == "#FFFFBB" then term = term .. "Incorrect loan</td></tr>" else term = term .. "Incorrect</td></tr>" end
			table.insert( terms, term )
		end
	end
	
	return testheader( 1 ) .. "<table><tr><td>Correct:</td><td>" .. correct .. "</td></tr><tr><td>Incorrect (native):</td><td>" .. wrong_native .. "</td></tr><tr ><td>Incorrect (loanwords):</td><td>" .. wrong_loan .. "</td></tr><tr><td>Accuracy:</td><td>" .. ( correct / ( correct + wrong_native ) ) * 100 .. "%</td></tr></table>" .. testheader( 2 ) .."<table class=\"wikitable sortable jquery-tablesorter\"><th class=\"headerSort\" tabindex=\"0\" role=\"columnheader button\" title=\"Sort ascending\">Class</th><th class=\"headerSort\" tabindex=\"0\" role=\"columnheader button\" title=\"Sort ascending\">Lemma</th><th class=\"headerSort\" tabindex=\"0\" role=\"columnheader button\" title=\"Sort ascending\">Reduced</th><th class=\"headerSort\" tabindex=\"0\" role=\"columnheader button\" title=\"Sort ascending\">Status</th>" .. table.concat( terms ) .. "</table>"
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 ) or lb( #lemmas ) ) and i <= ( tonumber( frame.args ) or ub( #lemmas ) ) and not v.bor then
			local vharm = vowelharmony( v.lemma )
			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 = "<tr " .. "style=\"background:" .. colour .. ";\"" .. "><td>" .. v.class .. "</td><td>" .. v.lemma .. "</td><td>" .. v.vh .. "</td><td>" .. predicted_vh .. "</td><td>"
			if colour == "#BBFFBB" then term = term .. "Correct</td></tr>" elseif colour == "#FFFFBB" then term = term .. "Incorrect loan</td></tr>" else term = term .. "Incorrect</td></tr>" end
			table.insert( terms, term )
		end
	end
	
	return testheader( 1 ) .. "<table><tr><td>Correct:</td><td>" .. correct .. "</td></tr><tr><td>Incorrect (native):</td><td>" .. wrong_native .. "</td></tr><tr ><td>Incorrect (loanwords):</td><td>" .. wrong_loan .. "</td></tr><tr><td>Accuracy:</td><td>" .. ( correct / ( correct + wrong_native ) ) * 100 .. "%</td></tr></table>" .. testheader( 2 ) .."<table class=\"wikitable sortable jquery-tablesorter\"><th class=\"headerSort\" tabindex=\"0\" role=\"columnheader button\" title=\"Sort ascending\">Class</th><th class=\"headerSort\" tabindex=\"0\" role=\"columnheader button\" title=\"Sort ascending\">Lemma</th><th class=\"headerSort\" tabindex=\"0\" role=\"columnheader button\" title=\"Sort ascending\">Vowel harmony</th><th class=\"headerSort\" tabindex=\"0\" role=\"columnheader button\" title=\"Sort ascending\">Predicted VH</th><th class=\"headerSort\" tabindex=\"0\" role=\"columnheader button\" title=\"Sort ascending\">Status</th>" .. table.concat( terms ) .. "</table>"
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 ) or lb( #lemmas ) ) and i <= ( tonumber( frame.args ) 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 = "<tr " .. "style=\"background:" .. colour .. ";\"" .. "><td>" .. v.class .. "</td><td>" .. v.lemma .. "</td><td>"
			if v.bor then
				if colour == "#BBFFBB" then
					term = term .. "Loanword</td><td>Loanword</td><td>Correct</td></tr>"
				else
					term = term .. "Loanword</td><td>Native word</td><td>Incorrect</td></tr>"
				end
			else
				if colour == "#BBFFBB" then
					term = term .. "Native word</td><td>Native word</td><td>Correct</td></tr>"
				else
					term = term .. "Native word</td><td>Loanword</td><td>Incorrect</td></tr>"
				end
			end
			table.insert( terms, term )
		end
	end
	
	return testheader( 1 ) .. "<table><tr><td>Correct:</td><td>" .. correct .. "</td></tr><tr><td>Incorrect:</td><td>" .. wrong .. "</td></tr><tr><td>Accuracy:</td><td>" .. ( correct / ( correct + wrong ) ) * 100 .. "%</td></tr></table>" .. testheader( 2 ) .. "<table class=\"wikitable sortable jquery-tablesorter\"><th class=\"headerSort\" tabindex=\"0\" role=\"columnheader button\" title=\"Sort ascending\">Class</th><th class=\"headerSort\" tabindex=\"0\" role=\"columnheader button\" title=\"Sort ascending\">Lemma</th><th class=\"headerSort\" tabindex=\"0\" role=\"columnheader button\" title=\"Sort ascending\">Type</th><th class=\"headerSort\" tabindex=\"0\" role=\"columnheader button\" title=\"Sort ascending\">Predicted</th><th class=\"headerSort\" tabindex=\"0\" role=\"columnheader button\" title=\"Sort ascending\">Status</th>" .. table.concat( terms ) .. "</table>"
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 ) or 0 ) and i <= ( tonumber( frame.args ) 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 = "<tr " .. "style=\"background:" .. colour .. ";\"" .. "><td>" .. v.class .. "</td><td>" .. data.text .. "</td><td>" .. known .. "</td><td>" .. gen .. "</td><td>"
			if colour == "#BBFFBB" then term = term .. "Correct</td></tr>" else term = term .. "Incorrect</td></tr>" end
			table.insert( terms, term )
		end
	end
	
	return testheader( 1 ) .. "<table><tr><td>Correct:</td><td>" .. correct .. "</td></tr><tr><td>Incorrect:</td><td>" .. wrong .. "</td></tr><tr><td>Accuracy:</td><td>" .. ( correct / ( correct + wrong ) ) * 100 .. "%</td></tr></table>" .. testheader( 2 ) .."<table class=\"wikitable sortable jquery-tablesorter\"><th class=\"headerSort\" tabindex=\"0\" role=\"columnheader button\" title=\"Sort ascending\">Class</th><th class=\"headerSort\" tabindex=\"0\" role=\"columnheader button\" title=\"Sort ascending\">Lemma</th><th class=\"headerSort\" tabindex=\"0\" role=\"columnheader button\" title=\"Sort ascending\">Known</th><th class=\"headerSort\" tabindex=\"0\" role=\"columnheader button\" title=\"Sort ascending\">Predicted</th><th class=\"headerSort\" tabindex=\"0\" role=\"columnheader button\" title=\"Sort ascending\">Status</th>" .. table.concat( terms ) .. "</table></div>"
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 ) or 0 ) and i <= ( tonumber( frame.args ) 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 = "<tr " .. "style=\"background:" .. colour .. ";\"" .. "><td>" .. v.class .. "</td><td>" .. data.text .. "</td><td>" .. known .. "</td><td>" .. datloc .. "</td><td>"
			if colour == "#BBFFBB" then term = term .. "Correct</td></tr>" else term = term .. "Incorrect</td></tr>" end
			table.insert( terms, term )
		end
	end
	
	return testheader( 1 ) .. "<table><tr><td>Correct:</td><td>" .. correct .. "</td></tr><tr><td>Incorrect:</td><td>" .. wrong .. "</td></tr><tr><td>Accuracy:</td><td>" .. ( correct / ( correct + wrong ) ) * 100 .. "%</td></tr></table>" .. testheader( 2 ) .."<table class=\"wikitable sortable jquery-tablesorter\"><th class=\"headerSort\" tabindex=\"0\" role=\"columnheader button\" title=\"Sort ascending\">Class</th><th class=\"headerSort\" tabindex=\"0\" role=\"columnheader button\" title=\"Sort ascending\">Lemma</th><th class=\"headerSort\" tabindex=\"0\" role=\"columnheader button\" title=\"Sort ascending\">Known</th><th class=\"headerSort\" tabindex=\"0\" role=\"columnheader button\" title=\"Sort ascending\">Predicted</th><th class=\"headerSort\" tabindex=\"0\" role=\"columnheader button\" title=\"Sort ascending\">Status</th>" .. table.concat( terms ) .. "</table>"
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 ) or 0 ) and i <= ( tonumber( frame.args ) 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 = "<tr " .. "style=\"background:" .. colour .. ";\"" .. "><td>" .. v.class .. "</td><td>" .. data.text .. "</td><td>" .. known .. "</td><td>" .. acc .. "</td><td>"
			if colour == "#BBFFBB" then term = term .. "Correct</td></tr>" else term = term .. "Incorrect</td></tr>" end
			table.insert( terms, term )
		end
	end
	
	return testheader( 1 ) .. "<tr><td>Correct:</td><td>" .. correct .. "</td></tr><tr><td>Incorrect:</td><td>" .. wrong .. "</td></tr><tr><td>Accuracy:</td><td>" .. ( correct / ( correct + wrong ) ) * 100 .. "%</td></tr></table>" .. testheader( 2 ) .."<table class=\"wikitable sortable jquery-tablesorter\"><th class=\"headerSort\" tabindex=\"0\" role=\"columnheader button\" title=\"Sort ascending\">Class</th><th class=\"headerSort\" tabindex=\"0\" role=\"columnheader button\" title=\"Sort ascending\">Lemma</th><th class=\"headerSort\" tabindex=\"0\" role=\"columnheader button\" title=\"Sort ascending\">Known</th><th class=\"headerSort\" tabindex=\"0\" role=\"columnheader button\" title=\"Sort ascending\">Predicted</th><th class=\"headerSort\" tabindex=\"0\" role=\"columnheader button\" title=\"Sort ascending\">Status</th>" .. table.concat( terms ) .. "</table>"
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 ) or 0 ) and i <= ( tonumber( frame.args ) 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 = "<tr " .. "style=\"background:" .. colour .. ";\"" .. "><td>" .. v.class .. "</td><td>" .. data.text .. "</td><td>" .. known .. "</td><td>" .. abl .. "</td><td>"
			if colour == "#BBFFBB" then term = term .. "Correct</td></tr>" else term = term .. "Incorrect</td></tr>" end
			table.insert( terms, term )
		end
	end
	
	return testheader( 1 ) .. "<tr><td>Correct:</td><td>" .. correct .. "</td></tr><tr><td>Incorrect:</td><td>" .. wrong .. "</td></tr><tr><td>Accuracy:</td><td>" .. ( correct / ( correct + wrong ) ) * 100 .. "%</td></tr></table>" .. testheader( 2 ) .."<table class=\"wikitable sortable jquery-tablesorter\"><th class=\"headerSort\" tabindex=\"0\" role=\"columnheader button\" title=\"Sort ascending\">Class</th><th class=\"headerSort\" tabindex=\"0\" role=\"columnheader button\" title=\"Sort ascending\">Lemma</th><th class=\"headerSort\" tabindex=\"0\" role=\"columnheader button\" title=\"Sort ascending\">Known</th><th class=\"headerSort\" tabindex=\"0\" role=\"columnheader button\" title=\"Sort ascending\">Predicted</th><th class=\"headerSort\" tabindex=\"0\" role=\"columnheader button\" title=\"Sort ascending\">Status</th>" .. table.concat( terms ) .. "</table>"
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 ) or 0 ) and i <= ( tonumber( frame.args ) 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 = "<tr " .. "style=\"background:" .. colour .. ";\"" .. "><td>" .. v.class .. "</td><td>" .. data.text .. "</td><td>" .. known .. "</td><td>" .. ins .. "</td><td>"
			if colour == "#BBFFBB" then term = term .. "Correct</td></tr>" else term = term .. "Incorrect</td></tr>" end
			table.insert( terms, term )
		end
	end
	
	return testheader( 1 ) .. "<tr><td>Correct:</td><td>" .. correct .. "</td></tr><tr><td>Incorrect:</td><td>" .. wrong .. "</td></tr><tr><td>Accuracy:</td><td>" .. ( correct / ( correct + wrong ) ) * 100 .. "%</td></tr></table>" .. testheader( 2 ) .."<table class=\"wikitable sortable jquery-tablesorter\"><th class=\"headerSort\" tabindex=\"0\" role=\"columnheader button\" title=\"Sort ascending\">Class</th><th class=\"headerSort\" tabindex=\"0\" role=\"columnheader button\" title=\"Sort ascending\">Lemma</th><th class=\"headerSort\" tabindex=\"0\" role=\"columnheader button\" title=\"Sort ascending\">Known</th><th class=\"headerSort\" tabindex=\"0\" role=\"columnheader button\" title=\"Sort ascending\">Predicted</th><th class=\"headerSort\" tabindex=\"0\" role=\"columnheader button\" title=\"Sort ascending\">Status</th>" .. table.concat( terms ) .. "</table>"
end

return export