Module:User:Surjection/fi-nominals

local m_utilities = require("Module:utilities") local m_links = require("Module:links") local m_bit32 = require("bit32")

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

-- Creates a link to a form. local function make_link(term, accel_form) -- if there is something difficult, use full module. if term:find(":") or term:find("<") then return m_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. if term == mw.title.getCurrentTitle.fullText then return ' ' .. term .. ' '	end if not accel_form then return ' ' .. term .. ' ' end 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 data.vh = data.words[1]["vh"] end -- join the inflected word components export.join_words(data, function (n) return args["space" .. tostring(n)] or args["space"] or " " 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["noposs"] and not args["poss"] -- initialize data for full inflection process local data = {infl_types = infl_types} local argobj = {args = args}

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 = m_utilities.format_categories(data.categories, lang)

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

function get_params(argobj, num, invert_grades) local params = {} local args = argobj.args local pos = argobj.pos if num == 5 then params.base = args[pos] or (mw.title.getCurrentTitle.nsText == "Template" and ""); if not params.base or params.base == "" then error("Parameter " .. tostring(pos) .. " (base stem) may not be empty.") end params.strong = args[pos + 1] or (mw.title.getCurrentTitle.nsText == "Template" and ""); if not params.strong then error("Parameter " .. tostring(pos + 1) .. " (nominative grade) may not be omitted.") end params.weak = args[pos + 2] or (mw.title.getCurrentTitle.nsText == "Template" and ""); if not params.weak then error("Parameter " .. tostring(pos + 2) .. " (genitive grade) may not be omitted.") end params.final = args[pos + 3] or (mw.title.getCurrentTitle.nsText == "Template" and ""); if not params.final or params.final == "" then error("Parameter " .. tostring(pos + 3) .. " (final letter(s)) may not be empty.") end params.a = args[pos + 4] or (mw.title.getCurrentTitle.nsText == "Template" and "a"); if params.a ~= "a" and params.a ~= "ä" then error("Parameter " .. tostring(pos + 4) .. " must be \"a\" or \"ä\".") end elseif num == 4 then params.base = args[pos] or (mw.title.getCurrentTitle.nsText == "Template" and ""); if not params.base or params.base == "" then error("Parameter " .. tostring(pos) .. " (base stem) may not be empty.") end params.strong = args[pos + 1] or (mw.title.getCurrentTitle.nsText == "Template" and ""); if not params.strong then error("Parameter " .. tostring(pos + 1) .. " (nominative grade) may not be omitted.") end params.weak = args[pos + 2] or (mw.title.getCurrentTitle.nsText == "Template" and ""); if not params.weak then error("Parameter " .. tostring(pos + 2) .. " (genitive grade) may not be omitted.") end params.a = args[pos + 3] or (mw.title.getCurrentTitle.nsText == "Template" and "a"); if params.a ~= "a" and params.a ~= "ä" then error("Parameter " .. tostring(pos + 3) .. " must be \"a\" or \"ä\".") end elseif num == 2 then params.base = args[pos] or (mw.title.getCurrentTitle.nsText == "Template" and ""); if not params.base or params.base == "" then error("Parameter " .. tostring(pos) .. " (base stem) may not be empty.") end params.a = args[pos + 1] or (mw.title.getCurrentTitle.nsText == "Template" and "a"); if params.a ~= "a" and params.a ~= "ä" then error("Parameter " .. tostring(pos + 1) .. " must be \"a\" or \"ä\".") end elseif num == 1 then params.base = args[pos] or "" end -- Swap the grades if invert_grades then params.strong, params.weak = params.weak, params.strong 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) return argobj.args[name .. wdata.num] or argobj.args[name] end

local make_weak = require("Module:fi-utilities").make_weak

--[=[	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", }

-- data for generating possessive forms -- suffixes per person local poss_forms = {["1s"] = "ni", ["2s"] = "si", ["3s"] = "nsa", ["1p"] = "mme", ["2p"] = "nne"} local poss_forms_optimized = { ["00"] = "",					["3s"] = "nsa"} local poss_alt = { ["00"] = false, ["1s"] = false, ["2s"] = false, ["3s"] = true, -- shorter form -Vn ["1p"] = false, ["2p"] = false} -- 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, }

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

-- Create any stems that were not given local function make_stems(data, stems) if not stems["nom_sg"] and stems["sg"] then stems["nom_sg"] = mw.clone(stems["sg"]) end if not stems["par_sg"] and stems["sg"] then stems["par_sg"] = mw.clone(stems["sg"]) end 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äö][iyü]$") then table.insert(stems["ill_sg"], stem .. "h") else table.insert(stems["ill_sg"], stem) end end end if not stems["par_pl"] and stems["pl"] then stems["par_pl"] = {} for _, stem in ipairs(stems["pl"]) do			table.insert(stems["par_pl"], stem) 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 if not stems["ill_pl"] and stems["pl"] then stems["ill_pl"] = {} for _, stem in ipairs(stems["pl"]) do			table.insert(stems["ill_pl"], stem) end end end

