Module:User:Surjection/izh-decl

This is a private module sandbox of Surjection, for their own experimentation. Items in this module may be added and removed at Surjection's discretion; do not rely on this module's stability.


local export = {}
local lang = require("Module:languages").getByCode("izh")

-- syllable handling begin
-- possibly could go into a shared module if/when a pronunciation module is implemented

local function guess_vowel_harmony(word)
	local l = mw.ustring.len(word)
	for i = l, 1, -1 do
		local c = mw.ustring.sub(word, i, i)
		if mw.ustring.match(c, "[aou]") then
			return "a"
		elseif mw.ustring.match(c, "[äöy]") then
			return "ä"
		end
	end
	return "ä"
end

local consonants = "bcdfghjklmnprsštvzž"
local consonant = "[" .. consonants .. "]"
local vowels = "aeiouyäö"
local vowel = "[" .. vowels .. "]"

-- orthographic symbols that signify separation of syllables
local sep_symbols = "-'’./ "

local vowel_sequences = {
	"[aeouyäö]i",
	"[aeo]u",
	"[äeö]y",
	"[aä]e",
	"aa", "ee", "ii", "oo", "uu", "yy", "ää", "öö"
}

-- adapted from [[Module:fi-hyphenation]]
local function split_syllables(word)
	local res = {}
	local syllable = ""
	local pos = 1
	local found_vowel = false
	
	while pos <= #word do
		if mw.ustring.find(mw.ustring.lower(word), "^" .. consonant .. vowel, pos) then
			-- CV: end current syllable if we have found a vowel
			if found_vowel then
				if syllable then table.insert(res, syllable) end
				found_vowel = false
				syllable = ""
			end
			syllable = syllable .. mw.ustring.sub(word, pos, pos)
			pos = pos + 1
		elseif mw.ustring.find(mw.ustring.lower(word), "^" .. consonant, pos) then
			-- C: continue
			syllable = syllable .. mw.ustring.sub(word, pos, pos)
			pos = pos + 1
		elseif mw.ustring.find(mw.ustring.lower(word), "^" .. vowel, pos) then
			if found_vowel then
				-- already found a vowel, end current syllable
				if syllable then
					table.insert(res, syllable)
				end
				syllable = ""
			end	
			found_vowel = true
			
			-- check for diphthongs or long vowels
			local seq_ok = false
			for k, v in pairs(vowel_sequences) do
				if mw.ustring.find(mw.ustring.lower(word), "^" .. v, pos) then
					seq_ok = true
					break
				end
			end
			
			if seq_ok then
				syllable = syllable .. mw.ustring.sub(word, pos, pos + 1)
				pos = pos + 2
			else
				syllable = syllable .. mw.ustring.sub(word, pos, pos)
				pos = pos + 1
			end
		elseif mw.ustring.find(mw.ustring.lower(word), "^[" .. sep_symbols .. "]", pos) then
			-- separates syllables
			if syllable then
				table.insert(res, syllable)
			end
			
			local sepchar = mw.ustring.sub(word, pos, pos)
			syllable = ""
			pos = pos + 1
			found_vowel = false
		else
			-- ?: continue
			syllable = syllable .. mw.ustring.sub(word, pos, pos)
			pos = pos + 1
		end
	end
	
	if syllable then
		table.insert(res, syllable)
	end
	
	return res
end

-- syllable handling end

local consonant_geminatable = "fhjklmnprsštv"

local function make_geminated_stem(word)
	local prefix, fc, fv = mw.ustring.match(word, "(.*)([" .. consonant_geminatable .. "])(" .. vowel .. ")$")
	if not prefix then return nil end
	if mw.ustring.match(prefix, consonant .. "$") then return nil end
	if fc == "j" then
		return prefix .. "i" .. fc
	else
		return prefix .. fc .. fc
	end
end

local function is_heavy_syllable(syl)
	return mw.ustring.match(syl, consonant .. "$") or mw.ustring.match(syl, vowel .. vowel)
end

local function guess_gemination(word)
	local syl = split_syllables(word)
	if not syl then return nil end
	local n = #syl
	if n < 2 then return nil end
	if n % 2 == 1 then return nil end
	if is_heavy_syllable(syl[n - 1]) then return nil end
	return make_geminated_stem(word)
