Module:fi-nominals

local export = {}

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

-- Functions that do the actual inflecting by creating the forms of a basic term. local inflections = {}

local kotus_grad_type = { ["kk-k"] = "A", ["pp-p"] = "B", ["tt-t"] = "C", ["k-"] = "D", ["p-v"] = "E", ["t-d"] = "F", ["nk-ng"] = "G", ["mp-mm"] = "H", ["lt-ll"] = "I", ["nt-nn"] = "J", ["rt-rr"] = "K", ["k-j"] = "L", ["k-v"] = "M" }

local m_bit32 -- loaded later if needed

local RARE = ' rare'

local function normalize_apostrophes(term, link_target) if link_target then if term and mw.ustring.find(term, "’") then term = mw.ustring.gsub(term, "’", "'") end else if term and mw.ustring.find(term, "'") then term = mw.ustring.gsub(term, "'", "’") end end return term end

-- Creates a link to a form. local function make_link(term, accel_form) -- do not link inflected forms of suffixes if term:match("^-") then if term == mw.title.getCurrentTitle.fullText then return ' ' .. term .. ' '		end

return ' ' .. term .. ' '	end -- if there is something difficult, use full module. if term:find(":") or term:find("<") then if term:find(":") then term = mw.ustring.gsub(term, ":", "\\:") end return require("Module:links").full_link({			lang = lang,			term = term,			accel = accel_form and ({ form = accel_form }) or nil		}) end -- otherwise, we can save a ton of memory by doing this manually. local target = normalize_apostrophes(term, true) if target == mw.title.getCurrentTitle.fullText then return ' ' .. term .. ' '	end if not accel_form then return ' ' .. term .. ' ' end return '' .. term .. ' ' end

local function tag_term(term) -- return require("Module:script utilities").tag_text(term, lang, nil, "term") return '' .. term .. '' end

local function do_inflection_internal(data, argobj) local args = argobj.args argobj.pos = 1