-- Create forms based on each stem, by adding endings to it local function process_stems(data, stems, vh) if not stems["sg_weak"] and stems["sg"] then stems["sg_weak"] = mw.clone(stems["sg"]) end if not stems["pl_weak"] and stems["pl"] then stems["pl_weak"] = mw.clone(stems["pl"]) end -- 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 merge_stems(stems_a, stems_b) local stems_m = {} -- implying same keys for both stems for stem_key, substems in pairs(stems_a) do		stems_m[stem_key] = mw.clone(substems) for _, bstem in pairs(stems_b[stem_key] or {}) do			table.insert(stems_m[stem_key], bstem) end end return stems_m 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

inflections["valo"] = function(args, data) local params = get_params(args, 5) inflection_type_is(data, 1, "valo", params.strong, params.weak) make_weak(params.base, params.strong, params.final, params.weak) 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"} make_stems(data, stems) process_stems(data, stems, params.a) end

inflections["palvelu"] = function(args, data) inflection_type_is(data, 2, "palvelu") 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"} make_stems(data, stems) process_stems(data, stems, params.a) end

inflections["valtio"] = function(args, data) inflection_type_is(data, 3, "valtio") 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"} make_stems(data, stems) process_stems(data, stems, params.a) end

inflections["laatikko"] = function(args, data) local params = get_params(args, 5, false, "kk", "k", "o")

inflection_type_is(data, 4, "laatikko", params.strong, params.weak) make_weak(params.base, params.strong, params.final, params.weak) 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"} make_stems(data, stems) 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

inflection_type_is(data, 5, "risti", params.strong, params.weak) make_weak(params.base, params.strong, "i", params.weak) 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"} make_stems(data, stems) process_stems(data, stems, params.a) end

inflections["paperi"] = function(args, data) inflection_type_is(data, 6, "paperi") 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"} make_stems(data, stems) 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 inflection_type_is(data, 7, "ovi", params.strong, params.weak) make_weak(params.base, params.strong, "e", params.weak) 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"} make_stems(data, stems) process_stems(data, stems, params.a) end

inflections["nalle"] = function(args, data) local params = get_params(args, 4) inflection_type_is(data, 8, "nalle", params.strong, params.weak) make_weak(params.base, params.strong, "e", params.weak) 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"} make_stems(data, stems) 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) inflection_type_is(data, 9, "kala", params.strong, params.weak) make_weak(params.base, params.strong, params.a, params.weak) 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"} make_stems(data, stems) process_stems(data, stems, params.a) data.forms["gen_pl"].rare = {params.base .. params.strong .. params.a .. "in"} 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 inflection_type_is(data, 10, "koira", params.strong, params.weak) make_weak(params.base, params.strong, params.a, params.weak) 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 .. "i"} stems["pl_weak"] = {params.base .. wk_pl .. "i"} make_stems(data, stems) process_stems(data, stems, params.a) data.forms["gen_pl"].rare = {params.base .. params.strong .. params.a .. "in"} end

inflections["omena"] = function(args, data) inflection_type_is(data, 11, "omena") 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"} make_stems(data, stems) 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) inflection_type_is(data, 12, "kulkija") 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"} make_stems(data, stems) process_stems(data, stems, params.a) data.forms["gen_pl"].rare = {params.base .. params.a .. "in"} end

inflections["katiska"] = function(args, data) inflection_type_is(data, 13, "katiska") 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"} make_stems(data, stems) 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) inflection_type_is(data, 14, "solakka", params.strong, params.weak) make_weak(params.base, params.strong, params.a, params.weak) 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"} make_stems(data, stems) process_stems(data, stems, params.a) data.forms["gen_pl"].rare = {params.base .. params.strong .. params.a .. "in"} end

inflections["korkea"] = function(args, data) inflection_type_is(data, 15, "korkea") 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"} make_stems(data, stems) process_stems(data, stems, params.a) data.forms["gen_pl"].rare = {params.base .. params.a .. "in"} end

inflections["vanhempi"] = function(args, data) inflection_type_is(data, 16, "vanhempi", "mp", "mm") 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"} make_stems(data, stems) process_stems(data, stems, params.a) data.forms["gen_pl"].rare = {params.base .. "mp" .. params.a .. "in"} end

inflections["vapaa"] = function(args, data) inflection_type_is(data, 17, "vapaa") 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"} make_stems(data, stems) process_stems(data, stems, params.a) data.forms["ill_pl"].rare = {params.base .. "ihin"} end