end

local function guess_elongation(word)
	local syl = split_syllables(word)
	if not syl then return nil end
	local n = #syl
	local s = n - 2
	if s < 1 then s = 1 end
	for i = s, n do
		if is_heavy_syllable(syl[i]) then
			return true
		end
	end
	return false
end

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

local function elongate(stem, condition)
	if condition == nil or condition then
		-- already long?
		if mw.ustring.match(stem, vowel .. vowel .. "$") then
			for k, v in pairs(vowel_sequences) do
				if mw.ustring.find(stem, v .. "$", pos) then
					return stem
				end
			end
		end
		return stem .. mw.ustring.sub(stem, -1)
	else
		return stem
	end
end

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

local function geminate(c)
	if mw.ustring.match(c, consonant .. consonant .. "$") then
		return c
	end
	return c .. mw.ustring.sub(c, -1)
end

local function join(...)
	local t = {}
	for _, s in ipairs(arg) 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 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 elongate_all(t)
	if type(t) == "string" then
		return elongate(t)
	end
	local r = {}
	for _, v in ipairs(t) do
		table.insert(r, elongate(v))
	end
	return r
end

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

local function get_geminated(data, w, fallback)
	if data.geminate == false then return fallback end
	if data.headword then
		local gemprefix = mw.ustring.len(data.title) - mw.ustring.len(data.headword)
		if gemprefix > 0 then
			local wp = mw.ustring.sub(w, 1, gemprefix)
			local wg = guess_gemination(mw.ustring.sub(w, gemprefix + 1))
			if not wg then return fallback end
			return wp .. wg
		end
	end
	return guess_gemination(w) or fallback
end

local function process(data, result, vh)
	local vh = data.vh
	-- genitive singular/plural stem
	local gs = result.stem_gs
	local gp = result.stem_gp
	-- partitive singular/plural stem
	local ps = result.stem_ps
	local pp = result.stem_pp
	-- oblique plural stem
	local op = result.stem_op or pp
	-- illative singular/plural stem
	local is = result.stem_is or gs
	local ip = result.stem_ip or op
	-- allative/inessive singular/plural stem
	local as = result.stem_as or gs
	local ap = result.stem_ap or op
	-- essive singular/plural stem
	local es = result.stem_es or is
	local ep = result.stem_ep or ip
	-- exessive singular/plural stem
	local xs = result.stem_xs or (result.exe_weak and gs or es)
	local xp = result.stem_xp or (result.exe_weak and op or ep)

	if not data.no_singular then
		result["nom_sg"] = { data.title }
		result["gen_sg"] = append(gs, "n")
		result["par_sg"] = append(ps, vh)
		if result.par_short_ok then
			result["par_sg"] = join(result["par_sg"], ps)
		end
		if result.ill_short_sg then
			result["ill_sg"] = append(is, result.ill_ending or result.ill_ending_sg or nil)
		else
			result["ill_sg"] = append(elongate_all(is), result.ill_ending or result.ill_ending_sg or nil)
		end
		result["ine_sg"] = append(as, "s")
		result["ela_sg"] = append(gs, "st")
		result["all_sg"] = append(gs, "lle")
		result["ade_sg"] = append(as, "l")
		result["abl_sg"] = append(gs, "lt")
		result["tra_sg"] = append(gs, "ks")
		result["ess_sg"] = join(append(es, "nn" .. vh), append(elongate_all(is), "n"))
		result["exe_sg"] = append(xs, "nt")
	end

	if not data.no_plural then
		result["nom_pl"] = append(gs, "t")
		result["gen_pl"] = append(gp, "n")
		result["par_pl"] = append(pp, vh)
		result["ill_pl"] = append(elongate_all(ip), result.ill_ending or result.ill_ending_pl or nil)
		result["ine_pl"] = append(ap, "s")
		result["ela_pl"] = append(op, "st")
		result["all_pl"] = append(op, "lle")
		result["ade_pl"] = append(ap, "l")
		result["abl_pl"] = append(op, "lt")
		result["tra_pl"] = append(op, "ks")
		result["ess_pl"] = join(append(ep, "nn" .. vh), append(elongate_all(ip), "n"))
		result["exe_pl"] = append(xp, "nt")
	end

	return result
