Module:vot-decl

From Wiktionary, the free dictionary
Jump to navigation Jump to search

local export = {}
local m_vot = require("Module:vot")

local function get_stem(word, ending)
	local fragment = mw.ustring.match(word, ending .. "$")
	if fragment then
		return mw.ustring.sub(word, 1, -mw.ustring.len(fragment) - 1), fragment
	end
	error("Unexpected ending (<" .. ending .. "> for <" .. word .. ">) for this inflection type! Wrong type?")
end

local function frontalize(w, vh)
	if vh == "ä" then
		w = mw.ustring.gsub(w, "[aõou]", { a = "ä", ["õ"] = "e", o = "ö", u = "y" })
	end
	return w
end

local function get_e(vh)
	return (vh == "ä") and "e" or "õ"
end

local function get_or(value, fallback)
	return (value and #value > 0) and value or fallback
end

local function join(...)
	local t = {}
	for _, s in ipairs({...}) do
		if type(s) == "table" then
			for _, v in ipairs(s) do
				table.insert(t, v)
			end
		else
			table.insert(t, s)
		end
	end
	return t
end

local function gsub_all(t, s, d)
	if type(t) == "string" then
		return mw.ustring.gsub(t, s, d)
	end
	local r = {}
	for _, v in ipairs(t) do
		table.insert(r, mw.ustring.gsub(v, s, d))
	end
	return r
end

local function append(t, x)
	if not x then return t end
	if type(t) == "string" then
		return t .. x
	end
	local r = {}
	for _, v in ipairs(t) do
		table.insert(r, v .. x)
	end
	return r
end

local function unreduce_full(t)
	local final = mw.ustring.sub(t, -1, -1)
	if final == "a" then
		return t .. "a"
	elseif final == "ä" then
		return t .. "ä"
	elseif final == "õ" then
		return mw.ustring.sub(t, 1, -2) .. "a"
	elseif final == "e" then
		return mw.ustring.sub(t, 1, -2) .. "ä"
	else
		return t
	end
end

local function unreduce(t)
	local final = mw.ustring.sub(t, -1, -1)
	if final == "õ" then
		return mw.ustring.sub(t, 1, -2) .. "a"
	elseif final == "e" then
		return mw.ustring.sub(t, 1, -2) .. "ä"
	else
		return t
	end
end

local function lengthen(t)
	if mw.ustring.find(t, m_vot.vowel .. "$") and not mw.ustring.find(t, m_vot.vowel .. m_vot.vowel .. "$") then
		return t .. mw.ustring.sub(t, -1)
	else
		return t
	end
end

local function make_gen_sg(data, x)
	return (m_vot.guess_reduction(x, data.first_stressed) and lengthen(x) or x)
	--return lengthen(x)
end

local function make_gradation(s, w)
	if s == w then
		return "no gradation"
	else
		return s .. "-" .. w .. " gradation"
	end
end

local function unwind(data, word, grade, final, ending, may_have_gemination)
	local stem = (ending and #ending > 0) and get_stem(word, ending) or word
	local ci, stem = m_vot.extract_ci(stem)
	data.ci = ci

	final = select(3, mw.ustring.find(stem, "(" .. final .. ")$"))
	if not final then final = "õ" end
	stem = get_stem(stem, grade .. final)
	if data.geminate ~= false and may_have_gemination then
		local repl
		stem, repl = mw.ustring.gsub(stem, grade .. "$", "")
		data.geminate = repl > 0
	end
	return stem, final
end

local function geminate(stem)
	local gem = m_vot.guess_gemination(stem)
	return gem and (gem .. mw.ustring.sub(stem, -1)) or stem
end

local function geminate_surface(stem, final)
	local gem = m_vot.guess_gemination(stem)
	return gem and (gem .. final) or stem
end

local function rewind(data, stem, grade, final, ending)
	local result
	if mw.ustring.find(grade, "k+$") then
		result = m_vot.apply_ci(data.ci, stem, grade, final)
	elseif grade == "vv" and (mw.ustring.find(stem, m_vot.vowel .. "[uü]$") or mw.ustring.find(stem, "[oö][oö]$")) then
		result = mw.ustring.sub(stem, 1, -2) .. grade .. (type(final) == "string" and final or "")
	else
		result = stem .. grade .. (type(final) == "string" and final or "")
	end
	return result .. (ending or "")
end

local function rewind_long(data, stem, grade, final, ending)
	return geminate_surface(m_vot.apply_ci(data.ci, stem, grade, final), final) .. (ending or "")
end

local function get_tuli_short_nom_sg(x)
	if not mw.ustring.find(x, "i$") then
		return nil
	end

	local syl = m_vot.split_syllables(x)
	if #syl % 2 == 0 and not mw.ustring.find(syl[#syl - 1], m_vot.vowel .. ".") then
		return nil
	end

	local form = mw.ustring.sub(x, 1, -2)
	if not mw.ustring.find(form, "[nrsz]$") then
		return nil
	end
	
	return form
end

local function do_geminate(data, t)
	if type(t) == "table" then
		local r = {}
		for _, v in ipairs(t) do
			table.insert(r, do_geminate(data, t))
		end
		return r
	end

	if data.geminate == false then return t end

	if not mw.ustring.find(t, "(" .. m_vot.vowel .. ")%1$") then
		return t
	end

	local g = m_vot.guess_gemination(mw.ustring.sub(t, 1, -2))
	if not g then return t end
	return g .. mw.ustring.sub(t, -2)
end

local function cleanup_form(data, form)
	local form = mw.ustring.gsub(form, m_vot.virtual_syllable_break, "")
	form = m_vot.apply_reduction(form, data.infl_root, nil, data.first_stressed)
	return mw.ustring.gsub(form, m_vot.ungeminate, "")
end

local function process(data)
	local vh = data.vh

	-- nominative singular stem
	local ns = data.stem_ns
	-- genitive singular stem
	local gs = data.stem_gs
	-- partitive singular stem
	local ps = data.stem_ps
	-- illative singular stem
	local is = data.stem_is or ps
	-- oblique singular stem
	local os = data.stem_os or gs
	-- oblique plural stem
	local op = data.stem_op
	-- genitive plural stem
	local gp = data.stem_gp or op
	-- partitive plural stem
	local pp = data.stem_pp or gp
	
	local is_short = data.stem_is_short or is

	local ill_sg = data.illative_sg or "long_short"

	local result = { }

	if not data.no_singular then
		result["nom_sg"] = ns
		result["gen_sg"] = gs
		result["par_sg"] = append(ps, vh)

		local short_ill = lengthen(do_geminate(data, is_short))
		local long_ill = append(lengthen(do_geminate(data, is)), frontalize("sõ", vh))

		if ill_sg == "short" then
			result["ill_sg"] = short_ill
		elseif ill_sg == "long" then
			result["ill_sg"] = long_ill
		elseif ill_sg == "short_long" then
			result["ill_sg"] = join(short_ill, long_ill)
		elseif ill_sg == "long_short" then
			result["ill_sg"] = join(long_ill, short_ill)
		else
			error("unsupported ill_sg")
		end

		result["ine_sg"] = append(os, "z")
		result["ela_sg"] = append(os, frontalize("ssa", vh))
		result["all_sg"] = append(os, frontalize("lõõ", vh))
		result["ade_sg"] = append(os, frontalize("lla", vh))
		result["abl_sg"] = append(os, frontalize("lta", vh))
		result["tra_sg"] = append(os, "ssi")
	end

	if not data.no_plural then
		result["nom_pl"] = append(os, "d")
		result["gen_pl"] = join(append(gp, frontalize("õ", vh)), not data.no_short_pl and op or nil)
		result["par_pl"] = join(append(pp, vh), not data.no_short_pl and op or nil)
		result["ill_pl"] = append(op, frontalize("sõ", vh))
		result["ine_pl"] = append(op, "z")
		result["ela_pl"] = append(op, frontalize("ssa", vh))
		result["all_pl"] = append(op, frontalize("lõõ", vh))
		result["ade_pl"] = append(op, frontalize("lla", vh))
		result["abl_pl"] = append(op, frontalize("lta", vh))
		result["tra_pl"] = append(op, "ssi")
	end

	-- cleanup virtual syllable breaks and do reductions
	for k, v in pairs(result) do
		if type(v) == "string" then
			result[k] = cleanup_form(data, v)
		elseif type(v) == "table" then
			for i, f in ipairs(v) do
				if type(f) == "table" and f.form then
					f.form = cleanup_form(data, f.form)
				elseif type(f) == "string" then
					v[i] = cleanup_form(data, f)
				end
			end
		end
	end
	return result
end

-- inflection classes begin
local inflections = {}

inflections["maa"] = function (data)
	data.typeno = "I"
	local word = data.title
	if data.no_singular then -- plural title -> singular
		word = get_stem(word, "d")
	end
	local stem = word
	local final = mw.ustring.sub(word, -1, -1)
	local stem_pl = (mw.ustring.find(word, m_vot.vowel .. m_vot.vowel .. "$") and mw.ustring.gsub(word, m_vot.vowel .. "$", "") or word) .. "i"

	data.stem_ns = stem
	data.stem_gs = stem
	data.stem_ps = stem .. "t"
	
	if mw.ustring.find(stem, final .. final .. "$") then
		local stem_ill = mw.ustring.sub(stem, 1, -2)
		data.stem_is = stem_ill .. "h" .. final
		data.stem_is_short = stem_ill .. "hh" .. final
	else
		data.stem_is = stem .. "h" .. final
	end

	data.stem_op = stem_pl
	data.stem_gp = stem_pl .. "j"
	data.stem_pp = stem_pl .. "t"

	data.illative_sg = "short_long"

	data.infl_root = stem
	data.no_short_pl = true
	return process(data)
end

inflections["võrkko"] = function (data)
	data.typeno = "II"
	local word = data.title
	local strong = data.args[1] or error("must specify strong grade")
	local weak = data.args[2] or error("must specify weak grade")
	local vh = data.vh
	local stem, final
	if data.no_singular then -- plural title -> singular
		stem, final = unwind(data, word, weak, m_vot.vowel, "d")
	else
		stem, final = unwind(data, word, strong, m_vot.vowel)
	end

	data.stem_ns = rewind(data, stem, strong, final)
	
	if mw.ustring.find(stem, final .. final .. "$") and weak == "" and strong ~= "" then
		data.stem_gs = stem
	else
		data.stem_gs = rewind(data, stem, weak, final)
	end
	
	data.stem_ps = rewind_long(data, stem, strong, final)
	data.stem_is = rewind_long(data, stem, strong, final)
	data.stem_os = data.stem_gs

	local pl = m_vot.apply_ci(data.ci, stem, strong, final)
	data.stem_op = pl .. "i"
	data.stem_gp = pl .. "j"
	data.stem_pp = pl .. "it"

	data.illative_sg = "long_short"

	data.grade = make_gradation(strong, weak)
	data.infl_root = stem
	return process(data)
end

inflections["jalkõ"] = function (data)
	data.typeno = "III"
	local word = data.title
	local strong = data.args[1] or error("must specify strong grade")
	local weak = data.args[2] or error("must specify weak grade")
	local vh = data.vh
	local stem, final
	if data.no_singular then -- plural title -> singular
		stem, final = unwind(data, word, weak, m_vot.vowel, "d")
	else
		stem, final = unwind(data, word, strong, m_vot.vowel)
	end
	final = unreduce(final)

	data.stem_ns = word
	data.stem_gs = make_gen_sg(data, rewind(data, stem, weak, final))
	data.stem_ps = rewind_long(data, stem, strong, final)
	data.stem_is = rewind_long(data, stem, strong, final)
	data.stem_os = rewind(data, stem, weak, final)

	local pl = rewind(data, stem, strong, frontalize("o", vh_plural))
	data.stem_op = pl .. "i"
	data.stem_gp = pl .. "j"
	data.stem_pp = pl .. "it"

	data.illative_sg = "long_short"

	data.grade = make_gradation(strong, weak)
	data.infl_root = stem
	return process(data)
end

inflections["poikõ"] = function (data)
	data.typeno = "V"
	local word = data.title
	local strong = data.args[1] or error("must specify strong grade")
	local weak = data.args[2] or error("must specify weak grade")
	local vh = data.vh
	local stem, final
	if data.no_singular then -- plural title -> singular
		stem, final = unwind(data, word, weak, m_vot.vowel, "d")
	else
		stem, final = unwind(data, word, strong, m_vot.vowel)
	end
	final = unreduce(final)
	local pl = rewind(data, stem, strong, vh == "ä") .. "i"

	data.stem_ns = word
	data.stem_gs = make_gen_sg(data, rewind(data, stem, weak, final))
	data.stem_ps = rewind_long(data, stem, strong, final)
	data.stem_is = rewind_long(data, stem, strong, final)
	data.stem_os = rewind(data, stem, weak, final)

	data.stem_op = pl .. m_vot.ungeminate .. "i"
	data.stem_gp = pl .. "j"
	data.stem_pp = data.stem_op .. "t"

	data.illative_sg = "long_short"

	data.grade = make_gradation(strong, weak)
	data.infl_root = stem
	return process(data)
end

inflections["valka"] = function (data)
	data.typeno = "VI"
	local word = data.title
	local vh = data.vh
	if data.no_singular then -- plural title -> singular
		stem = get_stem(word, "d")
	end
	local stem = lengthen(word)
	local pl_stem = word
	local pl_stem_g = pl_stem
	if mw.ustring.find(word, "i[äa]$") then
		pl_stem = mw.ustring.gsub(word, "iä$", get_e(vh))
		pl_stem_g = pl_stem .. "i"
	elseif mw.ustring.find(word, "õa$") then
		pl_stem = mw.ustring.gsub(word, "õa$", get_e(vh))
		pl_stem_g = pl_stem .. "i"
	end

	data.stem_ns = stem
	data.stem_gs = stem
	data.stem_ps = stem .. "t"
	data.stem_is = stem
	data.stem_os = stem

	data.stem_op = pl_stem .. "i"
	data.stem_gp = unreduce_full(pl_stem_g) .. "j"
	data.stem_pp = pl_stem .. "it"

	data.illative_sg = "long_short"

	data.grade = make_gradation(strong, weak)
	data.infl_root = mw.ustring.gsub(word, m_vot.vowel .. "+$", "")
	return process(data)
end

inflections["nimi"] = function (data)
	data.typeno = "VII"
	local word = data.title
	local strong = data.args[1] or error("must specify strong grade")
	local weak = data.args[2] or error("must specify weak grade")
	local vh = data.vh
	local stem, final
	if data.no_singular then -- plural title -> singular
		stem, final = unwind(data, word, weak, m_vot.vowel, "d")
	else
		stem, final = unwind(data, word, strong, m_vot.vowel)
	end
	local e = get_e(vh)
	local pl = rewind(data, stem, strong, "i")

	data.stem_ns = word
	data.stem_gs = rewind(data, stem, weak, e)
	data.stem_ps = rewind_long(data, stem, strong, "i")
	data.stem_is = rewind_long(data, stem, strong, e)
	data.stem_os = rewind(data, stem, weak, e)

	data.stem_op = pl .. m_vot.ungeminate .. "i"
	data.stem_gp = pl .. "j"
	data.stem_pp = data.stem_op .. "t"

	data.illative_sg = "long_short"

	data.grade = make_gradation(strong, weak)
	data.infl_root = stem
	return process(data)
end

inflections["päive"] = function (data)
	data.typeno = "VIII"
	local word = data.title
	local strong = data.args[1] or error("must specify strong grade")
	local weak = data.args[2] or error("must specify weak grade")
	local vh = data.vh
	local stem, final
	if data.no_singular then -- plural title -> singular
		stem, final = unwind(data, word, weak, m_vot.vowel, "d")
	else
		stem, final = unwind(data, word, strong, m_vot.vowel)
	end
	final = get_or(data.args[3], unreduce(final) or final)
	local pl = rewind(data, stem, strong, vh == "ä") .. "i"

	data.stem_ns = word
	data.stem_gs = make_gen_sg(data, rewind(data, stem, weak, final))
	data.stem_ps = rewind_long(data, stem, strong, final)
	data.stem_is = rewind_long(data, stem, strong, final)
	data.stem_os = rewind(data, stem, weak, final)

	data.stem_op = pl .. m_vot.ungeminate .. "i"
	data.stem_gp = pl .. "j"
	data.stem_pp = data.stem_op .. "t"

	data.illative_sg = "long_short"

	data.grade = make_gradation(strong, weak)
	data.infl_root = stem
	return process(data)
end

inflections["parõpi"] = function (data)
	data.typeno = "VIII"
	local word = data.title
	local vh = data.vh
	local stem
	if data.no_singular then -- plural title -> singular
		stem = get_stem(word, "[" .. vh .. get_e(vh) .. "]d")
	else
		stem = get_stem(word, "i")
	end
	local pl = stem .. "i"

	data.stem_ns = word
	data.stem_gs = stem .. vh .. vh
	data.stem_ps = stem .. vh
	data.stem_is = stem .. vh
	data.stem_os = stem .. vh

	data.stem_op = pl .. m_vot.ungeminate .. "i"
	data.stem_gp = pl .. "j"
	data.stem_pp = data.stem_op .. "t"

	data.illative_sg = "long_short"

	data.grade = make_gradation(strong, weak)
	data.infl_root = stem
	return process(data)
end

local LENGTHEN = mw.ustring.char(0xEEF0)

local tuli_weak = {
	["lt"] = "ll", ["rt"] = "rr", ["nt"] = "nn",
	["l"] = "l", ["m"] = "m", ["n"] = "n", ["r"] = "r",
	["s"] = "z", ["hs"] = "hz", ["ht"] = "h",
	["t"] = "d", ["h"] = "h"
}

local tuli_partitive = {
	["lt"] = "ltt", ["rt"] = "rtt", ["m"] = "nt", ["s"] = "ss", ["hs"] = "ss", ["nt"] = "ntt"
}

local tuli_plural = {
	["lt"] = "ls", ["rt"] = "rs", ["t"] = "s", ["ht"] = "hz", ["nt"] = LENGTHEN .. "s"
}

local function clean_lengthen(x)
	return mw.ustring.gsub(x, "(" .. m_vot.vowel .. "?)" .. LENGTHEN, "%1%1")
end

inflections["tuli"] = function (data)
	data.typeno = "X"
	local word = data.title
	local vh = data.vh
	local strong = data.args[1] or error("must specify final consonant")
	local weak = tuli_weak[strong] or error("unrecognized final consonant")
	local partitive = tuli_partitive[strong] or (strong .. "t")
	local plural = tuli_plural[strong] or strong
	local e = get_e(vh)
	local stem
	local has_i = true
	if data.no_singular then -- plural title -> singular
		stem = unwind(data, word, weak, e, "d")
	elseif not mw.ustring.find(word, "i$") then
		has_i = false
		stem = get_stem(word, mw.ustring.gsub(plural, LENGTHEN, "[" .. m_vot.vowels .. "n]"))
	else
		stem = unwind(data, word, mw.ustring.gsub(plural, LENGTHEN, "[" .. m_vot.vowels .. "n]"), "i")
	end
	
	local stemw = nil
	if weak == "d" then
		if mw.ustring.find(stem, m_vot.vowel .. "i$") then
			weak = "jj"
		elseif mw.ustring.find(stem, m_vot.vowel .. "[oöuü]$") then
			weak = "vv"
		else
			weak = ""
		end
	end

	if has_i then
		local short = get_tuli_short_nom_sg(word)
		data.stem_ns = (short and { word, short } or word)
	else
		data.stem_ns = { word, word .. "i" }
	end

	local pl = clean_lengthen(stem .. plural .. "i")
	
	data.stem_gs = join(stemw and rewind(data, stemw, weak, e) or {}, rewind(data, stem, weak, e))
	data.stem_ps = stem .. partitive
	data.stem_is = rewind_long(data, stem, strong, e)
	data.stem_os = join(stemw and rewind(data, stemw, weak, e) or {}, rewind(data, stem, weak, e))

	data.stem_op = pl .. m_vot.ungeminate .. "i"
	data.stem_gp = pl .. "j"
	data.stem_pp = data.stem_op .. "t"

	if mw.ustring.match(partitive, "^t+$") then
		data.stem_ps = mw.ustring.gsub(data.stem_ps, "(" .. m_vot.consonant .. "t)t$", "%1")
	end

	data.illative_sg = "short_long"

	data.grade = make_gradation(strong, weak)
	data.infl_root = stem
	return process(data)
end

inflections["jänez"] = function (data)
	data.typeno = "XI"
	local word = data.title
	local vh = data.vh
	local e = get_e(vh)
	local stem
	if data.no_singular then -- plural title -> singular
		stem = get_stem(word, "s" .. e .. "d")
	else
		stem = get_stem(word, "z")
	end
	local pl = stem .. "ssi"

	data.stem_ns = word
	data.stem_gs = stem .. "s" .. e
	data.stem_ps = stem .. "ss"
	data.stem_is = stem .. "ss" .. e
	data.stem_os = data.stem_gs

	data.stem_op = pl .. m_vot.ungeminate .. "i"
	data.stem_gp = pl .. "j"
	data.stem_pp = data.stem_op .. "t"

	data.illative_sg = "long"

	data.grade = make_gradation(strong, weak)
	data.infl_root = stem
	return process(data)
end

inflections["sinin"] = function (data)
	data.typeno = "XII"
	local word = data.title
	local vh = data.vh
	local e = get_e(vh)
	local stem
	if data.no_singular then -- plural title -> singular
		stem = get_stem(word, "z" .. e .. "d")
	else
		stem = get_stem(word, "n")
	end
	local pl = stem .. "zi"

	data.stem_ns = word
	data.stem_gs = stem .. "z" .. e
	data.stem_ps = stem .. "ss"
	data.stem_is = geminate(stem .. "z" .. e)
	data.stem_os = data.stem_gs

	data.stem_op = pl
	data.stem_gp = pl .. "j"
	data.stem_pp = data.stem_op .. "t"

	data.illative_sg = "short_long"

	data.grade = make_gradation(strong, weak)
	data.infl_root = stem
	return process(data)
end

inflections["seeme"] = function (data)
	data.typeno = "XIII"
	local word = data.title
	local weak = data.args[1] or error("must specify weak grade")
	local strong = data.args[2] or error("must specify strong grade")
	local vh = data.vh
	local e = get_e(vh)
	local v1, c1, v2
	if data.no_singular then -- plural title -> singular
		word = get_stem(word, "d")
		v2 = data.args[5] or mw.ustring.sub(word, -1, -1)
		c1 = data.args[4] or mw.ustring.sub(word, -2, -2)
		v1 = data.args[3] or mw.ustring.sub(word, -3, -3)
		if not mw.ustring.match(v1, m_vot.vowel) then error("unexpected sound for type 11") end
		if not mw.ustring.match(c1, m_vot.consonant) then error("unexpected sound for type 11") end
		if not mw.ustring.match(v2, m_vot.vowel) then error("unexpected sound for type 11") end
		word = get_stem(mw.ustring.sub(word, 1, -4), strong) .. weak .. v1
	else
		v1 = data.args[3] or error("must specify penultimate stem vowel")
		c1 = data.args[4] or error("must specify final stem consonant")
		v2 = data.args[5] or error("must specify final stem vowel")
	end
	local stem
	if mw.ustring.find(word, c1 .. "$") then
		stem = mw.ustring.sub(word, 1, -2)
	elseif mw.ustring.find(word, "[" .. v1 .. e .. "][" .. v1 .. "]$") then
		stem = mw.ustring.sub(word, 1, -2)
	else
		stem = word
	end
	local stem = unwind(data, stem, weak, "[" .. v1 .. "eõ]", nil, true)
	local pl = rewind(data, stem, strong, v1) .. c1 .. "i"
	data.ci = true

	data.stem_ns = word
	data.stem_gs = rewind(data, stem, strong, v1) .. c1 .. v2
	data.stem_ps = rewind(data, stem, weak, v1) .. (mw.ustring.match(c1, "[mn]") and "tt" or c1 .. "t")
	data.stem_is = geminate(rewind(data, stem, strong, v1).. c1 .. v2)
	data.stem_os = data.stem_gs

	data.stem_op = pl .. m_vot.ungeminate .. "i"
	data.stem_gp = pl .. "j"
	data.stem_pp = data.stem_op .. "t"

	data.illative_sg = "short_long"

	data.grade = make_gradation(strong, weak)
	data.infl_root = stem
	return process(data)
end

inflections["tšümme"] = function (data)
	data.typeno = "XIII"
	local word = data.title
	local weak = data.args[1] or error("must specify weak grade")
	local strong = data.args[2] or error("must specify strong grade")
	local vh = data.vh
	local v1, c1, v2
	if data.no_singular then -- plural title -> singular
		word = get_stem(word, "d")
		v2 = data.args[5] or mw.ustring.sub(word, -1, -1)
		c1 = data.args[4] or mw.ustring.sub(word, -2, -2)
		v1 = data.args[3] or mw.ustring.sub(word, -3, -3)
		if not mw.ustring.match(v1, m_vot.vowel) then error("unexpected sound for type 11") end
		if not mw.ustring.match(c1, m_vot.consonant) then error("unexpected sound for type 11") end
		if not mw.ustring.match(v2, m_vot.vowel) then error("unexpected sound for type 11") end
		word = get_stem(mw.ustring.sub(word, 1, -4), strong) .. weak .. v1
	else
		v1 = data.args[3] or error("must specify penultimate stem vowel")
		c1 = data.args[4] or error("must specify final stem consonant")
		v2 = data.args[5] or error("must specify final stem vowel")
	end
	local stem = get_stem(word, weak .. "[" .. v1 .. "eõ][" .. v1 .. c1 .. "]?")
	local pl = stem .. strong .. v1 .. c1 .. "i"
	data.ci = true

	data.stem_ns = word
	data.stem_gs = stem .. strong .. v1 .. c1 .. v2
	data.stem_ps = rewind(data, stem, weak, v1) .. c1 .. vh
	data.stem_is = geminate(stem .. strong .. v1 .. c1 .. v2)
	data.stem_os = data.stem_gs

	data.stem_op = pl .. m_vot.ungeminate .. "i"
	data.stem_gp = pl .. "j"
	data.stem_pp = data.stem_op .. "t"

	data.illative_sg = "short_long"

	data.grade = make_gradation(strong, weak)
	data.infl_root = stem
	return process(data)
end

inflections["seittse"] = function (data)
	data.typeno = "XIII"
	local word = data.title
	local weak = data.args[1] or error("must specify weak grade")
	local strong = data.args[2] or error("must specify strong grade")
	local vh = data.vh
	local v1, c1, v2
	if data.no_singular then -- plural title -> singular
		word = get_stem(word, "d")
		v2 = data.args[5] or mw.ustring.sub(word, -1, -1)
		c1 = data.args[4] or mw.ustring.sub(word, -2, -2)
		v1 = data.args[3] or mw.ustring.sub(word, -3, -3)
		if not mw.ustring.match(v1, m_vot.vowel) then error("unexpected sound for type 11") end
		if not mw.ustring.match(c1, m_vot.consonant) then error("unexpected sound for type 11") end
		if not mw.ustring.match(v2, m_vot.vowel) then error("unexpected sound for type 11") end
		word = get_stem(mw.ustring.sub(word, 1, -4), strong) .. weak .. v1
	else
		v1 = data.args[3] or error("must specify penultimate stem vowel")
		c1 = data.args[4] or error("must specify final stem vowel")
		v2 = data.args[5] or error("must specify final stem consonant")
	end
	local stem = get_stem(word, weak .. m_vot.vowel)
	local pl = stem .. strong .. v1 .. c1 .. "i"
	data.ci = true

	data.stem_ns = lengthen(word)
	data.stem_gs = stem .. strong .. v1 .. c1 .. v2
	data.stem_ps = geminate(rewind(data, stem, strong, v1) .. c1 .. vh)
	data.stem_is = geminate(stem .. strong .. v1 .. c1 .. v2)
	data.stem_os = data.stem_gs

	data.stem_op = stem .. strong .. v1 .. c1 .. "i"
	data.stem_gp = pl .. "j"
	data.stem_pp = pl .. "t"

	data.illative_sg = "short_long"

	data.grade = make_gradation(strong, weak)
	data.infl_root = stem
	return process(data)
end

inflections["kõlmõz"] = function (data)
	data.typeno = "XIII"
	local word = data.title
	local vh = data.vh
	if data.no_singular then -- plural title -> singular
		error("Plural-only not supported for type kolmaz")
	end
	local stem = get_stem(word, "z")
	local v1 = mw.ustring.sub(stem, -1)
	local c1 = "m"
	local v2 = data.args[1] or error("must specify final stem vowel")
	local pl = stem .. "tt" .. v1 .. c1 .. "i"

	data.stem_ns = word
	data.stem_gs = stem .. "tt" .. v1 .. c1 .. v2
	data.stem_ps = stem .. "tt"
	data.stem_is = geminate(stem .. "tt" .. v1 .. c1 .. v2)
	data.stem_os = stem .. "tt" .. v1 .. c1 .. v2

	data.stem_op = pl .. m_vot.ungeminate .. "i"
	data.stem_gp = pl .. "j"
	data.stem_pp = data.stem_op .. "t"

	data.illative_sg = "short_long"

	data.infl_root = stem
	return process(data)
end

inflections["ammõz"] = function (data)
	data.typeno = "XIV"
	local word = data.title
	local weak = data.args[1] or error("must specify weak grade")
	local strong = data.args[2] or error("must specify strong grade")
	local vh = data.vh
	local final
	if data.no_singular then -- plural title -> singular
		word = get_stem(word, "d")
		final = mw.ustring.sub(word, -1, -1)
		word = get_stem(mw.ustring.sub(word, 1, -2), strong) .. weak .. final .. "z"
	else
		final = mw.ustring.sub(word, -2, -2)
	end
	local stem = get_stem(word, weak .. final .. "z")
	final = get_or(data.args[3], unreduce(final))
	local h = false
	
	if final == "e" and mw.ustring.find(stem, "e$") then
		stem = stem .. "h"
		h = true
	end
	local pl = h and stem .. strong .. "i" or geminate(stem .. strong .. final)

	data.stem_ns = mw.ustring.sub(word, 1, -3) .. final .. "z"
	data.stem_gs = h and stem .. strong .. final or geminate(stem .. strong .. final) .. final
	data.stem_ps = mw.ustring.sub(word, 1, -3) .. final .. "ss"
	data.stem_is = h and stem .. strong .. final or geminate(stem .. strong .. final)
	data.stem_os = h and data.stem_is or lengthen(data.stem_is)

	data.stem_op = pl .. "i"
	data.stem_gp = unreduce_full(pl) .. "j"
	data.stem_pp = pl .. "it"

	data.illative_sg = "long_short"

	data.grade = make_gradation(strong, weak)
	data.infl_root = stem
	return process(data)
end

inflections["terve"] = function (data)
	data.typeno = "XIV"
	local word = data.title
	local weak = data.args[1] or error("must specify weak grade")
	local strong = data.args[2] or error("must specify strong grade")
	local vh = data.vh
	local e = get_e(vh)
	local v1, c1, v2
	local stem
	if data.no_singular then -- plural title -> singular
		stem = get_stem(word, strong .. e .. "d")
	else
		stem = get_stem(word, weak .. e)
	end

	data.stem_ns = word
	data.stem_gs = geminate(stem .. strong .. e) .. e
	data.stem_ps = stem .. weak .. e .. "tt"
	data.stem_is = geminate(stem .. strong .. e)
	data.stem_os = data.stem_gs

	data.stem_op = geminate(stem .. strong .. e) .. "i"
	data.stem_gp = data.stem_op .. "j"
	data.stem_pp = data.stem_op .. "t"

	data.illative_sg = "long_short"

	data.grade = make_gradation(strong, weak)
	data.infl_root = stem
	return process(data)
end

inflections["lühüd"] = function (data)
	data.typeno = "XV"
	local word = data.title
	local vh = data.vh
	local stem
	if data.no_singular then -- plural title -> singular
		stem = get_stem(word, "d")
	elseif mw.ustring.find(word, "[dz]$") then
		stem = mw.ustring.sub(word, 1, -2)
	else
		stem = word
	end
	local e = get_e(vh)
	local final = mw.ustring.sub(stem, -1, -1)
	stem = get_stem(stem, m_vot.vowel)
	local final2 = data.args[1] or error("Must specify final stem vowel")

	data.stem_ns = word
	data.stem_gs = make_gen_sg(data, geminate(stem .. final2))
	data.stem_ps = stem .. final .. "tt"
	data.stem_is = geminate(stem .. final2)
	data.stem_os = data.stem_gs

	data.stem_op = geminate(stem .. final) .. "i"
	data.stem_gp = data.stem_op .. "j"
	data.stem_pp = data.stem_op .. "t"

	data.illative_sg = "long"

	data.grade = make_gradation(strong, weak)
	data.infl_root = stem
	return process(data)
end

inflections["kõrkuz"] = function (data)
	data.typeno = "XV"
	local word = data.title
	local vh = data.vh
	local stem
	if data.no_singular then -- plural title -> singular
		stem = get_stem(word, "d")
	elseif mw.ustring.find(word, "[dz]$") then
		stem = mw.ustring.sub(word, 1, -2)
	else
		stem = word
	end
	local e = get_e(vh)
	local final = mw.ustring.sub(stem, -1, -1)
	stem = get_stem(stem, "[uü]")

	data.stem_ns = word
	data.stem_gs = geminate(stem .. final)
	data.stem_ps = stem .. final .. "tt"
	data.stem_is = geminate(stem .. final)
	data.stem_os = data.stem_gs

	data.stem_op = geminate(stem .. final) .. "i"
	data.stem_gp = data.stem_op .. "j"
	data.stem_pp = data.stem_op .. "t"

	data.illative_sg = "long"

	data.grade = make_gradation(strong, weak)
	data.infl_root = stem
	return process(data)
end

-- inflection classes end

local infl_table = [=[{| class="inflection-table vsSwitcher" data-toggle-category="declension" style="border:1px solid #CCCCFF"
|-
!colspan=3 class="vsToggleElement" style="background:rgb(80%,80%,100%);text-align:left;"|Declension of {{{title}}} (<span style="font-size:90%">{{{type}}}</span>)
|- class="vsHide" style="background:rgb(80%,80%,100%);vertical-align:top;"
! style="width:11em;" |
! style="width:12em;" | singular
! style="width:12em;" | plural
|- style="background:rgb(95%,95%,100%);vertical-align:top;" |
! style="background:rgb(80%,80%,100%);width:11em" | nominative
|style="width:12em" |  {{{nom_sg}}}
|style="width:12em" |  {{{nom_pl}}}
|- style="background:rgb(95%,95%,100%);vertical-align:top;" |
! style="background:rgb(80%,80%,100%);" | genitive
|{{{gen_sg}}}
|{{{gen_pl}}}
|- style="background:rgb(95%,95%,100%);vertical-align:top;" |
! style="background:rgb(80%,80%,100%);" | partitive
|{{{par_sg}}}
|{{{par_pl}}}
|- style="background:rgb(95%,95%,100%);vertical-align:top;" |
! style="background:rgb(80%,80%,100%);" | illative
|{{{ill_sg}}}
|{{{ill_pl}}}
|- class="vsHide" style="background:rgb(95%,95%,100%);vertical-align:top;" |
! style="background:rgb(80%,80%,100%);" | inessive
|{{{ine_sg}}}
|{{{ine_pl}}}
|- class="vsHide" style="background:rgb(95%,95%,100%);vertical-align:top;" |
! style="background:rgb(80%,80%,100%);" | elative
|{{{ela_sg}}}
|{{{ela_pl}}}
|- class="vsHide" style="background:rgb(95%,95%,100%);vertical-align:top;" |
! style="background:rgb(80%,80%,100%);" | allative
|{{{all_sg}}}
|{{{all_pl}}}
|- class="vsHide" style="background:rgb(95%,95%,100%);vertical-align:top;" |
! style="background:rgb(80%,80%,100%);" | adessive
|{{{ade_sg}}}
|{{{ade_pl}}}
|- class="vsHide" style="background:rgb(95%,95%,100%);vertical-align:top;" |
! style="background:rgb(80%,80%,100%);" | ablative
|{{{abl_sg}}}
|{{{abl_pl}}}
|- class="vsHide" style="background:rgb(95%,95%,100%);vertical-align:top;" |
! style="background:rgb(80%,80%,100%);" | translative
|{{{tra_sg}}}
|{{{tra_pl}}}
|- class="vsHide" style="background:rgb(95%,95%,100%);vertical-align:top;" |
| colspan="3" style="background:rgb(80%,80%,100%);font-size:smaller" | <sup>*)</sup> the '''accusative''' corresponds with either the '''genitive''' (<span class="gender"><abbr title="singular number">sg</abbr></span>) or '''nominative''' (<span class="gender"><abbr title="plural number">pl</abbr></span>)<br /> <sup>**)</sup> the '''terminative''' is formed by adding the suffix {{m|vot|-ssaa}} to {{{has_short_ill}}}the '''genitive'''.<br /> <sup>***)</sup> the '''comitative''' is formed by adding the suffix {{m|vot|-ka}} to the '''genitive'''.
|}]=]