inflections["maa"] = function(args, data) inflection_type_is(data, 18, "maa") 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["pl"]     = {pl_stem .. "i"} stems["par_pl"] = {pl_stem .. "it"} stems["ill_pl"] = {pl_stem .. "ih"} make_stems(data, stems) process_stems(data, stems, params.a) end

inflections["suo"] = function(args, data) inflection_type_is(data, 19, "suo") 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 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"} make_stems(data, stems) process_stems(data, stems, params.a) end

inflections["filee"] = function(args, data) inflection_type_is(data, 20, "filee") 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"} make_stems(data, stems) process_stems(data, stems, params.a) end

inflections["rosé"] = function(args, data) inflection_type_is(data, 21, "rosé") 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 stems = {} 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"} make_stems(data, stems) 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) inflection_type_is(data, 22, "parfait") local params = get_params(args, 2) local ill_sg_vowel = get_extra_arg(args, data, "ill_sg_vowel") or (mw.title.getCurrentTitle.nsText == "Template" and "e"); if not ill_sg_vowel or 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"} make_stems(data, stems) 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) inflection_type_is(data, 23, "tiili") 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"} make_stems(data, stems) process_stems(data, stems, params.a) end

inflections["uni"] = function(args, data) inflection_type_is(data, 24, "uni") 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"} make_stems(data, stems) 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) inflection_type_is(data, 25, "toimi") 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"} make_stems(data, stems) process_stems(data, stems, params.a) end

inflections["pieni"] = function(args, data) inflection_type_is(data, 26, "pieni") 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"} make_stems(data, stems) 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) inflection_type_is(data, 27, "käsi", "t", "d") 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"} make_stems(data, stems) 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 mw.title.getCurrentTitle.nsText ~= "Template" and cons == "" then error("Stem must end in \"l\", \"n\" or \"r\".") end inflection_type_is(data, 28, "kynsi", cons .. "t", cons .. cons) 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"} make_stems(data, stems) process_stems(data, stems, params.a) data.forms["gen_pl"].rare = {params.base .. "tten"} end

inflections["lapsi"] = function(args, data) inflection_type_is(data, 29, "lapsi") local params = get_params(args, 2, false, "p") local syncopated_stem, cons = mw.ustring.match(params.base, "^(.-)([kp]?)$") if mw.title.getCurrentTitle.nsText ~= "Template" and cons == "" then error("Stem must end in \"k\" or \"p\".") end 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"} make_stems(data, stems) process_stems(data, stems, params.a) end

inflections["veitsi"] = function(args, data) inflection_type_is(data, 30, "veitsi") 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"} make_stems(data, stems) process_stems(data, stems, params.a) data.forms["gen_pl"].rare = {params.base .. "sten"} end

inflections["kaksi"] = function(args, data) inflection_type_is(data, 31, "kaksi", "t", "d") 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"} make_stems(data, stems) 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 inflection_type_is(data, 32, "sisar", params.strong, params.weak) make_weak(params.base, params.strong, params.final, params.weak) 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"} make_stems(data, stems) process_stems(data, stems, params.a) end

inflections["kytkin"] = function(args, data) local params = get_params(args, 5, true) inflection_type_is(data, 33, "kytkin", params.strong, params.weak) make_weak(params.base, params.strong, params.final, params.weak) 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"} make_stems(data, stems) 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 inflection_type_is(data, 34, "onneton", strong, "t") 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"} make_stems(data, stems) 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) inflection_type_is(data, 35, "lämmin", params.strong, params.weak) make_weak(params.base, params.strong, params.final, params.weak) 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"} make_stems(data, stems) 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) inflection_type_is(data, 36, "sisin", "mp", "mm") 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"} make_stems(data, stems) process_stems(data, stems, params.a) data.forms["gen_pl"].rare = {params.base .. "imp" .. params.a .. "in"} end

inflections["vasen"] = function(args, data) inflection_type_is(data, 37, "vasen", "mp", "mm")

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"} make_stems(data, stems) process_stems(data, stems, params.a) data.forms["gen_pl"].rare = {params.base .. "mp" .. params.a .. "in"} end

inflections["nainen"] = function(args, data) inflection_type_is(data, 38, "nainen") 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"} make_stems(data, stems) process_stems(data, stems, params.a) end

inflections["vastaus"] = function(args, data) inflection_type_is(data, 39, "vastaus") 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"} make_stems(data, stems) process_stems(data, stems, params.a) end

inflections["kalleus"] = function(args, data) inflection_type_is(data, 40, "kalleus", "t", "d") 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"} make_stems(data, stems) process_stems(data, stems, params.a) end