end

-- inflection classes begin
local inflections = {}

inflections["kärpäin"] = function (data)
	local result = { typeno = "1" }
	local word = data.title
	if data.no_singular then -- plural title -> singular
		word = get_stem(word, "iset") .. "in"
	end
	local stem = get_stem(word, "in")

	result.stem_gs = stem .. "ise"
	result.stem_ps = stem .. "ist"
	result.stem_as = stem .. "isee"

	result.stem_gp = stem .. "isii"
	result.stem_pp = stem .. "isi"
	result.stem_ap = stem .. "isii"

	result.par_short_ok = true
	result.exe_weak = true

	return process(data, result)
end

inflections["jokahiin"] = function (data)
	local result = { typeno = "1" }
	local word = data.title
	if data.no_singular then -- plural title -> singular
		word = get_stem(word, "hiset") .. "hiin"
	end
	local stem = get_stem(word, "hiin")

	result.stem_gs = stem .. "hise"
	result.stem_ps = stem .. "hist"

	result.stem_gp = stem .. "hisii"
	result.stem_pp = stem .. "hisi"

	result.par_short_ok = true

	return process(data, result)
end

inflections["kolmas"] = function (data)
	local result = { typeno = "2" }
	local word = data.title
	if data.no_singular then -- plural title -> singular
		word = get_stem(word, "nnet") .. "s"
	end
	local stem = get_stem(word, "s")

	result.stem_gs = stem .. "nne"
	result.stem_ps = stem .. "tt"
	result.stem_is = stem .. "nte"
	result.stem_as = stem .. "nnee"

	result.stem_gp = stem .. "nsii"
	result.stem_pp = stem .. "nsi"
	result.stem_ip = stem .. "nsii"
	result.stem_ap = stem .. "nsii"

	result.exe_weak = true
	
	return process(data, result)
end

inflections["mees"] = function (data)
	local result = { typeno = "2" }
	local word = data.title
	if data.no_singular then -- plural title -> singular
		word = get_stem(word, "het") .. "s"
	end
	local stem = get_stem(word, "s")

	result.stem_gs = stem .. "he"
	result.stem_ps = stem .. "st"
	result.stem_as = stem .. "hee"

	result.stem_gp = stem .. "hii"
	result.stem_pp = stem .. "hi"
	result.stem_ap = stem .. "hii"

	result.exe_weak = true

	return process(data, result)
end

inflections["patsas"] = function (data)
	local result = { typeno = "2" }
	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")
	if data.no_singular then -- plural title -> singular
		-- does not have to be perfect, plural forms being right is enough
		word = get_stem(word, "t")
		local final = mw.ustring.sub(word, -1, -1)
		word = get_stem(mw.ustring.sub(word, 1, -3), strong) .. weak .. final .. "s"
	end
	local final = mw.ustring.sub(word, -2, -2)
	if not mw.ustring.match(final, vowel) then
		error("invalid penultimate character for tytär-type nominals (should be a vowel)")
	end
	local stem = get_stem(word, weak .. final .. "s")
	local longfinal = final .. final
	if longfinal == "ii" then longfinal = "ee" end
	local gem = get_geminated(data, stem .. strong .. final, stem .. strong)

	result.stem_gs = gem .. longfinal
	result.stem_ps = stem .. weak .. final .. "st"
	result.stem_is = gem .. longfinal

	result.stem_gp = gem .. final .. "i"
	result.stem_pp = result.stem_gp .. "t"
	result.stem_op = result.stem_gp

	result.par_short_ok = true
	result.ill_ending = "sse"
	result.exe_weak = true

	result.grade = make_gradation(strong, weak)
	result.geminate = gem ~= stem .. strong
	return process(data, result)
end

