Jump to content

Module:lud-conj

From Wiktionary, the free dictionary


local export = {}

local lang = require("Module:languages").getByCode("lud")

-- Remove ending from word to get an inflection stem. ending can contain Lua pattern syntax.
local function get_stem(word, ending)
    local fragment = mw.ustring.match(word, ending .. "$")
	if fragment then
		return mw.ustring.sub(word, 1, -mw.ustring.len(fragment) - 1), fragment
	end
	error("Unexpected ending 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

-- 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 gsub to string or list of strings.
local gsub_all = apply_on_factory(mw.ustring.gsub)

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

-- Append text to string or list of strings; append has_i if the text ends with -i, otherwise append no_i.
local append_i = apply_on_factory(function (v, no_i, has_i)
	return v .. (mw.ustring.match(v, "i$") and has_i or no_i)
end)

-- Appends something starting with a vowel and possibly removes an apostrophe.
local append_v = apply_on_factory(function (v, v2)
	if mw.ustring.match(v2, "^[äeiöü]") then
		v = mw.ustring.gsub(v, "['ʹ]$", "")
	end
	return v .. v2
end)

-- Temporary internal characters for a/ä, o/ö, u/ü
-- these are converted during postprocessing by postprocess().
local A = mw.ustring.char(0xF800)
local O = mw.ustring.char(0xF801)
local U = mw.ustring.char(0xF802)

local v2 = {
	["ai"] = true, ["au"] = true,
	["oi"] = true, ["ou"] = true,
	["ui"] = true,
	["äi"] = true, ["äu"] = true, ["äü"] = true,
	["öi"] = true, ["öu"] = true, ["öü"] = true,
	["üi"] = true, ["üu"] = true,
	["ei"] = true, ["eu"] = true,
	               ["iu"] = true,
	["ua"] = true, ["ia"] = true,
	["uo"] = true, ["iä"] = true,
	["üö"] = true, ["ie"] = true,
	[A.."i"] = true, [O.."i"] = true, [U.."i"] = true,
	[A.."u"] = true, [O.."u"] = true, [U.."u"] = true,
	[A..U] = true, [O..U] = true,
}

local v3 = {
	["uai"] = true, ["uoi"] = true, ["iäi"] = true,
	["üöi"] = true, ["iei"] = true, ["iai"] = true,
}

-- Checks whether a stem is monosyllabic.
local function is_monosyllabic(stem)
	local vowels = mw.ustring.match(stem, "^[^aeiouäöü"..A..O..U.."]*([aeiouäöü"..A..O..U.."]+)[^aeiouäöü"..A..O..U.."]*$")
	if not vowels then return false end

	return v3[vowels] or v2[vowels] or mw.ustring.len(vowels) == 1
end

-- Split Ludian word into "syllables". Consonant splits are undefined; this
-- only really cares about the vowels, so that vowels in separate syllables
-- end up in separate items, and that gluing all items together results in
-- the original word.
local function split_sylvowels(form)
	local syls = {}
	local i = 1
	local n = mw.ustring.len(form)

	while i <= n do
		local j, _, before, vowels = mw.ustring.find(form, "^(.-)([aeiouäöü"..A..O..U.."][aeiouäöü"..A..O..U.."]?[aeiouäöü"..A..O..U.."]?)", i)
		if not j then
			if #syls == 0 then table.insert(syls, "") end
			syls[#syls] = syls[#syls] .. mw.ustring.sub(form, i)
			break
		end

		local vlen

		if v3[vowels] then
			-- Triphthong
			vlen = 3
		elseif v2[mw.ustring.sub(vowels, 1, 2)] then
			-- Diphthong
			vlen = 2
		else
			vlen = 1
		end

		local syl = before .. mw.ustring.sub(vowels, 1, vlen)
		table.insert(syls, syl)
		i = i + mw.ustring.len(syl)
	end

	if form == "m"..A.."nem"..A.."h" then error(mw.dumpObject(syls)) end

	return syls
end

local AOU = "[" .. A .. O .. U .. "]"
local aou_back = { [A] = "a", [O] = "o", [U] = "u" }
local aou_front = { [A] = "ä", [O] = "ö", [U] = "ü" }

-- Postprocess A, O, U.
local function postprocess(form)
	local syl = split_sylvowels(form)
	local aou = nil
	local distance = 0

	for i, s in ipairs(syl) do
		if aou == nil then
			aou = mw.ustring.match(s, "[äöü]") and aou_front or aou_back
			distance = 0
		end

		if distance == 2 then
			aou = aou_back
		end

		syl[i] = mw.ustring.gsub(s, AOU, aou)
		distance = distance + 1
	end

	return table.concat(syl, "")
end

-- Make 3sg pres from vowel stem.
local make_3sg = apply_on_factory(function (v, force_poly)
	if is_monosyllabic(v) and not force_poly then
		-- monosyllabic, e.g. d'uo- -> d'uob
		return v .. "b"
	elseif mw.ustring.match(v, "e$") then
		-- luge- -> lugou
		return mw.ustring.gsub(v, "e$", "") .. O .. "u"
	else
		-- other cases, e.g. elä- -> eläu
		return v .. "u"
	end
end)

-- Make 3pl stem from consonant/(short) vowel stem.
local make_3pl = apply_on_factory(function (v, stem_type, force_poly)
	if stem_type == 2 then
		-- olda, mända, panda, tulda: always -d-
		return v .. "d"
	elseif mw.ustring.match(v, "[aeiouäöü]$") then
		-- vowel stem
		if is_monosyllabic(v) and not force_poly then
			-- one syllable
			return v .. "d"
		else
			-- more than one syllable
			return v .. "t"
		end
	else
		-- consonant stem
		if mw.ustring.match(v, "[tpšshkrl]$") then
			-- ends in certain consonants
			return v .. "t"
		else
			-- other cases
			return v .. "d"
		end
	end
end)

-- Make k stem from consonant/(short) vowel stem.
local make_t = apply_on_factory(function (v, force_poly)
	if is_monosyllabic(v) and not force_poly and not mw.ustring.match(v, "[ptksščsh]['ʹ]?$") then
		-- monosyllabic vowel stem or consonant stem ending in voiced sound: -d-
		return v .. "d"
	elseif mw.ustring.match(v, "[^aeiouäöü]$") then
		-- other consonant stems: -t-
		return v .. "t"
	else
		-- polysyllabic vowel stem: -tt-
		return v .. "tt"
	end
end)

-- Make impr 3sg/3pl from impr stem.
local make_k3 = apply_on_factory(function (v, force_poly)
	if not (is_monosyllabic(v) and not force_poly) and mw.ustring.match(v, "[^k]k$") then
		-- polysyllabic impr stem ending in -k-: -kk-
		return v .. "k"
	else
		-- else -g- or monosyllabic -k-
		return v
	end
end)

-- Make impr 3sg/3pl from impr stem.
local make_k3pl = apply_on_factory(function (v, impr, force_poly)
	return make_k3(v .. mw.ustring.match(impr, "[kg]$"), force_poly)
end)

local function make_k(stem_type, stem_v, stem_c)
	-- stem_type=0 for verbs with only one stem (default)
	-- stem_type=1 for verbs with two stems
	-- stem_type=2 for certain monosyllabic verbs with two stems

	if stem_type == 1 then
		return mw.ustring.gsub(stem_c, "z$", "s") .. "k"
	elseif stem_type == 2 then
		return stem_c .. "g"
	else -- stem_type == 0
		return stem_v .. "g"
	end
end

local function possibly_nongeminated(c)
	if mw.ustring.sub(c, -1, -1) == mw.ustring.sub(c, -2, -2) and mw.ustring.match(c, "^[čkpt]") then
		return c .. "?"
	end
	return c
end

local function make_gradation(s, w)
	if s == w then
		return "no gradation"
	else
		return s .. "-" .. w .. " gradation"
	end
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 function detect_reflexive(data, word)
	local stem, ok = mw.ustring.gsub(word, "kse$", "")
	if ok > 0 then data.reflexive = true end
	return stem
end

local function apply_grade_weak(data, stem)
	local stem_w = mw.ustring.gsub(stem, "([^aeiouäöü])%1([aeiouäöü])$", "%1%2")
	if stem ~= stem_w then
		local cons = mw.ustring.sub(stem_w, -2, -2)
		data.grade = make_gradation(cons .. cons, cons)
	end
	return stem_w
end

local function apply_grade_strong(data, stem, suffix)
	local stem_s = mw.ustring.gsub(stem, "([aeiouyäö])([cčfkpsšt])([aeiouyäö]" .. (suffix or "") .. ")$", "%1%2%2%3")
	if stem ~= stem_s then
		local cons = mw.ustring.sub(stem, -2, -2)
		data.grade = make_gradation(cons, cons .. cons)
	end
	return stem_s
end

local function make_forms(data)
	-- present stem (strong, weak)
	local pres_s = data.stem_pres_s or data.stem_pres
	local pres_w = data.stem_pres_w or data.stem_pres
	-- past/imperfect stem (strong, weak)
	local past_s = data.stem_past_s or data.stem_past
	local past_w = data.stem_past_w or data.stem_past
	-- consonant stem
	local cons = data.stem_cons or pres_w
	-- inf1 stem
	local inf1 = data.stem_inf1 or append(pres_s, "d")
	-- k/g stem
	local stem_k = make_k(data.stem_type, pres_s, cons)
	-- t stem (passive participle, conditional/potential 3pl)
	local stem_t = make_t(data.stem_t or cons, data.force_poly)
	-- 3pl stem
	local s3pl = make_3pl(data.stem_t or cons, data.stem_type, data.force_poly)
	-- conditional stem
	local cond = gsub_all(data.stem_cond or pres_s, "[ei]$", "") .. "iž"
	-- imperative stem
	local impr = data.stem_impr or stem_k

	local result = { }

	local past_part = append(cons, "n"..U)
	result.pres_part = append(gsub_all(pres_s, "e$", "i"), "j")
	result.pres_pasv_part = append_v(stem_t, A.."v")
	result.past_part = past_part
	result.past_pasv_part = append_v(stem_t, U)

	result.pres_1sg = append(pres_w, "n")
	result.pres_2sg = append(pres_w, "d")
	result.pres_3sg = make_3sg(pres_s, data.force_poly)
	result.pres_1pl = append(pres_w, "mme")
	result.pres_2pl = append(pres_w, "tte")
	result.pres_3pl = append_v(s3pl, A.."h")
	result.pres_conn = pres_w
	result.pres_conn_pl = append_v(stem_k, O.."i")
	result.pres_conn_3pl = append_v(s3pl, A)

	result.past_1sg = append(past_w, "n")
	result.past_2sg = append(past_w, "d")
	result.past_3sg = data.past_3sg_ii and append_v(past_s, "i") or past_s
	result.past_1pl = append(past_w, "mme")
	result.past_2pl = append(past_w, "tte")
	result.past_3pl = append_v(stem_t, "ih")
	result.past_conn_pl = result.past_pasv_part

	result.cond_1sg = append_v(cond, "in")
	result.cond_2sg = append_v(cond, "id")
	result.cond_3sg = cond
	result.cond_1pl = append_v(cond, "imme")
	result.cond_2pl = append_v(cond, "itte")
	result.cond_3pl = append_v(gsub_all(stem_t, "tt$", "t"), A.."iž")

	result.cond_past_1sg = append_v(past_part, "ižin")
	result.cond_past_2sg = append_v(past_part, "ižid")
	result.cond_past_3sg = append_v(past_part, "iž")
	result.cond_past_1pl = append_v(past_part, "ižimme")
	result.cond_past_2pl = append_v(past_part, "ižitte")
	result.cond_past_3pl = append_v(stem_t, A.."n"..U.."iž")

	result.impr_2sg = pres_w
	result.impr_3sg = append_v(make_k3(impr, data.force_poly), A.."h")
	result.impr_1pl = append_v(impr, A.."m")
	result.impr_2pl = append_v(impr, A.."t")
	result.impr_3pl = append_v(make_k3pl(append(s3pl, A), impr, data.force_poly), A.."h")
	result.impr_conn = append_v(impr, O.."i")

	result.potn_1sg = append(cons, "nen")
	result.potn_2sg = append(cons, "ned")
	result.potn_3sg = append(cons, "n"..O.."u")
	result.potn_1pl = append(cons, "nemme")
	result.potn_2pl = append(cons, "nette")
	result.potn_3pl = append(stem_t, A.."neh")
	result.potn_conn = append(cons, "ne")
	result.potn_conn_3pl = append_v(stem_t, A.."ne")

	local inf3 = append(pres_s, "m"..A)
	result.inf1 = append_v(inf1, A)
	result.inf2_ill = append_v(inf1, "es")
	result.inf2_ela = append_v(inf1, "en")
	result.inf3_ill = append(inf3, "h")
	result.inf3_ine = append(inf3, "s")
	result.inf3_ela = append(inf3, "spiä")
	result.inf3_ade = append(inf3, "l")
	result.inf3_abe = append(inf3, "ta")

	return result
end

local function make_forms_refl(data)
	-- present stem (strong, weak)
	local pres_s = data.stem_pres_s or data.stem_pres
	local pres_w = data.stem_pres_refl or data.stem_pres_w or data.stem_pres
	-- past/imperfect stem (strong, weak)
	local past_s = data.stem_past_s or data.stem_past
	local past_w = data.stem_past_refl or data.stem_past_w or data.stem_past
	-- consonant stem
	local cons = data.stem_cons or pres_w
	-- inf1 stem
	local inf1 = data.stem_inf1 or append(pres_s, "d")
	-- k/g stem
	local stem_k = make_k(data.stem_type, pres_s, cons)
	-- t stem (passive participle, conditional/potential 3pl)
	local stem_t = make_t(data.stem_t or cons, data.force_poly)
	-- 3pl stem
	local s3pl = make_3pl(data.stem_t or cons, data.stem_type, data.force_poly)
	-- conditional stem
	local cond = gsub_all(data.stem_cond or pres_s, "[ei]$", "") .. "iž"
	-- imperative stem
	local impr = data.stem_impr or stem_k

	local result = { }

	local past_part = append(cons, "n"..U)
	result.past_part = append(past_part, "ze")
	result.past_pasv_part = append(stem_t, U.."ze")

	result.pres_1sg = append(pres_w, "m"..O.."i")
	result.pres_2sg = append(pres_w, "t"..O.."i")
	result.pres_3sg = append_i(pres_w, "ze", "že")
	result.pres_1pl = append(pres_w, "m"..O.."iže")
	result.pres_2pl = append(pres_w, "t"..O.."iže")
	result.pres_3pl = append(s3pl, A.."heze")
	result.pres_conn = append(gsub_all(stem_k, "[kg]$", { k = "t", g = "d" }), "e")
	result.pres_conn_pl = append(stem_k, O.."iže")
	result.pres_conn_3pl = result.pres_conn_pl

	result.past_1sg = append(past_w, "m"..O.."i")
	result.past_2sg = append(past_w, "t"..O.."i")
	result.past_3sg = append(past_s, data.past_3sg_ii and "ihe" or "he")
	result.past_1pl = append(past_w, "m"..O.."iže")
	result.past_2pl = append(past_w, "t"..O.."iže")
	result.past_3pl = append(stem_t, "iheze")
	result.past_conn_pl = result.past_part

	result.cond_1sg = append(cond, "im"..O.."i")
	result.cond_2sg = append(cond, "it"..O.."i")
	result.cond_3sg = append(cond, "ihe")
	result.cond_1pl = append(cond, "im"..O.."iže")
	result.cond_2pl = append(cond, "it"..O.."iže")
	result.cond_3pl = append(gsub_all(stem_t, "tt$", "t"), A.."ižihe")

	result.cond_past_1sg = append(past_part, "ižim"..O.."i")
	result.cond_past_2sg = append(past_part, "ižit"..O.."i")
	result.cond_past_3sg = append(past_part, "ižihe")
	result.cond_past_1pl = append(past_part, "ižim"..O.."iže")
	result.cond_past_2pl = append(past_part, "ižit"..O.."iže")
	result.cond_past_3pl = append(stem_t, A.."n"..U.."ižihe")

	result.impr_2sg = append(make_3pl(cons, data.stem_type), "e")
	result.impr_3sg = append(make_k3(impr, data.force_poly), A.."h"..A.."ze")
	result.impr_1pl = append(impr, A.."m"..O.."iže")
	result.impr_2pl = append(impr, A.."t"..O.."iže")
	result.impr_3pl = append(make_k3pl(append(s3pl, A), impr, data.force_poly), A.."h"..A.."ze")
	result.impr_conn = append(impr, O.."iže")

	result.potn_1sg = append(cons, "nem"..O.."i")
	result.potn_2sg = append(cons, "net"..O.."i")
	result.potn_3sg = append(cons, "neze")
	result.potn_1pl = append(cons, "nem"..O.."iže")
	result.potn_2pl = append(cons, "net"..O.."iže")
	result.potn_3pl = append(stem_t, A.."neze")
	result.potn_conn = append(cons, "neze")
	result.potn_conn_3pl = append(stem_t, A.."neze")

	local inf3 = append(pres_s, "m"..A)
	result.inf1 = append(inf1, A.."kse")
	result.inf3_ill = append(inf3, "hakse")
	result.inf3_abe = append(inf3, "takse")

	return result
end

local function process(data)
	local result

	if data.reflexive then
		result = make_forms_refl(data)
	else
		result = make_forms(data)
	end

	for k, v in pairs(result) do
		if type(v) == "string" then
			result[k] = postprocess(v)
		elseif type(v) == "table" then
			for i, f in ipairs(v) do
				v[i] = postprocess(f)
			end
		end
	end

	return result
end

-- Extract from inf1 the d/t and a/ä
local function extract_inf1(data, word, expected_d)
	local stem = detect_reflexive(data, word)
	local d, a
	stem, a = get_stem(stem, "[aä]")
	stem, d = get_stem(stem, expected_d or "[dt]")
	return stem, d, a
end

-- Turn stem to consonant stem
local function make_cons_stem(stem, cons)
	if mw.ustring.match(stem, "[^aeiouäöü]$") then
		return stem .. cons
	else
		return stem
	end
end

-- inflection classes begin
local inflections = {}

inflections["voida"] = function (data)
	data.typeno = "I-1"
	local word = data.title
	local stem, d, a = extract_inf1(data, word, "d")

	data.stem_pres = stem
	data.stem_past = stem

	return process(data)
end

inflections["tuoda"] = function (data)
	data.typeno = "I-2"
	local word = data.title
	local stem, d, a = extract_inf1(data, word, "d")

	data.stem_pres = stem
	data.stem_past = stem .. "i"
	data.stem_cond = data.stem_past

	return process(data)
end

inflections["tahtoda"] = function (data)
	data.typeno = "I-3"
	local word = data.title
	local stem, d, a = extract_inf1(data, word, "d")
	local stem_w = apply_grade_weak(data, stem)

	data.stem_pres_s = stem
	data.stem_pres_w = stem_w
	data.stem_past = stem_w .. "i"

	return process(data)
end

inflections["kuluda"] = function (data)
	data.typeno = "I-4"
	local word = data.title
	local stem, d, a = extract_inf1(data, word, "d")
	local stem_w = apply_grade_weak(data, stem)

	data.stem_pres_s = stem
	data.stem_pres_w = stem_w
	data.stem_past = stem_w .. "i"

	return process(data)
end

inflections["nostada"] = function (data)
	data.typeno = "I-5"
	local word = data.title
	local stem, d, a = extract_inf1(data, word, "d")
	local stem_w = apply_grade_weak(data, stem)

	data.stem_pres_s = stem
	data.stem_pres_w = stem_w
	data.stem_past_s = get_stem(stem, "[aä]") .. "i"
	data.stem_past_w = get_stem(stem_w, "[aä]") .. "i"
	data.stem_t = get_stem(stem_w, "[aä]") .. "e"

	return process(data)
end

inflections["eččida"] = function (data)
	data.typeno = "I-6"
	local word = data.title
	local stem, d, a = extract_inf1(data, word, "d")
	local stem_w = apply_grade_weak(data, stem)

	data.stem_pres_s = stem
	data.stem_pres_w = stem_w
	data.stem_past = stem_w
	data.stem_cond = data.stem_past_s

	data.past_3sg_ii = true
	return process(data)
end

inflections["lugeda"] = function (data)
	data.typeno = "I-7"
	local word = data.title
	local stem, d, a = extract_inf1(data, word, "d")
	local stem_w = apply_grade_weak(data, stem)

	data.stem_pres_s = stem
	data.stem_pres_w = stem_w
	data.stem_past_s = get_stem(stem, "[aeiouäöü]") .. "i"
	data.stem_past_w = get_stem(stem_w, "[aeiouäöü]") .. "i"

	return process(data)
end

inflections["painada"] = function (data)
	data.typeno = "I-8"
	local word = data.title
	local stem, d, a = extract_inf1(data, word, "d")
	local stem_w = apply_grade_weak(data, stem)

	data.stem_pres_s = stem
	data.stem_pres_w = stem_w
	data.stem_past = get_stem(stem_w, "[aä]") .. "oi"
	data.stem_t = get_stem(stem_w, "[aä]") .. "e"

	return process(data)
end

inflections["nuolda"] = function (data)
	data.typeno = "II-1"
	local word = data.title
	local stem, d, a = extract_inf1(data, word)

	-- pese- > peze-, furiše- > furiže-
	local stem_z = mw.ustring.gsub(stem, "([aeiouäöü])([sš])$", function (v, c) 
		return v .. ({["s"] = "z", ["š"] = "ž"})[c]
	end)

	data.stem_pres = stem_z .. "e"
	data.stem_past = stem_z .. "i"
	data.stem_inf1 = stem .. d
	data.stem_cons = stem

	data.stem_type = 1
	return process(data)
end

inflections["tulda"] = function (data)
	data.typeno = "II-1"
	local word = data.title
	local stem, d, a = extract_inf1(data, word, "d")

	data.stem_pres = stem .. "e"
	data.stem_past = stem .. "i"
	data.stem_inf1 = stem .. "d"
	data.stem_cons = stem

	data.stem_type = 2
	return process(data)
end

inflections["olda"] = function (data)
	data.title = "olda"
	local result = inflections["tulda"](data)
	if not data.reflexive then result.pres_3sg = "on" end
	return result
end

inflections["souta"] = function (data)
	data.typeno = "II-2"
	local word = data.title
	local stem, d, a = extract_inf1(data, word, "t")

	data.stem_pres = stem .. "d" .. a
	data.stem_past = stem .. "di"
	data.stem_inf1 = stem .. "t"
	data.stem_cons = make_cons_stem(stem, "t")

	data.stem_type = 1
	return process(data)
end

inflections["kerata"] = function (data)
	data.typeno = "II-3"
	local word = data.title
	local stem, d, a = extract_inf1(data, word, "t")
	local stem_s = apply_grade_strong(data, stem)

	data.stem_pres = stem_s .. "d" .. a
	data.stem_past = append_i(stem_s, "zi", "ži")
	data.stem_pres_refl = stem .. "d" .. a
	data.stem_past_refl = append_i(stem, "zi", "ži")
	data.stem_inf1 = stem .. "t"
	data.stem_cons = make_cons_stem(stem, "t")

	data.stem_type = 1
	return process(data)
end

inflections["pageta"] = function (data)
	data.typeno = "II-4"
	local word = data.title
	local stem, d, a = extract_inf1(data, word, "t")
	local stem_s = apply_grade_strong(data, stem)

	data.stem_pres = stem_s .. "ne"
	data.stem_past = stem_s .. "ni"
	data.stem_pres_refl = stem .. "ne"
	data.stem_past_refl = stem .. "ni"
	data.stem_inf1 = stem .. "t"
	data.stem_cons = make_cons_stem(stem, "t")

	data.stem_type = 1
	return process(data)
end

inflections["mainita"] = function (data)
	data.typeno = "II-5"
	local word = data.title
	local stem, d, a = extract_inf1(data, word, "t")

	data.stem_pres_s = stem .. "čče"
	data.stem_pres_w = stem .. "če"
	data.stem_past_s = stem .. "čči"
	data.stem_past_w = stem .. "či"
	data.stem_pres_refl = stem .. "če"
	data.stem_inf1 = stem .. "t"
	data.stem_cons = make_cons_stem(stem, "t")

	data.stem_type = 1
	return process(data)
end

inflections["kebdeta"] = function (data)
	data.typeno = "II-6"
	local word = data.title
	local stem, d, a = extract_inf1(data, word, "t")

	data.stem_pres = stem .. "nd" .. a
	data.stem_past = stem .. "ndži"
	data.stem_inf1 = stem .. "t"
	data.stem_cons = make_cons_stem(stem, "t")

	data.stem_type = 1
	return process(data)
end

inflections["vihmuškata"] = function (data)
	data.typeno = "II-7"
	local word = data.title
	local stem, d, a = extract_inf1(data, word, "t")

	data.stem_pres = stem .. "nde"
	data.stem_past = stem .. "ndži"
	data.stem_inf1 = stem .. "t"
	data.stem_cons = make_cons_stem(stem, "t")

	data.stem_type = 1
	return process(data)
end

inflections["anta"] = function (data)
	data.typeno = "II-8"
	local word = data.title
	local stem, d, a = extract_inf1(data, word, "t")

	data.stem_pres = stem .. "d" .. a
	data.stem_past = stem .. "doi"
	data.stem_inf1 = stem .. "t"
	data.stem_cons = stem

	data.stem_type = 1
	return process(data)
end

inflections["luotelta"] = function (data)
	data.typeno = "II-9"
	local word = data.title
	local stem, d, a = extract_inf1(data, word, "t")
	local stem_s = apply_grade_strong(data, stem, "l")

	data.stem_pres = stem_s .. "e"
	data.stem_past = stem_s .. "i"
	data.stem_pres_refl = stem_s .. "e"
	data.stem_past_refl = stem_s .. "i"
	data.stem_inf1 = stem .. "t"
	data.stem_cons = stem

	data.stem_type = 1
	return process(data)
end

inflections["dʹuosta"] = function (data)
	data.typeno = "II-10"
	local word = data.title
	local stem, d, a = extract_inf1(data, word, "st")

	data.stem_pres = stem .. "kse"
	data.stem_past = stem .. "ksi"
	data.stem_inf1 = stem .. "st"
	data.stem_cons = stem .. "s"

	data.stem_type = 1
	return process(data)
end

inflections["nähtä"] = function (data)
	data.typeno = "II-11"
	local word = data.title
	local stem, d, a = extract_inf1(data, word, "ht")

	data.stem_pres = stem .. "ge"
	data.stem_past = stem .. "gi"
	data.stem_inf1 = stem .. "h"
	data.stem_cons = stem .. "h"

	data.stem_type = 1
	return process(data)
end

inflections["lähtä"] = function (data)
	data.typeno = "II-12"
	local word = data.title
	local stem, d, a = extract_inf1(data, word, "ht")

	data.stem_pres = stem .. "hte"
	data.stem_past = stem .. "ksi"
	data.stem_inf1 = stem .. "h"
	data.stem_cons = stem .. "h"
	data.stem_impr = stem .. "kk"

	data.stem_type = 1
	return process(data)
end

-- inflection classes end

local conj_table = [=[{| class="inflection-table vsSwitcher lud-conj" data-toggle-category="conjugation"
|-
! colspan=6 class="vsToggleElement lud-conj-header-toggle" | Conjugation of {{{title}}} (<span class="lud-conj-type">{{{type}}}</span>)
|- class="vsHide"
! colspan=6 class="lud-conj-header-tense" | Indicative
|- class="vsHide"
! colspan=3 class="lud-conj-header-tense" | Present
! colspan=3 class="lud-conj-header-tense" | Perfect
|- class="vsHide"
! class="lud-conj-header-tense" |
! class="lud-conj-header-posneg thsub" | positive
! class="lud-conj-header-posneg thsub" | negative
! class="lud-conj-header-tense" |
! class="lud-conj-header-posneg thsub" | positive
! class="lud-conj-header-posneg thsub" | negative
|- class="vsHide"
! class="lud-conj-header-person" | 1st singular
| {{{pres_1sg}}}
| {{{en !pres_conn}}}
! class="lud-conj-header-person" | 1st singular
| {{{olen !past_part}}}
| {{{en ole !past_part}}}
|- class="vsHide"
! class="lud-conj-header-person" | 2nd singular
| {{{pres_2sg}}}
| {{{ed !pres_conn}}}
! class="lud-conj-header-person" | 2nd singular
| {{{oled !past_part}}}
| {{{ed ole !past_part}}}
|- class="vsHide"
! class="lud-conj-header-person" | 3rd singular
| {{{pres_3sg}}}
| {{{ei !pres_conn}}}
! class="lud-conj-header-person" | 3rd singular
| {{{on !past_part}}}
| {{{ei ole !past_part}}}
|- class="vsHide"
! class="lud-conj-header-person" | 1st plural
| {{{pres_1pl}}}
| {{{emme !pres_conn_pl}}}
! class="lud-conj-header-person" | 1st plural
| {{{olemme !past_part}}}
| {{{emme olgoi !past_part}}}
|- class="vsHide"
! class="lud-conj-header-person" | 2nd plural
| {{{pres_2pl}}}
| {{{ette !pres_conn_pl}}}
! class="lud-conj-header-person" | 2nd plural
| {{{olette !past_part}}}
| {{{ette olgoi !past_part}}}
|- class="vsHide"
! class="lud-conj-header-person" | 3rd plural
| {{{pres_3pl}}}
| {{{ei !pres_conn_3pl}}}
! class="lud-conj-header-person" | 3rd plural
| {{{oldah !past_conn_pl}}}
| {{{ei olda !past_conn_pl}}}
|- class="vsHide"
! colspan=3 class="lud-conj-header-person" | Imperfect
! colspan=3 class="lud-conj-header-person" | Pluperfect
|- class="vsHide"
! class="lud-conj-header-person" |
! class="lud-conj-header-person thsub" | positive
! class="lud-conj-header-person thsub" | negative
! class="lud-conj-header-person" |
! class="lud-conj-header-person thsub" | positive
! class="lud-conj-header-person thsub" | negative
|- class="vsHide"
! class="lud-conj-header-person" | 1st singular
| {{{past_1sg}}}
| {{{en !past_part}}}
! class="lud-conj-header-person" | 1st singular
| {{{olin !past_part}}}
| {{{en olnu !past_part}}}
|- class="vsHide"
! class="lud-conj-header-person" | 2nd singular
| {{{past_2sg}}}
| {{{ed !past_part}}}
! class="lud-conj-header-person" | 2nd singular
| {{{olid !past_part}}}
| {{{ed olnu !past_part}}}
|- class="vsHide"
! class="lud-conj-header-person" | 3rd singular
| {{{past_3sg}}}
| {{{ei !past_part}}}
! class="lud-conj-header-person" | 3rd singular
| {{{oli !past_part}}}
| {{{ei olnu !past_part}}}
|- class="vsHide"
! class="lud-conj-header-person" | 1st plural
| {{{past_1pl}}}
| {{{emme !past_conn_pl}}}
! class="lud-conj-header-person" | 1st plural
| {{{olimme !past_part}}}
| {{{emme olnu !past_part}}}
|- class="vsHide"
! class="lud-conj-header-person" | 2nd plural
| {{{past_2pl}}}
| {{{ette !past_conn_pl}}}
! class="lud-conj-header-person" | 2nd plural
| {{{olitte !past_part}}}
| {{{ette olnu !past_part}}}
|- class="vsHide"
! class="lud-conj-header-person" | 3rd plural
| {{{past_3pl}}}
| {{{ei !past_conn_pl}}}
! class="lud-conj-header-person" | 3rd plural
| {{{oldih !past_conn_pl}}}
| {{{ei oldu !past_conn_pl}}}
|- class="vsHide"
! colspan=6 class="lud-conj-header-person" | Conditional
|- class="vsHide"
! colspan=3 class="lud-conj-header-person" | Present
! colspan=3 class="lud-conj-header-person" | Perfect
|- class="vsHide"
! class="lud-conj-header-person" |
! class="lud-conj-header-person thsub" | positive
! class="lud-conj-header-person thsub" | negative
! class="lud-conj-header-person" |
! class="lud-conj-header-person thsub" | positive
! class="lud-conj-header-person thsub" | negative
|- class="vsHide"
! class="lud-conj-header-person" | 1st singular
| {{{cond_1sg}}}
| {{{en !cond_3sg}}}
! class="lud-conj-header-person" | 1st singular
| {{{oližin !past_part}}}
| {{{en oliž !past_part}}}
|- class="vsHide"
! class="lud-conj-header-person" | 2nd singular
| {{{cond_2sg}}}
| {{{ed !cond_3sg}}}
! class="lud-conj-header-person" | 2nd singular
| {{{oližid !past_part}}}
| {{{ed oliž !past_part}}}
|- class="vsHide"
! class="lud-conj-header-person" | 3rd singular
| {{{cond_3sg}}}
| {{{ei !cond_3sg}}}
! class="lud-conj-header-person" | 3rd singular
| {{{oliž !past_part}}}
| {{{ei oliž !past_part}}}
|- class="vsHide"
! class="lud-conj-header-person" | 1st plural
| {{{cond_1pl}}}
| {{{emme !cond_3sg}}}
! class="lud-conj-header-person" | 1st plural
| {{{oližimme !past_part}}}
| {{{emme oliž !past_part}}}
|- class="vsHide"
! class="lud-conj-header-person" | 2nd plural
| {{{cond_2pl}}}
| {{{ette !cond_3sg}}}
! class="lud-conj-header-person" | 2nd plural
| {{{oližitte !past_part}}}
| {{{ette oliž !past_part}}}
|- class="vsHide"
! class="lud-conj-header-person" | 3rd plural
| {{{cond_3pl}}}
| {{{ei !cond_3pl}}}
! class="lud-conj-header-person" | 3rd plural
| {{{oldaiž !past_conn_pl}}}
| {{{ei oldaiž !past_conn_pl}}}
|- class="vsHide"
! colspan=3 class="lud-conj-header-person" | Imperfect
! colspan=3 class="lud-conj-header-person" | Pluperfect
|- class="vsHide"
! class="lud-conj-header-person" |
! class="lud-conj-header-person thsub" | positive
! class="lud-conj-header-person thsub" | negative
! class="lud-conj-header-person" |
! class="lud-conj-header-person thsub" | positive
! class="lud-conj-header-person thsub" | negative
|- class="vsHide"
! class="lud-conj-header-person" | 1st singular
| {{{cond_past_1sg}}}
| {{{en !cond_past_3sg}}}
! class="lud-conj-header-person" | 1st singular
| {{{olnuižin !past_part}}}
| {{{en olnuiž !past_part}}}
|- class="vsHide"
! class="lud-conj-header-person" | 2nd singular
| {{{cond_past_2sg}}}
| {{{ed !cond_past_3sg}}}
! class="lud-conj-header-person" | 2nd singular
| {{{olnuižid !past_part}}}
| {{{ed olnuiž !past_part}}}
|- class="vsHide"
! class="lud-conj-header-person" | 3rd singular
| {{{cond_past_3sg}}}
| {{{ei !cond_past_3sg}}}
! class="lud-conj-header-person" | 3rd singular
| {{{olnuiž !past_part}}}
| {{{ei olnuiž !past_part}}}
|- class="vsHide"
! class="lud-conj-header-person" | 1st plural
| {{{cond_past_1pl}}}
| {{{emme !cond_past_3sg}}}
! class="lud-conj-header-person" | 1st plural
| {{{olnuižimme !past_part}}}
| {{{emme olnuiž !past_part}}}
|- class="vsHide"
! class="lud-conj-header-person" | 2nd plural
| {{{cond_past_2pl}}}
| {{{ette !cond_past_3sg}}}
! class="lud-conj-header-person" | 2nd plural
| {{{olnuižitte !past_part}}}
| {{{ette olnuiž !past_part}}}
|- class="vsHide"
! class="lud-conj-header-person" | 3rd plural
| {{{cond_past_3pl}}}
| {{{ei !cond_past_3pl}}}
! class="lud-conj-header-person" | 3rd plural
| {{{oldanuiž !past_conn_pl}}}
| {{{ei oldanuiž !past_conn_pl}}}
|- class="vsHide"
! colspan=6 class="lud-conj-header-person" | Imperative
|- class="vsHide"
! colspan=3 class="lud-conj-header-person" | Present
! colspan=3 rowspan="9" |
|- class="vsHide"
! class="lud-conj-header-person" |
! class="lud-conj-header-person thsub" | positive
! class="lud-conj-header-person thsub" | negative
|- class="vsHide"
! class="lud-conj-header-person" | 1st singular
| —
| —
|- class="vsHide"
! class="lud-conj-header-person" | 2nd singular
| {{{impr_2sg}}}
| {{{ala !impr_2sg}}}
|- class="vsHide"
! class="lud-conj-header-person" | 3rd singular
| {{{impr_3sg}}}
| {{{algah !impr_conn}}}
|- class="vsHide"
! class="lud-conj-header-person" | 1st plural
| {{{impr_1pl}}}
| {{{algam !impr_conn}}}
|- class="vsHide"
! class="lud-conj-header-person" | 2nd plural
| {{{impr_2pl}}}
| {{{algat !impr_conn}}}
|- class="vsHide"
! class="lud-conj-header-person" | 3rd plural
| {{{impr_3pl}}}
| {{{aldagah !impr_conn}}}
|- class="vsHide"
! colspan=6 class="lud-conj-header-person" | Potential
|- class="vsHide"
! colspan=3 class="lud-conj-header-person" | Present
! colspan=3 class="lud-conj-header-person" | Perfect
|- class="vsHide"
! class="lud-conj-header-person" |
! class="lud-conj-header-person thsub" | positive
! class="lud-conj-header-person thsub" | negative
! class="lud-conj-header-person" |
! class="lud-conj-header-person thsub" | positive
! class="lud-conj-header-person thsub" | negative
|- class="vsHide"
! class="lud-conj-header-person" | 1st singular
| {{{potn_1sg}}}
| {{{en !potn_conn}}}
! class="lud-conj-header-person" | 1st singular
| {{{lienen !past_part}}}
| {{{en liene !past_part}}}
|- class="vsHide"
! class="lud-conj-header-person" | 2nd singular
| {{{potn_2sg}}}
| {{{ed !potn_conn}}}
! class="lud-conj-header-person" | 2nd singular
| {{{liened !past_part}}}
| {{{ed liene !past_part}}}
|- class="vsHide"
! class="lud-conj-header-person" | 3rd singular
| {{{potn_3sg}}}
| {{{ei !potn_conn}}}
! class="lud-conj-header-person" | 3rd singular
| {{{lienou !past_part}}}
| {{{ei liene !past_part}}}
|- class="vsHide"
! class="lud-conj-header-person" | 1st plural
| {{{potn_1pl}}}
| {{{emme !potn_conn}}}
! class="lud-conj-header-person" | 1st plural
| {{{lienemme !past_part}}}
| {{{emme liekoi !past_part}}}
|- class="vsHide"
! class="lud-conj-header-person" | 2nd plural
| {{{potn_2pl}}}
| {{{ette !potn_conn}}}
! class="lud-conj-header-person" | 2nd plural
| {{{lienette !past_part}}}
| {{{ette liekoi !past_part}}}
|- class="vsHide"
! class="lud-conj-header-person" | 3rd plural
| {{{potn_3pl}}}
| {{{ei !potn_conn_3pl}}}
! class="lud-conj-header-person" | 3rd plural
| {{{lietah !past_conn_pl}}}
| {{{ei liekoi !past_conn_pl}}}
|- class="vsHide"
! colspan=6 class="lud-conj-header-person" | Nominal forms
|- class="vsHide"
! colspan=3 class="lud-conj-header-person" | Infinitives
! colspan=3 class="lud-conj-header-person" | Participles
|- class="vsHide"
! colspan=2 class="lud-conj-header-person" |
! class="lud-conj-header-person thsub" |
! class="lud-conj-header-person" |
! class="lud-conj-header-person thsub" | active
! class="lud-conj-header-person thsub" | passive
|- class="vsHide"
! colspan=2 class="lud-conj-header-person" | I
| {{{inf1}}}
! class="lud-conj-header-person" | present
| {{{pres_part}}}
| {{{pres_pasv_part}}}
|- class="vsHide"
! rowspan=2 class="lud-conj-header-person" | II
! class="lud-conj-header-person thsub" | inessive
| {{{inf2_ill}}}
! class="lud-conj-header-person" | past
| {{{past_part}}}
| {{{past_pasv_part}}}
|- class="vsHide"
! class="lud-conj-header-person thsub" | essive
| {{{inf2_ela}}}
| colspan=3 rowspan=6 class="lud-conj-notes" | {{{notes}}}
|- class="vsHide"
! rowspan=5 class="lud-conj-header-person" | III
! class="lud-conj-header-person thsub" | illative
| {{{inf3_ill}}}
|- class="vsHide"
! class="lud-conj-header-person thsub" | inessive
| {{{inf3_ine}}}
|- class="vsHide"
! class="lud-conj-header-person thsub" | elative
| {{{inf3_ela}}}
|- class="vsHide"
! class="lud-conj-header-person thsub" | adessive
| {{{inf3_ade}}}
|- class="vsHide"
! class="lud-conj-header-person thsub" | abessive
| {{{inf3_abe}}}
|}]=]

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

local function link(text, prefix)
	return require("Module:script utilities").tag_text((prefix and prefix or "") .. require("Module:links").language_link{ term = text, lang = lang }, lang)
end

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

function export.raw(word, infl_type, grad1, grad2, 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")

	args[1] = args[1] or grad1
	args[2] = args[2] or grad2
	local data = { title = word,  args = args }
	return infl(data)
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.loadData("Module:headword/data").pagename

	local data = { title = title, args = args }
	local categories = {}
	
	if args["poly"] then
		data.force_poly = true
	end

	local forms = infl(data)
	format_notes(data, forms)

	local function repl(form)
		local prefix = ""
		if form == "title" then
			return mention(title)
		elseif form == "type" then
			if data.irregular then
				return "irregular"
			end
			local s = "type " .. data.typeno .. "/" .. mention(infl_type)
			if data.reflexive then
				s = s .. ", reflexive"
			end
			if data.grade then
				s = s .. ", " .. data.grade
			else
				s = s .. ", " .. make_gradation(nil, nil)
			end
			if data.geminate then
				s = s .. ", gemination"
			end
			return s
		elseif form == "notes" then
			local results = {}
			for index, note in ipairs(data.notes) do
				table.insert(results, note_text(tostring(index), note))
			end
			return table.concat(results, "<br />")
		elseif mw.ustring.find(form, "!") then
			local excl = mw.ustring.find(form, "!")
			prefix = mw.ustring.sub(form, 1, excl - 1)
			form = mw.ustring.sub(form, 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))
			end
			return table.concat(result, ", ")
		elseif type(value) == "string" and #value > 0 then
			return link(value, prefix)
		else
			return "&mdash;"
		end
	end

	--if mw.title.getCurrentTitle().namespace == 0 then
	--	table.insert(categories, "Ludian " .. infl_type .. "-type verbs")
	--end
	--if data.reflexive then
	--	table.insert(categories, "Ludian reflexive verbs")
	--end

	local result = mw.ustring.gsub(conj_table, "{{{([a-z0-9čšžäöüʹ _:!]+)}}}", repl)
	result = mw.ustring.gsub(result, "{{m|lud|([^}]-)}}", mention)
	return result .. require("Module:utilities").format_categories(categories, lang)
		.. require("Module:TemplateStyles")("Module:lud-conj/style.css")
end

return export