Hello, you have come here looking for the meaning of the word . In DICTIOUS you will not only get to know all the dictionary meanings for the word , but we will also tell you about its etymology, its characteristics and you will know how to say in singular and plural. Everything you need to know about the word you have here. The definition of the word will help you to be more precise and correct when speaking or writing your texts. Knowing the definition of, as well as those of other words, enriches your vocabulary and provides you with more and better linguistic resources.


local export = {}

local m_links = require('Module:links')
local m_adj = require('Module:hsb-adecl')
local m_g = require('Module:gender and number')
local lang = require("Module:languages").getByCode("hsb")

-- local consonants = "";

-- case information
local cases = {
	{ key = "nom"; en = "nominative"; pl = "nominatiw (što?)" },
	{ key = "gen"; en = "genitive"; pl = "genitiw (čeho?)" },
	{ key = "dat"; en = "dative"; pl = "datiw (čemu?)" },
	{ key = "acc"; en = "accusative"; pl = "akuzatiw (što? čo?)" },
	{ key = "ins"; en = "instrumental"; pl = "instrumental (z/ze čim?)" },
	{ key = "loc"; en = "locative"; pl = "lokatiw (wo čim?)" },
	{ key = "voc"; en = "vocative"; pl = "wokatiw (o!)" },
}

-- columns for normal nouns
local noun_cols = {
	{ key = "s"; title = "singular" },
	{ key = "d"; title = "dual" },
	{ key = "p"; title = "plural" },
}

-- columns for pronouns
local pronoun_cols = {
	{ key = "sm"; title = m_g.format_list({"m"}, lang) },
	{ key = "sf"; title = m_g.format_list({"f"}, lang) },
	{ key = "sn"; title = m_g.format_list({"n"}, lang) },
	{ key = "dm"; title = m_g.format_list({"vr-d"}, lang) },
	{ key = "do"; title = m_g.format_list({"nv-d"}, lang) },
	{ key = "pm"; title = m_g.format_list({"vr-p"}, lang) },
	{ key = "po"; title = m_g.format_list({"nv-p"}, lang) },
}

local altsep = "/"

function empty_item(text)
	return (not text) or text == "" or text == "-" or text == "–" or text == "—"
end

-- add link markers, with links separated by any of splitchars
-- exported for use in testcases
-- normally the separator is altsep
function export.make_links(text, splitchars, title)
	if not title then
		title = mw.title.getCurrentTitle().fullText
	end
	if empty_item(text) then
		return "—"
	elseif not splitchars or splitchars == "" then
		return ("]"):format(text, text)
	else
		items = {}
		for word in mw.ustring.gmatch(text, "+") do
			add_archaic = ""
			add_deprecative = ""
			if word:sub(-string.len(" (archaic)")) == " (archaic)" then
				word = (mw.text.split(word, "% %(archaic%)"))
				add_archaic = " (archaic)"
			end
			if word:sub(-string.len(" (deprecative)")) == " (deprecative)" then
				word = (mw.text.split(word, "% %(deprecative%)"))
				add_deprecative = " (deprecative)"
			end
			
			if word == title then
				table.insert(items, ("]"):format(word) .. add_archaic .. add_deprecative)
			else
				table.insert(items, ("]"):format(word, word) .. add_archaic .. add_deprecative)
			end
		end
		
		if #items > 1 then
			require("Module:debug").track("hsb-noun/splitchars")
		end
		
		return table.concat(items, "/")
	end
end

local function linkify_info(declinfo, splitchars, nolinks)
	if nolinks then
		require("Module:debug").track("hsb-noun/nolinks")
	end
	
	local linked = {}
	local title = mw.title.getCurrentTitle().fullText
	for k, v in pairs(declinfo) do
		if v == title then
			linked = "]"
		elseif nolinks then
			linked = v
		else
			linked = export.make_links(v, splitchars, title)
		end
	end
	return linked
end

local function nowiki_info(declinfo)
	require("Module:debug").track("hsb-noun/nowiki")
	local nowikied = {}
	for k, v in pairs(declinfo) do
		nowikied = mw.text.nowiki(v)
	end
	return nowikied
end

local function override_col_titles(heads, cols)
	if not heads then
		return cols or {}
	end
	local new_cols = {}
	local index = 1
	for word in mw.ustring.gmatch(heads, "+") do
		if cols then
			table.insert(new_cols, { key = cols.key; title = word } )
		else
			table.insert(new_cols, { key = tostring(index); title = word } )
		end
		index = index + 1
	end
	for ci, col in ipairs(cols) do
		if ci >= index then
			table.insert(new_cols, cols)
		end
	end
	return new_cols
end

local function normalize_tantum(pargs)
	-- support "num" as fallback for "tantum" to match Latin templates
	local tantum = pargs.tantum or pargs.num
	if not tantum then
		return nil
	end
	if tantum == "sg" then
		tantum = "s"
	elseif tantum == "pl" then
		tantum = "p"
	end
	return tantum
end

