မဝ်ဂျူ:vot-decl

နူ ဝိက်ရှေန်နရဳ

Documentation for this module may be created at မဝ်ဂျူ:vot-decl/doc

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 reduce(t)
	if type(t) == "string" then
		return select(2, m_vot.guess_reduction(t))
	end
	local r = {}
	for _, v in ipairs(t) do
		table.insert(r, reduce(v))
	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 never_reduce(x)
	return (m_vot.guess_reduction(x) and lengthen(x) or x)
end

local function handle_reduction(t, a)
    return reduce(t .. unreduce(a))
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 .. "[uy]$") 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(type, data, form)
	local form = mw.ustring.gsub(form, m_vot.virtual_syllable_break, "")
	if type ~= "ill_sg" then
		form = m_vot.apply_reduction(form, data.infl_root)
	end
	return form
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

	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 = m_vot.apply_reduction(lengthen(do_geminate(data, is_short)), data.infl_root)
        local long_ill = append(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)
        else
            --result["ill_sg"] = join(long_ill, short_ill)
            result["ill_sg"] = join(short_ill, long_ill)
        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(vh == "a" and append(gp, "e") or {}, 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"] = join(vh == "a" and append(gp, "e") or {}, append(gp, frontalize("õ", vh)), 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(k, 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(k, data, f.form)
				elseif type(f) == "string" then
					v[i] = cleanup_form(k, 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.gsub(word, m_vot.vowel .. "$", "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 = "long_short"

	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)
    data.stem_gs = 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 = data.stem_gs

	local pl = 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 = never_reduce(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)

    data.stem_ns = word
    data.stem_gs = never_reduce(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 = rewind(data, stem, strong, vh == "ä") .. "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["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ä$") then
		pl_stem = mw.ustring.gsub(word, "iä$", 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 = 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["autši"] = 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)

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

    data.stem_op = word
    data.stem_gp = word .. "j"
    data.stem_pp = word .. "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)

    data.stem_ns = word
    data.stem_gs = never_reduce(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 = rewind(data, stem, strong, vh == "ä") .. "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["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 .. "d")
    else
		stem = get_stem(word, "i")
	end

    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 = stem .. "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

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"
}

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
	if data.no_singular then -- plural title -> singular
		stem = unwind(data, word, weak, e, "d")
    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

	local short = get_tuli_short_nom_sg(word)
    data.stem_ns = short and { word, short } or word
    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 = clean_lengthen(stem .. plural .. "i")
    data.stem_gp = data.stem_op .. "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

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

    data.stem_op = stem .. "si"
    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["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

    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 = stem .. "zi"
    data.stem_gp = data.stem_op .. "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)
	data.ci = true

    data.stem_ns = word
    data.stem_gs = 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(stem .. strong .. v1 .. c1 .. v2)
    data.stem_os = data.stem_gs

    data.stem_op = stem .. strong .. v1 .. c1 .. "i"
    data.stem_gp = data.stem_op .. "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 .. "]?")
	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 = stem .. strong .. v1 .. c1 .. "i"
    data.stem_gp = data.stem_op .. "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)
	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 = data.stem_op .. "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["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 = vh

    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 = stem .. "tt" .. v1 .. c1 .. "i"
    data.stem_gp = data.stem_op .. "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

    data.stem_ns = word
    data.stem_gs = stem .. strong .. (h and final or final .. final)
    data.stem_ps = mw.ustring.sub(word, 1, -2) .. "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 = stem .. strong .. (h and "" or final) .. "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["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 = stem .. strong .. e
    data.stem_ps = stem .. weak .. e .. "tt"
    data.stem_is = geminate(stem .. strong .. e)
    data.stem_os = data.stem_gs

    data.stem_op = 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 = 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 the short '''illative''' or 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"]
		vh = m_vot.guess_vowel_harmony(headword or title)
	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(title)
	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

	local data = { title = title, headword = headword, geminate = geminate, prefix = prefix, vh = vh, args = args }
	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
		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