inflections["petos"] = function (data)
	local result = { typeno = "2" }
	local word = data.title
	if data.no_singular then -- plural title -> singular
		word = get_stem(word, "kset") .. "s"
	end
	local stem = get_stem(word, "s")

	result.stem_gs = stem .. "kse"
	result.stem_ps = stem .. "st"
	result.stem_as = stem .. "ksee"

	result.stem_gp = stem .. "ksii"
	result.stem_pp = stem .. "ksi"
	result.stem_ap = stem .. "ksii"

	result.par_short_ok = true
	result.exe_weak = true

	return process(data, result)
end

inflections["oikehus"] = function (data)
	local result = { typeno = "2" }
	local word = data.title
	if data.no_singular then -- plural title -> singular
		word = get_stem(word, "et") .. "s"
	end
	local stem = get_stem(word, "s")

	result.stem_gs = stem .. "e"
	result.stem_ps = stem .. "tt"

	result.stem_gp = stem .. "ksi"
	result.stem_pp = result.stem_gp

	result.exe_weak = true

	return process(data, result)
end

inflections["kana"] = function (data)
	local result = { typeno = "3" }
	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
	if data.no_singular then -- plural title -> singular
		word = get_stem(word, weak .. vh .. "t") .. strong .. vh
	end
	local stem = get_stem(word, strong .. vh)
	local gem = get_geminated(data, word, stem .. strong)
	local long = guess_elongation(stem .. weak .. vh)

	result.stem_gs = stem .. weak .. vh
	result.stem_ps = gem .. vh
	result.stem_is = gem .. vh
	result.stem_as = elongate(stem .. weak .. vh, long)
	result.stem_es = stem .. strong .. vh

	result.stem_gp = gem .. frontalize("oi", vh)
	result.stem_pp = gem .. frontalize("oj", vh)
	result.stem_ip = result.stem_gp
	result.stem_op = stem .. weak .. frontalize("oi", vh)
	result.stem_ep = stem .. strong .. frontalize("oi", vh)

	result.grade = make_gradation(strong, weak)
	result.geminate = gem ~= stem .. strong
	return process(data, result)
end

inflections["koira"] = function (data)
	local result = { typeno = "3" }
	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
	if data.no_singular then -- plural title -> singular
		-- does not have to be perfect, plural forms being right is enough
		word = get_stem(word, weak .. vh .. "t") .. strong .. vh
	end
	local stem
	if mw.ustring.match(word, vh .. "$") then
		stem = get_stem(word, strong .. vh)
	else
		stem = get_stem(word, strong)
	end
	local gem = get_geminated(data, word, stem .. strong)
	local long = guess_elongation(stem .. weak .. vh)

	result.stem_gs = stem .. weak .. vh
	result.stem_ps = gem .. vh
	result.stem_is = result.stem_gs
	result.stem_as = elongate(result.stem_gs, long)
	result.stem_es = stem .. strong .. vh

	result.stem_gp = gem .. "ii"
	result.stem_pp = gem .. "i"
	result.stem_op = stem .. weak .. "i"
	result.stem_ip = result.stem_pp
	result.stem_ap = elongate(stem .. weak .. "i", long)
	result.stem_ep = stem .. strong .. "i"

	result.grade = make_gradation(strong, weak)
	result.geminate = gem ~= stem .. strong
	return process(data, result)
end

inflections["koivu"] = function (data)
	local result = { typeno = "4" }
	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
	if data.no_singular then -- plural title -> singular
		-- does not have to be perfect, plural forms being right is enough
		word = get_stem(word, "t")
		local final = mw.ustring.sub(word, -1, -1)
		word = get_stem(mw.ustring.sub(word, 1, -2), weak) .. strong .. final
	end
	local final = mw.ustring.sub(word, -1, -1)
	local stem = get_stem(word, strong .. final)
	local gem = get_geminated(data, word, stem .. strong)
	local long = guess_elongation(stem .. weak .. final)

	result.stem_gs = stem .. weak .. final
	result.stem_ps = gem .. final
	result.stem_is = result.stem_ps
	result.stem_as = elongate(stem .. weak .. final, long)
	result.stem_es = stem .. strong .. final

	result.stem_gp = { gem .. final .. "i", stem .. strong .. final .. frontalize("loi", vh) }
	result.stem_pp = { gem .. final .. "j", stem .. strong .. final .. frontalize("loj", vh) }
	result.stem_op = { stem .. weak .. final .. "i", stem .. weak .. final .. frontalize("loi", vh) }
	result.stem_ip = result.stem_gp
	result.stem_ap = result.stem_op
	result.stem_ep = { stem .. strong .. final .. "i", stem .. strong .. final .. frontalize("loi", vh) }

	result.ill_ending_pl = "he"

	result.grade = make_gradation(strong, weak)
	result.geminate = gem ~= stem .. strong
	return process(data, result)
