Jump to content

Module:liv-decl

From Wiktionary, the free dictionary


local export = {}
local m_str_utils = require("Module:string utilities")

local char = m_str_utils.char
local find = m_str_utils.find
local gsub = m_str_utils.gsub
local len = m_str_utils.len
local lower = m_str_utils.lower
local match = m_str_utils.match
local sub = m_str_utils.sub
local unpack = unpack or table.unpack -- Lua 5.2 compatibility
local upper = m_str_utils.upper

local lang = require("Module:languages").getByCode("liv")
local listToSet = require("Module:table").listToSet

-- Remove ending from word to get an inflection stem. ending can contain Lua pattern syntax.
local function get_stem(word, ending)
    local fragment = match(word, ending .. "$")
	if fragment then
		return sub(word, 1, -len(fragment) - 1), fragment
	end
	error("Unexpected ending for this inflection type! Wrong type?")
end

-- Get value from list or fallback if empty/nil.
local function get_or(value, fallback)
	return (value and #value > 0) and value or fallback
end

-- Return argument
local function identity(value)
	return value
end

-- Combine strings or lists of strings to one list.
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
		elseif type(s) == "string" then
			table.insert(t, s)
		end
	end
	return t
end

-- Creates a function g(t, ...) which applies f(t, ...) if t is string,
-- and else applies f(t, ...) to all values in the table t.
local function apply_on_factory(f)
	return function (t, ...)
		if type(t) == "string" then
			return (f(t, ...))
		end
		local r = {}
		for _, v in ipairs(t) do
			table.insert(r, (f(v, ...)))
		end
		return r
	end
end

-- Apply f to a string or every string in a list of strings.
-- If f returns a table, add all strings from that table into the result.
-- Otherwise, if f returns a string, add the string into the result.
-- Returns the final result.
local function flat_map(t, f, ...)
	local apply = apply_on_factory(f)
	local applied = apply(t, ...)
	if type(applied) == "string" then return applied end
	return join(unpack(applied))
end

-- Apply gsub to string or list of strings.
local gsub_all = apply_on_factory(gsub)

-- Append text to string or list of strings.
local append = apply_on_factory(function (v, x)
	return v .. x
end)

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

local vowels = "aāeēiīoōuūäǟõȭȯȱǭöȫüṻ"
local apos = "’"

-- Append d/t to stem.
local append_dt = apply_on_factory(function (v)
	return v .. (match(v, "[mnņptțksšzž]" .. apos .. "?$") and "t" or "d")
end)

local polyphthongs = {
	["ie"] = true, ["ie’"] = true, ["uo"] = true, ["uo’"] = true,
	["īe"] = true, ["ī’e"] = true, ["ūo"] = true, ["ū’o"] = true,
	["iu"] = true, ["i’u"] = true, ["īu"] = true, ["ieu"] = true,
	["ei"] = true, ["e’i"] = true, ["ēi"] = true, ["eu"] = true, ["e’u"] = true,
	["äu"] = true, ["ä’u"] = true,
	["õi"] = true, ["õ’i"] = true, ["ȭi"] = true, ["õu"] = true, ["õ’u"] = true,
	["ȯi"] = true, ["ȯ’i"] = true, ["ȱi"] = true,
	["ai"] = true, ["a’i"] = true, ["āi"] = true,
	["ui"] = true, ["u’i"] = true, ["ūi"] = true,
	["uoi"] = true, ["uo’i"] = true, ["ūoi"] = true, ["ū’oi"] = true,
	["oi"] = true, ["o’i"] = true, ["ou"] = true, ["o’u"] = true,
	["ǭi"] = true, ["ǭ’i"] = true,
}

-- Checks whether a stem is monosyllabic.
local function is_monosyllabic(stem)
	local vowels = match(stem, "^[^"..vowels.."]*(["..vowels..apos.."]+)[^"..vowels.."]*$")
	if not vowels then return false end
	return polyphthongs[vowels] or len(gsub(vowels, apos, "")) == 1
end

-- Checks whether a stem ends in a vowel.
local function ends_in_vowel(stem)
	return match(stem, "[" .. vowels .. "][" .. vowels .. apos .. "]*$")
end

-- Checks whether a vowel is short.
local function is_vowel_short(vowels)
	vowels = gsub(vowels, apos, "")
	return vowels == "ie" or vowels == "uo" or match(vowels, "^[aeiouäõȯöü]$")
end

-- Checks whether the final stem in a vowel is short.
local function is_final_vowel_short(stem)
	local vowels = match(stem, "(["..vowels..apos.."]+)[^"..vowels.."]*$")
	return (vowels and is_vowel_short(vowels)) or false
end

-- If the stem is monosyllabic, append mono; otherwise append poly.
local append_monopoly = apply_on_factory(function (stem, mono, poly)
	return is_monosyllabic(stem) and stem .. mono or stem .. poly
end)

-- Drops a final long consonant.
local ungeminate_final = apply_on_factory(function (stem)
	return gsub(stem, "([mnņptțkbdḑgfsšhvzžrŗjlļ])%1$", "%1")
end)

-- Geminates the final consonant if allowed.
local geminate_final = apply_on_factory(function (stem, ignore_prev_vowel)
	local v, c = match(stem, "^[^" .. vowels .. "]*([" .. vowels .. "][" .. vowels .. apos .. "]*)([mnņptțkbdḑgfsšhvzžrŗjlļ])$")
	if not v or not is_vowel_short(v) then return stem end
	return gsub(stem, "([" .. vowels .. "][" .. vowels .. apos .. "]*)([mnņptțkbdḑgfsšhvzžrŗjlļ])$", "%1%2%2")
end)

-- Make allative from illative
local make_all_from_ill = apply_on_factory(function (ill, par_forms, dat_forms)
	if match(ill, "z$") then
		return gsub(ill, "z$", "l")
	end
	if match(ill, "zõ$") then
		local append = false
		if ill == par_forms or ill .. "n" == dat_forms then
			append = true
		else
			if type(par_forms) == "table" then
				for _, par in ipairs(par_forms) do
					if ill == par then
						append = true
						break
					end
				end
			end
			if type(dat_forms) == "table" then
				for _, dat in ipairs(dat_forms) do
					if ill .. "n" == dat then
						append = true
						break
					end
				end
			end
		end
		if not append then
			return gsub(ill, apos .. "?zõ$", "lõ")
		end
	end
	return ill .. "lõ"
end)

local palatalized_cons = {
	["n"] = "ņ", ["t"] = "ț", ["d"] = "ḑ", ["r"] = "ŗ", ["l"] = "ļ",
	["s"] = "š", ["z"] = "ž"
}

-- Palatalizes the final set of consonants.
function palatalize_final_cons(stem)
	local stem = gsub(stem, "[^" .. vowels .. "]+$", function (f)
		return gsub(f, "[dlnrstz]", palatalized_cons)
	end)
	return gsub(stem, "[țḑ][šž]", { ["țš"] = "tš", ["ḑž"] = "dž" })
end

local DEPALATALIZE_BLOCKER = char(0xE801)

local depalatalized_cons = {
	["ņ"] = "n", ["ț"] = "t", ["ḑ"] = "d", ["ŗ"] = "r", ["ļ"] = "l",
	["š"] = DEPALATALIZE_BLOCKER.."s", ["ž"] = DEPALATALIZE_BLOCKER.."z"
}

-- Depalatalizes the final set of consonants.
function depalatalize_final_cons(stem)
	local stem = gsub(stem, "[^" .. vowels .. "]+$", function (f)
		return gsub(f, "[ḑļņŗšțž]", depalatalized_cons)
	end)
	stem = gsub(stem, "[td]"..DEPALATALIZE_BLOCKER.."[sz]",
			 { ["t"..DEPALATALIZE_BLOCKER.."s"] = "tš",
			   ["d"..DEPALATALIZE_BLOCKER.."z"] = "dž" })
	return gsub(stem, DEPALATALIZE_BLOCKER, "")
end

local vowel_short_to_long = {
	-- TODO some of these are probably wrong
	["a"]  = "ā",  ["ai"]  = "āi",  ["au"] = "ā",
	["ä"]  = "ǟ",  ["äi"]  = "ǟi",  ["äu"] = "ǟ",
	["e"]  = "ē",  ["ei"]  = "ēi",  ["eu"] = "ē",
	["i"]  = "ī",                   ["iu"] = "ī",
	["o"]  = "ō",  ["oi"]  = "ōi",  ["ou"] = "ō",
	["ȯ"]  = "ȱ",  ["ȯi"]  = "ȱi",  ["ȯu"] = "ȱ",
	["õ"]  = "ȭ",  ["õi"]  = "ȭi",  ["õu"] = "ȭ",
	["u"]  = "ū",  ["ui"]  = "ūi",
	["ie"] = "īe",                  ["ieu"] = "īe",
	["uo"] = "ūo", ["uoi"] = "ūoi",
}

local vowel_short_to_long_no_i = {
	["ai"] = "ā", ["äi"] = "ǟ", ["ei"] = "ē",
	["oi"] = "ō", ["ȯi"] = "ȱ", ["õi"] = "ȭ", ["ui"] = "ū",
}

-- Vowel short -> long conversion
local function do_vowel_short_to_long(stem, no_i)
	return gsub(stem,
			"([" .. vowels .. "][" .. vowels .. apos .. "]*)([^" .. vowels .. "]*)$",
			function (v, c)
				if no_i and vowel_short_to_long_no_i[v] then
					return vowel_short_to_long_no_i[v] .. c
				end
				return (vowel_short_to_long[v] or v) .. c
			end)
end

local vowel_long_to_short = {
	-- TODO some of these are probably wrong
	["ā"]  = "a",  ["ǟ"]  = "ä",  ["ē"]  = "e",  ["ī"]  = "i",
	["ō"]  = "o",  ["ȱ"]  = "ȯ",  ["ȭ"]  = "õ",  ["ū"]  = "u",
	["īe"] = "ie", ["ūo"] = "uo", ["ǭ"]  = "a",
}

local vowel_long_to_short2 = {
	-- TODO some of these are probably wrong
	["āi"] = "ai", ["ǟi"] = "äi", ["ēi"] = "ei",
	["ōi"] = "oi", ["ȱi"] = "ȯi", ["ȭi"] = "õi", ["ūi"] = "ui",
	["ūoi"] = "uoi",
	["āu"] = "au", ["ǟu"] = "äu", ["ēu"] = "eu", ["īu"] = "iu",
	["ōu"] = "ou", ["ȱu"] = "ȯu", ["ȭu"] = "õu",
}

-- Vowel long -> short conversion
local function do_vowel_long_to_short(stem, suffix)
	stem = gsub(stem,
			"([" .. vowels .. "][" .. vowels .. apos .. "]*)([^" .. vowels .. "]*)$",
			function (v, c)
				if vowel_long_to_short[v] then
					return vowel_long_to_short[v] .. (suffix or "") .. c
				end
				return (vowel_long_to_short2[v] or v) .. c
			end)
	return stem
end

-- Add apostrophe to make stem broken tone
local function make_broken(stem)
	if find(stem, apos) then return stem end
	-- Find first vowel
	local i, _, v = find(stem, "([" .. vowels .. "]+)")
	if not i then return stem end

	if match(v, "^ie") or match(v, "^uo") then
		-- Apostrophe after short ie or uo
		i = i + 1
	else
		-- Apostrophe after first vowel
	end
	return sub(stem, 1, i) .. apos .. sub(stem, i + 1)
end

-- Remove apostrophe to make stem plain tone
local function make_plain(stem)
	return gsub(stem, apos, "")
end

-- Return z or ž for illative
local function get_illative_z(stem)
	return match(stem, "[šž][^" .. vowels .. "]*[" .. vowels .. "]*$") and "z" or "ž"
end

local OPT_ILL_Z = char(0xE800)

local function handle_opt_ill_z(form)
	if match(form, OPT_ILL_Z .. "$") then
		form = gsub(form, OPT_ILL_Z, "")
		return { form, form .. "z" }
	else
		return form
	end
end

local function make_inessive_forms(result)
	if result["sg_ill"] then
		-- handle OPT_ILL_Z
		result["sg_ill"] = flat_map(result["sg_ill"], handle_opt_ill_z)
	end

	if result["sg_ela"] then
		-- sg_ela -> sg_ine by removing t
		result["sg_ine"] = gsub_all(result["sg_ela"], "t(õ?)$", "%1")
	end

	if result["pl_ela"] then
		-- pl_ela -> pl_ine by removing t
		result["pl_ine"] = gsub_all(result["pl_ela"], "t(i?)$", "%1")
	end

	return result
end

-- Livonian nominal stems
-- nsg:					nominative singular
-- gsg:					genitive singular
-- psg:					partitive singular
-- dsg:		(= psg)		dative singular	(without -n)
-- inssg:	(= dsg)		instructive singular (without -ks)
-- ilsg:				illative singular (use OPT_ILL_Z for optional z suffix)
-- elsg:				elative singular (without -st(õ))
-- npl:		(= gsg)		nominative plural
-- gpl:		(= npl)		genitive plural
-- ppl:					partitive plural (without -i)
-- dpl:		(= gpl)		dative plural (without -n)
-- inspl:	(= dpl)		instructive plural (without -ks)
-- ilpl:				illative plural
-- elpl:				elative plural
-- 
local function make_forms(stems)
	local result
	
	local nsg = stems.nsg
	local gsg = stems.gsg
	local psg = stems.psg
	local dsg = stems.dsg or psg
	local inssg = stems.inssg or dsg
	local ilsg = stems.ilsg
	local elsg = stems.elsg
	local npl = stems.npl or gsg
	local gpl = stems.gpl or npl
	local ppl = stems.ppl
	local ppl = stems.ppl
	local dpl = stems.dpl or gpl
	local inspl = stems.inspl or dpl
	local ilpl = stems.ilpl
	local elpl = stems.elpl

	local result = { }

	result["sg_nom"] = nsg
	result["sg_gen"] = gsg
	result["sg_par"] = psg
	result["sg_dat"] = append(dsg, "n")
	result["sg_ins"] = append(inssg, "ks")
	result["sg_ill"] = ilsg
	result["sg_ela"] = append_monopoly(gsub_all(elsg, "ss$", "s"), "stõ", "st")
	-- sg_ela s -> š
	result["sg_ela"] = gsub_all(result["sg_ela"], "([ņțḑŗļ])s(tõ?)$", "%1š%2")
	
	result["pl_nom"] = npl
	result["pl_gen"] = gpl
	result["pl_par"] = append(ppl, "i")
	result["pl_dat"] = append(dpl, "n")
	result["pl_ins"] = append(inspl, "ks")
	result["pl_ill"] = ilpl
	result["pl_ela"] = elpl

	return make_inessive_forms(result)
end

local function postprocess(data, result)
	if data.has_ad then
		result["sg_ade"] = gsub_all(result["sg_dat"], "n$", "l")
		-- result["sg_all"] = make_all_from_ill(result["sg_ill"], result["sg_par"], result["sg_dat"])
		result["sg_all"] = append(result["sg_ade"], "õ")
		result["sg_abl"] = append(result["sg_ade"], "d")
	end
	
    if data.prefix and #data.prefix > 0 then
    	local prefix = data.prefix
	    for k, v in pairs(result) do
	    	result[k] = gsub_all(v, "^", prefix)
	    end
    end

    if data.suffix and #data.suffix > 0 then
    	local suffix = data.suffix
	    for k, v in pairs(result) do
	    	result[k] = gsub_all(v, "$", suffix)
	    end
    end

	if data.no_singular then
		for k, v in pairs(result) do
			if k:match("^sg_") then
				result[k] = nil
			end
		end
	end

	if data.no_plural then
		for k, v in pairs(result) do
			if k:match("^pl_") then
				result[k] = nil
			end
		end
	end
	
	if data.capitalize then
    	local prefix = data.prefix
	    for k, v in pairs(result) do
	    	result[k] = gsub_all(v, "^%l", upper)
	    end
	end

	return result
end

-- inflection classes begin
local inflections = {}

local manual_infls = {
	[1] = make_inessive_forms{
		["sg_nom"] = "mis",
		["sg_gen"] = "mis",
		["sg_par"] = { "midā", "mis" },
		["sg_dat"] = "missõn",
		["sg_ins"] = "missõks",
		["sg_ill"] = "missõ",
		["sg_ela"] = "missõst",
	},
	[2] = make_inessive_forms{
		["sg_nom"] = "jegā",
		["sg_gen"] = "jegā",
		["sg_par"] = "jegā",
		["sg_dat"] = "jegān",
		["sg_ins"] = "jegāks",
		["sg_ill"] = "je’ggõ",
		["sg_ela"] = "jegāst",
	},
	[3] = make_inessive_forms{
		["sg_nom"] = "mū", ["pl_nom"] = "munt",
		["sg_gen"] = "mū", ["pl_gen"] = "munt",
		["sg_par"] = "mūdõ", ["pl_par"] = "mūḑi",
		["sg_dat"] = "mūn", ["pl_dat"] = "muntõn",
		["sg_ins"] = "mūkõks", ["pl_ins"] = "muntkõks",
		["sg_ill"] = "mū’zõ", ["pl_ill"] = "mū’ži",
		["sg_ela"] = "mūstõ", ["pl_ela"] = "mūšti",
	},
	[4] = make_inessive_forms{
		["sg_nom"] = "se", ["pl_nom"] = "ne",
		["sg_gen"] = "sīe", ["pl_gen"] = "nänt",
		["sg_par"] = "siedā", ["pl_par"] = "nēḑi",
		["sg_dat"] = "sīen", ["pl_dat"] = "näntõn",
		["sg_ins"] = "sīekõks", ["pl_ins"] = "näntkõks",
		["sg_ill"] = "sī’ezõ", ["pl_ill"] = "nē’ži",
		["sg_ela"] = "sīestõ", ["pl_ela"] = "nēšti",
	},
	[5] = make_inessive_forms{
		["sg_nom"] = { "tämā", "ta" }, ["pl_nom"] = { "ne", "nämād" },
		["sg_gen"] = "tä’m", ["pl_gen"] = "nänt",
		["sg_par"] = "tǟnda", ["pl_par"] = "nēḑi",
		["sg_dat"] = "tä’mmõn", ["pl_dat"] = "näntõn",
		["sg_ins"] = "tä’mkõks", ["pl_ins"] = "näntkõks",
		["sg_ill"] = "tä’mmõ"..OPT_ILL_Z, ["pl_ill"] = "nē’ži",
		["sg_ela"] = "tä’mstõ", ["pl_ela"] = "nēšti",
	},
	[6] = make_inessive_forms{
		["sg_nom"] = { "minā", "ma" }, ["pl_nom"] = { "mēg", "meg" },
		["sg_gen"] = "mi’n", ["pl_gen"] = "mä’d",
		["sg_par"] = "mīnda", ["pl_par"] = "mēḑi",
		["sg_dat"] = { "mi’nnõn", "mi’n" }, ["pl_dat"] = { "mä’ddõn", "mä’n" },
		["sg_ins"] = "mi’nkõks", ["pl_ins"] = "mä’dkõks",
		["sg_ill"] = "mi’nnõ"..OPT_ILL_Z, ["pl_ill"] = "mē’ži",
		["sg_ela"] = "mi’nstõ", ["pl_ela"] = "mēšti",
	},
	[7] = make_inessive_forms{
		["sg_nom"] = { "sinā", "sa" }, ["pl_nom"] = { "tēg", "teg" },
		["sg_gen"] = "si’n", ["pl_gen"] = "tä’d",
		["sg_par"] = "sīnda", ["pl_par"] = "tēḑi",
		["sg_dat"] = { "si’nnõn", "si’n" }, ["pl_dat"] = { "tä’ddõn", "tä’n" },
		["sg_ins"] = "si’nkõks", ["pl_ins"] = "tä’dkõks",
		["sg_ill"] = "si’nnõ"..OPT_ILL_Z, ["pl_ill"] = "tē’ži",
		["sg_ela"] = "si’nstõ", ["pl_ela"] = "tēšti",
	},
	[8] = make_inessive_forms{
		["sg_nom"] = "kis", ["pl_nom"] = { "kis", "kīend" },
		["sg_gen"] = { "kīen", "kīnga" }, ["pl_gen"] = "kīend",
		["sg_par"] = { "kīenta", "kīenda" }, ["pl_par"] = "kīendi",
		["sg_dat"] = { "kīen", "kīngan" }, ["pl_dat"] = "kīendõn",
		["sg_ins"] = { "kīenkõks", "kīngaks" }, ["pl_ins"] = "kīendõks",
		["sg_ill"] = "kīenõ", ["pl_ill"] = "kīeniž",
		["sg_ela"] = "kīenstõ", ["pl_ela"] = "kīenšti",
	},
	[9] = make_inessive_forms{
		["sg_nom"] = "ī’ž", ["pl_nom"] = "eņtšõd",
		["sg_gen"] = "eņtš", ["pl_gen"] = "eņtšõd",
		["sg_par"] = "ēņtšta", ["pl_par"] = "eņtšidi",
		["sg_dat"] = "eņtšõn", ["pl_dat"] = "eņtšõdõn",
		["sg_ins"] = "eņtšõks", ["pl_ins"] = "eņtšõdõks",
		["sg_ill"] = "eņtšõ"..OPT_ILL_Z, ["pl_ill"] = "eņtšiz",
		["sg_ela"] = "eņtšõst", ["pl_ela"] = "eņtšist",
	},
	[10] = make_inessive_forms{
		["sg_nom"] = "midāgõd", ["pl_nom"] = "midāgõd",
		["sg_gen"] = "midāgõd", ["pl_gen"] = "midāgõd",
		["sg_par"] = "midāgõd", ["pl_par"] = "midāgidi",
		["sg_dat"] = "midāgõn", ["pl_dat"] = "midāgõdõn",
		["sg_ins"] = "midāgõks", ["pl_ins"] = "midāgõdõks",
		["sg_ill"] = "midāgõ"..OPT_ILL_Z, ["pl_ill"] = "midāgiž",
		["sg_ela"] = "midāgõst", ["pl_ela"] = "midāgist",
	},
	[11] = make_inessive_forms{
		["sg_nom"] = "mits", ["pl_nom"] = "mitsmõd",
		["sg_gen"] = "mits", ["pl_gen"] = "mitsmõd",
		["sg_par"] = "mits", ["pl_par"] = "mitsmidi",
		["sg_dat"] = "mitsõn", ["pl_dat"] = "mitsmõdõn",
		["sg_ins"] = "mitsõks", ["pl_ins"] = "mitsmõdõks",
		["sg_ill"] = "mitsõ"..OPT_ILL_Z, ["pl_ill"] = "mitsmiž",
		["sg_ela"] = "mitsõs", ["pl_ela"] = "mitsmist",
	},
}

-- Handles types 1-11
local function infl_manual(typeno, data)
	return manual_infls[typeno]
end

for i = 1, 11 do inflections[i] = infl_manual end

-- Handles types 12-17
local function infl_monosyllabic(typeno, data)
	local lemma = data.title
	local stem = get_stem(lemma, data.no_singular and "d" or "")
	local pl2 = stem

	if typeno == 14 or typeno == 15 or typeno == 17 then
		-- Adjust plural stem vowel
		pl2 = gsub(pl2, "[" .. vowels .. "]+$", {
			["ǟ"] = "ē", ["īe"] = "ē", ["ei"] = "ē"
		})
	end

	return make_forms {
		nsg = stem,
		gsg = stem,
		psg = typeno <= 15 and stem .. "dõ" or stem .. "õ",
		dsg = typeno <= 15 and stem or stem .. "õ",
		inssg = typeno <= 15 and stem .. "kõ" or stem .. "õ",
		ilsg = typeno <= 15 and make_broken(stem) .. "zõ" or stem .. "õ",
		elsg = typeno <= 15 and stem or stem .. "õ",
		npl = stem .. "d",
		gpl = stem .. "d",
		ppl = pl2 .. "ḑ",
		dpl = stem .. "dõ",
		inspl = typeno <= 15 and stem .. "dkõ" or stem .. "dõ",
		ilpl = (typeno <= 15 and make_broken(pl2) or pl2) .. "ži",
		elpl = pl2 .. "šti",
	}
end

for i = 12, 17 do inflections[i] = infl_monosyllabic end

-- Nominal types within 18-54 that introduce diphthongs of this type into stems from long vowels
local bisyllabic_a_diphthongize = {
	[21] = "i", [22] = "i", [23] = "i", [37] = "i", [47] = "i",

	[24] = "u", [38] = "u", [48] = "u",
}

-- Nominal types within 18-54 with broken tone in the other stem
local bisyllabic_a_stem2_broken = listToSet({
	18, 19, 20, 25, 26, 39, 40, 49,
})

-- Nominal types within 18-54 with gemination in the other stem
local bisyllabic_a_stem2_gem = listToSet({
	18, 19, 25, 26, 27, 28, 29, 39, 41, 43, 44, 45, 50, 54,
})

-- Handles types 18-54
local function infl_bisyllabic_a_final(typeno, data)
	local lemma = data.title
	local stem, a
	
	if data.no_singular and typeno == 54 then
		stem = get_stem(lemma, "õd")
		stem = ungeminate_final(stem)
		a = "a"
	else
		stem = data.no_singular and get_stem(lemma, "d") or lemma
		stem, a = get_stem(stem, "[" .. vowels .. "]")
	end

	-- Two stems, called stem (nominative) and stem2 (partitive)
	-- Forms mixed from the two.

	-- Modify the stem-final vowel
	local stem2 = do_vowel_long_to_short(stem, bisyllabic_a_diphthongize[typeno])

	if typeno == 22 or typeno == 45 then
		-- puoi- > pȯi-, puol- > pȯl-
		stem2 = gsub(stem2, "uo(i?)([^" .. vowels .. "]*)$", "ȯ%1%2")
	elseif typeno == 47 then
		-- uišk- > uisk-
		stem2 = gsub(stem2, "iš([^" .. vowels .. "]*)$", "is%1")
	end

	if bisyllabic_a_stem2_broken[typeno] then
		stem2 = make_broken(stem2)
	end

	-- Plural stem
	local pl2
	local ppl
	if typeno < 39 then
		pl2 = stem2
		if typeno == 19 then
			-- a'm- -> ä'm-
			pl2 = gsub(stem2, "%f[" .. vowels .. "]a([^" .. vowels .. "]*)$", "ä%1")
		elseif typeno == 25 or typeno == 29 or typeno == 32 then
			-- pie’ž- -> pe’ž-
			ppl = gsub(stem2, "%f[" .. vowels .. "]ie([^" .. vowels .. "]*)$", "e%1")
		elseif typeno == 35 then
			-- käņg- -> keņg-
			pl2 = gsub(stem2, "%f[" .. vowels .. "]ä([^" .. vowels .. "]*)$", "e%1")
		end

		if typeno >= 34 then
			-- silm- > siļm-
			pl2 = palatalize_final_cons(pl2)
		end
		ppl = ppl or pl2

		if typeno >= 33 then
			ppl = ppl
		elseif match(ppl, "st$") then
			ppl = gsub(ppl, "st", "št")
		elseif match(ppl, "[sš]$") then
			ppl = palatalize_final_cons(ungeminate_final(ppl)) .. "t"
		elseif match(ppl, "[kpt]$") then
			ppl = palatalize_final_cons(ungeminate_final(ppl)) .. "ț"
		else
			ppl = palatalize_final_cons(ungeminate_final(ppl)) .. "ḑ"
		end

		pl2 = make_plain(pl2)
	else
		pl2 = stem
	end

	if bisyllabic_a_stem2_gem[typeno] then
		stem2 = geminate_final(stem2)
	end

	local ilpl, elpl
	if typeno <= 20 then
		ilpl = palatalize_final_cons(make_broken(pl2)) .. "ži"
		elpl = palatalize_final_cons(make_broken(pl2)) .. "šti"
	elseif typeno <= 24 then
		ilpl = palatalize_final_cons(pl2) .. "ži"
		elpl = palatalize_final_cons(pl2) .. "šti"
	elseif typeno == 33 then
		ilpl = { pl2 .. "ži", stem .. "iž" }
		elpl = { pl2 .. "šti", stem .. "ist" }
	elseif 34 <= typeno and typeno <= 35 then
		ilpl = { palatalize_final_cons(pl2) .. "ži", stem .. "iž" }
		elpl = { palatalize_final_cons(pl2) .. "šti", stem .. "ist" }
	else
		local ii = is_final_vowel_short(stem) and "ī" or "i"
		ilpl = stem .. ii .. get_illative_z(stem)
		elpl = stem .. ii .. "st"
	end

	if typeno == 54 then
		-- 54 is too different from everything else
		return make_forms {
			nsg = stem .. a,
			gsg = stem .. a,
			psg = stem2 .. "õ",
			dsg = stem .. a,
			inssg = stem2 .. "õ",
			ilsg = stem2 .. "õ",
			elsg = stem .. a,
			npl = stem2 .. "õd",
			ppl = stem2 .. "id",
			dpl = stem2 .. "õdõ",
			ilpl = stem2 .. "iž",
			elpl = stem2 .. "ist",
		}
	end

	return make_forms {
		nsg = stem .. a,
		gsg = stem .. a,
		psg = stem2 .. (typeno == 53 and "i" or "õ"),
		dsg = stem .. a,
		inssg = stem .. a,
		ilsg = stem2 .. "õ",
		elsg = stem .. a,
		npl = stem .. a .. "d",
		ppl = ppl or stem .. (is_final_vowel_short(stem) and "īd" or "id"),
		dpl = stem .. a .. "dõ",
		ilpl = ilpl,
		elpl = elpl,
	}
end

for i = 18, 54 do inflections[i] = infl_bisyllabic_a_final end

-- Handles types 55-58
local function infl_bisyllabic_a_final_invariant(typeno, data)
	local lemma = data.title
	local stem = data.no_singular and get_stem(lemma, "d") or lemma
	local a
	stem, a = get_stem(stem, "[" .. vowels .. "]")
	-- One stem.

	local psg
	if typeno == 55 then
		psg = stem .. "õ"
	elseif typeno == 56 then
		psg = stem .. a
	else
		psg = stem .. a .. "zt"
	end

	local ilsg
	if typeno == 55 then
		ilsg = stem .. "õ"
	else
		ilsg = stem .. a .. "z"
	end

	local pl = stem .. ((is_final_vowel_short(stem) and typeno >= 57) and "ī" or "i")

	return make_forms {
		nsg = stem .. a,
		gsg = stem .. a,
		psg = psg,
		dsg = stem .. a,
		ilsg = ilsg,
		elsg = stem .. a,
		npl = stem .. a .. "d",
		ppl = pl .. "d",
		dpl = stem .. a .. "dõ",
		ilpl = pl .. (typeno == 58 and "z" or "ž"),
		elpl = pl .. "st",
	}
end

for i = 55, 58 do inflections[i] = infl_bisyllabic_a_final_invariant end

-- Nominal types within 59-114 with length variation between stems
local bisyllabic_v_stem2_lenvar = listToSet({
	63, 67, 70
})

-- Nominal types within 59-114 with a broken tone in the nominative singular
local bisyllabic_v_stem2_broken_nom_sg = listToSet({
	59, 60, 64, 72, 73, 74, 75, 76, 81, 82, 83, 84, 85, 86, 87, 88, 89
})

-- Nominal types within 59-114 with -ž in the nominative singular and -d- in the stem
local bisyllabic_v_zh_d = listToSet({
	86, 87, 88, 112, 113
})

-- Nominal types within 59-114 that introduce diphthongs of this type into stems from long vowels
local bisyllabic_v_stem2_diphthongize = {
	[107] = "i", [108] = "i", [109] = "i",
	[93] = "u", [98] = "u", [106] = "u", [112] = "u",
}

-- Nominal types within 59-114 that ban gemination of the stem
local bisyllabic_v_no_gem_stem = listToSet({
	60, 75, 93, 98, 107, 108, 109
})

-- Nominal types within 59-114 that always have a long ī in the plural stem
local bisyllabic_v_always_plural_ii = listToSet({
	60, 75
})

-- Nominal types within 59-114 that have palatalization in nom.sg. only
local bisyllabic_v_palatalized_nom_sg = listToSet({
	78, 89, 96, 110, 111, 112, 113
})

-- Handles types 59-114 except 71, 90, 91
local function infl_bisyllabic_v_final(typeno, data)
	local lemma = data.title
	local stem, stem2

	-- Final stem vowel.
	local pl_v
	if typeno <= 71 then
		pl_v = "i"
	elseif typeno <= 91 then
		pl_v = "ū"
	else
		pl_v = "õ"
	end

	local lenvar = bisyllabic_v_stem2_lenvar[typeno] or typeno >= 92
	local nsg

	-- Two stems, the nominative stem and plural stem stem2.
	if data.no_singular then
		stem2 = get_stem(lemma, "d")
		if pl_v == "i" then
			stem2 = get_stem(stem2, "[iī]")
		else
			stem2 = get_stem(stem2, pl_v)
		end

		stem = stem2
		if lenvar then
			stem = do_vowel_long_to_short(stem2, bisyllabic_v_stem2_diphthongize[typeno])
		end
		if typeno == 83 then
			-- mier- > mer-
			stem = gsub(stem, "%f[" .. vowels .. "]ie([^" .. vowels .. "]*)$", "e%1")
		elseif typeno == 95 or typeno == 109 then
			-- puort- > pȯrt-
			stem = gsub(stem, "%f[" .. vowels .. "]uo([^" .. vowels .. "]*)$", "ȯ%1")
		end

		if bisyllabic_v_stem2_broken_nom_sg[typeno] then
			stem = make_broken(stem)
		end
		if typeno == 108 or typeno == 109 then
			stem = gsub(stem, "š$", "s")
		end
		if bisyllabic_v_zh_d[typeno] then
			stem = gsub(stem, "d$", "ž")
			if typeno == 87 then
				-- käd- > ked-
				stem = gsub(stem, "%f[" .. vowels .. "]ä([^" .. vowels .. "]*)$", "e%1")
			elseif typeno == 88 then
				-- vied- > ved-
				stem = gsub(stem, "%f[" .. vowels .. "]ie([^" .. vowels .. "]*)$", "e%1")
			end
		end
		if bisyllabic_v_palatalized_nom_sg[typeno] then
			stem = palatalize_final_cons(stem)
		end
	else
		stem = lemma
		if bisyllabic_v_zh_d[typeno] then
			nsg = stem
			stem = get_stem(stem, "ž") .. "d"
			if typeno == 87 then
				-- ked- > käd-
				stem = gsub(stem, "%f[" .. vowels .. "]e([^" .. vowels .. "]*)$", "ä%1")
			elseif typeno == 88 then
				-- ved- > vied-
				stem = gsub(stem, "%f[" .. vowels .. "]e([^" .. vowels .. "]*)$", "ie%1")
			end
			if bisyllabic_v_palatalized_nom_sg[typeno] then
				stem = depalatalize_final_cons(stem)
			end
		elseif bisyllabic_v_palatalized_nom_sg[typeno] then
			nsg = stem
			stem = depalatalize_final_cons(stem)
		elseif typeno == 83 then
			-- me'r- -> mie'r-
			nsg = stem
			stem = gsub(stem, "%f[" .. vowels .. "]e([^" .. vowels .. "]*)$", "ie%1")
		elseif typeno == 101 then
			-- täm -> tam-
			nsg = stem
			stem = gsub(stem, "%f[" .. vowels .. "]ä([^" .. vowels .. "]*)$", "a%1")
		end

		stem2 = stem
		if bisyllabic_v_stem2_broken_nom_sg[typeno] then
			stem2 = make_plain(stem2)
		end
		if lenvar then
			stem2 = do_vowel_short_to_long(stem2, typeno >= 107)
		end
		if typeno == 83 then
			-- mer- -> mier-
			stem2 = gsub(stem2, "%f[" .. vowels .. "]e([^" .. vowels .. "]*)$", "ie%1")
		elseif typeno == 95 or typeno == 109 then
			-- pȱrt- -> pūort-
			stem2 = gsub(stem2, "%f[" .. vowels .. "]ȱ(i?[^" .. vowels .. "]*)$", "ūo%1")
		elseif typeno == 100 or typeno == 101 or typeno == 104 then
			-- tām- -> tǭm-
			stem2 = gsub(stem2, "%f[" .. vowels .. "]ā([^" .. vowels .. "]*)$", "ǭ%1")
		end
		if typeno == 108 or typeno == 109 then
			stem2 = gsub(stem2, "s$", "š")
		end
		if typeno == 97 then
			stem2 = gsub(stem2, "t$", "")
		end
	end
	nsg = nsg or stem

	local gemstem = geminate_final(stem)

	-- Partitive singular.
	local psg
	if (81 <= typeno and typeno <= 89) or (110 <= typeno and typeno <= 114) then
		psg = stem
		if typeno ~= 84 then
			psg = make_plain(psg)
		end
		if not (84 <= typeno and typeno <= 89) then
			psg = do_vowel_short_to_long(psg)
		end

		if match(psg, "d$") or typeno == 84 then
			psg = gsub(psg, "d$", "") .. "t"
		elseif typeno == 110 then
			psg = gsub(psg, "g$", "") .. "t"
		elseif typeno <= 83 then
			psg = psg .. "d"
		else
			psg = psg .. "t"
		end
		psg = gsub(psg, "md$", "nd")

		if typeno <= 83 or typeno >= 110 then
			psg = psg .. "a"
		elseif typeno == 84 then
			psg = psg .. "õ"
		else
			psg = psg .. "ā"
		end
	else
		psg = gemstem .. "õ"
	end

	-- Instructive singular.
	local inssg
	if typeno <= 60 or typeno == 64 or (72 <= typeno and typeno <= 76)
			or (81 <= typeno and typeno <= 94)
			or (96 <= typeno and typeno <= 97)
			or (110 <= typeno and typeno <= 114) then
		if match(stem, "k$") then
			inssg = stem .. "õ"
		else
			inssg = stem .. "kõ"
		end
	else
		inssg = gemstem .. "õ"
	end

	-- Elative singular.
	local elsg
	if (68 <= typeno and typeno <= 71) or typeno == 80
			or typeno == 89 or (105 <= typeno and typeno <= 109) then
		elsg = gemstem .. "õ"
	elseif typeno == 114 then
		elsg = gsub(gemstem, "z$", "") .. "gõ"
	else
		elsg = stem
	end

	-- Nominative plural.
	local pl2 = stem2 .. ((is_final_vowel_short(stem2) or bisyllabic_v_always_plural_ii[typeno]) and "ī" or "i")
	local npl = (pl_v == "i" and pl2 or stem2 .. pl_v) .. "d"

	-- Partitive plural.
	local ppl
	if typeno == 72 or typeno == 92 then
		ppl = stem .. "ḑ"
	elseif typeno == 73 or typeno == 93 then
		ppl = palatalize_final_cons(stem) .. "ḑ"
	elseif typeno == 83 then
		ppl = palatalize_final_cons(make_plain(nsg)) .. "ḑ"
	elseif 86 <= typeno and typeno <= 89 then
		ppl = geminate_final(nsg)
	elseif 110 <= typeno and typeno <= 113 then
		ppl = nsg
	else
		ppl = pl2 .. "d"
	end

	-- Illative/elative plural.
	if 87 <= typeno and typeno <= 89 then
		pl2 = make_plain(nsg)
		pl2 = pl2 .. (is_final_vowel_short(pl2) and "ī" or "i")
	end

	local z = get_illative_z(pl2)
	local ilpl
	local elpl

	if 72 <= typeno and typeno <= 75 then
		ilpl = palatalize_final_cons(stem) .. z .. "i" 
	elseif 112 <= typeno and typeno <= 113 then
		ilpl = palatalize_final_cons(nsg) .. "iz"
	else
		ilpl = pl2 .. z
	end
	
	if 112 <= typeno and typeno <= 113 then
		elpl = palatalize_final_cons(gsub(stem, "d$", "ž")) .. "ist"
	elseif (72 <= typeno and typeno <= 75) or (110 <= typeno and typeno <= 111) then
		elpl = palatalize_final_cons(stem) .. "šti"
	elseif typeno == 83 then
		elpl = palatalize_final_cons(nsg) .. "šti"
	elseif (81 <= typeno and typeno <= 84) then
		elpl = make_broken(stem) .. "šti"
	else
		elpl = pl2 .. "st"
	end

	return make_forms {
		nsg = nsg,
		gsg = stem,
		psg = psg,
		dsg = gemstem .. "õ",
		inssg = inssg,
		ilsg = gemstem .. (typeno == 114 and "gõ" or "õ"),
		elsg = elsg,
		npl = npl,
		ppl = ppl,
		dpl = npl .. "õ",
		ilpl = ilpl,
		elpl = elpl,
	}
end

for i = 59, 114 do inflections[i] = infl_bisyllabic_v_final end

inflections[71] = function (typeno, data)
	local lemma = data.title
	local stem = lemma
	if data.no_singular then stem = get_stem(stem, "d") end
	return make_forms {
		nsg = stem,
		gsg = stem,
		psg = stem,
		dsg = stem,
		ilsg = stem .. OPT_ILL_Z,
		elsg = stem,
		npl = stem .. "d",
		ppl = stem .. "d",
		dpl = stem .. "dõ",
		ilpl = stem .. "ž",
		elpl = stem .. "sti",
	}
end

inflections[90] = function (typeno, data)
	local lemma = data.title
	local stem = lemma
	if data.no_singular then stem = get_stem(stem, "d") end
	local stempl = gsub(stem, "[" .. vowels .. "]$", "ī")
	return make_forms {
		nsg = stem,
		gsg = stem,
		psg = stem,
		dsg = stem,
		ilsg = stem .. OPT_ILL_Z,
		elsg = stem,
		npl = stem .. "d",
		ppl = stempl .. "d",
		dpl = stem .. "dõ",
		ilpl = stempl .. "ž",
		elpl = stempl .. "st",
	}
end

inflections[91] = function (typeno, data)
	local lemma = data.title
	local stem = lemma
	if data.no_singular then stem = get_stem(stem, "d") end
	return make_forms {
		nsg = stem,
		gsg = stem,
		psg = stem,
		dsg = stem,
		ilsg = stem .. OPT_ILL_Z,
		elsg = stem,
		npl = stem .. "d",
		ppl = stem .. "d",
		dpl = stem .. "dõ",
		ilpl = stem .. "iž",
		elpl = stem .. "ist",
	}
end

-- Handles types 115-121
local function infl_bisyllabic_v_final_v(typeno, data)
	local lemma = data.title
	local stem, v
	
	if data.no_singular then
		stem = get_stem(lemma, "d")
		stem, v = get_stem(stem, "õ")
	else
		stem, v = get_stem(lemma, "õ?")
	end

	local stemv = stem .. "õ"

	local psg
	if typeno >= 118 then
		psg = stemv
	elseif typeno == 117 then
		psg = gsub(stem, "d[" .. vowels .. "]g$", "") .. "ta"
	else
		psg = gsub(do_vowel_short_to_long(stem), "([^" .. vowels .. "])g", "%1") .. (typeno == 116 and "ța" or "ta")
	end

	return make_forms {
		nsg = lemma,
		gsg = lemma,
		psg = psg,
		dsg = stemv,
		ilsg = stemv .. (#v > 0 and OPT_ILL_Z or ""),
		elsg = stemv,
		npl = stemv .. "d",
		ppl = stem .. "id",
		dpl = stemv .. "dõ",
		ilpl = stem .. "i" .. get_illative_z(stem),
		elpl = stem .. "ist",
	}
end

for i = 115, 121 do inflections[i] = infl_bisyllabic_v_final_v end

-- Handles types 122-150
local function infl_cons_misc(typeno, data)
	local lemma = data.title
	local stem = lemma

	-- No gemination for 131
	local possibly_geminate_final = typeno == 131 and identity or geminate_final

	if data.no_singular then
		stem = ungeminate_final(get_stem(stem, "õ?d"))
		if typeno == 127 or typeno == 146 or typeno == 147 then
			stem = palatalize_final_cons(stem)
		end
		if typeno == 146 or typeno == 150 then
			-- tǟ’d- > tē’d-
			stem = gsub(stem, "%f[" .. vowels .. "]ǟ(" .. apos .. "?)([^" .. vowels .. "]*)$", "ē%1%2")
		elseif typeno == 147 then
			-- lī’ed- > lē’d-
			stem = gsub(stem, "%f[" .. vowels .. "]ī(" .. apos .. "?)e([^" .. vowels .. "]*)$", "ē%1%2")
		end
		if typeno >= 148 then
			stem = get_stem(stem, "d")
			if typeno >= 149 then
				stem = palatalize_final_cons(stem)
			end
			stem = stem .. "tš"
		end
	end

	local nsg = stem
	if typeno == 127 or typeno == 146 or typeno == 147 then
		stem = depalatalize_final_cons(stem)
	elseif typeno >= 148 then
		stem = get_stem(stem, "tš")
		if typeno >= 149 then
			stem = depalatalize_final_cons(stem)
		end
		stem = stem .. "d"
	end

	-- Genitive and partitive singular
	local stemv
	local gsg
	local psg
	if 137 <= typeno and typeno <= 139 then
		stemv = gsub(stem, "[dt]$", "") .. "õ"
		gsg = typeno == 139 and stem or { stem, stemv }
		psg = stem .. "õ"
	elseif typeno >= 146 then
		if typeno == 146 or typeno == 150 then
			-- tē’d- > tǟ’d-
			stem = gsub(stem, "%f[" .. vowels .. "]ē(" .. apos .. "?)([^" .. vowels .. "]*)$", "ǟ%1%2")
		elseif typeno == 147 then
			-- lē’d- > lī’ed-
			stem = gsub(stem, "%f[" .. vowels .. "]ē(" .. apos .. "?)([^" .. vowels .. "]*)$", "ī%1e%2")
		end
		stemv = stem .. "õ"
		psg = stem .. "tõ"
	else
		stemv = possibly_geminate_final(stem) .. "õ"
		psg = stemv
	end
	gsg = gsg or stem

	-- Dative and instrumental singular
	local dsg, inssg
	if typeno == 139 then
		dsg, inssg = stem .. "õ", stemv
	elseif typeno <= 145 then
		dsg, inssg = stemv, stemv
	elseif typeno <= 147 then
		dsg, inssg = stemv, stem .. "kõ"
	else
		dsg, inssg = stem .. "õ", stem .. "kõ"
	end

	-- Partitive plural
	local ppl
	if typeno <= 124 then
		ppl = stem .. "ḑ"
	elseif typeno == 127 then
		ppl = possibly_geminate_final(do_vowel_short_to_long(stem)) .. "id"
	elseif 137 <= typeno and typeno <= 139 then
		local nsg_no_t = gsub(nsg, "[td]$", "")
		ppl = nsg_no_t .. "id"
	elseif typeno >= 146 then
		ppl = nsg
	else
		ppl = possibly_geminate_final(stem) .. "id"
	end

	-- Illative singular 
	local ilsg
	if typeno == 139 then
		ilsg = stem .. "õ"
	else
		ilsg = stemv
	end

	-- Elative singular
	local elsg
	if typeno <= 134 or typeno >= 146 then
		elsg = stem
	else
		elsg = stemv
	end

	-- Illative and elative plural
	local plstem, ilpl, elpl
	if typeno <= 124 then
		plstem = stem
		ilpl = nsg .. get_illative_z(nsg) .. "i"
		elpl = nsg .. "šti"
	elseif 137 <= typeno and typeno <= 139 then
		local nsg_no_t = gsub(nsg, "[td]$", "")
		plstem = stemv
		ilpl = possibly_geminate_final(nsg_no_t) .. "i" .. get_illative_z(nsg_no_t)
		elpl = possibly_geminate_final(nsg_no_t) .. "ist"
	elseif 148 <= typeno and typeno <= 149 then
		plstem = stemv
		ilpl = possibly_geminate_final(stem) .. "i" .. get_illative_z(stem)
		elpl = possibly_geminate_final(stem) .. "ist"
	else
		plstem = stemv
		ilpl = possibly_geminate_final(nsg) .. "i" .. get_illative_z(nsg)
		elpl = possibly_geminate_final(nsg) .. "ist"
	end

	return make_forms {
		nsg = nsg,
		gsg = gsg,
		psg = psg,
		dsg = dsg,
		inssg = inssg,
		ilsg = ilsg,
		elsg = elsg,
		npl = plstem .. "d",
		ppl = ppl,
		dpl = plstem .. "dõ",
		inspl = plstem .. ((typeno == 123 or typeno == 124) and "dkõ" or "dõ"),
		ilpl = ilpl,
		elpl = elpl,
	}
end

for i = 122, 150 do inflections[i] = infl_cons_misc end

-- Handles types 151-156
local function infl_cons_final_m(typeno, data)
	local lemma = data.title
	local stem
	local c
	
	if data.no_singular then
		stem = get_stem(lemma, "õd")
		stem, c = get_stem(stem, "[^" .. vowels .. "]")
		stem = ungeminate_final(stem)
		if typeno >= 153 then
			stem = gsub(stem, c .. "$", "õ" .. c)
		end
	else
		stem, c = get_stem(lemma, "[^" .. vowels .. "]")
	end

	local stem1m = stem .. c
	local stem1mg

	if typeno <= 152 then
		stem1mg = geminate_final(stem1m)
	else
		stem1mg = stem1m
	end

	local stem1mv = stem1mg .. "õ"
	local stem2m
	if typeno <= 152 then
		stem2m = stem1mg
	else
		stem2m = gsub(stem, "[" .. vowels .. "]+$", "")
		if typeno == 153 then
			stem2m = make_broken(stem2m)
		elseif typeno >= 155 then
			stem2m = do_vowel_long_to_short(stem2m, typeno == 156 and "i" or "")
		end
		stem2m = stem2m .. "m"
	end

	return make_forms {
		nsg = stem1m,
		gsg = stem1m,
		psg = append_monopoly(stem1m, "tõ", "t"),
		dsg = stem1mv,
		inssg = typeno <= 152 and stem1m .. "kõ" or stem1mv,
		ilsg = stem1mv,
		elsg = typeno <= 152 and stem1m or stem1mv,
		npl = stem2m .. "õd",
		ppl = stem2m .. "id",
		dpl = stem2m .. "õdõ",
		ilpl = stem2m .. "i" .. get_illative_z(stem2m),
		elpl = stem2m .. "ist",
	}
end

for i = 151, 156 do inflections[i] = infl_cons_final_m end

-- Handles types 157-162
local function infl_cons_final_c_invariant(typeno, data)
	local lemma = data.title
	local stem = lemma

	if data.no_singular and typeno < 161 then
		stem = get_stem(stem, "õd")
	end

	-- Elative singular
	local elsg
	if typeno >= 161 then
		elsg = gsub(lemma, "d$", "") 
	elseif typeno == 157 then
		elsg = lemma .. "õ"
	else
		elsg = lemma
	end

	local bare
	if typeno >= 161 then
		bare = gsub(lemma, "õd$", "")
	else
		bare = stem
	end

	return make_forms {
		nsg = stem,
		gsg = stem,
		psg = append_monopoly(stem, "tõ", "t"),
		dsg = bare .. "õ",
		ilsg = bare .. (typeno >= 161 and "õz" or "õ"),
		elsg = elsg,
		npl = bare .. "õd",
		ppl = bare .. "id",
		dpl = (typeno >= 161 and stem .. "õ" or stem .. "õdõ"),
		ilpl = bare .. "iž",
		elpl = bare .. "ist",
	}
end

for i = 157, 162 do inflections[i] = infl_cons_final_c_invariant end

-- Handles types 163-165
local function infl_cons_final_z_nd(typeno, data)
	local lemma = data.title
	local stem
	
	if data.no_singular then
		stem = get_stem(lemma, "ndõd")
	else
		stem = get_stem(lemma, "z")
	end
	
	local stem_nd = gsub(stem, "n$", "") .. "nd"
	local stem_ndv = stem_nd .. "õ"

	return make_forms {
		nsg = stem .. "z",
		gsg = stem_nd,
		psg = stem_nd .. "t",
		dsg = stem_ndv,
		ilsg = stem_ndv,
		elsg = stem_ndv,
		npl = stem_ndv .. "d",
		ppl = stem_nd .. "id",
		dpl = stem_ndv .. "dõ",
		ilpl = stem_nd .. "i" .. get_illative_z(stem_nd),
		elpl = stem_nd .. "ist",
	}
end

for i = 163, 165 do inflections[i] = infl_cons_final_z_nd end

-- Nominal types within 166-185 that introduce diphthongs of this type into stems from long vowels
local cons_final_z_diphthongize = {
	[176] = "i", [177] = "u"
}

-- Handles types 166-185
local function infl_cons_final_z(typeno, data)
	local lemma = data.title

	local stem
	if data.no_singular then
		local v
		stem, v = get_stem(lemma, "d")
		if typeno >= 170 then
			stem, v = get_stem(stem, "[aāõ]")
			if typeno <= 178 then
				stem = ungeminate_final(stem)
			end
			if 173 <= typeno and typeno <= 178 then
				stem = do_vowel_short_to_long(stem)
				if typeno == 175 then
					-- pāļ- > pǭļ-
					stem = gsub(stem, "%f[" .. vowels .. "]ā(" .. apos .. "?)([^" .. vowels .. "]*)$", "ǭ%2")
				elseif typeno == 178 then
					-- pȱrz- > pūor-
					stem = gsub(stem, "%f[" .. vowels .. "]ȱ(" .. apos .. "?)([^" .. vowels .. "]*)$", "ū%1o%2")
					stem = gsub(stem, "z$", "")
				end
			end
			stem = stem .. v
		end
	else
		stem = get_stem(lemma, "z")
	end
	
	local a

	if typeno <= 167 then
		-- Special case 166, 167
		local stemv = make_broken(stem)
		local stemvg = geminate_final(stemv)
		local plstem = stemv

		if typeno == 166 then
			plstem = gsub(plstem, "%f[" .. vowels .. "]ī(" .. apos .. "?)e([^" .. vowels .. "]*)$", "ē%1%2")
		end

		return make_forms {
			nsg = stem .. "z",
			gsg = stemv,
			psg = append_monopoly(stem, "ztõ", "zt"),
			dsg = gsub(stemvg, "([^" .. vowels .. "])$", "%1õ"),
			inssg = stemv .. "kõ",
			ilsg = typeno == 166 and stemv .. "zõ" or stemvg .. "õ",
			elsg = stemv,
			npl = stemv .. "d",
			ppl = plstem .. "ḑ",
			dpl = stemv .. "dõ",
			inspl = stemv .. "dkõ",
			ilpl = plstem .. get_illative_z(plstem) .. "i",
			elpl = plstem .. "šti",
		}
	end

	stem, a = get_stem(stem, "[aāõ]")

	-- Two stems: stem (nominative) and stem2 (illative).
	local stem2
	if typeno <= 169 then
		stem2 = make_broken(stem)
	elseif typeno >= 179 then
		stem2 = stem
	else
		stem2 = stem
		if 173 <= typeno and typeno <= 178 then
			stem2 = do_vowel_long_to_short(stem2, cons_final_z_diphthongize[typeno])
			if typeno == 178 then
				-- puor- > pȯrz-
				stem2 = gsub(stem2, "%f[" .. vowels .. "]uo(" .. apos .. "?)([^" .. vowels .. "]*)$", "ȯ%1%2")
				stem2 = gsub(stem2, "r$", "rz")
			end
		end

		stem2 = geminate_final(stem2)
	end

	local stem1a = stem .. a
	local stem2v = stem2 .. "õ"
	local stempl = typeno <= 172 and stem .. "ī" or stem2 .. "i"

	return make_forms {
		nsg = stem1a .. "z",
		gsg = typeno == 168 and stem1a or stem2v,
		psg = stem1a .. "zt",
		dsg = typeno <= 170 and stem1a or stem2v,
		ilsg = stem2v .. OPT_ILL_Z,
		elsg = typeno <= 172 and stem1a or stem2v,
		npl = (typeno <= 169 and stem1a or stem2v) .. "d",
		ppl = stempl .. "d",
		dpl = (typeno <= 169 and stem1a or stem2v) .. "dõ",
		ilpl = stempl .. get_illative_z(stempl),
		elpl = stempl .. "st",
	}
end

for i = 166, 185 do inflections[i] = infl_cons_final_z end

-- Nominal types within 186-202 that have ī in the stem for oblique forms.
local cons_i_iz_long_i = listToSet({
	192, 193, 194, 195, 198
})

-- Handles types 186-202
local function infl_cons_i_iz(typeno, data)
	local lemma = data.title

	local stem, i

	stem = lemma
	if data.no_singular then
		if match(stem, "zt$") then
			stem = get_stem(stem, "zt")
		else
			stem = get_stem(stem, "d")
		end
	end
	stem, i = get_stem(stem, "[iī]")
	
	local stem2
	-- Two stems: nominative (stem) and genitive (stem2)
	if 192 <= typeno and typeno <= 195 then
		stem2 = ungeminate_final(make_plain(stem))
	elseif 196 <= typeno and typeno <= 197 then
		stem2 = do_vowel_short_to_long(stem)
	else
		stem2 = stem
	end

	local ii = cons_i_iz_long_i[typeno] and "ī" or "i"

	-- Partitive singular
	local psg 
	if typeno == 191 then 
		psg = do_vowel_short_to_long(stem) .. "izta"
	elseif typeno <= 189 or typeno >= 201 then
		psg = { stem2 .. ii .. "zt", stem2 .. ii .. "t" }
	else
		psg = stem2 .. ii .. "zt"
	end

	-- Elative singular
	local elsg 
	if typeno == 190 then 
		elsg = stem2 .. ii
	elseif typeno == 186 or typeno == 188 or typeno == 189 or typeno == 191 then
		elsg = { stem2 .. ii .. "zõ", stem2 .. ii }
	else
		elsg = stem2 .. ii .. "zõ"
	end

	local dpl_base = stem .. i .. "zt"
	local ilpl_base = stem2 .. ii .. "ž"

	local stems = {
		nsg = stem .. i,
		gsg = stem2 .. ii .. "z",
		psg = psg,
		dsg = typeno == 190 and (stem2 .. ii) or (stem2 .. ii .. "zõ"),
		ilsg = stem2 .. ii .. "zõ",
		elsg = elsg,
		npl = stem .. i .. "zt",
		ppl = stem2 .. ii .. "ž",
		dpl = dpl_base .. "õ",
		inspl = dpl_base .. (typeno == 191 and "kõ" or "õ"),
		ilpl = ilpl_base .. "i" .. get_illative_z(ilpl_base),
		elpl = ilpl_base .. "ist",
	}

	if typeno == 187 or typeno == 190 then
		stems.npl = stem .. ii .. "d"
		stems.ppl = stem .. ii .. "d"
		stems.dpl = stem .. ii .. "dõ"
		stems.inspl = stem .. ii .. "dõ"
		stems.ilpl = ilpl_base
		stems.elpl = stem .. "isti"
	elseif typeno <= 189 then
		stems.npl = join(stems.npl, stem .. ii .. "d")
		stems.ppl = join(stems.ppl, stem .. ii .. "d")
		stems.dpl = join(stems.dpl, stem .. ii .. "dõ")
		stems.inspl = join(stems.inspl, stem .. ii .. "dõ")
		stems.ilpl = join(stems.ilpl, ilpl_base .. (typeno == 186 and "i" or ""))
		stems.elpl = join(stems.elpl, stem .. "isti")
	end

	return make_forms(stems)
end

for i = 186, 202 do inflections[i] = infl_cons_i_iz end

-- Handles types 203-209
local function infl_cons_z_ks(typeno, data)
	local lemma = data.title

	local stem = lemma
	if data.no_singular then
		stem = get_stem(stem, "t")
	end

	local stemv = stem .. "õ"
	local stempl = palatalize_final_cons(stem)

	if typeno == 204 then
		-- nȭŗkõ- > nȭŗki-
		stempl = gsub(stempl, "%f[" .. vowels .. "]õ([^" .. vowels .. "]*)$", "i%1")
	end
	
	return make_forms {
		nsg = stem,
		gsg = stem,
		psg = stem .. "t",
		dsg = stemv,
		ilsg = stemv,
		elsg = stemv,
		npl = stem .. "t",
		ppl = stempl,
		dpl = (typeno == 209 and stempl or stem) .. "tõ",
		ilpl = stempl .. "i" .. get_illative_z(stempl),
		elpl = stempl .. "ist",
	}
end

for i = 203, 209 do inflections[i] = infl_cons_z_ks end

-- Nominal types within 210-223: nom->gen consonant alternations
local cons_mono_i_cons_altern = {
	[217] = { "kš", "d" },
	[218] = { "kš", "d" },
	[219] = { "ž", "d" },
}

-- Nominal types within 210-223: vowel alternations
local cons_mono_i_vowel_altern = {
	[211] = { "ä", "a" },
	[215] = { "ē", "īe" },
	[217] = { "i", "ī" },
	[218] = { "a", "ǭ" },
	[220] = { "ē", "īe" },
	[223] = { "ē", "ǟ" },
}

-- Nominal types within 210-223 with plain/broken tone alternations
local cons_mono_i_plain_broken = listToSet({
	217, 218
})

-- Nominal types within 210-223 with -kõ- in the instructive singular.
local cons_mono_i_inssg_k = listToSet({
	213, 214, 215, 216, 217, 218, 219, 220, 221, 223
})

-- Handles types 210-223
local function infl_cons_mono_i(typeno, data)
	local lemma = data.title

	-- Two stems: stem (nominative singular, some plural forms) and stem2
	local stem
	if data.no_singular then
		if 217 <= typeno and typeno <= 219 then
			stem = lemma
		else
			stem = get_stem(lemma, "[dt]")
		end

		if cons_mono_i_plain_broken[typeno] then
			stem = make_plain(stem)
		end

		if cons_mono_i_cons_altern[typeno] then
			local c1, c2 = unpack(cons_mono_i_cons_altern[typeno])
			stem = get_stem(stem, c2) .. c1
		elseif typeno ~= 212 and typeno ~= 222 then
			stem = palatalize_final_cons(stem)
		end

		if cons_mono_i_vowel_altern[typeno] then
			local v1, v2 = unpack(cons_mono_i_vowel_altern[typeno])
			stem = gsub(stem, "%f[" .. vowels .. "]" .. v2 .. "([^" .. vowels .. "]*)$", v1 .. "%1")
		end
	else
		stem = lemma
	end

	local stem2 = stem
	
	if cons_mono_i_cons_altern[typeno] then
		local c1, c2 = unpack(cons_mono_i_cons_altern[typeno])
		stem2 = get_stem(stem2, c1) .. c2
	else
		stem2 = depalatalize_final_cons(stem2)
	end

	if cons_mono_i_vowel_altern[typeno] then
		local v1, v2 = unpack(cons_mono_i_vowel_altern[typeno])
		stem2 = gsub(stem2, "%f[" .. vowels .. "]" .. v1 .. "([^" .. vowels .. "]*)$", v2 .. "%1")
	end

	if cons_mono_i_plain_broken[typeno] then
		stem2 = make_broken(stem2)
	end

	-- Partitive singular
	local psg
	if typeno <= 212 then
		psg = stem2 .. "tā"
	elseif typeno == 213 then
		psg = do_vowel_short_to_long(make_plain(stem2)) .. "da"
	elseif typeno <= 216 then
		psg = stem2 .. "dõ"
	else
		psg = stem2 .. "tõ"
	end

	-- Plural forms
	local nsg = stem
	if typeno ~= 212 and typeno ~= 222 then
		stem = palatalize_final_cons(stem)
	end

	local stemdpl
	if typeno <= 212 then
		stemdpl = stem2 .. "t"
	elseif typeno <= 215 or typeno >= 220 then
		stemdpl = stem2 .. "d"
	else
		stemdpl = stem2
	end

	-- Partitive plural
	local steminspl = typeno == 212 and stemdpl or stemdpl .. "k"
	local plstem = typeno == 212 and stemdpl or stem
	local pl_i = typeno <= 211 and "ī" or "i"

	local ppl
	if typeno == 212 then
		ppl = palatalize_final_cons(stem2) .. "t"
	elseif typeno == 213 then
		ppl = plstem .. "ḑ"
	else
		ppl = plstem
	end

	-- Illative plural
	local ilpl
	if typeno == 213 then
		ilpl = make_plain(plstem) .. "ī"
	elseif typeno == 217 or typeno == 218 then
		ilpl = stem2 .. "i"
	else
		ilpl = plstem .. pl_i
	end

	-- Elative plural
	local elpl
	if typeno <= 212 or typeno == 219 then
		elpl = plstem .. pl_i .. "st"
	elseif typeno == 217 or typeno == 218 then
		elpl = stem2 .. "šti"
	else
		elpl = plstem .. "šti"
	end
	
	return make_forms {
		nsg = nsg,
		gsg = stem2,
		psg = psg,
		dsg = geminate_final(stem2) .. "õ",
		inssg = cons_mono_i_inssg_k[typeno] and stem2 .. "kõ" or geminate_final(stem2) .. "õ",
		ilsg = geminate_final(stem2) .. "õ",
		elsg = typeno <= 212 and stem2 .. "õ" or stem2,
		npl = ungeminate_final(stem2 .. (typeno <= 212 and "t" or "d")),
		ppl = ppl,
		dpl = stemdpl .. "õ",
		inspl = steminspl .. "õ",
		ilpl = ilpl .. get_illative_z(ilpl),
		elpl = elpl,
	}
end

for i = 210, 223 do inflections[i] = infl_cons_mono_i end

-- Nominal types within 224-242 with -õ- before the elative ending
local cons_poly_elsg_vowel = listToSet({
	226, 227, 228, 229, 230, 232, 233, 234, 235, 236, 237, 238
})

-- Nominal types within 224-242 with -õ- > -i- in some plural endings
local cons_poly_pl_i = listToSet({
	229, 230
})

-- Nominal types within 224-242 with -õ- > -i- and palatalization in some plural endings
local cons_poly_pl_i_pal = listToSet({
	226, 227, 228, 232, 233, 234, 235, 236, 241
})

-- Handles types 224-242
local function infl_cons_poly(typeno, data)
	local lemma = data.title

	local nsg = lemma
	local stem, z
	if data.no_singular then
		stem = get_stem(lemma, "d")
		z = ""
	else
		stem, z = get_stem(lemma, "[zž]?")
	end
	
	local plstem = stem

	if typeno == 232 then
		stem = depalatalize_final_cons(stem)
	end

	if cons_poly_pl_i_pal[typeno] then
		plstem = gsub(plstem, "%f[" .. vowels .. "]õ([^" .. vowels .. "]*)$", "i%1")
		plstem = palatalize_final_cons(plstem)
	elseif cons_poly_pl_i[typeno] then
		plstem = gsub(plstem, "%f[" .. vowels .. "]õ([^" .. vowels .. "]*)$", "i%1")
	end

	return make_forms {
		nsg = nsg,
		gsg = stem,
		psg = stem .. (typeno >= 229 and z .. "t" or "t"),
		dsg = typeno == 224 and stem or stem .. "õ",
		ilsg = stem .. "õ",
		elsg = cons_poly_elsg_vowel[typeno] and stem .. "õ" or stem,
		npl = stem .. "d",
		ppl = typeno <= 225 and plstem .. "ḑ" or plstem,
		dpl = stem .. "dõ",
		inspl = stem .. (typeno <= 225 and "dkõ" or "dõ"),
		ilpl = typeno <= 225 and plstem .. get_illative_z(plstem) .. "i" or plstem .. "i" .. get_illative_z(plstem),
		elpl = typeno <= 225 and plstem .. "šti" or plstem .. "ist",
	}
end

for i = 224, 242 do inflections[i] = infl_cons_poly end

-- inflection classes end

local decl_table_base = [=[
!
! singular <small>({{l|liv|ikšlu’g}})</small>
! plural <small>({{l|liv|pǟgiņlu’g}})</small>
|-
! nominative <small>({{l|liv|nominatīv}})</small>
| {{{sg_nom}}}
| {{{pl_nom}}}
|-
! genitive <small>({{l|liv|genitīv}})</small>
| {{{sg_gen}}}
| {{{pl_gen}}}
|-
! partitive <small>({{l|liv|partitīv}})</small>
| {{{sg_par}}}
| {{{pl_par}}}
|-
! dative <small>({{l|liv|datīv}})</small>
| {{{sg_dat}}}
| {{{pl_dat}}}
|-
! instrumental <small>({{l|liv|instrumentāl}})</small>
| {{{sg_ins}}}
| {{{pl_ins}}}
|-
! illative <small>({{l|liv|illatīv}})</small>
| {{{sg_ill}}}
| {{{pl_ill}}}
|-
! inessive <small>({{l|liv|inesīv}})</small>
| {{{sg_ine}}}
| {{{pl_ine}}}
|-
! elative <small>({{l|liv|elatīv}})</small>
| {{{sg_ela}}}
| {{{pl_ela}}}
]=]

local decl_table_ad = [=[
|-
! allative <small>({{l|liv|allatīv}})</small>
| {{{sg_all}}}
| {{{pl_all}}}
|-
! adessive <small>({{l|liv|adesīv}})</small>
| {{{sg_ade}}}
| {{{pl_ade}}}
|-
! ablative <small>({{l|liv|ablatīv}})</small>
| {{{sg_abl}}}
| {{{pl_abl}}}
]=]

local function tag(text)
	return require("Module:script utilities").tag_text(text, lang, nil, "term")
end

local function link(text, prefix, suffix)
	if prefix and find(prefix, "%[%[") then
		if not find(text, "%[%[") then
			text = "[[" .. text .. "]]"
		end
		return require("Module:script utilities").tag_text(require("Module:links").language_link{ term = (prefix and prefix or "") .. text, lang = lang } .. (suffix and suffix or ""), lang)
	else
		return require("Module:script utilities").tag_text((prefix and prefix or "") .. require("Module:links").language_link{ term = text, lang = lang } .. (suffix and suffix or ""), lang)
	end
end

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

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

local function help_tooltip(text)
	return "<sup>" .. tostring(mw.html.create("span"):attr("style", "cursor:help"):attr("title", text):wikitext("?")) .. "</sup>"
end

local function note_reference(note)
	if note then
		return "<sup>" .. note .. ")</sup>"
	else
		return ""
	end
end

local function note_text(reference, text)
	return note_reference(reference) .. " " .. text
end

-- extract notes from forms into a list and replace by their index
local function format_notes_for_form(data, form)
	local note = form.note
	if note then
		local index = data.notes_reverse[note]
		if index then
			form.note = index
		else
			index = tostring(#data.notes + 1)
			table.insert(data.notes, note)
			data.notes_reverse[note] = index
			form.note = index
		end
	end
end

local function format_notes(data, forms)
	data.notes = {}
	data.notes_reverse = {}
	for k, v in pairs(forms) do
		if type(v) == "table" then
			if v[1] then
				for _, form in ipairs(v) do
					if type(form) == "table" then
						format_notes_for_form(data, form)
					end
				end
			else
				format_notes_for_form(data, v)
			end
		end
	end
	data.notes_reverse = nil
end

local commabelow = char(0x326)

local function normalize(word)
	word = gsub(word, "[ţ']", { ["ţ"] = "ț", ["'"] = "’" })
	word = gsub(word, "d"..commabelow, "ḑ")
	word = gsub(word, "_", " ")
	return word
end

function export.raw(inflected_part, infl_type, word_prefix, args)
	if not infl_type then error("inflection class not specified") end
	args = args or {}
	local infl = inflections[infl_type] or error("unsupported inflection type")
	
	inflected_part = normalize(inflected_part)
	local data = { title = inflected_part, args = args, prefix = word_prefix }
	
	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
	elseif args["sg"] then
		data.no_plural = true
	elseif args["pl"] then
		data.no_singular = true
	end
	
	if args["ad"] then
		data.has_ad = true
	end

	local forms = infl(infl_type, data)
	postprocess(data, forms)
	return forms
end

local function parse_inflections(args, title)
	local infl_type = args[1]
	if not infl_type then error("inflection class not specified") end
	
	local infl_types = {}
	for itype in infl_type:gmatch("[^/]+") do
		local itypeno = tonumber(itype)
		if not itypeno then error("inflection types must be numbers") end
		table.insert(infl_types, itypeno)
	end
	
	local function make_data(prefix, lemma)
		return { title = lemma, args = args, prefix = prefix }
	end
	
	local infl_datas
	if not args[2] then
		args[2] = title
	end
	
	local respelled = ""
	local word_prefix = ""
	local word_suffix = args["suffix"]
	
	infl_datas = {}
	local i = 2
	while args[i] do
		local normarg = normalize(args[i])
		if match(normarg, "^%s*$") then
			if not match(respelled, "^%s*$") then
				table.insert(infl_datas, make_data(word_prefix, respelled))
			end
			word_prefix = ""
		else
			word_prefix = word_prefix .. respelled
		end
		respelled = normalize(args[i])
		i = i + 1
	end
	
	table.insert(infl_datas, make_data(word_prefix, respelled))
	
	if #infl_types ~= #infl_datas then
		error("Different number of inflection types and inflectable parts specified")
	end
	
	return infl_types, infl_datas
end

local function combine_forms(infl_results)
	if #infl_results <= 1 then return infl_results[1] end
	
	local final_result = {}
	
	local function find_next(form, result, index)
		if index > #infl_results then
			if not final_result[form] then final_result[form] = {} end
			table.insert(final_result[form], result)
		else
			local v = infl_results[index][form]
			if v == nil then
				-- do nothing
			elseif type(v) == "string" then
				find_next(form, result .. v, index + 1)
			else
				for _, f in ipairs(v) do
					if f then
						find_next(form, result .. f, index + 1)
					end
				end
			end
		end
	end
	
	for form, _ in pairs(infl_results[1]) do
		find_next(form, "", 1)
	end
	
	return final_result
end

function export.show(frame)
	local args = frame:getParent().args
	
	local title = args["title"] or mw.loadData("Module:headword/data").pagename
	local infl_types, infl_datas = parse_inflections(args, title)
	
	local word_suffix = args["suffix"]
	if word_suffix then
		infl_datas[#infl_datas].suffix = word_suffix
	end
	
	local joined = {}
	for _, data in ipairs(infl_datas) do
		table.insert(joined, (data.prefix or "") .. data.title)
	end
	joined = table.concat(joined)
	
	if lang:stripDiacritics(joined) ~= title then
		error("Respelling does not match title")
	end
	
	for _, data in ipairs(infl_datas) do
		if match(data.title, "^%u%U") then
			data.capitalize = true
			data.title = gsub(data.title, "^%u", lower)
		end
	end
	
	local categories = {}

	local all_data = {}
	if args["n"] then
		if args["n"] == "s" or args["n"] == "sg" then
			all_data.no_plural = true
		elseif args["n"] == "p" or args["n"] == "pl" then
			all_data.no_singular = true
		end
	elseif args["sg"] then
		all_data.no_plural = true
	elseif args["pl"] then
		all_data.no_singular = true
	end
	
	if args["ad"] then
		all_data.has_ad = true
	end
	
	local inflection_results = {}
	for index, data in ipairs(infl_datas) do
		for k, v in pairs(all_data) do
			data[k] = v
		end
		
		local infl_type = infl_types[index]
		local infl = inflections[infl_type] or error("unsupported inflection type " .. infl_type)
		local forms = infl(infl_type, data)
		postprocess(data, forms)
		format_notes(data, forms)
		table.insert(inflection_results, forms)
	end
	
	local forms = combine_forms(inflection_results)

	word_suffix = word_suffix or ""
	
	local table_title = "Declension of " .. mention(joined .. word_suffix) .. " ([[Appendix:Livonian declension#" .. infl_types[#infl_types] .. "|" .. infl_types[#infl_types] .. "]])"
	
	local function repl(form)
		local prefix = ""
		local suffix = ""
		
		if find(form, "^%^") then
			local value = forms[sub(form, 2)]
			if type(value) == "table" and value[1] then
				return value[1]
			elseif type(value) == "string" and #value > 0 then
				return value
			else
				return "&mdash;"
			end
		end
		
		if find(form, "%$") then
			local excl = find(form, "%$")
			prefix = sub(form, 1, excl - 1)
			form = sub(form, excl + 1)
		end
		if find(form, "@") then
			local excl = find(form, "@")
			suffix = sub(form, excl + 1)
			form = sub(form, 1, excl - 1)
		end

		local value = forms[form]
		if type(value) == "table" and value[1] then
			local result = {}
			for _, f in ipairs(value) do
				table.insert(result, link(f, prefix, suffix))
			end
			return table.concat(result, "<br/>")
		elseif type(value) == "string" and #value > 0 then
			return link(value, prefix, suffix)
		else
			return "&mdash;"
		end
	end
	
	local function process_text(text)
		local result = gsub(text, "{{{([^%}]+)}}}", repl)
		result = gsub(result, "{{l|liv|([^}]-)}}", link)
		result = gsub(result, "{{m|liv|([^|}]-)||([^}]-)}}", mention_with_gloss)
		result = gsub(result, "{{m|liv||([^}]-)}}", tag)
		result = gsub(result, "{{m|liv|([^}]-)}}", mention)
		return result
	end
	
	local notes = {}
	local builtin_notes = {}
	for index, note in ipairs(builtin_notes) do
		table.insert(notes, note_text(tostring(#notes + 1), process_text(note)))
	end
	for _, data in ipairs(infl_datas) do
		for index, note in ipairs(data.notes) do
			table.insert(notes, note_text(tostring(#notes + 1), note))
		end
	end
	notes = table.concat(notes, "<br />")

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

	local decl_table = decl_table_base
	if all_data.has_ad then
		decl_table = decl_table .. decl_table_ad
	end
	
	local result = process_text(decl_table)
	return require("Module:inflection table").inflection_table(frame, {
		title = table_title,
		palette = "teal",
		tall = "yes",
		notes = notes,
	}, result) .. require("Module:utilities").format_categories(categories, lang)
end

return export