inflections["vieras"] = function(args, data) local params = get_params(args, 5, true) inflection_type_is(data, 41, "vieras", params.strong, params.weak) make_weak(params.base, params.strong, params.final, params.weak) local stems = {} stems["nom_sg"] = {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"} make_stems(data, stems) process_stems(data, stems, params.a) data.forms["ill_pl"].rare = {params.base .. params.strong .. params.final .. "ihin"} end

inflections["mies"] = function(args, data) inflection_type_is(data, 42, "mies") local params = get_params(args, 1) params.base = params.base .. "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"} make_stems(data, stems) process_stems(data, stems, params.a) end

inflections["ohut"] = function(args, data) local params = get_params(args, 5, true) inflection_type_is(data, 43, "ohut", params.strong, params.weak) make_weak(params.base, params.strong, params.final, params.weak) 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"} make_stems(data, stems) process_stems(data, stems, params.a) end

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

inflections["kahdeksas"] = function(args, data) inflection_type_is(data, 45, "kahdeksas", "nt", "nn") 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"} make_stems(data, stems) process_stems(data, stems, params.a) end

inflections["tuhat"] = function(args, data) inflection_type_is(data, 46, "tuhat", "nt", "nn") 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"} make_stems(data, stems) process_stems(data, stems, params.a) data.forms["gen_pl"].rare = {params.base .. "nten"} end

inflections["kuollut"] = function(args, data) inflection_type_is(data, 47, "kuollut") 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"} make_stems(data, stems) 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" inflection_type_is(data, 48, "hame", params.strong, params.weak) make_weak(params.base, params.strong, stem_vowel, params.weak) local stems = {} stems["nom_sg"] = {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"} make_stems(data, stems) 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"

inflection_type_is(data, 49, "askel", params.strong, params.weak) make_weak(params.base, params.strong, "", params.weak) local stems_sisar = {} stems_sisar["nom_sg"] = {nom_sg or params.base .. params.weak .. params.final} stems_sisar["sg"]     = {params.base .. params.strong .. params.final .. "e"} stems_sisar["par_sg"] = {params.base .. params.weak .. params.final .. "t"} 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"} make_stems(data, stems_sisar)

local stems_hame = {} stems_hame["nom_sg"] = {params.base .. params.strong .. params.final .. "e"} stems_hame["sg"]     = {params.base .. params.strong .. params.final .. "ee"} 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["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"} make_stems(data, stems_hame) if prefer_hame then process_stems(data, stems_hame, params.a)		process_stems(data, stems_sisar, params.a)		data["stems"] = merge_stems(stems_hame, stems_sisar) else process_stems(data, stems_sisar, params.a)		process_stems(data, stems_hame, params.a)		data["stems"] = merge_stems(stems_sisar, stems_hame) end 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 < 2 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

-- 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 = {} -- 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 suffix = get_extra_arg(args, data, "suffix"); if not suffix then suffix = "" 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], suffix, 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 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 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 = 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 if not appendix and lang:makeEntryName(data.forms[(nosg or n == "pl") and "nom_pl" or "nom_sg"][1], nil, false) ~= mw.title.getCurrentTitle.text then table.insert(data.categories, "Finnish entries with inflection not matching pagename") end if has_no_nom then data.forms["nom_sg"] = nil data.forms["nom_pl"] = nil end end

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

if 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(args, infl_type, note, get_forms) 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 function show_form(forms, code, no_rare) if code:find("^com_") and not data.poss and data.tagged_com_pl then if code:find("_pl$") then return "" end return ' colspan="2" | \'\'See the possessive forms below.\'\'' end

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" then accel = nil 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 local pagename, unsupported = mw.title.getCurrentTitle.text:gsub("^Unsupported titles/", "") if unsupported > 0 then for title, page in pairs(mw.loadData("Module:links/data").unsupported_titles) do					if page == pagename then pagename = title break end end end return "Inflection of " .. m_links.full_link({lang = lang, alt = pagename}, "term") .. ( data.title and " (" .. data.title .. ")" or "") elseif param == "maybenote" then if note then return [=[ else return "" end 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 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 if par_nom_sg then result["nom_sg"] = result["par_sg"] else result["nom_sg"] = result["gen_sg"] end 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 possessor", ["2s"] = "second-person singular possessor", ["1p"] = "first-person plural possessor", ["2p"] = "second-person plural possessor", ["3"] = "third-person possessor", }

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

function make_possessive_table_internal(args, infl_type, note, get_forms) local function show_subtable(code) return make_table(get_forms(code), false, poss_headings[code], "possessive inflection", poss_accel[code] .. "|poss|form|of|") end local function repl(param) if param == "lemma" then return m_links.full_link({lang = lang, alt = mw.title.getCurrentTitle.text}, "term") elseif param == "info" then if infl_type:match("%-") then return " (type nuoripari)" else return " (type " .. infl_type .. ")" end 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