end

inflections["keeli"] = function (data)
	local result = { typeno = "5" }
	local word = data.title
	local vh = data.vh
	if data.no_singular then -- plural title -> singular
		-- does not have to be perfect, plural forms being right is enough
		word = get_stem(word, "et") .. "i"
	end
	local stem, final
	if mw.ustring.match(word, "i$") then
		stem, final = mw.ustring.match(word, "(.-)(" .. consonant .. "+)i$")
	elseif mw.ustring.match(word, consonant .. "$") then
		stem, final = mw.ustring.match(word, "(.-)(" .. consonant .. "+)$")
	else
		error("keeli-type nominals may only end in i or a consonant!")
	end
	if not final then
		error("keeli-type, but has no consonant?")
	end
	local gem = get_geminated(data, word, stem .. final)
	local long = guess_elongation(stem .. final .. "i")

	result.stem_gs = stem .. final .. "e"
	if mw.ustring.match(final, "[kpt]s") then
		result.stem_ps = stem .. "st"
	elseif final == "m" then
		result.stem_ps = stem .. "nt"
	else
		result.stem_ps = stem .. final .. "t"
	end
	result.stem_is = gem .. "e"
	result.stem_as = stem .. final .. "ee"
	result.stem_es = stem .. final .. "e"

	result.stem_gp = gem .. "ii"
	result.stem_pp = gem .. "i"
	result.stem_ap = gem .. "ii"
	result.stem_ep = stem .. final .. "i"

	result.exe_weak = true

	result.geminate = gem ~= stem .. strong
	return process(data, result)
end

inflections["viis"] = function (data)
	local result = { typeno = "5" }
	local word = data.title
	local vh = data.vh
	if data.no_singular then -- plural title -> singular
		-- does not have to be perfect, plural forms being right is enough
		word = get_stem(word, "et")
		if mw.ustring.match(word, "vv$") then word = mw.ustring.sub(word, 1, -3) end
		if mw.ustring.match(word, "ij$") then word = mw.ustring.sub(word, 1, -3) .. "ii" end
		word = word .. "s"
	end
	local stem, final
	if mw.ustring.match(word, vowel .. "si$") then
		stem, final = mw.ustring.match(word, "(.-)(" .. vowel .. "+)si$")
	elseif mw.ustring.match(word, vowel .. "s$") then
		stem, final = mw.ustring.match(word, "(.-)(" .. vowel .. "+)s$")
	end
	if not final then
		error("invalid viis-type nominal")
	end
	local glide = final
	if glide == "ii" then glide = "ij" end
	local weak = data.args[1] or (mw.ustring.match(glide, "u$") and "vv" or "")

	result.stem_gs = stem .. glide .. weak .. "e"
	result.stem_ps = stem .. final .. "tt"
	result.stem_is = stem .. final .. "te"
	result.stem_as = stem .. glide .. weak .. "e"
	result.stem_xs = stem .. final .. "te"

	result.stem_gp = stem .. final .. "sii"
	result.stem_pp = stem .. final .. "si"

	if weak then
		result.grade = make_gradation("t", weak)
	end
	return process(data, result)
end

inflections["lehti"] = function (data)
	local result = { typeno = "5" }
	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")
	if data.no_singular then -- plural title -> singular
		-- does not have to be perfect, plural forms being right is enough
		word = get_stem(word, weak .. "et") .. strong .. "i"
	end
	local vh = data.vh
	local stem = get_stem(word, strong .. "i")
	local gem = get_geminated(data, word, stem .. strong)
	local long = guess_elongation(stem .. weak .. "e")

	result.stem_gs = stem .. weak .. "e"
	result.stem_ps = gem .. "i"
	result.stem_is = gem .. "e"
	result.stem_as = elongate(stem .. weak .. "e", long)
	result.stem_es = stem .. strong .. "e"

	result.stem_gp = stem .. strong .. frontalize("iloi", vh)
	result.stem_pp = stem .. strong .. frontalize("iloj", vh)
	result.stem_op = result.stem_gp
	result.stem_ap = elongate(result.stem_op, long)

	result.ill_ending_pl = "he"

	result.grade = make_gradation(strong, weak)
	result.geminate = gem ~= stem .. strong
	return process(data, result)