local function link(text)
	return require("Module:links").full_link{ term = text, lang = m_vot.lang }
end

local function mention(text)
	return require("Module:links").full_link({ term = text, lang = m_vot.lang }, "term")
end

function export.show(frame)
	local infl_type = frame.args[1] or error("inflection class not specified")
	local infl = inflections[infl_type] or error("unsupported inflection type")
	local args = frame:getParent().args
	local title = args["title"] or mw.title.getCurrentTitle().text
	local categories = {}

	local geminate, vh, headword
	if args["g"] == "1" then
		geminate = true
	elseif args["g"] == "0" or args["g"] == "-" then
		geminate = false
	else
		headword = args["g"]
	end

	local prefix = args["prefix"]

	if prefix then
		local prefix_len = mw.ustring.len(prefix)
		local split
		split, title = mw.ustring.sub(title, 1, prefix_len), mw.ustring.sub(title, prefix_len + 1)
		if #title == 0 or prefix ~= split then
			error("Prefix is too long or does not match title")
		end
	end
	
	if args["v"] then
		vh = args["v"]
		if vh ~= "a" and vh ~= "ä" then
			error("Invalid vowel harmony specification")
		end
	elseif not vh then
		vh = m_vot.guess_vowel_harmony(headword or title)
	end

	local first_stressed = args["s"] and tonumber(args["s"]) or 1
	local data = { title = title, headword = headword, geminate = geminate, prefix = prefix, vh = vh, args = args, first_stressed = first_stressed }
	local word_prefix = prefix or ""

	if args["n"] then
		if args["n"] == "s" or args["n"] == "sg" then
			data.no_plural = true
		elseif args["n"] == "p" or args["n"] == "pl" then
			data.no_singular = true
		end
	end

	local forms = infl(data)

	local function repl(form)
		if form == "title" then
			return "'''" .. title .. "'''"
		elseif form == "type" then
			if data.irregular then
				return "irregular"
			end
			local s = "type " .. data.typeno .. "/" .. mention(infl_type)
			if data.grade then
				s = s .. ", " .. data.grade
			else
				s = s .. ", " .. make_gradation(nil, nil)
			end
			if data.geminate then
				s = s .. ", gemination"
			end
			return s
		elseif form == "has_short_ill" then
			if data.illative_sg == "long" or data.no_singular then
				return ""
			elseif data.illative_sg == "short" then
				return "the '''illative''' (<span class=\"gender\"><abbr title=\"singular number\">sg</abbr></span>) or "
			else
				return "the short '''illative''' (<span class=\"gender\"><abbr title=\"singular number\">sg</abbr></span>) or "
			end
		else
			local value = forms[form]
			if not value then
				return "&mdash;"
			elseif type(value) == "table" then
				local result = {}
				for _, f in ipairs(value) do
					table.insert(result, link(word_prefix .. f))
				end
				return table.concat(result, ", ")
			else
				return link(word_prefix .. value)
			end
		end
	end

	if mw.title.getCurrentTitle().namespace == 0 then
		table.insert(categories, "Votic " .. infl_type .. "-type nominals")
	end

	local result = mw.ustring.gsub(infl_table, "{{{([a-z0-9_:]+)}}}", repl)
	result = mw.ustring.gsub(result, "{{m|vot|([^}]-)}}", mention)
	return result .. require("Module:utilities").format_categories(categories, m_vot.lang)
end

return export