data.words = {} data.num = 1 data.forms = nil data.categories = {} for num, infl_type in ipairs(data.infl_types) do		-- initialize data for single word local word_data = {forms = {}, title = nil, categories = {}} -- word index word_data.num = num data.num = num -- Generate the forms inflections[infl_type](argobj, word_data) postprocess_word(argobj, word_data, data, num == #data.infl_types) word_data.class = infl_type data.words[num] = word_data end

if #data.words > 1 then -- join the inflected word components export.join_words(data, function (n) return args["space" .. tostring(n)] or args["space"] or " " end) else data.vh = data.words[1].vh		data.forms = data.words[1].forms data.title = data.words[1].title data.categories = data.words[1].categories end -- Postprocess postprocess(args, data) end

-- The main entry point. -- This is the only function that can be invoked from a template. function export.show(frame) local infl_type = frame.args[1] or error("Inflection type has not been specified. Please pass parameter 1 to the module invocation") local args = frame:getParent.args local infl_types = {infl_type} infl_types = mw.text.split(infl_type, "%-") for _, type in ipairs(infl_types) do		if not inflections[type] then error("Unknown inflection type '" .. infl_type .. "'") end end local pos = args["pos"]; if not pos or pos == "" then pos = "noun" end local allow_possessive = (pos == "noun" or pos == "adj") and not args["poss"] -- initialize data for full inflection process local data = { pagename = mw.title.getCurrentTitle.text, infl_types = infl_types, poss = args["poss"] }	local argobj = {args = args}

if args["title"] and mw.title.getCurrentTitle.namespace > 0 then data.pagename = args.title elseif data.pagename:find("^Unsupported titles/") then data.pagename = data.pagename:gsub("^Unsupported titles/", "") end data.pagename = normalize_apostrophes(data.pagename)

do_inflection_internal(data, argobj)

--if args["type"] then table.insert(data.categories, "fi-decl with type") end --if args["nocheck"] then table.insert(data.categories, "fi-decl with nocheck") end --if args["nosg"] then table.insert(data.categories, "fi-decl with nosg") end --if args["nopl"] then table.insert(data.categories, "fi-decl with nopl") end local function get_poss_forms(poss) data.poss = poss do_inflection_internal(data, argobj) return data end

local categories if args["appendix"] then categories = "" else categories = require("Module:utilities").format_categories(data.categories, lang) end

return make_table(data, true) .. (allow_possessive and make_possessive_table(data.pagename, args, pos, data.title, get_poss_forms) or "") .. categories .. require("Module:TemplateStyles")("Module:fi-nominals/style.css") end

local function args_get_required(args, i, purpose) local v = args[i] if not v then error(purpose .. " (parameter " .. i .. ") may not be omitted.") end return v end

local function args_get_vowel_harmony(args, i)	local v = args[i] if not v or not mw.ustring.match(v, "^[aä]$") then error("Vowel harmony (parameter " .. i .. ") must be \"a\" or \"ä\".") end return v end

function get_params(argobj, num, invert_grades) local params = {} local args = argobj.args local pos = argobj.pos

params.base = normalize_apostrophes(args[pos]) if num >= 2 then if num >= 4 then params.strong = normalize_apostrophes(args_get_required(args, pos + 1, "Nominative grade")) params.weak = normalize_apostrophes(args_get_required(args, pos + 2, "Genitive grade")) -- Swap the grades if invert_grades then params.strong, params.weak = params.weak, params.strong end end

if num >= 5 then params.final = args_get_required(args, pos + 3, "Final letter(s)") end

params.a = args_get_vowel_harmony(args, pos + num - 1) else params.base = params.base or "" end if params.a then params.o = params.a == "ä" and "ö" or "o" params.u = params.a == "ä" and "y" or "u" end argobj.pos = argobj.pos + num return params end

function get_extra_arg(argobj, wdata, name, fallback) return argobj.args[name .. wdata.num] or argobj.args[name] end

--[=[	Inflection functions ]=]--

local stem_endings = {}

stem_endings["nom_sg"] = { ["nom_sg"] = "", }

stem_endings["sg"] = { ["ess_sg"] = "na", }

stem_endings["sg_weak"] = { ["gen_sg"] = "n", ["ine_sg"] = "ssa", ["ela_sg"] = "sta", ["ade_sg"] = "lla", ["abl_sg"] = "lta", ["all_sg"] = "lle", ["tra_sg"] = "ksi", ["ins_sg"] = "n", ["abe_sg"] = "tta", ["nom_pl"] = "t", }

stem_endings["par_sg"] = { ["par_sg"] = "a", }

stem_endings["ill_sg"] = { ["ill_sg"] = "Vn", }

stem_endings["pl"] = { ["ess_pl"] = "na", ["com_pl"] = "ne", }

stem_endings["pl_weak"] = { ["ine_pl"] = "ssa", ["ela_pl"] = "sta", ["ade_pl"] = "lla", ["abl_pl"] = "lta", ["all_pl"] = "lle", ["tra_pl"] = "ksi", ["ins_pl"] = "n", ["abe_pl"] = "tta", }

stem_endings["par_pl"] = { ["par_pl"] = "a", }

stem_endings["gen_pl"] = { ["gen_pl"] = "en", }

stem_endings["ill_pl"] = { ["ill_pl"] = "Vn", }

-- Make a copy of the endings, with front vowels stem_endings = {["a"] = stem_endings, ["ä"] = mw.clone(stem_endings)}

for stem_key, endings in pairs(stem_endings["ä"]) do	for key, ending in pairs(endings) do		endings[key] = mw.ustring.gsub(endings[key], "([aou])", {["a"] = "ä", ["o"] = "ö", ["u"] = "y"}) end end

-- data for generating possessive forms -- suffixes per person local poss_forms = {["1s"] = "ni", ["2s"] = "si", ["3s"] = "nsa", ["1p"] = "mme", ["2p"] = "nne"} local poss_alt = {--["00"] = false, ["1s"] = false, ["2s"] = false, ["1p"] = false, ["2p"] = false, ["3s"] = true, -- shorter form -Vn } -- which forms allow -nsa > -Vn? local forms_alt_ok = { ["gen_sg"] = false, ["gen_pl"] = false, ["par_sg"] = false, ["par_pl"] = true, ["ine_sg"] = true, ["ine_pl"] = true, ["ela_sg"] = true, ["ela_pl"] = true, ["ill_sg"] = false, ["ill_pl"] = false, ["ade_sg"] = true, ["ade_pl"] = true, ["abl_sg"] = true, ["abl_pl"] = true, ["all_sg"] = true, ["all_pl"] = true, ["ess_sg"] = true, ["ess_pl"] = true, ["tra_sg"] = true, ["tra_pl"] = true, ["ins_sg"] = false, ["ins_pl"] = false, ["abe_sg"] = true, ["abe_pl"] = true, ["com_sg"] = true, ["com_pl"] = true, } -- which forms end in -n? -- (in which case it is dropped before the possessive suffix) local forms_gen_ok = { ["gen_sg"] = true, ["gen_pl"] = true, ["ill_sg"] = true, ["ill_pl"] = true, ["ins_sg"] = true, ["ins_pl"] = true, }

local function feed_list(outputs, inputs) for key, values in pairs(inputs) do		outputs[key] = outputs[key] or {} for _, value in ipairs(values) do			table.insert(outputs[key], value) end end end

local function process_stems(data, stems, vh) -- Create any stems that were not given stems["nom_sg"] = stems["nom_sg"] or mw.clone(stems["sg"]) stems["par_sg"] = stems["par_sg"] or mw.clone(stems["sg"]) stems["par_pl"] = stems["par_pl"] or mw.clone(stems["pl"]) stems["ill_pl"] = stems["ill_pl"] or mw.clone(stems["pl"]) if not stems["ill_sg"] and stems["sg"] then stems["ill_sg"] = {} for _, stem in ipairs(stems["sg"]) do			-- If the stem ends in a long vowel or diphthong, then add -h if mw.ustring.find(stem, "([aeiouyäö])%1$") or mw.ustring.find(stem, "[aeiouyäö][iuyü]$") then table.insert(stems["ill_sg"], stem .. "h") else table.insert(stems["ill_sg"], stem) end end end if not stems["gen_pl"] and stems["par_pl"] then stems["gen_pl"] = {} for _, stem in ipairs(stems["par_pl"]) do			-- If the partitive plural stem ends in -it, then replace the t with d or tt			if mw.ustring.find(stem, "it$") then table.insert(stems["gen_pl"], (mw.ustring.gsub(stem, "t$", "d"))) table.insert(stems["gen_pl"], stem .. "t") else table.insert(stems["gen_pl"], stem) end end end

-- Create forms based on each stem, by adding endings to it	stems["sg_weak"] = stems["sg_weak"] or mw.clone(stems["sg"]) stems["pl_weak"] = stems["pl_weak"] or mw.clone(stems["pl"]) -- Go through each of the stems given for stem_key, substems in pairs(stems) do		for _, stem in ipairs(substems) do			-- Attach the endings to the stem for form_key, ending in pairs(stem_endings[vh][stem_key]) do				if not data.forms[form_key] then data.forms[form_key] = {} end -- "V" is a copy of the last vowel in the stem if mw.ustring.find(ending, "V") then local vowel = mw.ustring.match(stem, "([aeiouyäö])[^aeiouyäö]*$") ending = mw.ustring.gsub(ending, "V", vowel or "?") end table.insert(data.forms[form_key], stem .. ending) end end end data["stems"] = stems data["vh"] = vh end

local function make_kotus_title_number(type_number) return "Kotus type " .. type_number end

local function make_kotus_title_word(reference_word) return '/ ' .. reference_word .. ' ' end

local function make_kotus_title(number, reference_word) return make_kotus_title_number(number) .. make_kotus_title_word(reference_word) end

local function inflection_type_is(data, number, reference_word, strong, weak) local title = make_kotus_title_number(number) local has_gradation = strong and strong ~= weak

if has_gradation then local letter = kotus_grad_type[strong .. "-" .. weak] if letter then title = title .. "*" .. letter else title = title .. "*"		end end

title = title .. make_kotus_title_word(reference_word)

if has_gradation then local EMPTY = " ∅ " local function format(grade) if grade == "" then return EMPTY else return "''" .. grade .. "''"			end end title = title .. ", " .. format(strong) .. "-" .. format(weak) .. " gradation" else title = title .. ", no gradation" end

data.title = title

table.insert(data.categories, "Finnish " .. reference_word .. "-type nominals") end

--[=[	Inflection types ]=]--

inflections["valo"] = function(args, data) local params = get_params(args, 5) local wk_sg = params.weak local wk_pl = params.weak if mw.ustring.sub(params.base, -1) == params.final then if wk_sg == "" and (mw.ustring.find(params.base, "[aeiouyäö][iuy]$") or mw.ustring.find(params.base, "[iuy][eoö]$")) then wk_sg = "’" end if wk_pl == "" then wk_pl = "’" end end local stems = {} stems["sg"]     = {params.base .. params.strong .. params.final} stems["sg_weak"] = {params.base .. wk_sg .. params.final} stems["pl"]     = {params.base .. params.strong .. params.final .. "i"} stems["pl_weak"] = {params.base .. wk_pl .. params.final .. "i"} stems["par_pl"] = {params.base .. params.strong .. params.final .. "j"} stems["ill_pl"] = {params.base .. params.strong .. params.final .. "ih"} inflection_type_is(data, 1, "valo", params.strong, params.weak) process_stems(data, stems, params.a) end

inflections["palvelu"] = function(args, data) local params = get_params(args, 2) local stems = {} stems["sg"]     = {params.base} stems["pl"]     = {params.base .. "i"} stems["par_pl"] = {params.base .. "j", params.base .. "it"} stems["ill_pl"] = {params.base .. "ih"} inflection_type_is(data, 2, "palvelu") process_stems(data, stems, params.a) end

inflections["valtio"] = function(args, data) local params = get_params(args, 2) local final = mw.ustring.sub(params.base, -1) local stems = {} stems["sg"]     = {params.base} stems["par_sg"] = {params.base .. "t"} stems["pl"]     = {params.base .. "i"} stems["par_pl"] = {params.base .. "it"} stems["ill_pl"] = {params.base .. "ih"} inflection_type_is(data, 3, "valtio") process_stems(data, stems, params.a) end

inflections["laatikko"] = function(args, data) local params = get_params(args, 5, false, "kk", "k", "o") local stems = {} stems["sg"]     = {params.base .. params.strong .. params.final} stems["sg_weak"] = {params.base .. params.weak .. params.final} stems["pl"]     = {params.base .. params.strong .. params.final .. "i"} stems["pl_weak"] = {params.base .. params.weak .. params.final .. "i"} stems["par_pl"] = {params.base .. params.strong .. params.final .. "j", params.base .. params.weak .. params.final .. "it"} stems["ill_pl"] = {params.base .. params.strong .. params.final .. "ih", params.base .. params.weak .. params.final .. "ih"} inflection_type_is(data, 4, "laatikko", params.strong, params.weak) process_stems(data, stems, params.a) end

inflections["risti"] = function(args, data) local params = get_params(args, 4) local i = get_extra_arg(args, data, "i"); if i == "0" then i = "" else i = "i" end

local stems = {} stems["nom_sg"] = {params.base .. params.strong .. i} stems["sg"]     = {params.base .. params.strong .. "i"} stems["sg_weak"] = {params.base .. params.weak .. "i"} stems["pl"]     = {params.base .. params.strong .. "ei"} stems["pl_weak"] = {params.base .. params.weak .. "ei"} stems["gen_pl"] = {params.base .. params.strong .. "i"} stems["par_pl"] = {params.base .. params.strong .. "ej"} stems["ill_pl"] = {params.base .. params.strong .. "eih"} inflection_type_is(data, 5, "risti", params.strong, params.weak) process_stems(data, stems, params.a) end

inflections["paperi"] = function(args, data) local params = get_params(args, 2) local i = get_extra_arg(args, data, "i"); if i == "0" then i = "" else i = "i" end local stems = {} stems["nom_sg"] = {params.base .. i} stems["sg"]     = {params.base .. "i"} stems["pl"]     = {params.base .. "ei"} stems["par_pl"] = {params.base .. "eit", params.base .. "ej"} stems["gen_pl"] = {params.base .. "i", params.base .. "eid", params.base .. "eitt"} stems["ill_pl"] = {params.base .. "eih"} inflection_type_is(data, 6, "paperi") process_stems(data, stems, params.a) end

inflections["ovi"] = function(args, data) local params = get_params(args, 4) local nom_sg = get_extra_arg(args, data, "nom_sg"); if nom_sg == "" then nom_sg = nil end local wk_pl = params.weak if mw.ustring.sub(params.base, -1) == "i" and params.strong == "k" and params.weak == "" then wk_pl = "’" end

local stems = {} stems["nom_sg"] = {nom_sg or params.base .. params.strong .. "i"} stems["sg"]     = {params.base .. params.strong .. "e"} stems["sg_weak"] = {params.base .. params.weak .. "e"} stems["pl"]     = {params.base .. params.strong .. "i"} stems["pl_weak"] = {params.base .. wk_pl .. "i"} inflection_type_is(data, 7, "ovi", params.strong, params.weak) process_stems(data, stems, params.a) end

inflections["nalle"] = function(args, data) local params = get_params(args, 4) local stems = {} stems["sg"]     = {params.base .. params.strong .. "e"} stems["sg_weak"] = {params.base .. params.weak .. "e"} stems["pl"]     = {params.base .. params.strong .. "ei"} stems["pl_weak"] = {params.base .. params.weak .. "ei"} stems["par_pl"] = {params.base .. params.strong .. "ej"} stems["ill_pl"] = {params.base .. params.strong .. "eih"} inflection_type_is(data, 8, "nalle", params.strong, params.weak) process_stems(data, stems, params.a) data.forms["gen_pl"].rare = {params.base .. params.strong .. "ein"} end

inflections["kala"] = function(args, data) local params = get_params(args, 4) local ain = get_extra_arg(args, data, "ain"); if ain == "" then ain = nil end local wk_sg = params.weak if wk_sg == "" and mw.ustring.sub(params.base, -2) == params.a .. params.a then wk_sg = "’" end local stems = {} stems["sg"]     = {params.base .. params.strong .. params.a} stems["sg_weak"] = {params.base .. wk_sg .. params.a} stems["pl"]     = {params.base .. params.strong .. params.o .. "i"} stems["pl_weak"] = {params.base .. params.weak .. params.o .. "i"} stems["par_pl"] = {params.base .. params.strong .. params.o .. "j"} stems["ill_pl"] = {params.base .. params.strong .. params.o .. "ih"} inflection_type_is(data, 9, "kala", params.strong, params.weak) process_stems(data, stems, params.a) local ain_form = params.base .. params.strong .. params.a .. "in" if ain == "2" then table.insert(data.forms["gen_pl"], 1, ain_form) elseif ain == "1" then table.insert(data.forms["gen_pl"], ain_form) else data.forms["gen_pl"].rare = {ain_form} end end

inflections["koira"] = function(args, data) local params = get_params(args, 4) local nom_sg = get_extra_arg(args, data, "nom_sg"); if nom_sg == "" then nom_sg = nil end local apo_pl = get_extra_arg(args, data, "apo_pl"); if apo_pl == "" then apo_pl = nil end local ain = get_extra_arg(args, data, "ain"); if ain == "" then ain = nil end local wk_sg = params.weak local wk_pl = params.weak if wk_sg == "" and mw.ustring.sub(params.base, -2) == params.a .. params.a then wk_sg = "’" end if wk_pl == "" and mw.ustring.sub(params.base, -1) == "i" then wk_pl = "’" end local stems = {} stems["nom_sg"] = {nom_sg or params.base .. params.strong .. params.a} stems["sg"]     = {params.base .. params.strong .. params.a} stems["sg_weak"] = {params.base .. wk_sg .. params.a} stems["pl"]     = {params.base .. params.strong .. (apo_pl and "'" or "") .. "i"} stems["pl_weak"] = {params.base .. wk_pl .. (apo_pl and "'" or "") .. "i"} inflection_type_is(data, 10, "koira", params.strong, params.weak) process_stems(data, stems, params.a) local ain_form = params.base .. params.strong .. params.a .. "in" if ain == "2" then table.insert(data.forms["gen_pl"], 1, ain_form) elseif ain == "1" then table.insert(data.forms["gen_pl"], ain_form) else data.forms["gen_pl"].rare = {ain_form} end end

inflections["omena"] = function(args, data) local params = get_params(args, 2) local stems = {} stems["sg"]     = {params.base .. params.a} stems["pl"]     = {params.base .. params.o .. "i", params.base .. "i"} stems["par_pl"] = {params.base .. "i", params.base .. params.o .. "it"} stems["ill_pl"] = {params.base .. "i", params.base .. params.o .. "ih"} inflection_type_is(data, 11, "omena") process_stems(data, stems, params.a) data.forms["gen_pl"].rare = {params.base .. params.o .. "jen", params.base .. params.a .. "in"} data.forms["par_pl"].rare = {params.base .. params.o .. "j" .. params.a} end

inflections["kulkija"] = function(args, data) local params = get_params(args, 2) local stems = {} stems["sg"]     = {params.base .. params.a} stems["pl"]     = {params.base .. params.o .. "i"} stems["par_pl"] = {params.base .. params.o .. "it"} stems["ill_pl"] = {params.base .. params.o .. "ih"} inflection_type_is(data, 12, "kulkija") process_stems(data, stems, params.a) data.forms["gen_pl"].rare = {params.base .. params.a .. "in"} end

inflections["katiska"] = function(args, data) local params = get_params(args, 2) local stems = {} stems["sg"]     = {params.base .. params.a} stems["pl"]     = {params.base .. params.o .. "i"} stems["par_pl"] = {params.base .. params.o .. "it", params.base .. params.o .. "j"} stems["ill_pl"] = {params.base .. params.o .. "ih"} inflection_type_is(data, 13, "katiska") process_stems(data, stems, params.a) data.forms["gen_pl"].rare = {params.base .. params.a .. "in"} end

inflections["solakka"] = function(args, data) local params = get_params(args, 4) local stems = {} stems["sg"]     = {params.base .. params.strong .. params.a} stems["sg_weak"] = {params.base .. params.weak .. params.a} stems["pl"]     = {params.base .. params.strong .. params.o .. "i"} stems["pl_weak"] = {params.base .. params.weak .. params.o .. "i"} stems["par_pl"] = {params.base .. params.weak .. params.o .. "it", params.base .. params.strong .. params.o .. "j"} stems["ill_pl"] = {params.base .. params.weak .. params.o .. "ih", params.base .. params.strong .. params.o .. "ih"} inflection_type_is(data, 14, "solakka", params.strong, params.weak) process_stems(data, stems, params.a) data.forms["gen_pl"].rare = {params.base .. params.strong .. params.a .. "in"} end

inflections["korkea"] = function(args, data) local params = get_params(args, 2) local final = mw.ustring.sub(params.base, -1) local stems = {} stems["sg"]     = {params.base .. params.a} stems["par_sg"] = {params.base .. params.a, params.base .. params.a .. "t"} stems["pl"]     = {params.base .. "i"} stems["par_pl"] = {params.base .. "it"} stems["ill_pl"] = {params.base .. "isi", params.base .. "ih"} inflection_type_is(data, 15, "korkea") process_stems(data, stems, params.a) data.forms["gen_pl"].rare = {params.base .. params.a .. "in"} end

inflections["vanhempi"] = function(args, data) local params = get_params(args, 2) local stems = {} stems["nom_sg"] = {params.base .. "mpi"} stems["sg"]     = {params.base .. "mp" .. params.a} stems["sg_weak"] = {params.base .. "mm" .. params.a} stems["pl"]     = {params.base .. "mpi"} stems["pl_weak"] = {params.base .. "mmi"} inflection_type_is(data, 16, "vanhempi", "mp", "mm") process_stems(data, stems, params.a) data.forms["gen_pl"].rare = {params.base .. "mp" .. params.a .. "in"} end

inflections["vapaa"] = function(args, data) local params = get_params(args, 2) local final = mw.ustring.sub(params.base, -1) local stems = {} stems["sg"]     = {params.base .. final} stems["par_sg"] = {params.base .. final .. "t"} stems["ill_sg"] = {params.base .. final .. "se"} stems["pl"]     = {params.base .. "i"} stems["par_pl"] = {params.base .. "it"} stems["ill_pl"] = {params.base .. "isi"} inflection_type_is(data, 17, "vapaa") process_stems(data, stems, params.a) data.forms["ill_pl"].rare = {params.base .. "ihin"} end

inflections["maa"] = function(args, data) local params = get_params(args, 2) local pl_stem = mw.ustring.sub(params.base, 1, -2) local stems = {} stems["sg"]     = {params.base} stems["par_sg"] = {params.base .. "t"} stems["pl"]     = {pl_stem .. "i"} stems["par_pl"] = {pl_stem .. "it"} stems["ill_pl"] = {pl_stem .. "ih"} inflection_type_is(data, 18, "maa") process_stems(data, stems, params.a) end

inflections["suo"] = function(args, data) local params = get_params(args, 2) local final = mw.ustring.sub(params.base, -1) local stem = mw.ustring.sub(params.base, 1, -3) local plural if mw.ustring.sub(stem, -1) == final then plural = stem .. "-" .. final elseif mw.ustring.find(stem, "-$") and mw.ustring.sub(stem, -1) ~= final then plural = mw.ustring.sub(stem, 1, -2) .. final else plural = stem .. final end local stems = {} stems["sg"]     = {params.base} stems["par_sg"] = {params.base .. "t"} stems["ill_sg"] = {params.base .. "h"} stems["pl"]     = {plural .. "i"} stems["par_pl"] = {plural .. "it"} stems["ill_pl"] = {plural .. "ih"} inflection_type_is(data, 19, "suo") process_stems(data, stems, params.a) end

inflections["filee"] = function(args, data) local params = get_params(args, 2) local nom_sg = get_extra_arg(args, data, "nom_sg"); if nom_sg == "" then nom_sg = nil end local pl_stem = mw.ustring.sub(params.base, 1, -2) local stems = {} stems["nom_sg"] = {nom_sg or params.base} stems["sg"]     = {params.base} stems["par_sg"] = {params.base .. "t"} stems["ill_sg"] = {params.base .. "h", params.base .. "se"} stems["pl"]     = {pl_stem .. "i"} stems["par_pl"] = {pl_stem .. "it"} stems["ill_pl"] = {pl_stem .. "ih", pl_stem .. "isi"} inflection_type_is(data, 20, "filee") process_stems(data, stems, params.a) end

inflections["rosé"] = function(args, data) local params = get_params(args, 2) local ill_sg_vowel = get_extra_arg(args, data, "ill_sg_vowel"); if ill_sg_vowel == "" then error("Parameter \"ill_sg_vowel=\" cannot be empty.") end local ill_sg_vowel2 = get_extra_arg(args, data, "ill_sg_vowel2"); if ill_sg_vowel2 == "" then error("Parameter \"ill_sg_vowel2=\" cannot be empty.") end local nom_sg = get_extra_arg(args, data, "nom_sg"); if nom_sg == "" then nom_sg = nil end local stems = {} stems["nom_sg"] = {nom_sg or params.base} stems["sg"]     = {params.base} stems["par_sg"] = {params.base .. "t"} stems["ill_sg"] = {params.base .. "h"} stems["pl"]     = {params.base .. "i"} stems["par_pl"] = {params.base .. "it"} stems["ill_pl"] = {params.base .. "ih"} inflection_type_is(data, 21, "rosé") process_stems(data, stems, params.a)	if ill_sg_vowel then data.forms["ill_sg"] = {params.base .. "h" .. ill_sg_vowel .. "n"} end if ill_sg_vowel2 then table.insert(data.forms["ill_sg"], params.base .. "h" .. ill_sg_vowel2 .. "n") end end

inflections["parfait"] = function(args, data) local params = get_params(args, 2) local ill_sg_vowel = get_extra_arg(args, data, "ill_sg_vowel"); if ill_sg_vowel == "" then error("Parameter \"ill_sg_vowel=\" is missing.") end local ill_sg_vowel2 = get_extra_arg(args, data, "ill_sg_vowel2"); if ill_sg_vowel2 == "" then error("Parameter \"ill_sg_vowel2=\" cannot be empty.") end local stems = {} stems["nom_sg"] = {params.base} stems["sg"]     = {params.base .. "’"}	stems["par_sg"] = {params.base .. "’t"} stems["pl"]     = {params.base .. "’i"} stems["par_pl"] = {params.base .. "’it"} stems["ill_pl"] = {params.base .. "’ih"} inflection_type_is(data, 22, "parfait") process_stems(data, stems, params.a) data.forms["ill_sg"] = {params.base .. "’h" .. ill_sg_vowel .. "n"} if ill_sg_vowel2 then table.insert(data.forms["ill_sg"], params.base .. "h" .. ill_sg_vowel2 .. "n") end end

inflections["tiili"] = function(args, data) local params = get_params(args, 2) local stems = {} stems["nom_sg"] = {params.base .. "i"} stems["sg"]     = {params.base .. "e"} stems["par_sg"] = {params.base .. "t"} stems["pl"]     = {params.base .. "i"} inflection_type_is(data, 23, "tiili") process_stems(data, stems, params.a) end

inflections["uni"] = function(args, data) local params = get_params(args, 2) local par_sg_a = get_extra_arg(args, data, "par_sg_a"); if par_sg_a and par_sg_a ~= "a" and par_sg_a ~= "ä" then error("Parameter \"par_sg_a=\" must be \"a\" or \"ä\".") end local stems = {} stems["nom_sg"] = {params.base .. "i"} stems["sg"]     = {params.base .. "e"} stems["par_sg"] = {params.base .. "t"} stems["pl"]     = {params.base .. "i"} stems["gen_pl"] = {params.base .. "i", params.base .. "t"} inflection_type_is(data, 24, "uni") process_stems(data, stems, params.a)	if par_sg_a then data.forms["par_sg"] = {} for _, stem in ipairs(stems["par_sg"]) do table.insert(data.forms["par_sg"], stem .. par_sg_a) end end end

inflections["toimi"] = function(args, data) local params = get_params(args, 2) local stems = {} stems["nom_sg"] = {params.base .. "mi"} stems["sg"]     = {params.base .. "me"} stems["par_sg"] = {params.base .. "nt", params.base .. "me"} stems["pl"]     = {params.base .. "mi"} stems["gen_pl"] = {params.base .. "mi", params.base .. "nt"} inflection_type_is(data, 25, "toimi") process_stems(data, stems, params.a) end

inflections["pieni"] = function(args, data) local params = get_params(args, 2) local par_sg_a = get_extra_arg(args, data, "par_sg_a"); if par_sg_a and par_sg_a ~= "a" and par_sg_a ~= "ä" then error("Parameter \"par_sg_a=\" must be \"a\" or \"ä\".") end local stems = {} stems["nom_sg"] = {params.base .. "i"} stems["sg"]     = {params.base .. "e"} stems["par_sg"] = {params.base .. "t"} stems["pl"]     = {params.base .. "i"} stems["gen_pl"] = {params.base .. "t", params.base .. "i"} inflection_type_is(data, 26, "pieni") process_stems(data, stems, params.a)	if par_sg_a then data.forms["par_sg"] = {} for _, stem in ipairs(stems["par_sg"]) do table.insert(data.forms["par_sg"], stem .. par_sg_a) end end end

inflections["käsi"] = function(args, data) local params = get_params(args, 2) local stems = {} stems["nom_sg"] = {params.base .. "si"} stems["sg"]     = {params.base .. "te"} stems["sg_weak"] = {params.base .. "de"} stems["par_sg"] = {params.base .. "tt"} stems["pl"]     = {params.base .. "si"} inflection_type_is(data, 27, "käsi", "t", "d") process_stems(data, stems, params.a) data.forms["gen_pl"].rare = {params.base .. "tten"} end

inflections["kynsi"] = function(args, data) local params = get_params(args, 2, false, "n") local cons = mw.ustring.match(params.base, "[lnr]$") if not cons then error("Stem must end in \"l\", \"n\" or \"r\".") end local stems = {} stems["nom_sg"] = {params.base .. "si"} stems["sg"]     = {params.base .. "te"} stems["sg_weak"] = {params.base .. cons .. "e"} stems["par_sg"] = {params.base .. "tt"} stems["pl"]     = {params.base .. "si"} inflection_type_is(data, 28, "kynsi", cons .. "t", cons .. cons) process_stems(data, stems, params.a) data.forms["gen_pl"].rare = {params.base .. "tten"} end

inflections["lapsi"] = function(args, data) local params = get_params(args, 2, false, "p") if not mw.ustring.match(params.base, "[kp]$") then error("Stem must end in \"k\" or \"p\".") end local syncopated_stem = mw.ustring.sub(params.base, 1, -2) local stems = {} stems["nom_sg"] = {params.base .. "si"} stems["sg"]     = {params.base .. "se"} stems["par_sg"] = {syncopated_stem .. "st"} stems["pl"]     = {params.base .. "si"} stems["gen_pl"] = {params.base .. "si", syncopated_stem .. "st"} inflection_type_is(data, 29, "lapsi") process_stems(data, stems, params.a) end

inflections["veitsi"] = function(args, data) local params = get_params(args, 2) local stems = {} stems["nom_sg"] = {params.base .. "tsi"} stems["sg"]     = {params.base .. "tse"} stems["par_sg"] = {params.base .. "st"} stems["pl"]     = {params.base .. "tsi"} inflection_type_is(data, 30, "veitsi") process_stems(data, stems, params.a) data.forms["gen_pl"].rare = {params.base .. "sten"} end

inflections["kaksi"] = function(args, data) local params = get_params(args, 2) local stems = {} stems["nom_sg"] = {params.base .. "ksi"} stems["sg"]     = {params.base .. "hte"} stems["sg_weak"] = {params.base .. "hde"} stems["par_sg"] = {params.base .. "ht"} stems["pl"]     = {params.base .. "ksi"} inflection_type_is(data, 31, "kaksi", "t", "d") process_stems(data, stems, params.a) end

inflections["sisar"] = function(args, data) local params = get_params(args, 5, true) local nom_sg = get_extra_arg(args, data, "nom_sg"); if nom_sg == "" then nom_sg = nil end local stems = {} stems["nom_sg"] = {nom_sg or params.base .. params.weak .. params.final} stems["sg"]     = {params.base .. params.strong .. params.final .. "e"} stems["par_sg"] = {params.base .. params.weak .. params.final .. "t"} stems["pl"]     = {params.base .. params.strong .. params.final .. "i"} stems["gen_pl"] = {params.base .. params.strong .. params.final .. "i", params.base .. params.weak .. params.final .. "t"} inflection_type_is(data, 32, "sisar", params.strong, params.weak) process_stems(data, stems, params.a) end

inflections["kytkin"] = function(args, data) local params = get_params(args, 5, true) local stems = {} stems["nom_sg"] = {params.base .. params.weak .. params.final .. "n"} stems["sg"]     = {params.base .. params.strong .. params.final .. "me"} stems["par_sg"] = {params.base .. params.weak .. params.final .. "nt"} stems["pl"]     = {params.base .. params.strong .. params.final .. "mi"} stems["gen_pl"] = {params.base .. params.strong .. params.final .. "mi", params.base .. params.weak .. params.final .. "nt"} inflection_type_is(data, 33, "kytkin", params.strong, params.weak) process_stems(data, stems, params.a) end

inflections["onneton"] = function(args, data) local no_gradation = get_extra_arg(args, data, "no_tt") == "1" local strong if no_gradation then strong = "t" else strong = "tt" end local params = get_params(args, 2) local stems = {} stems["nom_sg"] = {params.base .. "t" .. params.o .. "n"} stems["sg"]     = {params.base .. strong .. params.o .. "m" .. params.a} stems["par_sg"] = {params.base .. "t" .. params.o .. "nt"} stems["pl"]     = {params.base .. strong .. params.o .. "mi"} inflection_type_is(data, 34, "onneton", strong, "t") process_stems(data, stems, params.a) data.forms["gen_pl"].rare = {params.base .. "t" .. params.o .. "nten"} end

inflections["lämmin"] = function(args, data) local params = get_params(args, 5, true) local stems = {} stems["nom_sg"] = {params.base .. params.weak .. params.final .. "n"} stems["sg"]     = {params.base .. params.strong .. params.final .. "m" .. params.a} stems["par_sg"] = {params.base .. params.weak .. params.final .. "nt"} stems["pl"]     = {params.base .. params.strong .. params.final .. "mi"} inflection_type_is(data, 35, "lämmin", params.strong, params.weak) process_stems(data, stems, params.a) data.forms["gen_pl"].rare = {params.base .. params.strong .. params.final .. "m" .. params.a .. "in"} end

inflections["sisin"] = function(args, data) local params = get_params(args, 2) local stems = {} stems["nom_sg"] = {params.base .. "in"} stems["sg"]     = {params.base .. "imp" .. params.a} stems["sg_weak"] = {params.base .. "imm" .. params.a} stems["par_sg"] = {params.base .. "int"} stems["pl"]     = {params.base .. "impi"} stems["pl_weak"] = {params.base .. "immi"} stems["gen_pl"] = {params.base .. "impi", params.base .. "int"} inflection_type_is(data, 36, "sisin", "mp", "mm") process_stems(data, stems, params.a) data.forms["gen_pl"].rare = {params.base .. "imp" .. params.a .. "in"} end

inflections["vasen"] = function(args, data) local params = get_params(args, 1) params.base = params.base .. "vase" params.a = "a" local stems = {} stems["nom_sg"] = {params.base .. "n"} stems["sg"]     = {params.base .. "mp" .. params.a} stems["sg_weak"] = {params.base .. "mm" .. params.a} stems["par_sg"] = {params.base .. "nt", params.base .. "mp" .. params.a} stems["pl"]     = {params.base .. "mpi"} stems["pl_weak"] = {params.base .. "mmi"} stems["gen_pl"] = {params.base .. "mpi", params.base .. "nt"} inflection_type_is(data, 37, "vasen", "mp", "mm") process_stems(data, stems, params.a) data.forms["gen_pl"].rare = {params.base .. "mp" .. params.a .. "in"} end

inflections["nainen"] = function(args, data) local params = get_params(args, 2) local stems = {} stems["nom_sg"] = {params.base .. "nen"} stems["sg"]     = {params.base .. "se"} stems["par_sg"] = {params.base .. "st"} stems["pl"]     = {params.base .. "si"} stems["gen_pl"] = {params.base .. "st", params.base .. "si"} inflection_type_is(data, 38, "nainen") process_stems(data, stems, params.a) end

inflections["vastaus"] = function(args, data) local params = get_params(args, 2) local nom_sg = get_extra_arg(args, data, "nom_sg"); if nom_sg == "" then nom_sg = nil end local stems = {} stems["nom_sg"] = {nom_sg or params.base .. "s"} stems["sg"]     = {params.base .. "kse"} stems["par_sg"] = {params.base .. "st"} stems["pl"]     = {params.base .. "ksi"} stems["gen_pl"] = {params.base .. "st", params.base .. "ksi"} inflection_type_is(data, 39, "vastaus") process_stems(data, stems, params.a) end

inflections["kalleus"] = function(args, data) local params = get_params(args, 2) local stems = {} stems["nom_sg"] = {params.base .. "s"} stems["sg"]     = {params.base .. "te"} stems["sg_weak"] = {params.base .. "de"} stems["par_sg"] = {params.base .. "tt"} stems["pl"]     = {params.base .. "ksi"} inflection_type_is(data, 40, "kalleus", "t", "d") process_stems(data, stems, params.a) end

inflections["vieras"] = function(args, data) local params = get_params(args, 5, true) local nom_sg = get_extra_arg(args, data, "nom_sg"); if nom_sg == "" then nom_sg = nil end local stems = {} stems["nom_sg"] = {nom_sg or (params.base .. params.weak .. params.final .. "s")} stems["sg"]     = {params.base .. params.strong .. params.final .. params.final} stems["par_sg"] = {params.base .. params.weak .. params.final .. "st"} stems["ill_sg"] = {params.base .. params.strong .. params.final .. params.final .. "se"} stems["pl"]     = {params.base .. params.strong .. params.final .. "i"} stems["par_pl"] = {params.base .. params.strong .. params.final .. "it"} stems["ill_pl"] = {params.base .. params.strong .. params.final .. "isi"} inflection_type_is(data, 41, "vieras", params.strong, params.weak) process_stems(data, stems, params.a) data.forms["gen_pl"].rare = {params.base .. params.weak .. params.final .. "sten"} data.forms["ill_pl"].rare = {params.base .. params.strong .. params.final .. "ihin"} end

inflections["mies"] = function(args, data) local params = get_params(args, 1) local cap = get_extra_arg(args, data, "cap"); if cap == "" then nom_sg = nil end params.base = params.base .. (cap and "Mie" or "mie") params.a = "ä" local stems = {} stems["nom_sg"] = {params.base .. "s"} stems["sg"]     = {params.base .. "he"} stems["par_sg"] = {params.base .. "st"} stems["pl"]     = {params.base .. "hi"} stems["gen_pl"] = {params.base .. "st", params.base .. "hi"} inflection_type_is(data, 42, "mies") process_stems(data, stems, params.a) end

inflections["ohut"] = function(args, data) local params = get_params(args, 5, true) local stems = {} stems["nom_sg"] = {params.base .. params.weak .. params.final .. "t"} stems["sg"]     = {params.base .. params.strong .. params.final .. "e"} stems["par_sg"] = {params.base .. params.weak .. params.final .. "tt"} stems["pl"]     = {params.base .. params.strong .. params.final .. "i"} stems["par_pl"] = {params.base .. params.strong .. params.final .. "it"} stems["ill_pl"] = {params.base .. params.strong .. params.final .. "isi", params.base .. params.strong .. params.final .. "ih"} inflection_type_is(data, 43, "ohut", params.strong, params.weak) process_stems(data, stems, params.a) end

inflections["kevät"] = function(args, data) local params = get_params(args, 4, true) local vowel = params.a	local stems = {} stems["nom_sg"] = {params.base .. params.weak .. vowel .. "t"} stems["sg"]     = {params.base .. params.strong .. vowel .. vowel} stems["par_sg"] = {params.base .. params.weak .. vowel .. "tt"} stems["ill_sg"] = {params.base .. params.strong .. vowel .. vowel .. "se"} stems["pl"]     = {params.base .. params.strong .. vowel .. "i"} stems["par_pl"] = {params.base .. params.strong .. vowel .. "it"} stems["ill_pl"] = {params.base .. params.strong .. vowel .. "isi"} inflection_type_is(data, 44, "kevät", params.strong, params.weak) process_stems(data, stems, params.a) data.forms["ill_pl"].rare = {params.base .. params.strong .. vowel .. "ihin"} end

inflections["kahdeksas"] = function(args, data) local params = get_params(args, 2) local stems = {} stems["nom_sg"] = {params.base .. "s"} stems["sg"]     = {params.base .. "nte"} stems["sg_weak"] = {params.base .. "nne"} stems["par_sg"] = {params.base .. "tt"} stems["pl"]     = {params.base .. "nsi"} inflection_type_is(data, 45, "kahdeksas", "nt", "nn") process_stems(data, stems, params.a) end

inflections["tuhat"] = function(args, data) local params = get_params(args, 2) local stems = {} stems["nom_sg"] = {params.base .. "t"} stems["sg"]     = {params.base .. "nte"} stems["sg_weak"] = {params.base .. "nne"} stems["par_sg"] = {params.base .. "tt"} stems["pl"]     = {params.base .. "nsi"}

inflection_type_is(data, 46, "tuhat", "nt", "nn") process_stems(data, stems, params.a) data.forms["gen_pl"].rare = {params.base .. "nten"} end

inflections["kuollut"] = function(args, data) local params = get_params(args, 2) local stems = {} stems["nom_sg"] = {params.base .. params.u .. "t"} stems["sg"]     = {params.base .. "ee"} stems["par_sg"] = {params.base .. params.u .. "tt"} stems["ill_sg"] = {params.base .. "eese"} stems["pl"]     = {params.base .. "ei"} stems["par_pl"] = {params.base .. "eit"} stems["ill_pl"] = {params.base .. "eisi", params.base .. "eih"} inflection_type_is(data, 47, "kuollut") process_stems(data, stems, params.a) end

inflections["hame"] = function(args, data) local params = get_params(args, 4, true) local stem_vowel = get_extra_arg(args, data, "stem_vowel"); if stem_vowel == "" then stem_vowel = nil end stem_vowel = stem_vowel or "e" local nom_sg = get_extra_arg(args, data, "nom_sg"); if nom_sg == "" then nom_sg = nil end local stems = {} stems["nom_sg"] = {nom_sg or (params.base .. params.weak .. stem_vowel)} stems["sg"]     = {params.base .. params.strong .. stem_vowel .. stem_vowel} stems["par_sg"] = {params.base .. params.weak .. stem_vowel .. "tt"} stems["ill_sg"] = {params.base .. params.strong .. stem_vowel .. stem_vowel .. "se"} stems["pl"]     = {params.base .. params.strong .. stem_vowel .. "i"} stems["par_pl"] = {params.base .. params.strong .. stem_vowel .. "it"} stems["ill_pl"] = {params.base .. params.strong .. stem_vowel .. "isi", params.base .. params.strong .. stem_vowel .. "ih"} inflection_type_is(data, 48, "hame", params.strong, params.weak) process_stems(data, stems, params.a) end

inflections["askel"] = function(args, data) local params = get_params(args, 5, true) local prefer_hame = get_extra_arg(args, data, "e") == "1" local stems_sisar = {} stems_sisar["sg"]     = {params.base .. params.strong .. params.final .. "e"} stems_sisar["nom_sg"] = {params.base .. params.weak .. params.final} stems_sisar["par_sg"] = {params.base .. params.weak .. params.final .. "t"} stems_sisar["ill_sg"] = {params.base .. params.strong .. params.final .. "e"} stems_sisar["pl"]     = {params.base .. params.strong .. params.final .. "i"} stems_sisar["gen_pl"] = {params.base .. params.strong .. params.final .. "i", params.base .. params.weak .. params.final .. "t"} stems_sisar["par_pl"] = {params.base .. params.strong .. params.final .. "i"} stems_sisar["ill_pl"] = {params.base .. params.strong .. params.final .. "i"}

local stems_hame = {} stems_hame["sg"]     = {params.base .. params.strong .. params.final .. "ee"} stems_hame["nom_sg"] = {params.base .. params.strong .. params.final .. "e"} stems_hame["par_sg"] = {params.base .. params.strong .. params.final .. "ett"} stems_hame["ill_sg"] = {params.base .. params.strong .. params.final .. "eese"} stems_hame["pl"]     = {params.base .. params.strong .. params.final .. "ei"} stems_hame["gen_pl"] = {params.base .. params.strong .. params.final .. "eid", params.base .. params.strong .. params.final .. "eitt"} stems_hame["par_pl"] = {params.base .. params.strong .. params.final .. "eit"} stems_hame["ill_pl"] = {params.base .. params.strong .. params.final .. "eisi", params.base .. params.strong .. params.final .. "eih"}

local stems = {} if prefer_hame then feed_list(stems, stems_hame) feed_list(stems, stems_sisar) else feed_list(stems, stems_sisar) feed_list(stems, stems_hame) end

inflection_type_is(data, 49, "askel", params.strong, params.weak) process_stems(data, stems, params.a) end

-- Helper functions

-- joins data.words[...].forms to data.forms function export.join_words(data, sep_supplier) local reorganized = {} local classes = {} -- reorganize from words[n].forms[case](.rare) to forms[case],words[n](.rare) for windex, word in ipairs(data.words) do		table.insert(classes, word.class) for case, forms in pairs(word.forms) do			if reorganized[case] == nil then reorganized[case] = {} end reorganized[case][windex] = {} for _, form in ipairs(forms) do				table.insert(reorganized[case][windex], form) end if word.forms[case].rare then reorganized[case][windex].rare = {} for _, form in ipairs(word.forms[case].rare) do					table.insert(reorganized[case][windex].rare, form) end end end end -- merge the forms with a Cartesian product to produce all possible combinations data.forms = {} for case, words in pairs(reorganized) do		data.forms[case] = forms_cart_product(words, case, sep_supplier, classes) end

if #data.words <= 1 then -- use title and categories of the sole word if there is only one data.title = data.words[1].title data.categories = data.words[1].categories else -- if there are multiple words, force nuoripari type data.title = make_kotus_title(51, "nuoripari") data.categories = {"Finnish nuoripari-type nominals"} end data.words = nil end

local function cleanup_suffix(suffix) if suffix and mw.ustring.find(suffix, "_") then return mw.ustring.gsub(suffix, "_", " ") else return suffix end end

-- computes the Cartesian product of tables function cart_product(words, depth) depth = depth or 1 local prod = {} for _, val in ipairs(words[depth]) do		if depth < #words then -- go over the next list for _, res in ipairs(cart_product(words, depth + 1)) do				table.insert(prod, { val, unpack(res) }) end else -- end of list, simply return the original table.insert(prod, { val }) end end return prod end

local function supplied_concat(list, sep_supplier) local result = "" local n = #list if n >= 1 then for i = 1, n - 1 do			local v = mw.ustring.match(list[i], "^[aeiouyäö]") if v and mw.ustring.find(result, v .. "$") then result = result .. "-"			end result = result .. list[i] .. sep_supplier(i) end local v = mw.ustring.match(list[n], "^[aeiouyäö]") if v and mw.ustring.find(result, v .. "$") then result = result .. "-"		end result = result .. list[n] end return result end

-- computes the Cartesian product of tables, also concats function cart_product_concat(words, sep_supplier) local res = {}

for _, combination in ipairs(cart_product(words)) do		table.insert(res, supplied_concat(combination, sep_supplier)) end

return res end

-- returns a bit mask (!) or nil function get_rhyming_pattern(word, case, class) if class == "askel" then return nil end if case == "gen_pl" then if mw.ustring.match(word, "tten$") then return 2 elseif mw.ustring.match(word, "ten$") then return 3 else return 1 end elseif case == "ill_sg" then if mw.ustring.match(word, "seen$") then return 1 elseif mw.ustring.match(word, "hen$") then return 2 end elseif case == "ill_pl" then if mw.ustring.match(word, "siin$") then return 1 elseif mw.ustring.match(word, "hin$") then return 2 end end return nil -- not applicable end

function is_nonrhyming(form, case, classes) local expected = m_bit32.bnot(0) -- -1 for i, word in ipairs(form) do		local got = get_rhyming_pattern(word, case, classes[i]) if got then expected = m_bit32.band(expected, got) end if expected == 0 then return true end end

return false end

-- computes the Cartesian product of tables, also concats -- returns non-rhyming combinations as rare function cart_product_concat_nonrhyming_rare(words, case, sep_supplier, classes) local res = {} local rare = {} local multichoice = 0 local allow_pruning = false

for _, position in ipairs(words) do		if #position > 1 then multichoice = multichoice + 1 end end

allow_pruning = multichoice > 1

for _, combination in ipairs(cart_product(words)) do		local item = supplied_concat(combination, sep_supplier) if is_nonrhyming(combination, case, classes) then table.insert(rare, item) else table.insert(res, item) end end

if #res < 1 then rare.rare = {} return rare end

res.rare = rare return res end

-- converts a list of words to extract the rare forms for ipairs -- the number is interpreted bit-by-bit to decide which combination -- to choose function prepare_rare_tables(words, code) local result = {} for _, forms in ipairs(words) do		-- replace with rare if bit is 1 if m_bit32.band(code, 1) == 1 then table.insert(result, forms.rare or {}) else table.insert(result, forms) end -- shift right to test next bit code = m_bit32.rshift(code, 1) end return result end

-- copies all entries of source and inserts them to target function merge_table(target, source) for _, value in ipairs(source) do		table.insert(target, value) end end

function merge_table_rare(target, source) for _, value in ipairs(source) do		table.insert(target, value) end target.rare = source.rare end

-- the Cartesian product of possible forms function forms_cart_product(words, case, sep_supplier, classes) local result = {} result.rare = {}

m_bit32 = require("bit32") -- merge possible non-rare forms merge_table_rare(result, cart_product_concat_nonrhyming_rare(words, case, sep_supplier, classes)) -- merge possible rare forms -- for example, with two words: --       1 = rare A, common B	--        2 = common A, rare B	--        3 = rare A, rare B	-- (2 ^ #words) - 1 == m_bit32.lshift(1,#words)-1 -- (prepare_rare_tables actually takes out the rare forms) for i = 1,m_bit32.lshift(1,#words)-1 do		merge_table(result.rare, cart_product_concat(prepare_rare_tables(words, i), sep_supplier)) end -- if no rare forms, remove the table completely if #result.rare < 1 then result.rare = nil end return result end

function make_word_possessive(args, data, poss, always_add) local pos = get_extra_arg(args, data, "pos"); if not pos or pos == "" then pos = "noun" end local par_nom_sg = get_extra_arg(args, data, "par_nom_sg") == "1"

-- "no possessive forms exist" sentinel value if poss == "-" then return data.forms end

if always_add or pos == "noun" then -- add possessive suffix if poss == "3" or poss == "3p" then poss = "3s" -- 3rd person forms are identical end if not poss_forms[poss] then error("Invalid poss value: '" .. p .. "'") end return make_poss_with_suffix(data.forms, data.stems, poss_forms[poss], "", poss_alt[poss], par_nom_sg) end return data.forms end

function postprocess_word(args, data, gdata, always_add) local pos = get_extra_arg(args, data, "pos"); if not pos or pos == "" then pos = "noun" end local alwayspl = get_extra_arg(args, data, "alwayspl"); if alwayspl == "" then alwayspl = nil end if gdata.poss then data.forms = make_word_possessive(args, data, gdata.poss, always_add) elseif pos == "noun" and data.forms["com_pl"] then -- Add the possessive suffix to the comitative plural, if the word is a noun for key, subform in ipairs(data.forms["com_pl"]) do data.forms["com_pl"][key] = subform .. "en" end gdata.tagged_com_pl = true end if get_extra_arg(args, data, "gen_nom_sg") == "1" then data.forms["nom_sg"] = data.forms["gen_sg"] elseif get_extra_arg(args, data, "par_nom_sg") == "1" then data.forms["nom_sg"] = data.forms["par_sg"] end if gdata.poss then data.forms["ins_pl"] = nil end

if alwayspl then -- Saint Vincent ja Grenadiinit for k, v in pairs(data.forms) do			local k_sg = k:gsub("_pl", "_sg") if data.forms[k_sg] then data.forms[k_sg] = data.forms[k] end end end end

function postprocess(args, data) local pos = args["pos"]; if not pos or pos == "" then pos = "noun" end local nosg = args["nosg"]; if nosg == "" then nosg = nil end local nopl = args["nopl"]; if nopl == "" then nopl = nil end local n = args["n"]; if n == "" then n = nil end local suffix = cleanup_suffix(args["suffix"]); if suffix == "" then suffix = nil end local appendix = args["appendix"]; if appendix == "" then appendix = nil end local has_ins_sg = args["has_ins_sg"]; if has_ins_sg == "" then has_ins_sg = nil end local has_no_nom = args["has_no_nom"]; if has_no_nom == "" then has_no_nom = nil end -- Add the possessive suffix to the comitative plural, if the word is a noun -- now done per word; see postprocess_word if nosg or n == "pl" then table.insert(data.categories, "Finnish pluralia tantum") end -- TODO: This says "nouns", but this module is also used for adjectives! if nopl or n == "sg" then table.insert(data.categories, "Finnish uncountable nouns") end if n == "csg" then         -- "chiefly singular" data.rare_plural = true end if not has_ins_sg then data.forms["ins_sg"] = nil end for key, form in pairs(data.forms) do		-- Add suffix to forms for i, subform in ipairs(form) do subform = subform .. (suffix or "") form[i] = subform end if form.rare then for i, subform in ipairs(form.rare) do subform = subform .. (suffix or "") form.rare[i] = subform end end -- Do not show singular or plural forms for nominals that don't have them if ((nosg or n == "pl") and key:find("_sg$")) or ((nopl or n == "sg") and key:find("_pl$")) then form = nil end data.forms[key] = form end -- Check if the lemma form matches the page name local lemma = data.forms[(nosg or n == "pl") and "nom_pl" or "nom_sg"][1] if not appendix and lemma ~= data.pagename and not data.poss then --error("The lemma " .. lemma .. " does not match the page title. Check the parameters!") mw.addWarning("Please check the fi-decl-... parameters!") table.insert(data.categories, "Finnish entries with inflection not matching pagename") end data.is_appendix = appendix if has_no_nom then data.forms["nom_sg"] = nil data.forms["nom_pl"] = nil end end

-- Make the table function make_table(data, preview, title_override, collapse_class_override, accel_class_prefix) local rare_plural = data.rare_plural local note = rare_plural and "Plural forms of this word are not commonly used, but might be found in figurative uses, in some set phrases or in colloquial language." or nil

local com_forms

local function show_form(forms, code, no_rare) local form = forms[code] if not form then return "&mdash;" elseif type(form) ~= "table" then error("a non-table value was given in the list of inflected forms.") end local ret = {} local accel

if rare_plural and code:find("_pl$") then -- plural is marginal for key, subform in ipairs(form) do table.insert(ret, "(''" .. make_link(subform) .. "'')")			end if not no_rare and form.rare then for key, subform in ipairs(form.rare) do table.insert(ret, "(''" .. make_link(subform) .. "''" .. RARE .. ")")				end end

return table.concat(ret, " ") end

-- See Module talk:fi-nominals. if code == "nom_sg" and not data.poss then accel = nil elseif code == "gen_sg" then accel = "gen//acc|s" elseif code == "nom_pl" then accel = "nom//acc|p" else accel = code:gsub("%f[^_](%a%a)$", {sg = "s", pl = "p"}):gsub("ins", "ist"):gsub("_", "|") end if accel_class_prefix and accel then accel = accel_class_prefix .. accel end

for key, subform in ipairs(form) do			table.insert(ret, make_link(subform, accel)) end if not no_rare and form.rare then if accel then accel = 'rare-' .. accel end

for key, subform in ipairs(form.rare) do table.insert(ret, make_link(subform, accel) .. RARE) end end return table.concat(ret, " ") end local function repl(param) if param == "title" then if title_override then return title_override end return "Inflection of " .. (data.is_appendix and make_link(data.pagename) or tag_term(data.pagename)) .. ( data.title and " (" .. data.title .. ")" or "") elseif param == "maybenote" then if note then return [=[ else return "" end elseif param == "com_forms" then return com_forms else local param2 = mw.ustring.match(param, "^(.-):c$") if param2 then return show_form(data.forms, param2, true) else return show_form(data.forms, param) end end end
 * - class="vsHide"
 * colspan="4" style="width:100px" | ]=] .. note .. "\n"

if not data.poss and data.tagged_com_pl then com_forms = 'colspan="2" | \'\'See the possessive forms below.\'\'' else com_forms = mw.ustring.gsub(' || ', "", repl) end

if preview then preview = [=[ ! class="case-column" colspan="2" | nominative ! colspan="2" | genitive ! colspan="2" | partitive ! colspan="2" | illative
 * - class="vsShow"
 * class="number-column" |
 * class="number-column" |
 * - class="vsShow"
 * - class="vsShow"
 * - class="vsShow"
 * - class="vsShow"
 * - class="vsShow"
 * - class="vsShow"
 * - class="vsShow"

]=]	else preview = "" end local wikicode = [=[ return mw.ustring.gsub(wikicode, "", repl) end

-- -- POSSESSIVE FORM GENERATION & DISPLAY -- --

local function prepare_possessive_list(forms) local res = {} for _, v in ipairs(forms) do		table.insert(res, v)	end if forms["rare"] then for _, v in ipairs(forms["rare"]) do			table.insert(res, v)			res[v] = "rare" end end return res end

local function wrap_rare_forms(forms) local newforms = {} for case, subforms in pairs(forms) do		local common = {} local rare = {} for _, v in ipairs(subforms) do			if subforms[v] == "rare" then table.insert(rare, v)			else table.insert(common, v)			end end common.rare = rare newforms[case] = common end return newforms end

local function make_possessives_from_stems(stems, suffix, extra_suffix) local pforms = {} for _, stem in pairs(stems) do table.insert(pforms, stem .. suffix .. extra_suffix) end return pforms end

function make_poss_with_suffix(forms, stems, poss_suffix, extra_suffix, allow_alt, par_nom_sg) local result = {} local par_sg_a = false if poss_suffix:find("a") and mw.ustring.sub(forms["ine_sg"][1], -1) == "ä" then poss_suffix = mw.ustring.gsub(poss_suffix, "a", "ä") end if mw.ustring.sub(forms["ine_sg"][1], -1) ~= mw.ustring.sub(forms["par_sg"][1], -1) then par_sg_a = true end

for k, v in pairs(forms_alt_ok) do		if forms[k] then local suffix = poss_suffix if k == "par_sg" and par_sg_a and mw.ustring.find(suffix, "ä$") then suffix = mw.ustring.gsub(poss_suffix, "ä", "a") end

result[k] = {} if k == "par_sg" and allow_alt then -- par_sg is a bit of an exception: it allows -- alt form if it doesn't end in two "aa"/"ää" local prepared = prepare_possessive_list(forms[k]) for _, form in ipairs(prepared) do					local modform = form if mw.ustring.sub(modform, -2, -2) ~= mw.ustring.sub(modform, -1) then local final = modform .. mw.ustring.sub(modform, -1) .. "n" table.insert(result[k], final) result[k][final] = prepared[form] end end elseif forms_alt_ok[k] and allow_alt then local prepared = prepare_possessive_list(forms[k]) for _, form in ipairs(prepared) do					local modform = form if k == "tra_sg" or k == "tra_pl" then modform = mw.ustring.sub(form, 1, -2) .. "e" end local final = modform .. mw.ustring.sub(modform, -1) .. "n" table.insert(result[k], final) result[k][final] = prepared[form] end end if k == "gen_sg" or k == "ins_sg" then result[k] = make_possessives_from_stems(stems["sg"], suffix, extra_suffix) elseif k == "ins_pl" then result[k] = make_possessives_from_stems(stems["pl"], suffix, extra_suffix) elseif forms_gen_ok[k] then local prepared = prepare_possessive_list(forms[k]) for _, form in ipairs(prepared) do					local tmp = form tmp = mw.ustring.sub(tmp, 1, -2) local final = tmp .. suffix .. extra_suffix table.insert(result[k], final) result[k][final] = prepared[form] end else local prepared = prepare_possessive_list(forms[k]) for _, form in ipairs(prepared) do					local modform = form if k == "tra_sg" or k == "tra_pl" then modform = mw.ustring.sub(form, 1, -2) .. "e" end local final = modform .. suffix .. extra_suffix table.insert(result[k], final) result[k][final] = prepared[form] end end end end

-- nominative forms are (usually) identical to genitive singular result["nom_sg"] = result[par_nom_sg and "par_sg" or "gen_sg"] result["nom_pl"] = result["gen_sg"] return wrap_rare_forms(result) end

local function serialize_args(args) local items = {} local entries = {} local max_number = 0

for key, value in pairs(args) do		if type(key) == "number" and key > 0 and key == math.floor(key) then items[key] = value max_number = math.max(key, max_number) else table.insert(entries, key .. "=" .. value) end end

for i = 1,max_number,1 do		items[i] = items[i] or "" end -- entries before items for i, v in ipairs(entries) do		table.insert(items, i, v)	end return table.concat(items, "|") end

local poss_headings = { ["1s"] = "first-person singular", ["2s"] = "second-person singular", ["1p"] = "first-person plural", ["2p"] = "second-person plural", ["3"] = "third-person", }

local poss_accel = { ["1s"] = "1|s", ["2s"] = "2|s", ["1p"] = "1|p", ["2p"] = "2|p", ["3"] = "3" }

function make_possessive_table(pagename, args, pos, infl_title, get_forms) local note = nil

if args["noposs"] then return "" elseif pos == "adj" then note = not args["hideadjnote"] and "Rare. Only used with substantive adjectives." or "" elseif pos ~= "noun" then return "" end

return "\n" .. make_possessive_table_internal(pagename, args, infl_title, note, get_forms) end

function make_possessive_table_internal(pagename, args, infl_title, note, get_forms) local function show_subtable(code) return make_table(get_forms(code), false, poss_headings[code] .. " possessor", "possessive inflection", poss_accel[code] .. "|poss|form|of|") end local function repl(param) if param == "lemma" then return tag_term(pagename) elseif param == "info" then return " (" .. infl_title .. ") " elseif param == "maybenote" then if note then return [=[ else return "" end else return show_subtable(param) end end local wikicode = [=[ return mw.ustring.gsub(wikicode, "", repl) end
 * - class="vsHide"
 * colspan="3" | ]=] .. note .. "\n"

export.inflections = inflections return export