end

inflections["vahti"] = function (data)
	local result = { typeno = "5" }
	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")
	if data.no_singular then -- plural title -> singular
		-- does not have to be perfect, plural forms being right is enough
		word = get_stem(word, weak .. "it") .. strong .. "i"
	end
	local vh = data.vh
	local stem = get_stem(word, strong .. "i")
	local gem = get_geminated(data, word, stem .. strong)
	local long = guess_elongation(stem .. weak .. "i")

	result.stem_gs = stem .. weak .. "i"
	result.stem_ps = gem .. "i"
	result.stem_is = gem .. "i"
	result.stem_as = elongate(stem .. weak .. "i", long)
	result.stem_es = stem .. strong .. "i"

	result.stem_gp = stem .. strong .. frontalize("iloi", vh)
	result.stem_pp = stem .. strong .. frontalize("iloj", vh)
	result.stem_op = result.stem_gp
	result.stem_ap = elongate(result.stem_op, long)

	result.ill_ending_pl = "he"

	result.grade = make_gradation(strong, weak)
	result.geminate = gem ~= stem .. strong
	return process(data, result)
end

local vesi_strong = {["s"] = "t", ["rs"] = "rt"}
local vesi_weak = {["s"] = "", ["rs"] = "rr"}
inflections["vesi"] = function (data)
	local result = { typeno = "5" }
	local word = data.title
	local vh = data.vh
	if data.no_singular then -- plural title -> singular
		-- does not have to be perfect, plural forms being right is enough
		word = get_stem(word, "et")
		if mw.ustring.match(word, "rr$") then
			word = mw.ustring.sub(word, 1, -3) .. "rsi"
		else
			word = word .. "si"
		end
	end
	local stem, final, strong, weak
	if mw.ustring.match(word, "si$") then
		stem, final = mw.ustring.match(word, "(.-)(" .. consonant .. "+)i$")
	elseif mw.ustring.match(word, "s$") then
		stem, final = mw.ustring.match(word, "(.-)(" .. consonant .. "+)$")
	end
	if not final then
		error("invalid vesi-type nominal")
	end
	local strong = vesi_strong[final] or error("final consonant not supported")
	local weak = vesi_weak[final] or error("final consonant not supported")
	local gem = geminate(final)
	local long = weak ~= ""

	result.stem_gs = stem .. weak .. "e"
	result.stem_ps = stem .. strong .. "t"
	result.stem_is = stem .. strong .. "te"
	result.stem_as = elongate(stem .. weak .. "e", long)
	result.stem_es = stem .. strong .. "tee"
	result.stem_xs = stem .. strong .. "te"

	result.stem_gp = stem .. gem .. "ii"
	result.stem_pp = stem .. gem .. "i"
	result.stem_op = stem .. final .. "i"
	result.stem_ip = stem .. gem .. "i"
	result.stem_ap = elongate(stem .. final .. "i", long)
	result.stem_ep = stem .. gem .. "ii"
	result.stem_xp = stem .. gem .. "i"

	if weak == "" then
		result.grade = make_gradation("t", weak)
	end
	return process(data, result)
end