local function guess_width(declinfo, cols)
	local maxl = 0
	for k, v in pairs(declinfo) do
		local l = mw.ustring.len(mw.text.trim(v))
		if maxl < l then
			maxl = l
		end
	end
	local width = math.floor(maxl * 0.78) -- number obtained by anecdotal evidence
	width = (width < 10) and 10 or width
	width = 9 + (width * #cols)
	return width
end

-- generate the HTML code of an inflection table
-- each entry in heads must have "key" and "title"
local function make_table(declinfo, cols, preproc, width, title, tantum, nolinks)
	local result = {}
	if not cols or not cols then
		error("make_table: invalid cols parameter")
	end

	local lemma_key = cases.key .. cols.key
	local lemma = m_links.remove_links(declinfo)
	title = title or ('Declension of <span class="Latn mention" lang="hsb" xml:lang="hsb">%s</span>'):format(lemma)

	local emwidth = width or guess_width(declinfo, cols)

	if preproc == "nowiki" then
		declinfo = nowiki_info(declinfo)
	elseif preproc == "linkify" then
		declinfo = linkify_info(declinfo, altsep, nolinks)
	end

	if cols and (#cols > 0) then
		table.insert(result, '|-\n! style="background:#d9ebff; width: 8em;" |\n')
		for i, col in ipairs(cols) do
			table.insert(result, ('! style="background:#d9ebff;" scope="col" | %s\n'):format(col.title))
		end
	end

	local maxl = 0
	for i, case in ipairs(cases) do
		table.insert(result, ('|-\n! title="%s" style="background:#eff7ff;" scope="row" | %s\n'):format(case.pl, case.en))
		for _, col in ipairs(cols) do
			local declkey = case.key .. col.key
			local item = mw.text.trim(declinfo)
			if empty_item(item) then
				table.insert(result, '| —\n')
			else
				local link = m_links.full_link({lang = lang, term = item, accel = {form = ("%s|%s"):format(case.key, col.key)}})
				table.insert(result, ('| %s\n'):format(link))
			end
		end
	end

	local outtext = ([=[<div class="NavFrame" style="display: block; max-width: %uem;">
<div class="NavHead" style="background:#eff7ff" >%s</div>
<div class="NavContent">
{| style="background:#F9F9F9; text-align:center; width: 100%%; margin: 0;" class="inflection-table"
]=]):format(emwidth, title) .. table.concat(result, "") .. "|}</div></div>"

	if tantum == "s" then
		outtext = outtext .. "]"
	elseif tantum == "p" then
		outtext = outtext .. "]"
	end
	return outtext
end

local function get_mode()
	local frame = mw.getCurrentFrame()
	if mw.isSubsting() then
		return 'subst'
	elseif frame:getParent():getTitle() == mw.title.getCurrentTitle().fullText then
		return 'demo'
	else
		return 'xclude'
	end
end

local function make_table_from_pargs(pargs, cols, tantum)
	local width = pargs.width and tonumber(pargs.width)
	
	local declinfo = {}
	if get_mode() == 'demo' then
		if not cols then
			cols = {
				{ key = "1"; title = "column 1" },
				{ key = "2"; title = "column 2" },
				{ key = "3"; title = "column 3" },
			}
		end
		for i = 1, 7 do
			for j, col in ipairs(cols) do
				local case_key = cases.key .. col.key
				local argn = (i-1) * #cols + j
				declinfo = '{{{' .. case_key .. '| {{{' .. argn .. '}}} }}}'
			end
		end
		return make_table(declinfo, cols, "nowiki", width, nil, nil, nil)
	else
		cols = override_col_titles(pargs.heads, cols or {})
		for i = 1, 7 do
			for j, col in ipairs(cols) do
				local case_key = cases.key .. col.key
				local argn = ((i-1) * #cols) + j
				declinfo = m_links.remove_links(mw.text.trim(pargs or pargs or "-"))
			end
		end
		return make_table(declinfo, cols, "linkify", pargs.width, pargs.title, normalize_tantum(pargs), pargs.nolinks)
	end
end

-- Generate declension table for a singulare tantum with all forms passed explicitly as parameters
function export.template_decl_noun_sg(frame)
	local pargs = frame:getParent().args
	local cols = { noun_cols }
	return make_table_from_pargs(pargs, cols, "s")
end

-- Generate declension table for a plurale tantum with all forms passed explicitly as parameters
function export.template_decl_noun_pl(frame)
	local pargs = frame:getParent().args
	local cols = { noun_cols }
	return make_table_from_pargs(pargs, cols, "p")
end

-- Generate declension table for a regular noun with all forms passed explicitly as parameters
function export.template_decl_noun(frame)
	local pargs = frame:getParent().args
	return make_table_from_pargs(pargs, noun_cols, nil)
end

function export.template_decl_pronoun(frame)
	local pargs = frame:getParent().args
	return make_table_from_pargs(pargs, pronoun_cols, nil)
end

function export.template_decl_generic(frame)
	local pargs = frame:getParent().args
	local heads = pargs.heads
	if not heads then
		if get_mode() == "demo" then
			heads = "column 1,column 2"
		else
			error("No column headings defined!")
		end
	end
	local cols = override_col_titles(heads, {})
	return make_table_from_pargs(pargs, cols, nil)
end


-- -----------------------------------------------------------------------------
-- -----------------------------------------------------------------------------
-- ------------- Semi-automatic generation of inflected forms ------------------
-- -----------------------------------------------------------------------------
-- -----------------------------------------------------------------------------

local function nonempty(str)
	if not str or str == "" then
		return nil
	else
		return str
	end
end

-- Generate a table contains true for each space-separated word in str
local function make_lookup_table(str)
	local ret = {}
	for i in mw.ustring.gmatch(str, "%a+") do
		ret = true
	end
	return ret
end

-- table that converts nominative soft endings to their genitive form
-- required for proper functioning of masc_common
local soft_ending_lookup = {
	 = "ce";
	 = "nj";
}

-- Given a word and a lookup table of accepted endings,
-- split the word into a stem and an ending.
-- Ending is returned in genitive form, i.e. soft consonants
-- and digraphs such as ć, ś, dź are converted to midword
-- i-forms ci, si, dzi.
local function split_stem(word, lookup)
	-- match the longest possible ending
	local last, last_gen
	local limit = math.min(mw.ustring.len(word), 5)
	for i = -limit, -1 do
		if not last_gen then
			last = mw.ustring.sub(word, i)
			if lookup or soft_ending_lookup then
				last_gen = soft_ending_lookup or last
			end
		end
	end

	if last_gen then
		local stem = mw.ustring.sub(word, 1, -mw.ustring.len(last)-1)
		return stem, last_gen
	else
		return nil, nil
	end
end

local function check_split(stem, last)
	if not stem then
		error("nil stem encountered in declension pattern")
	end
	if not last then
		error("nil ending encountered in declension pattern")
	end
end

local function handle_overrides(pargs, declinfo, ovinfo)
	-- process cases in order
	local ret = {}
	local singpl = { "s", "p" }
	for i = 1, 14 do
		local caseno = math.floor((i+1)/2)
		local sp = 2 - (i % 2)
		local case_key = cases.key .. singpl
		if pargs then
			ret = pargs
			if ovinfo then
				for dummy, v in pairs(ovinfo) do
					ret = pargs
				end
			end
		elseif not ret then
			ret = declinfo
		end
	end
	return ret
end

-- This table will hold patterns
local patterns = {}


-- -----------------------------------------------------------------------------
-- -------------------------- Masculine declension -----------------------------
-- -----------------------------------------------------------------------------

-- accepted masculine endings in genitive singular form
local masc_endings = make_lookup_table(
	"ač b bi c ch ci ćec ćel č d dz dzi dziec ek el eł f g gel h ik j k kel l ł m n ni niec p pi r " ..
	"s soł sl st sz t w z ż zd zoł zm")

-- common function for masculine declensions
local function masc_common(pattern, stem, last, gens_ending, noms_form, nomp_form, altgenp, explicit_stem)
	-- verify that the word ending is supported
	if not masc_endings then
		-- suppress module error on the template page
		if not last or not mw.ustring.match(last, "^{{{") then
			error("Unsupported word ending: " .. (last or "nil"))
		end
	end
	local initial_last = last

	-- nominative singular
	local noms_lookup = { bi = "b"; ci = "ć"; dzi = "dź"; ni = "ń";  = "p"; si = "ś"; wi = "w"; zi = "ź"; }
	if not noms_form then
		noms_form = stem .. (noms_lookup or last)
	end

	-- fix the only exceptions to -iec vowel elision
	if last == "iec" and (stem == "w" or stem == "p") then
		if not gens_ending and stem == "p" then
			gens_ending = "a"
		end
		stem = stem .. "ie"
		last = "c"
	end

	-- limited guessing of masculine inanimate endings here
	-- personal and animate nouns (except for ''wół'') always have "a"
	local gens_a = make_lookup_table("ač ćec ćel dzi dziec ek el eł ik kel ni niec soł zoł")
	if not gens_ending then
		if gens_a then
			gens_ending = "a"
		else
			gens_ending = "u"
		end
	end

	-- handle the following things here:
	--   regular vowel elisions
	--   overlong endings used only for genitive singular guessing, e.g. acz, unek
	--   x becoming ks in inflected forms
	--   stem-final ó becoming o in inflected forms
	local last_lookup = {
		 = "č";
		 = "c";  = "l";
		dziec = "c";
		ek = "k"; el = "l";  = "ł";
		 = "ł"; ik = "k";
		kel = "l";
		 = "sł";
		 = "zł";
	}
	local stem_add = {
		acz = "a";
		ciec = "ć"; ciel = "će";
		dziec = "dź";
		ik = "i";
		kel = "k";
		niec = "ń";
	}
	stem = stem .. (stem_add or "")
	last = last_lookup or last

	if mw.ustring.match(stem, "ó$") and not explicit_stem then
		stem = mw.ustring.sub(stem, 1, -2) .. "o"
	end

	-- genitive singular
	local gens_form = stem .. last .. gens_ending

	-- accusative singular
	local accs_form = (pattern == "m-in") and noms_form or gens_form

	-- instrumental singular
	local inss_form
	if (last == "g") or (last == "k") then
		inss_form = stem .. last .. "iem"
	else
		inss_form = stem .. last .. "em"
	end

	-- locative singular
	local locs_lookup = {
		b = "bie"; bi = "biu";
		c = "cu"; ch = "chu"; ci = "ciu"; cz = "czu";
		d = "dzie"; dz = "dzu"; dzi = "dziu";
		f = "fie";
		g = "gu";
		h = "hu";
		j = "ju";
		k = "ku";
		l = "lu";  = "le";
		m = "mie";
		n = "nie"; ni = "niu";
		p = "pie";  = "piu";
		r = "rze"; rz = "rzu";
		s = "sie"; si = "siu";  = "śle"; sm = "śmie"; sn = "śnie";
			st = "ście"; sz = "szu";
		t = "cie";
		w = "wie"; wi = "wiu";
		z = "zie"; zd = "ździe"; zi = "ziu";  = "źle"; zm = "zmie";
			zn = "źnie";  = "żu";
		-- the -zm ending should not be palatalized, since it normally occurs
		-- in borrowed nouns such as "marazm", "komunizm", "faszyzm"
	}
	local locs_form = stem .. (locs_lookup or last)

	-- vocative singular is the same as locative singular, with the exception of -iec
	local vocs_form = locs_form
	if pattern == "m-pr" and mw.ustring.match(initial_last, "iec$") then
		vocs_form = stem .. "cze"
	end

	-- nominative plural
	-- accusative and vocative plural are the same
	if not nomp_form then
		local nomp_e_ending = make_lookup_table("bi c ci cz dz dzi j l ni pi rz si sz wi zi ż")
		if (last == "g") or (last == "k") then
			nomp_form = stem .. last .. "i"
		elseif nomp_e_ending then
			nomp_form = stem ..last .. "e"
		else
			nomp_form = stem .. last .. "y"
		end
	end

	-- genitive plural
	local genp_form
	if mw.ustring.match(last, "i$") and (last ~= "pi") then
		genp_form = stem .. last
	elseif (last == "l") then
		genp_form = stem .. last .. "i"
	elseif (last == "j") then
		genp_form = stem .. last .. "ów"
		if not altgenp or (altgenp == "") then
			altgenp = stem .. "i"
		end
	elseif (last == "cz") or (last == "rz") or (last == "sz") or (last == "ż") then
		genp_form = stem .. last .. "y"
	else
		genp_form = stem .. last .. "ów"
	end
	if nonempty(altgenp) then
		genp_form = genp_form .. "/" .. altgenp
	end

	local deprecate_nomp_form = ""

	if pattern == "m-pr" then
		local nomp_e_ending = make_lookup_table("bi c ci cz dz dzi j l ni pi rz si sz wi zi ż")
		if (last == "g") or (last == "k") then
			deprecate_nomp_form = stem .. last .. "i"
		elseif nomp_e_ending then
			deprecate_nomp_form = stem ..last .. "e"
		else
			deprecate_nomp_form = stem .. last .. "y"
		end
	end

	-- accusative plural - same as genitive or nominative depending on animacy
	local accp_form = (pattern == "m-pr") and genp_form or nomp_form
	local full_nomp_form = ((pattern == "m-pr") and deprecate_nomp_form ~= nomp_form and nomp_form .. "/" .. deprecate_nomp_form .. " (deprecative)") or nomp_form

	return {
		noms = noms_form;             nomp = full_nomp_form;
		gens = gens_form;             genp = genp_form;
		dats = stem .. last .. "owi"; datp = stem .. last .. "om";
		accs = accs_form;             accp = accp_form;
		inss = inss_form;             insp = stem .. last .. "ami";
		locs = locs_form;             locp = stem .. last .. "ach";
		vocs = vocs_form;             vocp = nomp_form;
	}
end

-- masculine inanimate nouns, e.g. bruk, beton, słup, szczaw, kurnik
-- default genitive singular ending is "u", but "a" endings are common as well
patterns = function (pargs, word)
	local stem, last = split_stem(word, masc_endings)
	local explicit_stem = false
	if nonempty(pargs) then
		stem = pargs or ""
		last = pargs
		explicit_stem = true
	end
	local gens_ending = pargs or gens_ending
	local noms_form = nonempty(pargs)
	local altgenp = nonempty(pargs)

	local declinfo = masc_common("m-in", stem, last, gens_ending, noms_form,
		nil, altgenp, explicit_stem)
	return handle_overrides(pargs, declinfo,
		{ noms = {"accs"}; locs = {"vocs"}; nomp = {"accp", "vocp"} })
end

-- masculine animate nouns, e.g. bocian, dzik, jeleń
patterns = function (pargs, word)
	local stem, last = split_stem(word, masc_endings)
	local explicit_stem = false
	if nonempty(pargs) then
		stem = pargs or ""
		last = pargs
		explicit_stem = true
	end
	local noms_form = nonempty(pargs)
	local altgenp = nonempty(pargs)
	
	local declinfo =  masc_common("m-an", stem, last, "a", noms_form,
		nil, altgenp, explicit_stem)
	return handle_overrides(pargs, declinfo,
		{ gens = {"accs"}; locs = {"vocs"}; nomp = {"accp", "vocp"} })
end

-- masculine personal nouns, e.g. policjant, sknerus, polityk
patterns = function (pargs, word)
	if not pargs and not pargs then
		if mw.ustring.match(word, "a$") then
			return patterns(pargs, word)
		elseif mw.ustring.match(word, "$") then
			return patterns(pargs, word)
		elseif mw.ustring.match(word, "nin$") then
			return patterns(pargs, word)
		end
	end
	local stem, last = split_stem(word, masc_endings)
	local explicit_stem = false
	if nonempty(pargs) then
		stem = pargs or ""
		last = pargs or ""
		explicit_stem = true
	end
	local nomp_form = nonempty(pargs) or nonempty(pargs.nomp)
	local noms_form = nonempty(pargs) or nonempty(pargs.noms)
	local altgenp = nonempty(pargs)

	-- note: this is only a default, and will need to be overridden often
	if not nomp_form then
		nomp_lookup = {
			acz = "acze"; -- palacz
			ch = "chowie"; ci = "cie"; ciec = "ćcy"; cz = "cze";
				ciel = "ciele"; -- eunuch, cieć, ?, ?, nauczyciel
			d = "dzi", dziec = "dźcy"; -- kloszard, jeździec
			ek = "kowie"; el = "ele"; -- świadek, ?
			f = "fowie"; -- szeryf
			g = "dzy"; -- szpieg
			h = "howie"; -- druh
			iec = "cy"; ik = "icy"; -- pogrobowiec, czytelnik
			j = "je"; -- lokaj
			k = "cy"; -- alkoholik
			l = "le";  = "łowie"; -- ?, apostoł
			m = "mi"; -- pielgrzym
			n = "ni"; ni = "nie"; niec = "ńcy"; -- kompan, leń, jeniec
			p = "pi"; -- chłop
			r = "rzy"; rz = "rze"; -- konduktor, murarz
			s = "si"; si = "sie"; siec = "ścy"; sz = "sze"; -- ordynans, rabuś, ?, jarosz
			t = "ci"; -- policjant
			z = "zi"; zi = "zie"; ziec = "źcy";  = "żowie"; -- intruz, kniaź, ?, mąż
		}
		if not nomp_lookup then
			error("Unsupported word ending: " .. last ..
				  "; please define the nominative plural form")
		end
		if last == "g" and mw.ustring.match(stem, "lo$") then
			nomp_form = stem .. "dzy/" .. stem .. "gowie"
		else
			nomp_form = stem .. nomp_lookup
		end
	end

	local declinfo = masc_common("m-pr", stem, last, "a", noms_form,
		nomp_form, altgenp, explicit_stem)
	return handle_overrides(pargs, declinfo,
		{ gens = {"accs"}; locs = {"vocs"}; nomp = {"vocp"}; genp = {"accp"} })
end

-- masculine personal nouns with adjectival declension
-- e.g. radny, łowczy, salowy
patterns = function (pargs, title)
	local word = pargs or title
	if mw.ustring.match(word, "^{{{") then
		word = "przykładowy"
	end
	local decl = m_adj.autoinflect(word)
	local declinfo = {
		noms = decl;  nomp = decl;
		gens = decl;  genp = decl;
		dats = decl;  datp = decl;
		accs = decl;  accp = decl;
		inss = decl; insp = decl;
		locs = decl; locp = decl;
		vocs = decl;  vocp = decl;
	}
	return handle_overrides(pargs, declinfo,
		{ noms = {"vocs"}; gens = {"accs"}; nomp = {"vocp"}; genp = {"accp", "locp"} })
end

-- masculine personal nouns that end in -a
-- e.g. stażysta, dawca, banita
patterns = function (pargs, word)
	local word_no_a = mw.ustring.match(word, "^(.*)a$") or ""
	local endings = make_lookup_table("b bi c ch d g j p r st t zd zn ż")
	local stem, last = split_stem(word_no_a, endings)
	stem = nonempty(pargs) or stem
	last = nonempty(pargs) or last

	if not endings then
		-- suppress module error on the template page
		if not mw.ustring.match(last, "^{{{") then
			error("Unsupported word ending: " .. last)
		end
	end

	local gens_form = stem .. last .. "y"
	if last == "j" then
		gens_form = stem .. "i"
    elseif last == "g" then
	    gens_form = stem .. "gi"
	end
	local dats_lookup = {
		b = "bie"; -- Barnaba
		bi = "bi";
		c = "cy"; -- zdrajca
		ch = "sze"; -- monarcha
		d = "dzie"; -- nomada (?)
		g = "dze"; -- sługa
		j = "i"; -- kaznodzieja
		p = "pie"; -- satrapa
		r = "rze"; -- sknera
		st = "ście"; -- starosta
		t = "cie"; -- idiota
		zd = "ździe"; -- gazda
		zn = "źnie"; -- mężczyzna
		 = "ży"; -- doża
	}
	local dats_form = stem .. (dats_lookup or last)

	local nomp_lookup = {
		b = "bowie";
		bi = "biowie";
		c = "cy";
		ch = "chowie";
		d = "dzi";
		g = "dzy";
		j = "je";
		p = "powie";
		r = "rzy";
		st = "ści";
		t = "ci";
		zd = "zdowie";
		zn = "źni";
		 = "żowie";
	}
	local nomp_form = stem .. (nomp_lookup or (last .. "i"))
	local genp_form = stem .. last .. "ów"
	if last == "zn" then
		genp_form = stem .. last
	end

	local deprecate_nomp_form = ""

	local nomp_e_ending = make_lookup_table("bi c ci cz dz dzi j l ni pi rz si sz wi zi ż")
	if (last == "g") or (last == "k") then
		deprecate_nomp_form = stem .. last .. "i"
	elseif nomp_e_ending then
		deprecate_nomp_form = stem ..last .. "e"
	else
		deprecate_nomp_form = stem .. last .. "y"
	end

	local full_nomp_form = (deprecate_nomp_form ~= nomp_form and nomp_form .. "/" .. deprecate_nomp_form .. " (deprecative)") or nomp_form

	-- TODO: alternative adjectival declension for -bia
	local declinfo = {
		noms = stem .. last .. "a";  nomp = full_nomp_form;
		gens = gens_form;            genp = genp_form;
		dats = dats_form;            datp = stem .. last .. "om";
		accs = stem .. last .. "ę";  accp = genp_form;
		inss = stem .. last .. "ą";  insp = stem .. last .. "ami";
		locs = dats_form;            locp = stem .. last .. "ach";
		vocs = stem .. last .. "o";  vocp = nomp_form;
	}
	return handle_overrides(pargs, declinfo,
		{ dats = {"locs"}; nomp = {"vocp"}; genp = {"accp"} })
end

-- masculine nouns ending in -log, both personal and inanimate
-- inanimate form is chosen when the first parameter is empty
patterns = function (pargs, title)
	local stem = nonempty(pargs) or mw.ustring.match(title, "^(.*)log$") or ""
	local nomp_form = nonempty(pargs)
	local inanimate = not nonempty(pargs)

	if not nomp_form then
		if inanimate then
			nomp_form = stem .. "logi"
		else
			nomp_form = stem .. "lodzy/" .. stem .. "logowie"
		end
	end

	local gens_form = stem .. "loga"
	if inanimate then
		gens_form = stem .. "logu"
	end

	local accs_form = stem .. "loga"
	if inanimate then
		accs_form = stem .. "log"
	end

	local declinfo = {
		noms = stem .. "log";     nomp = nomp_form;
		gens = gens_form;         genp = stem .. "logów";
		dats = stem .. "logowi";  datp = stem .. "logom";
		accs = accs_form;         accp = stem .. "logów";
		inss = stem .. "logiem";  insp = stem .. "logami";
		locs = stem .. "logu";    locp = stem .. "logach";
		vocs = stem .. "logu";    vocp = nomp_form;
	}
	return handle_overrides(pargs, declinfo,
		{ locs = {"vocs"}; nomp = {"vocp"}; genp = {"accp"} })
end

-- masculine personal nouns ending with -nin, e.g. Rosjanin, łodzianin
-- popular for demonyms
patterns = function (pargs, word)
	local stem = nonempty(pargs) or mw.ustring.match(word, "^(.*)nin$")
	local genp_ending = pargs or "n"
	local declinfo = {
		noms = stem .. "nin";     nomp = stem .. "nie";
		gens = stem .. "nina";    genp = stem .. genp_ending;
		dats = stem .. "ninowi";  datp = stem .. "nom";
		accs = stem .. "nina";    accp = stem .. genp_ending;
		inss = stem .. "ninem";   insp = stem .. "nami";
		locs = stem .. "ninie";   locp = stem .. "nach";
		vocs = stem .. "ninie";   vocp = stem .. "nie";
	}
	return handle_overrides(pargs, declinfo,
		{ gens = {"accs"}; locs = {"vocs"}; nomp = {"vocp"}; genp = {"accp"} })
end

-- masculine inanimate adjectives in noun phrases
patterns = function (pargs, word)
	word = nonempty(pargs) or word
	if mw.ustring.match(word, "^{{{") then
		word = "przykładowy"
	end
	local decl = m_adj.autoinflect(word)
	local declinfo = {
		noms = decl;  nomp = decl;
		gens = decl;  genp = decl;
		dats = decl;  datp = decl;
		accs = decl;  accp = decl;
		inss = decl; insp = decl;
		locs = decl; locp = decl;
		vocs = decl;  vocp = decl;
	}
	return handle_overrides(pargs, declinfo,
		{ noms = {"accs", "vocs"}; inss = {"locs"}; nomp = {"accp", "vocp"}; genp = {"locp"} })
end

-- masculine animate adjectives in noun phrases
patterns = function (pargs, word)
	word = nonempty(pargs) or word
	if mw.ustring.match(word, "^{{{") then
		word = "przykładowy"
	end
	local decl = m_adj.autoinflect(word)
	local declinfo = {
		noms = decl;  nomp = decl;
		gens = decl;  genp = decl;
		dats = decl;  datp = decl;
		accs = decl;  accp = decl;
		inss = decl; insp = decl;
		locs = decl; locp = decl;
		vocs = decl;  vocp = decl;
	}
	return handle_overrides(pargs, declinfo,
		{ noms = {"vocs"}; gens = {"accs"}; inss = {"locs"}; nomp = {"accp", "vocp"}; genp = {"locp"} })
end

-- masculine personal adjectives in noun phrases are identical
-- to masculine personal adjectival declension


-- -----------------------------------------------------------------------------
-- -------------------------- Feminine declension ------------------------------
-- -----------------------------------------------------------------------------

local fem_endings = make_lookup_table(
	"b bi c ch ci cz d dz dzi dż f g h j k l ł m n ni p pi r rz " ..
	"s si sł sm sn st sz t w wi z zd zi zł zm zn ż")

-- most feminine nouns ending in -a
-- note that some nouns ending in -ia have this declension and others
-- follow the "f-ia" pattern - it's impossible to tell from the word alone.
patterns = function (pargs, word)
	-- to handle pluralia tantum as well
	local word_no_a = mw.ustring.match(word, "^(.*)$")
	local pars3 = pargs and pargs and pargs
	local nopars = not pargs and not pargs and not pargs

	-- 2 positional parameters given OR a at the and -> use a-final declension
	-- 3+ positional parameters given OR consonant at the end -> use f-softcons
	if nopars then
		if mw.ustring.match(word, "ia$") then
			native_ia = mw.ustring.match(word, "nia$")
			native_ia = native_ia or mw.ustring.match(word, "ia$")
			if not native_ia then
				return patterns(pargs, word)
			end
		elseif mw.ustring.match(word, "ni$") then
			return patterns(pargs, word)
		end
	end
	if pars3 or not word_no_a then
		return patterns(pargs, word)
	end

	word_no_a = word_no_a or word
	local stem, last = split_stem(word_no_a, fem_endings)
	stem = nonempty(pargs) or stem
	last = nonempty(pargs) or last

	if not fem_endings then
		-- suppress module error on the template page
		if not mw.ustring.match(last, "^{{{") then
			error("Unsupported word ending: " .. last)
		end
	end

	local soft_endings = make_lookup_table("bi ci dzi ni pi si wi zi")

	local gens_form = stem .. last .. "y"
	if soft_endings then
		gens_form = stem .. last
	elseif last == "j" then
		if mw.ustring.match(stem, "$") then
			gens_form = stem .. "i"
		else
			gens_form = stem .. last .. "i"
		end
	elseif (last == "g") or (last == "k") or (last == "l") then
		gens_form = stem .. last .. "i"
	end

	local dats_lookup = {
		b = "bie"; bi = "bi";
		c = "cy"; ch = "sze"; ci = "ci"; cz = "czy";
		d = "dzie"; dz = "dzy"; dzi = "dzi";  = "dży";
		f = "fie";
		g = "dze";
		h = "że";
		j = "ji";
		k = "ce";
		l = "li";  = "le";
		m = "mie";
		n = "nie"; ni = "ni";
		p = "pie";  = "pi";
		r = "rze"; rz = "rzy";
		s = "sie"; si = "si";  = "śle"; sm = "śmie"; sn = "śnie";
			st = "ście"; sz = "szy";
		t = "cie";
		w = "wie"; wi = "wi";
		z = "zie"; zd = "ździe"; zi = "zi";  = "źle"; zm = "zmie";
			zn = "źnie";  = "ży";
		-- -zm should not be palatalized
	}
	local dats_form = stem .. (dats_lookup or last)
	
	if last == "j" and mw.ustring.match(stem, "$") then
		dats_form = stem .. "i"
	end

	local nomp_e_ending = make_lookup_table("bi c ci cz dz dzi dż j l ni pi rz si sz wi zi ż")
	
	local nomp_form = stem .. last .. "y"

	if (last == "g") or (last == "k") then
		nomp_form = stem .. last .. "i"
	elseif nomp_e_ending then
		nomp_form = stem .. last .. "e"
	end

	local genp_form = stem .. last
	if last == "j" then
		if mw.ustring.match(stem, "$") then
			-- zgraja, breja, żmija, koja, szuja, chryja ->
			-- zgraj, brej, żmij, koj, szuj, chryj
			genp_form = stem .. last
		else
			-- e.g. gracja, torsja
			genp_form = stem .. "ji/" .. stem .. "yj" .. " (archaic)";
		end
	elseif last == "l" then
		if mw.ustring.match(stem, "$") then
			-- hala, tabela, mila, rola, kula
			genp_form = stem .. "l"
		else
			-- hodowla, grobla, bernikla
			genp_form = stem .. "li"
		end
	elseif last == "k" then
		local kstem, klast = split_stem(stem, soft_ending_lookup)
		if kstem then
			kstem = kstem .. klast
		else
			kstem = stem
		end
		if mw.ustring.match(stem, "$") then
			genp_form = kstem .. "k"
		else
			genp_form = kstem .. "ek"
		end
	end

	local declinfo = {
		noms = stem .. last .. "a";  nomp = nomp_form;
		gens = gens_form;            genp = genp_form;
		dats = dats_form;            datp = stem .. last .. "om";
		accs = stem .. last .. "ę";  accp = nomp_form;
		inss = stem .. last .. "ą";  insp = stem .. last .. "ami";
		locs = dats_form;            locp = stem .. last .. "ach";
		vocs = stem .. last .. "o";  vocp = nomp_form;
	}
	return handle_overrides(pargs, declinfo,
		{ dats = {"locs"}; nomp = {"accp", "vocp"} })
end

-- feminine nouns ending with a consonant, e.g. stal, sól, brew
patterns = function (pargs, title)
	local endings = make_lookup_table("c ć cz dz dź ew iew j l ń rz ś sz w z ż ź")
	local accepted_lasts = make_lookup_table("c ci cz dz dzi j l ni rz si sz wi zi ż")
	local stem, last = split_stem(title, endings)
	local explicit_stem = false
	stem = nonempty(pargs) or stem
	last = nonempty(pargs) or last
	if nonempty(pargs) then
		stem = pargs or ""
		last = pargs or ""
		explicit_stem = true
	end
	local nomp_ending = pargs -- may be empty
	local noms_form = nonempty(pargs)

	-- Narew, brukiew, żagiew, brew - elide -e- or -ie-
	if last == "iew" or last == "ew" then
		if not noms_form then
			noms_form = stem .. last
		end
		last = "wi"
	end

	if last == "w" or last == "z" then
		last = last .. "i"
	end
	if not accepted_lasts then
		-- suppress module error on the template page
		if not last or not mw.ustring.match(last, "^{{{") then
			error("Unsupported word ending: " .. (last or "nil"))
		end
	end

	-- nominative singular
	if not noms_form or (noms_form == "") then
		local noms_lookup = {
			ci = "ć"; dzi = "dź"; ni = "ń"; si = "ś"; wi = "w"; zi = "ź"; }
		noms_form = stem .. (noms_lookup or last)
	end
	
	-- Stem-final ó becoming o in inflected forms
	if mw.ustring.match(stem, "ó$") and not explicit_stem then
		stem = mw.ustring.sub(stem, 1, -2) .. "o"
	end

	-- genitive singular
	local gens_form = stem .. last
	local gens_y_ending = make_lookup_table("c cz dz sz rz ż")
	if last == "j" then
		gens_form = stem .. "i"
	elseif last == "l" then
		gens_form = gens_form .. "i"
	elseif gens_y_ending then
		gens_form = gens_form .. "y"
	end

	-- nominative plural
	if not nomp_ending then
		if last == "ci" then
			-- some words have -e, but this is a lot more common
			nomp_ending = ""
		else
			nomp_ending = "e"
		end
	end
	-- this is trivial, but used in 3 places in the table
	local nomp_form = stem .. last .. nomp_ending;

	local declinfo = {
		noms = noms_form;            nomp = nomp_form;
		gens = gens_form;            genp = gens_form;
		dats = gens_form;            datp = stem .. last .. "om";
		accs = noms_form;            accp = nomp_form;
		inss = stem .. last .. "ą";  insp = stem .. last .. "ami";
		locs = gens_form;            locp = stem .. last .. "ach";
		vocs = gens_form;            vocp = nomp_form;
	}
	return handle_overrides(pargs, declinfo,
		{ noms = {"accs"}; gens = {"dats", "locs", "vocs", "genp"}; nomp = {"accp", "vocp"} })
end

-- feminine nouns with adjectival declension
patterns = function (pargs, title)
	local word = nonempty(pargs) or title
	if mw.ustring.match(word, "^{{{") then
		word = "przykładowa"
	end
	local decl = m_adj.autoinflect(word)
	local declinfo = {
		noms = decl;   nomp = decl;
		gens = decl;   genp = decl;
		dats = decl;   datp = decl;
		accs = decl;  accp = decl;
		inss = decl;  insp = decl;
		locs = decl;   locp = decl;
		vocs = decl;   vocp = decl;
	}
	return handle_overrides(pargs, declinfo,
		{ gens = {"dats", "locs"}; accs = {"inss"}; nomp = {"accp", "vocp"}; genp = {"locp"} })
		-- "vocs" may not always be equal to "noms" (e.g. "teściowa")
end

-- feminine nouns ending with -ja, e.g. fuzja, anomizja, animacja
-- obsolete - "f" handles them as well
patterns = function (pargs, title)
	local stem = nonempty(pargs) or mw.ustring.match(title, "^(.*)ja$")
	local yj = "yj"
	if pargs and (pargs ~= "") then
		yj = "ij"	
	end
	genp_form = stem .. "ji/" .. stem .. yj .. " (archaic)";

	local declinfo = {
		noms = stem .. "ja";  nomp = stem .. "je";
		gens = stem .. "ji";  genp = genp_form;
		dats = stem .. "ji";  datp = stem .. "jom";
		accs = stem .. "ję";  accp = stem .. "je";
		inss = stem .. "ją";  insp = stem .. "jami";
		locs = stem .. "ji";  locp = stem .. "jach";
		vocs = stem .. "jo";  vocp = stem .. "je";
	}
	return handle_overrides(pargs, declinfo,
		{ gens = {"dats", "locs"}; nomp = {"accp", "vocp"} })
end

-- most feminine nouns ending with -ia, e.g. mafia, kopia, balia
-- note that some nouns follow the "f" pattern instead, e.g. konopia, głębia
patterns = function (pargs, title)
	local stem = nonempty(pargs) or mw.ustring.match(title, "^(.*)ia$")
	local oldgenp = stem .. "ij"
	if mw.ustring.match(stem, "$") then
		oldgenp = stem .. "yj"
	end
	local genp_form = stem .. "ii/" .. oldgenp .. " (archaic)";
	
	local declinfo = {
		noms = stem .. "ia";  nomp = stem .. "ie";
		gens = stem .. "ii";  genp = genp_form;
		dats = stem .. "ii";  datp = stem .. "iom";
		accs = stem .. "ię";  accp = stem .. "ie";
		inss = stem .. "ią";  insp = stem .. "iami";
		locs = stem .. "ii";  locp = stem .. "iach";
		vocs = stem .. "io";  vocp = stem .. "ie";
	}
	return handle_overrides(pargs, declinfo,
		{ gens = {"dats", "locs"}; nomp = {"accp", "vocp"} })
end

-- feminine nouns ending in -ni, e.g. mistrzyni, mędrczyni
patterns = function (pargs, title)
	local stem = nonempty(pargs) or mw.ustring.match(title, "^(.*)ni$")
	local declinfo = {
		noms = stem .. "ni";  nomp = stem .. "nie";
		gens = stem .. "ni";  genp = stem .. "ń";
		dats = stem .. "ni";  datp = stem .. "niom";
		accs = stem .. "nię"; accp = stem .. "nie";
		inss = stem .. "nią"; insp = stem .. "niami";
		locs = stem .. "ni";  locp = stem .. "niach";
		vocs = stem .. "ni";  vocp = stem .. "nie";
	}
	return handle_overrides(pargs, declinfo,
		{ noms = {"gens", "dats", "locs", "vocs"}; nomp = {"accp", "vocp"} })
end

-- feminine adjectives in noun phrases
patterns = function (pargs, title)
	local word = nonempty(pargs) or title
	if mw.ustring.match(word, "^{{{") then
		word = "przykładowa"
	end
	local decl = m_adj.autoinflect(word)
	local declinfo = {
		noms = decl;  nomp = decl;
		gens = decl;  genp = decl;
		dats = decl;  datp = decl;
		accs = decl; accp = decl;
		inss = decl; insp = decl;
		locs = decl;  locp = decl;
		vocs = decl;  vocp = decl;
	}
	return handle_overrides(pargs, declinfo,
		{ noms = {"vocs"}; gens = {"dats", "locs"}; accs = {"inss"}; nomp = {"accp", "vocp"}; genp = {"locp"} })
end


-- -----------------------------------------------------------------------------
-- -------------------------- Neuter declension -----------------------------
-- -----------------------------------------------------------------------------

-- neuter nouns ending in -o, e.g. jajko, jarzmo, gusło, cudo
patterns = function (pargs, word)
	if pargs and mw.ustring.match(pargs, "^{{{") then
		word = "jajko"
	end

	-- forward to other patterns depending on the last letter
	if not nonempty(pargs) and not nonempty(pargs) then
		if mw.ustring.match(word, "e$") then
			return patterns(pargs, word)
		elseif mw.ustring.match(word, "ę$") then
			return patterns(pargs, word)
		elseif mw.ustring.match(word, "um$") then
			return patterns(pargs, word)
		end
	end

	local word_no_o = mw.ustring.match(word, "^(.*)o$")
	local endings = make_lookup_table(
		"b bn c ch d f g gn j k kn l ł m mn n nd p r rz rzm " ..
		"s sk sł sm sn st t tt tw w wn z zd zł zm zn zz")
	local stem, last
	if word_no_o then
		stem, last = split_stem(word_no_o, endings)
	end
	stem = nonempty(pargs) or stem
	last = nonempty(pargs) or last
	local genp_form = nonempty(pargs)

	if not endings then
		-- suppress module error on the template page
		if not mw.ustring.match(last, "^{{{") then
			error("Unsupported word ending: " .. last)
		end
	end

	local inss_form = stem .. last .. "em"
	if last == "g" or last == "k" then
		inss_form = stem .. last .. "iem"
	end

	local locs_lookup = {
		b = "bie"; bn = "bnie";
		c = "cu"; ch = "chu";
		d = "dzie";
		f = "fie";
		g = "gu"; gn = "gnie";
		j = "ju";
		k = "ku"; kn = "knie";
		l = "lu";  = "le";
		m = "mie"; mn = "mnie";
		n = "nie"; nd = "ndzie";
		p = "pie";
		r = "rze"; rz = "rzu"; rzm = "rzmie";  -- piórze, scherzu, jarzmie
		s = "sie"; sk = "sku";  = "śle"; sm = "śmie"; sn = "śnie"; st = "ście";
		t = "cie"; tt = "tcie"; tw = "twie";
		w = "wie"; wn = "wnie";
		z = "zie"; zd = "ździe";  = "źle"; zm = "źmie"; zn = "źnie"; zz = "zzu";
	}

	locs_form = stem .. (locs_lookup or (last .. "u"))

	if not genp_form then
		if last == "sn" or last == "zn" then
			genp_form = stem .. mw.ustring.sub(last, 1, 1) .. "en"  -- e.g. krosno -> krosen
		elseif mw.ustring.match(last, "^.n$") then
			genp_form = stem .. mw.ustring.sub(last, 1, 1) .. "ien"  -- e.g. bagno -> bagien
		elseif last == "tw" or last == "sk" or mw.ustring.match(stem, "$") then
			genp_form = stem .. last  -- e.g. bogactwo -> bogactw, dyktando -> dyktand (nd treated as digraph)
		else
			if mw.ustring.match(stem, "$") then
				genp_form = stem .. "ie" .. last  -- e.g. szkło -> szkieł
			else	
				genp_form = stem .. "e" .. last  -- e.g. jajko -> jajek
			end	
		end
	end
	
	local declinfo = {
		noms = stem .. last .. "o";  nomp = stem .. last .. "a";
		gens = stem .. last .. "a";  genp = genp_form;
		dats = stem .. last .. "u";  datp = stem .. last .. "om";
		accs = stem .. last .. "o";  accp = stem .. last .. "a";
		inss = inss_form;            insp = stem .. last .. "ami";
		locs = locs_form;            locp = stem .. last .. "ach";
		vocs = stem .. last .. "o";  vocp = stem .. last .. "a";
	}
	return handle_overrides(pargs, declinfo,
		{ noms = {"accs", "vocs"}; gens = {"nomp", "accp", "vocp"}; nomp = {"accp", "vocp"} })
end

-- neuter nouns ending in -e
patterns = function (pargs, word)
	local word_no_e = mw.ustring.match(word, "^(.*)e$")
	local endings = make_lookup_table("bi c ci cz dz dzi fi j l mi ni pi rz si sz wi zi ż")
	local stem, last
	if word_no_e then
		stem, last = split_stem(word_no_e, endings)
	end
	if nonempty(pargs) then
		stem = pargs
		last = ""
	end
	
	local genp_lookup = { ci = "ć"; cz = "czy"; ni = "ń"; rz = "rzy"; sz = "szy";  = "ży"; }
	local genp_form = pargs or (stem .. (genp_lookup or last))
	local sl = stem .. last

	local declinfo = {
		noms = sl .. "e";   nomp = sl .. "a";
		gens = sl .. "a";   genp = genp_form;
		dats = sl .. "u";   datp = sl .. "om";
		accs = sl .. "e";   accp = sl .. "a";
		inss = sl .. "em";  insp = sl .. "ami";
		locs = sl .. "u";   locp = sl .. "ach";
		vocs = sl .. "e";   vocp = sl .. "a";
	}
	return handle_overrides(pargs, declinfo,
		{ noms = {"accs", "vocs"}; gens = {"nomp", "accp", "vocp"}; dats = {"locs"}; nomp = {"accp", "vocp"} })
end

-- neuter nouns ending in -ę but not in -mię, e.g. kocię, szczenię, dziecię
patterns = function (pargs, title)
	local mstem = mw.ustring.match(title, "^(.*)mię$")
	if mstem then
		pargs = mstem
		return patterns(pargs, title)
	end
	local stem = nonempty(pargs) or mw.ustring.match(title, "^(.*)ę$")
	local declinfo = {
		noms = stem .. "ę";     nomp = stem .. "ęta";
		gens = stem .. "ęcia";  genp = stem .. "ąt";
		dats = stem .. "ęciu";  datp = stem .. "ętom";
		accs = stem .. "ę";     accp = stem .. "ęta";
		inss = stem .. "ęciem"; insp = stem .. "ętami";
		locs = stem .. "ęciu";  locp = stem .. "ętach";
		vocs = stem .. "ę";     vocp = stem .. "ęta";
	}
	return handle_overrides(pargs, declinfo,
		{ noms = {"accs", "vocs"}; dats = {"locs"}; nomp = {"accp", "vocp"} })
end

-- neuter nouns ending in -mię, e.g. wymię, znamię, plemię
patterns = function (pargs, title)
	local stem = nonempty(pargs) or mw.ustring.match(title, "^(.*)mię$")
	local declinfo = {
		noms = stem .. "mię";      nomp = stem .. "miona";
		gens = stem .. "mienia";   genp = stem .. "mion";
		dats = stem .. "mieniu";   datp = stem .. "mionom";
		accs = stem .. "mię";      accp = stem .. "miona";
		inss = stem .. "mieniem";  insp = stem .. "mionami";
		locs = stem .. "mieniu";   locp = stem .. "mionach";
		vocs = stem .. "mię";      vocp = stem .. "miona";
	}
	return handle_overrides(pargs, declinfo,
		{ noms = {"accs", "vocs"}; dats = {"locs"}; nomp = {"accp", "vocp"} })
end

-- neuter nouns with adjectival declension, e.g. bykowe
patterns = function (pargs, title)
	local word = nonempty(pargs) or title
	if mw.ustring.match(word, "^{{{") then
		word = "przykładowe"
	end

	local decl = m_adj.autoinflect(word)
	local declinfo = {
		noms = decl;   nomp = decl;
		gens = decl;   genp = decl;
		dats = decl;   datp = decl;
		accs = decl;   accp = decl;
		inss = decl;  insp = decl;
		locs = decl;  locp = decl;
		vocs = decl;   vocp = decl;
	}
	return handle_overrides(pargs, declinfo,
		{ noms = {"accs", "vocs"}; inss = {"locs"}; nomp = {"accp", "vocp"}; genp = {"locp"} })
end

-- neuter nouns ending in -um, e.g. liceum, gimnazjum, kryterium
-- mostly nouns borrow from ancient Greek
patterns = function (pargs, title)
	local stem = nonempty(pargs) or mw.ustring.match(title, "^(.*)um$")
	local declinfo = {
		noms = stem .. "um";  nomp = stem .. "a";
		gens = stem .. "um";  genp = stem .. "ów";
		dats = stem .. "um";  datp = stem .. "om";
		accs = stem .. "um";  accp = stem .. "a";
		inss = stem .. "um";  insp = stem .. "ami";
		locs = stem .. "um";  locp = stem .. "ach";
		vocs = stem .. "um";  vocp = stem .. "a";
	}
	return handle_overrides(pargs, declinfo,
		{ nomp = {"accp", "vocp"} })
end

-- highly irregular nouns
patterns = function (pargs, title)
	
	local word = make_lookup_table("brat dziecko ksiądz książę rok")
	
	if not word then
		if not mw.ustring.match(title, "^{{{") then
			error("Unsupported word")
		end
	end
	
	-- brat	
	if title == "brat" then
		noms_form = "brat"
		gens_form = "brata"
		dats_form = "bratu"
		accs_form = "brata"
		inss_form = "bratem"
		locs_form = "bracie"
		vocs_form = "bracie"
		nomp_form = "bracia"
		genp_form = "braci"
		datp_form = "braciom"
		accp_form = "braci"
		insp_form = "braćmi"
		locp_form = "braciach"
		vocp_form = "bracia"
	end
	
	-- dziecko
	if title == "dziecko" then
		noms_form = "dziecko"
		gens_form = "dziecka"
		dats_form = "dziecku"
		accs_form = "dziecko"
		inss_form = "dzieckiem"
		locs_form = "dziecku"
		vocs_form = "dziecko"
		nomp_form = "dzieci"
		genp_form = "dzieci"
		datp_form = "dzieciom"
		accp_form = "dzieci"
		insp_form = "dziećmi"
		locp_form = "dzieciach"
		vocp_form = "dzieci"
	end
	
	--ksiądz
	if title == "ksiądz" then
		noms_form = "ksiądz"
		gens_form = "księdza"
		dats_form = "księdzu"
		accs_form = "księdza"
		inss_form = "księdzem"
		locs_form = "księdzu"
		vocs_form = "księże"
		nomp_form = "księża"
		genp_form = "księży"
		datp_form = "księżom"
		accp_form = "księży"
		insp_form = "księżmi"
		locp_form = "księżach"
		vocp_form = "księża"
	end
	
	--książę
	if title == "książę" then
		noms_form = "książę"
		gens_form = "księcia"
		dats_form = "księciu"
		accs_form = "księcia"
		inss_form = "księciem"
		locs_form = "księciu"
		vocs_form = "książę"
		nomp_form = "książęta"
		genp_form = "książąt"
		datp_form = "książętom"
		accp_form = "książąt"
		insp_form = "książętami"
		locp_form = "książętach"
		vocp_form = "książęta"
	end
	
	-- rok
	if title == "rok" then
		noms_form = "rok"
		gens_form = "roku"
		dats_form = "rokowi"
		accs_form = "rok"
		inss_form = "rokiem"
		locs_form = "roku"
		vocs_form = "roku"
		nomp_form = "lata"
		genp_form = "lat"
		datp_form = "latom"
		accp_form = "lata"
		insp_form = "latami"
		locp_form = "latach"
		vocp_form = "lata"
	end	
	
	local declinfo = {
		noms = noms_form;            nomp = nomp_form;
		gens = gens_form;            genp = genp_form;
		dats = dats_form;            datp = datp_form;
		accs = accs_form;            accp = accp_form;
		inss = inss_form;            insp = insp_form;
		locs = locs_form;            locp = locp_form;
		vocs = vocs_form;            vocp = vocp_form;
	}
	return handle_overrides(pargs, declinfo, {})
end	

-- indeclinable pattern, i.e. same word for all cases
patterns = function (pargs, title)
	local word = nonempty(pargs) or title
	local declinfo = {}
	for i = 1, 7 do
		for _, col in ipairs(noun_cols) do
			declinfo.key .. col.key] = word
		end
	end
	return handle_overrides(pargs, declinfo, {})
end

-- shorthands
patterns = patterns
patterns = patterns

-- aliases for adjectives in phrases
patterns = patterns
patterns = patterns

-- used for autodetection of adjective genders in phrases
local function pattern_gender(pattern)
	if pattern == "m-in" or pattern == "adj-m-in" then
		return "m-in"
	elseif pattern == "m-an" or pattern == "adj-m-an" then
		return "m-an"
	elseif string.match(pattern, "^m-pr") or pattern == "adj-m-pr" then
		return "m-pr"
	elseif string.match(pattern, "^f") or pattern == "adj-f" then
		return "f"
	elseif string.match(pattern, "^n") or pattern == "adj-n" then
		return "n"
	end
	return nil
end

-- generate inflected forms given pattern, parameters, and full word
-- returned words do not contain links
function export.autoinflect(pattern, pargs, word)
	if not pattern then
		error("Declension pattern not specified")
	end
	if mw.ustring.match(pattern, "^{{{") then
		pattern = "m-in"
	end
	if not patterns then
		error("Invalid declension pattern: " .. pattern)
	end
	return patterns(pargs, pargs or word)
end

local function make_substitutable_table(pargs, declinfo, preproc)
	local tantum = normalize_tantum(pargs)
	if mw.isSubsting() then
		if tantum == "s" or tantum == "p" then
			local tname = "sing"
			if tantum == "p" then
				tname = "pl"
			end
			local rows = {}
			for i = 1, 7 do
				local case_key = cases.key .. tantum
				table.insert(rows, ("| <!-- %s --> %s"):format(case_key, m_links.remove_links(declinfo)))
			end
			return "{{pl-decl-noun-" .. tname .. "\n" .. table.concat(rows, "\n") .. "\n}}"
		end

		local rows = {}
		for i = 1, 7 do
			local case_skey = cases.key .. "s"
			local case_pkey = cases.key .. "p"
			table.insert(rows, ("| <!-- %s --> %s"):format(case_skey, m_links.remove_links(declinfo)))
			table.insert(rows, ("| <!-- %s --> %s"):format(case_pkey, m_links.remove_links(declinfo)))
		end
		return "{{pl-decl-noun\n" .. table.concat(rows, "\n") .. "\n}}"
	else
		local cols = noun_cols
		if tantum == "s" then
			cols = { noun_cols }
		elseif tantum == "p" then
			cols = { noun_cols }
		end
		cols = override_col_titles(pargs.heads, cols)

		if pargs.nocat then
			tantum = nil
		end
		return make_table(declinfo, cols, preproc, pargs.width, pargs.title, tantum, pargs.nolinks)
	end
end

-- Generate declension table for a specified declension pattern
function export.template_decl_pattern(frame)
	local args = frame.args
	local pargs = frame:getParent().args
	local pagetitle = mw.title.getCurrentTitle().fullText

	-- support "num" as fallback for "tantum" to match Latin templates
	local tantum = pargs.tantum or pargs.num
	if tantum == "sg" then
		tantum = "s"
	elseif tantum == "pl" then
		tantum = "p"
	end

	if get_mode() == 'demo' then
		pargs = { "{{{1}}}", "{{{2}}}", "{{{3}}}", "{{{4}}}", "{{{5}}}" }
	end

	-- if args is empty, use pargs as the pattern name and shift pargs
	local offset = 0
	local pattern = args
	if not pattern then
		-- extreme brokenness: table.remove(pargs, 1) has absolutely no effect!
		-- is this because pargs is somehow immutable?
		pattern = pargs
		offset = -1
	end

	local fixed_pargs = {}
	for key, parg in pairs(pargs) do
		if type(key) == "number" then
			local i = key + offset
			if i >= 1 and parg then
				fixed_pargs = m_links.remove_links(parg)
			end
		else
			fixed_pargs = m_links.remove_links(parg)
		end
	end

	declinfo = export.autoinflect(pattern, fixed_pargs, pagetitle)
	return make_substitutable_table(pargs, declinfo, "linkify")
end

local function push_down_indices(tbl)
	local ret = {}
	for index, decl in ipairs(tbl) do
		for k, v in pairs(decl) do
			ret = ret or {}
			table.insert(ret, v)
		end
	end
	return ret
end

-- Generate declension table for a noun phrase
function export.template_decl_phrase(frame)
	local args = frame.args
	local pargs = frame:getParent().args
	local page_title = mw.title.getCurrentTitle().fullText
	local lemma = pargs or page_title
	local words = {}

	if get_mode() == "demo" then
		pargs = { "adj", "n", "adj" }
		lemma = "przykładowe wyrażenie rzeczownikowe"
	end

	-- split title into words
	for t in mw.ustring.gmatch(lemma, "+") do
		table.insert(words, t)
	end

	local word_args = {}
	for index, word in ipairs(words) do
		-- parse arguments to declension patterns
		word_args = {}
		local numkey = 1
		local arg = pargs or ""
		for i in mw.ustring.gmatch(arg, "+") do
			local param = mw.text.trim(i)
			local k, v = mw.ustring.match(param, "^(*):(.*)$")
			if k and v then
				word_args = v
			else
				word_args = param
				numkey = numkey + 1
			end
		end
	end

	local adj_gender = nil
	for index, word in ipairs(words) do
		-- find the first gendered pattern and use it to match adjectives
		if not adj_gender then
			adj_gender = pattern_gender(word_args)
		end
	end

	local result = {}
	local result_raw = {}

	for index, word in ipairs(words) do
		local word_result = {}
		local pattern = table.remove(word_args, 1)
		if pattern == "adj" then
			if adj_gender then
				pattern = "adj-" .. adj_gender
			else
				error("Unable to guess adjective gender")
			end
		end
		if not patterns then
			-- indeclinable
			pattern = "indec"
		end
		result_raw = export.autoinflect(pattern, word_args, word)
		result = linkify_info(result_raw, altsep, true)
	end

	-- rearrange the table so that word indices are at the lower level
	result_raw = push_down_indices(result_raw)
	result = push_down_indices(result)

	-- make table entries
	for case, v in pairs(result_raw) do
		local phrase = table.concat(v, " ")
		if phrase == page_title then
			-- bold self-link if the phrase is equal to the page title
			result = "]"
		else
			result = table.concat(result, " ")
			-- do not split if linking full phrase declensions
			result = export.make_links(result, nil)
		end
	end

	-- width determination
	if not pargs.width then
		local result_concat = {}
		for case, v in pairs(result_raw) do
			result_concat = table.concat(v, " ")
		end
		pargs.width = guess_width(result_concat, noun_cols)
	end

	-- disable linkifying, since we already made per-word links
	return make_substitutable_table(pargs, result, nil)
end

return export