inflections["veitsi"] = function (data)
	local result = { typeno = "5" }
	local word = data.title
	local vh = data.vh
	if data.no_singular then -- plural title -> singular
		-- does not have to be perfect, plural forms being right is enough
		word = get_stem(word, "et") .. "i"
	end
	local stem, final = mw.ustring.match(word, "(.-)(" .. consonant .. "+)i")
	if not final then
		error("Invalid veitsi-type nominal")
	end
	local base = stem .. final
	local gem = get_geminated(data, base .. "e", stem .. final)

	result.stem_gs = base .. "e"
	if mw.ustring.match(final, "[kpt]s") then
		result.stem_ps = stem .. "st"
	elseif final == "m" then
		result.stem_ps = stem .. "nt"
	else
		result.stem_ps = stem .. final .. "t"
	end
	result.stem_is = gem .. "e"
	result.stem_as = base .. "e"
	result.stem_es = base .. "e"

	result.stem_gp = gem .. "ii"
	result.stem_pp = gem .. "i"
	result.stem_op = base .. "i"
	result.stem_ip = gem .. "i"
	result.stem_xp = base .. "i"

	result.geminate = gem ~= base
	return process(data, result)
end

inflections["lähe"] = function (data)
	local result = { typeno = "6" }
	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 stem, gem
	if data.no_singular then -- plural title -> singular
		-- does not have to be perfect, plural forms being right is enough
		gem = get_stem(word, "eet")
		stem = gem
	else
		stem = get_stem(word, weak .. "e")
		gem = get_geminated(data, stem .. strong .. "e", stem .. strong)
	end

	result.stem_gs = gem .. "ee"
	result.stem_ps = stem .. weak .. "ett"

	result.stem_gp = gem .. "ei"
	result.stem_pp = result.stem_gp .. "t"
	result.stem_op = result.stem_gp

	result.ill_ending = "sse"
	
	result.grade = make_gradation(strong, weak)
	result.geminate = gem ~= stem .. strong
	return process(data, result)
end

inflections["kevät"] = function (data)
	local result = { typeno = "7" }
	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 final, stem, gem
	if data.no_singular then -- plural title -> singular
		-- does not have to be perfect, plural forms being right is enough
		word = get_stem(word, "t")
		final = mw.ustring.sub(word, -2, -2)
		gem = mw.ustring.sub(word, 1, -3)
		stem = gem
	else
		final = mw.ustring.sub(word, -2, -2)
		stem = get_stem(word, weak .. final .. "t")
		gem = get_geminated(data, stem .. strong .. final, stem .. strong)
	end

	result.stem_gs = gem .. final .. "e"
	result.stem_ps = stem .. weak .. final .. "tt"
	result.stem_as = result.stem_gs .. "e"

	result.stem_gp = gem .. final .. "ei"
	result.stem_pp = result.stem_gp .. "t"
	result.stem_op = result.stem_gp

	result.ill_short_sg = true
	result.ill_ending = "sse"
	
	result.grade = make_gradation(strong, weak)
	result.geminate = gem ~= stem .. strong
	return process(data, result)
end

inflections["maa"] = function (data)
	local result = { typeno = "8" }
	local word = data.title
	if data.no_singular then -- plural title -> singular
		word = get_stem(word, vh .. "t")
	end
	local stem = mw.ustring.sub(word, 1, -2)

	result.stem_gs = word
	result.stem_ps = word .. "t"

	result.stem_gp = stem .. "ije"
	result.stem_pp = stem .. "it"
	result.stem_op = stem .. "i"

	result.ill_ending_sg = "h" .. mw.ustring.sub(word, -1, -1)
	if result.ill_ending_sg == "hi" then result.ill_ending_sg = "he" end
	result.ill_ending_pl = "he"
	
	return process(data, result)
end

inflections["yks"] = function (data)
	local result = { typeno = "9" }
	local word = data.title
	if data.no_singular then -- plural title -> singular
		word = get_stem(word, vh .. "het") .. "ks"
	end
	local stem = get_stem(word, "ks")

	result.stem_gs = stem .. "he"
	result.stem_ps = stem .. "ht"
	result.stem_is = stem .. "hte"

	result.stem_gp = stem .. "ksii"
	result.stem_pp = stem .. "ksi"
	result.stem_op = stem .. "ksi"

	return process(data, result)
end

inflections["kolt"] = function (data)
	local result = { typeno = "10" }
	local word = data.title
	if data.no_singular then -- plural title -> singular
		word = get_stem(word, vh .. "met") .. "t"
	end
	local stem = get_stem(word, "t")

	result.stem_gs = stem .. "me"
	result.stem_ps = result.stem_gs
	result.stem_as = result.stem_gs .. "e"

	result.stem_gp = stem .. "mii"
	result.stem_pp = stem .. "mi"
	result.stem_ap = result.stem_pp .. "i"

	return process(data, result)
end

inflections["kaheksan"] = function (data)
	local result = { typeno = "11" }
	local word = data.title
	local final = data.args[1]
	if data.no_singular then -- plural title -> singular
		word = get_stem(word, vh .. "t")
		if mw.ustring.sub(word, -1, -1) == "m" then
			word = mw.ustring.sub(word, 1, -2) .. "n"
		elseif mw.ustring.match(word, "[s]$") then
			word = word .. vh .. "n"
		end
	end
	local vh = data.vh
	local stem
	if mw.ustring.match(word, vh .. "n$") then
		stem = get_stem(word, vh .. "n")
		final = final or ""
	else
		stem = get_stem(word, "n")
		final = final or "n"
	end

	result.stem_gs = stem .. final .. vh
	result.stem_ps = result.stem_gs
	result.stem_as = elongate(result.stem_gs)

	result.stem_gp = stem .. final .. "ii"
	result.stem_pp = stem .. final .. "i"
	result.stem_ap = elongate(result.stem_pp)

	return process(data, result)
end

inflections["tytär"] = function (data)
	local result = { typeno = "12" }
	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")
	if data.no_singular then -- plural title -> singular
		-- does not have to be perfect, plural forms being right is enough
		word = get_stem(word, "et")
		local coda = mw.ustring.sub(word, -1, -1)
		local final = mw.ustring.sub(word, -2, -2)
		word = get_stem(word, strong .. final .. coda) .. weak .. final .. coda
	end
	local final = mw.ustring.sub(word, -2, -2)
	local coda = mw.ustring.sub(word, -1, -1)
	if coda ~= "l" and coda ~= "r" then
		error("invalid final character for tytär-type nominals")
	end
	if not mw.ustring.match(final, vowel) then
		error("invalid penultimate character for tytär-type nominals (should be a vowel)")
	end
	local stem = get_stem(word, weak .. final .. coda)

	result.stem_gs = stem .. strong .. final .. coda .. "e"
	result.stem_ps = stem .. weak .. final .. coda .. "t"
	result.stem_as = elongate(result.stem_gs)

	result.stem_gp = stem .. strong .. final .. coda .. "ii"
	result.stem_pp = stem .. strong .. final .. coda .. "i"
	result.stem_ap = elongate(result.stem_pp)

	result.grade = make_gradation(strong, weak)
	return process(data, result)
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;" |
! style="background:rgb(80%,80%,100%);" | essive
|{{{ess_sg}}}
|{{{ess_pl}}}
|- class="vsHide" style="background:rgb(95%,95%,100%);vertical-align:top;" |
! style="background:rgb(80%,80%,100%);" | exessive<sup>1)</sup>
|{{{exe_sg}}}
|{{{exe_pl}}}
|- class="vsHide" style="background:rgb(95%,95%,100%);vertical-align:top;" |
| colspan="3" style="background:rgb(80%,80%,100%);font-size:smaller" | <sup>1)</sup> obsolete <br /> <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 '''comitative''' is formed by adding the suffix {{m|izh|-ka}}<sup><span style="cursor:help;" title="in back-vocalic stems">?</span></sup> or {{m|izh|-kä}}<sup><span style="cursor:help;" title="in front-vocalic stems">?</span></sup> to the '''genitive'''.
|}
]=]

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

local function mention(text)
	return require("Module:links").full_link({ term = text, lang = 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 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 = 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
	end

	local data = { title = title, headword = headword, geminate = geminate, vh = vh, args = args }

	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 forms.irregular then
				return "irregular"
			end
			local s = "type " .. forms.typeno .. "/" .. mention(infl_type)
			if forms.grade then
				s = s .. ", " .. forms.grade
			end
			if forms.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(f))
				end
				return table.concat(result, ", ")
			else
				return link(value)
			end
		end
	end

	local result = mw.ustring.gsub(infl_table, "{{{([a-z0-9_:]+)}}}", repl)
	result = mw.ustring.gsub(result, "{{m|izh|([^}]-)}}", mention)
	return result
end

return export