Jump to content

Module:grc-decl/decl/data

From Wiktionary, the free dictionary

This module needs documentation.
Please document this module by describing its purpose and usage on the documentation page.

local module_path = 'Module:grc-decl/decl'

local m_decl_static_data = mw.loadData(module_path .. '/classes')
local m_paradigms = mw.loadData(module_path .. '/staticdata/paradigms')
local m_dialect_groups = mw.loadData(module_path .. '/staticdata/dialects')
local m_accent = require('Module:grc-accent')
local m_links = require('Module:links')
local m_str_utils = require("Module:string utilities")
local check_type = require('libraryUtil').checkType
local grc = require('Module:languages').getByCode('grc')
local grk_pro = require('Module:languages').getByCode('grk-pro')

local codepoint = m_str_utils.codepoint
local deep_copy = require('Module:table').deepCopy
local to_NFC = mw.ustring.toNFC
local to_NFD = mw.ustring.toNFD
local ufind = m_str_utils.find
local umatch = m_str_utils.match
local usub = m_str_utils.sub

local export = {
	inflections = {},
	adjinflections = m_decl_static_data.adjinflections,
	adjinflections_con = m_decl_static_data.adjinflections_con,
}

local function ine(var)
	if var == '' then
		return nil
	else
		return var
	end
end

local function quote(text)
	return '“' .. text .. '”'
end

local function mention(alt)
	return m_links.full_link({ alt = alt, lang = grc }, 'term')
end

local function proto_mention(alt)
	return m_links.full_link({ alt = alt, lang = grk_pro }, 'term')
end

local dental_lookup = {
	['τ'] = '*-ts',
	['δ'] = '*-ds',
	['θ'] = '*-tʰs',
}

-- Return one of the three options, or an error message.
-- suffix is true if args[1] does not contain an accent mark and begins in a hyphen.
local function accent_switch(accent_term, if_oxytone, if_perispomenon, otherwise, error_message, unaccented_suffix)
	local value
	-- accent_term can be nil if this is a paradigm for an unaccented_suffix.
	check_type('accent_term', 1, accent_term, 'string', unaccented_suffix)
	if accent_term == 'oxytone' then
		value = if_oxytone
	elseif accent_term == 'perispomenon' then
		value = if_perispomenon
	else
		value = otherwise
	end
	return value or error_message and error(error_message)
end

local function uncontracted_error(contracted, dialect, nom_ending)
	if contracted == false and
			(dialect == 'att' or dialect == 'koi' or dialect == 'byz') then
		--[[Special:WhatLinksHere/Wiktionary:Tracking/grc-decl/uncontracted error]]
		
		require('Module:debug').track('grc-decl/uncontracted error')
		
		mw.log('In the Attic, Koine, or Byzantine dialects, nouns in ' ..
			quote(nom_ending) .. ' are contracted. Remove ' ..
			quote('con') .. ' from the ' ..
			quote('form') .. ' parameter or change the dialect.')
	end
end

local function nonrecessive_error(accent_term, error_message)
	if accent_term == 'oxytone' or accent_term == 'perispomenon' then
		error(error_message)
	end
end

local function some(t, func)
	for _, item in ipairs(t) do
		if func(item) then
			return true
		end
	end
	return false
end

local function dial_condition(actual_dialect, dialect1, dialect2)
	if not actual_dialect then
		return false
	end
	
	if type(dialect1) == "string" then
		return actual_dialect == dialect1 or
			dialect2 and actual_dialect == dialect2 or
			m_dialect_groups[dialect1] and m_dialect_groups[dialect1][actual_dialect]
	elseif type(dialect1) == "table" then
		return some(dialect1, function(dialect)
				return dial_condition(actual_dialect, dialect)
			end)
	end
end

local function dial_form_multi(args, forms, dialects)
	local actual_dialect = args.dial
	if not actual_dialect then
		return
	end
	local ctable = args.ctable
	
	-- Forms is an array of tables containing individual forms in
	-- the same dialect or dialects:
	-- { <case–number>, <form> }
	-- Dialects is a sequence of strings or tables:
	-- "<dialect code>"
	--		or
	-- { "<dialect code 1>", "<dialect code 2>", ... }
	if dialects then
		if dial_condition(actual_dialect, dialects) then
			for _, form in ipairs(forms) do
				if type(form[2]) == 'string' then
					ctable[form[1]] = form[2]
				end
			end
		end
	
	-- Forms is a sequence of tables:
	-- { <case–number>, <form>, <dialect code> }
	--		or
	-- { <case–number>, <form>, { <dialect code 1>, <dialect code 2>, ... } }
	elseif type(forms) == 'table' then
		for _, form_data in ipairs(forms) do
			if dial_condition(actual_dialect, form_data[3]) and type(form_data[2]) == 'string' then
				ctable[form_data[1]] = form_data[2]
			end
		end
	end	
end

local function dial_form(args, f, suffix, dialect1, dialect2)
	if dial_condition(args.dial, dialect1, dialect2) then
		args.ctable[f] = suffix
	end
end

-- Should Aeolic be moved here?
local function dial_forms_first(args)
	dial_form_multi(args, {
			{ 'GD', '$αι(ῐ)ν/$ῃῐν' },
			{ 'GP', '$ᾱ́ων/$έ͜ων/$ῶν' },
			{ 'DP', '$ῃσῐ(ν)/$ῃς/$αις' },
		},
		'epi')
	
	dial_form_multi(args, {
		{ 'GP', '$έων/$ῶν', 'ion' },
		
		{ 'DS', '$η', 'boi' }, -- 104.3
		{ 'DS', '$αι', { 'ara', 'ele' } },
		{ 'DS', '$ᾱ', { 'the', 'les' } },
		
		{ 'GD', '$αίαιρ', 'ele' },
		
		{ 'GP', '$έων/$ῶν', 'ion' }, -- 104.6
		{ 'GP', '$ᾶν', 'nonIA' },
		{ 'GP', '$ᾱ́ων', 'boi' },
		
		{ 'DP', '$ῃσῐ(ν)', 'ion' }, -- 104.7, ato must be dealt with separately
		{ 'DP', '$αισῐ(ν)', 'les' },
		{ 'DP', '$ᾱσῐ(ν)', 'ato' },
		{ 'DP', '$ῃσῐ(ν)', 'ion' },
		
		{ 'AP', '$ᾰς', 'buck78' }, -- 104.8
		{ 'AP', '$ᾰνς', { 'kre', 'arg' } },
		{ 'AP', '$αις', 'les' },
		{ 'AP', '$αιρ', 'ele' }, })
end

local function dial_forms_first_oxy(args)
	dial_form_multi(args, {
			{ 'DP', '$ῇσῐ(ν)/$ῇς/$αῖς' },
			{ 'GD', '$αῖ(ῐ)ν/$ῇῐν' },
			{ 'GP', '$ᾱ́ων/$έ͜ων/$ῶν' },
		},
		'epi')
	
	dial_form_multi(args, {
		{ 'DS', '$η', 'boi' }, -- 104.3
		{ 'DS', '$αι', { 'ara', 'ele' } },
		{ 'DS', '$ᾱ', { 'the', 'les' } },

		{ 'GP', '$έων/$ῶν', 'ion' }, -- 104.6
		{ 'GP', '$ᾶν', 'nonIA' },
		{ 'GP', '$ᾱ́ων', 'boi' },

		{ 'DP', '$ῃσῐ(ν)', 'ion' }, -- 104.7, ato must be dealt with separately
		{ 'DP', '$αισῐ(ν)', 'les' },
		{ 'DP', '$ᾶσῐ(ν)', 'ato' },

		{ 'AP', '$ᾰς', 'buck78' }, -- 104.8
		{ 'AP', '$ᾰνς', { 'kre', 'arg' } },
		{ 'AP', '$αις', 'les' },
		{ 'AP', '$αιρ', 'ele' },

		{ 'GD', '$αίαιρ', 'ele' }, })
end

export.inflections['1st-alp'] = function(args)
	local suffix = accent_switch(args.accent.term, '', '_con', '_pax', nil, args.suffix)
	args.ctable = deep_copy(m_paradigms['alp' .. suffix])
	
	if args.accent.term == 'oxytone' then
		dial_forms_first_oxy(args)
	else
		if args.adjective then
			args.ctable['GP'] = '$ων'
		end
		if args.accent.term ~= 'perispomenon' then
			dial_forms_first(args)
		end
	end
end

--[=[
	Only difference from "1st-alp-pax" and "1st-eta-pax" is that because of the "-prx",
	[[Module:grc-decl/decl]] doesn't shift the accent to the end of the stem
	in all forms. Used only in 1st-and-2nd-declension adjectives.
]=]
-- export.inflections['1st-alp-prx'] = export.inflections['1st-alp-pax']

-- export.inflections['1st-eta-prx'] = export.inflections['1st-eta-pax']

export.inflections['1st-eta'] = function(args)
	local suffix = accent_switch(args.accent.term, '', '_con', '_pax', nil, args.suffix)
	args.ctable = deep_copy(m_paradigms['eta' .. suffix])
	if args.accent.term == 'oxytone' then
		dial_form(args, 'DP', '$ῆσῐ(ν)', 'ato')
		-- Assuming this applies for perispomenon as well as oxytone.
		dial_forms_first_oxy(args)
	elseif args.accent.term ~= 'perispomenon' then
		if args.adjective then
			args.ctable['GP'] = '$ων'
		end
		dial_form(args, 'DP', '$ησῐ(ν)', 'ato')
		dial_forms_first(args)
	end
end

-- Always recessive, except in adjectives or derivatives of them.
local function short_alpha(code)
	return function(args)
		if code == 'als' then
			-- Ionic and Epic have eta, not alpha, in all forms except the
			-- nominative and accusative singular.
			if args.dial == 'ion' or args.dial == 'epi' then
				mw.logObject(args, 'args')
				short_alpha('ets')(args)
				return
			end
		elseif code ~= 'ets' then
			error('Invalid code ' .. tostring(code))
		end
		
		local accent_on_ultima = args.adjective and '_prx' or nil
		local suffix = accent_switch(args.accent.term,
			accent_on_ultima, accent_on_ultima, '_prx',
			'First-declension feminine nouns with nominative singular in ' ..
				'short alpha must have recessive accent.', args.suffix)
		args.ctable = deep_copy(m_paradigms[code .. suffix])
		dial_forms_first(args)
		if code == 'als' then
			dial_form(args, 'DP', '$ᾱσῐ(ν)', 'ato')
		else
			dial_form(args, 'DP', '$ησῐ(ν)', 'ato')
		end
	end
end

export.inflections['1st-als'] = short_alpha('als')

export.inflections['1st-ets'] = short_alpha('ets')

export.inflections['1st-M-alp'] = function(args)
	local suffix = accent_switch(args.accent.term, nil, '_con', '_pax',
		'Oxytone masculine first declension not supported.', args.suffix)
	args.ctable = deep_copy(m_paradigms['M_alp' .. suffix])
	if args.accent.term ~= 'perispomenon' then
		dial_forms_first(args)
		
		dial_form_multi(args, {
			{ 'GS', '$ᾱο/$ε͜ω/$ω', 'epi' },
	
			{ 'GS', '$εω/$ω', 'ion' },
			{ 'GS', '$ᾱ', 'nonIA' },
			{ 'GS', '$ᾱο', 'boi' },
			{ 'GS', '$ᾱυ', 'ark' }, })
	end
end

export.inflections['1st-M-eta'] = function(args)
	local suffix = accent_switch(args.accent.term, '', '_con', '_pax', nil, args.suffix)
	args.ctable = deep_copy(m_paradigms['M_eta' .. suffix])
	
	-- [[ἰδιώτης]], genitive singular ἰδιώτεω, not *ἰδιωτέω,
	-- at least in Herodotus 1.123.
	if args.dial == 'ion' or args.dial == 'epi' then
		args.synaeresis = true
	end
	
	if args.accent.term == 'oxytone' then
		dial_forms_first_oxy(args)
		
		dial_form_multi(args, {
			{ 'GS', '$ᾶο/$έ͜ω/$ῶ', 'epi' },
			{ 'GS', '$έω/$ῶ', 'ion' },
			{ 'GS', '$ᾶ', 'nonIA' },
			{ 'GS', '$ᾶο', 'boi' },
			{ 'GS', '$ᾶυ', 'ark' }, })
	elseif args.accent.term ~= 'perispomenon' then
		dial_forms_first(args)
		
		dial_form_multi(args, {
			{ 'GS', '$ᾱο/$ε͜ω/$ω', 'epi' },
			{ 'GS', '$εω/$ω', 'ion' },
			{ 'GS', '$ᾱ', 'nonIA' },
			{ 'GS', '$ᾱο', 'boi' },
			{ 'GS', '$ᾱυ', 'ark' }, })
	end
	
	if args['voc'] == 'α' or usub(args.stem, -1) == 'τ' then
		args.ctable['VS'] = '$ᾰ'
	end
end

local function dial_forms_second(args)
	dial_form_multi(args, {
			{ 'GS', '$ου/$οῖο/$οιο/$όο/$οο' },
			{ 'GD', '$οιῐν' },
			{ 'DP', '$οισῐ(ν)/$οις' },
		},
		'epi')
	
	dial_form_multi(args, {
		{ 'GS', '$οιο', 'dor' },
	
		{ 'GS', '$οι', 'the' }, -- 106.1
		{ 'GS', '$ω', 'severe', 'boi' },
		{ 'GS', '$ων', 'kyp' },
	
		{ 'DS', '$ω', 'les' }, -- 106.2
		{ 'DS', '$ου', 'the' },
		{ 'DS', '$οι', { 'ele', 'boi' } },
		{ 'DS', '$οι', { 'ara', 'eub' } },
	
		{ 'DP', '$οισῐ(ν)/$οις', { 'ato', 'ion' } }, -- 106.4
		{ 'DP', '$οισῐ(ν)', 'les' },
	
		{ 'AP', '$ως', 'severe' }, -- 106.5
		{ 'AP', '$ος', 'buck78' },
		{ 'AP', '$ονς', { 'kre', 'arg' } },
		{ 'AP', '$οις', 'les' },
		{ 'AP', '$οιρ', 'ele' },
	
		{ 'GD', '$οίοιρ', 'ele' }, -- 106.6
	
		{ 'ND', '$ου', 'the' }, -- 23
		{ 'GP', '$ουν', 'the' }, })
end

local function dial_forms_second_oxy(args)
	dial_form_multi(args, {
			{ 'GS', '$οῦ/$οῖο/$όο' },
			{ 'GD', '$οῖῐν' },
			{ 'DP', '$οῖσῐ(ν)/$οῖς' },
		},
		'epi')
	
	dial_form_multi(args, {
		{ 'GS', '$οῖο', 'dor' },
	
		{ 'GS', '$οῖ', 'the' }, -- 106.1
		{ 'GS', '$ῶ', 'severe', 'boi' },
		{ 'GS', '$ῶν', 'kyp' },
	
		{ 'DS', '$ω', 'les' }, -- 106.2
		{ 'DS', '$οῦ', 'the' },
		{ 'DS', '$οῖ', { 'ele', 'boi' } },
		{ 'DS', '$οῖ', { 'ara', 'eub' } },
	
		{ 'DP', '$οῖσῐ(ν)/$οῖς', { 'ato', 'ion' } }, -- 106.4
		{ 'DP', '$οισῐ(ν)', 'les' },
	
		{ 'AP', '$ώς', 'severe' }, -- 106.5
		{ 'AP', '$ός', 'buck78' },
		{ 'AP', '$όνς', { 'kre', 'arg' } },
		{ 'AP', '$οίς', 'les' },
		{ 'AP', '$οίρ', 'ele' },
	
		{ 'GD', '$οίοιρ', 'ele' }, -- 106.6
	
		{ 'ND', '$ού', 'the' }, -- 23
		{ 'GP', '$οῦν', 'the' }, })
end

-- For nouns like λόγος and ἔργον.
local function second_basic(gender)
	return function(args)
		local suffix = accent_switch(args.accent.term, '', nil, '_prx',
			'Perispomenon basic second-declension nouns not supported.', args.suffix)
		args.ctable = deep_copy(m_paradigms['second' .. (gender or '') .. suffix])
		if args.accent.term == 'oxytone' then
			dial_forms_second_oxy(args)
		else
			dial_forms_second(args)
		end
	end
end

export.inflections['2nd'] = second_basic()

export.inflections['2nd-N'] = second_basic('_N')

local function second_contracted(gender)
	return function(args)
		local suffix = accent_switch(args.accent.term, nil, '', '_pax',
			'Oxytone contracted second-declension nouns are not supported.', args.suffix)
		if gender == '_N' and suffix == '_pax' then
			error('Paroxytone contracted neuter second-declension nouns are not supported')
		end
		args.ctable = deep_copy(m_paradigms['second' .. (gender or '') .. '_con' .. suffix])
	end
end

export.inflections['2nd-con'] = second_contracted()

export.inflections['2nd-N-con'] = second_contracted('_N', true)

local function second_Attic(gender)
	return function(args)
		local suffix = accent_switch(args.accent.term, '', '_con', '_prx', nil, args.suffix)
		if gender == '_N' and suffix == '_con' then
			error('Perispomenon Attic declension neuter nouns are not supported.')
		end
		args.force_antepenult = true
		args.ctable = deep_copy(m_paradigms['second' .. (gender or '') .. '_att' .. suffix])
	end
end

export.inflections['2nd-att'] = second_Attic()

export.inflections['2nd-N-att'] = second_Attic('_N')

--[[
	In order:
		- vowel before ντ at end of stem, or declension category
		- masculine nominative singular ending
		- neuter nominative singular ending
		- segment preceding ῐ in dative plural
]]
local nt = {
	['ο'] = { 'ων', 'ον', 'ουσ' },			-- ἔχων (ἔχω)
	['1&3-ουντ'] = { 'ους', 'ον', 'ουσ' },	-- δούς (δίδωμι)
	['ου'] = { 'ων', 'ουν', 'ουσ' },		-- ποιῶν (ποιέω), δηλῶν (δηλόω)
	['ε'] = { 'εις', 'εν', 'εισ' },			-- θείς (τίθημι), ἀχθείς (ἄγω)
	['ω'] = { 'ων', 'ων', 'ωσ' },			-- ζῶν (ζάω)
}

-- Get nominative singular and dative plural (minus ῐ(ν)) for ντ-stems.
local function handle_nt(base, decl_type, gender, is_neuter, is_neuter_noun, is_adjective, arg1, arg2)
	local NS, DP
	
	local m_data = mw.loadData('Module:grc-utilities/data')
	base = to_NFD(base)
	
	-- Here we must match α, ε, ο, υ, ω, or ου (with any number of
	-- diacritics) followed by ντ.
	local whole_match, vowel, diacritic = umatch(base,
		'((ου)(' .. m_data.combining_diacritic .. '*)ντ)$')
	
	if not whole_match then
		whole_match, vowel, diacritic = umatch(base,
			'(([αεουω])(' .. m_data.combining_diacritic .. '*)ντ)$')
	end
	
	if not whole_match then
		error('Something is wrong with the stem ' .. base .. '.')
	end
	
	local nom_base = base:gsub(whole_match, '')
	
	-- Declension type has priority over vowel because δούς (δοντ-) and
	-- λαβών (λαβόντ-) have the same stem vowel, but different masculine
	-- nominative singulars.
	local data = nt[decl_type] or nt[vowel]
	
	if data then
		if is_adjective then
			NS = nom_base .. (is_neuter and data[2] or data[1])
		end
		
		DP = nom_base .. data[3]
	else
		local macron, breve = m_data.diacritics.macron, m_data.diacritics.breve
		if diacritic == macron or diacritic == breve or diacritic == '' then
			if vowel then
				local intermediate_base = nom_base .. vowel
				DP = intermediate_base .. macron .. 'σ'
				
				if is_adjective then
					if is_neuter then
						NS = intermediate_base .. breve .. 'ν'
					else
						NS = intermediate_base .. macron .. 'ς'
					end
				end
			else
				error('Failed to generate nominative singular and dative plural for ' ..
					quote(arg1) .. ', ' .. quote(arg2) .. '.')
			end
		else
			error('Invalid diacritic ' ..
				(type(diacritic) == 'string' and string.format("U+%X", codepoint(diacritic)) or tostring(diacritic)) ..
				') in the stem ' .. base .. '.')
		end
	end
	
	if not adjective and not gender[1] then
		gender[1] = 'M'
		gender.M = true
	end
	
	return NS, DP
end
	
local finals = {
	['β'] = 'ψ',
	['γ'] = 'ξ',
	['κ'] = 'ξ',
	['κτ'] = 'ξ',
	['π'] = 'ψ',
	['φ'] = 'ψ',
	['χ'] = 'ξ',
	--[[
	['τ'] = ,
	['δ'] = ,
	['θ'] = ,
	--]]
}

local function new_gender(gender)
	return { gender, [gender] = true }
end

-- is_neuter: boolean
-- accent_alternating: boolean
local function third_nom(args, is_neuter, accent_alternating)
	-- strings
	local arg1, arg2, base = args[1], args[2], to_NFC(args.stem)
	local ctable = args.ctable
		-- output
	local NS, DS, AS, DP = ctable.NS, ctable.DS, ctable.AS, ctable.DP
	-- boolean
	local adjective = args.adjective
	-- tables
	local gender, notes = args.gender, args.notes
	
	--[[
	local notes = {
		labial = 'Nominative singular ' ..
			mention('-ψ') ..
			(('φβ'):find(last1) and ' is a neutralization of ' or ' is a respelling of ') ..
			mention(last1) ..
			' + the nominative singular suffix ' ..
			mention('-ς') .. '.',
		velar_dental = 'Nominative singular ' ..
			mention('-ξ') ..
			' is a neutralization of ' ..
			mention(last2) ..
			' + the nominative singular suffix ' ..
			mention('-ς') .. '.',
		'Nominative singular ' ..
			mention('-ξ') ..
			(umatch('χγ', last1) and ' is a neutralization of ' or ' is a respelling of ') ..
			mention(last1) ..
			' + the nominative singular suffix ' ..
			mention('-ς') .. '.'
	}
	--]]
	
	local identifying_ending
	local chars, char2 = umatch(base, '(.(.))$')
	-- '[πβφκγχρνσω]$', '[κν]τ$', '[τδθ]'
	if chars == 'ντ' or chars == 'κτ' then
		identifying_ending = chars
	else
		identifying_ending = char2
	end
	
	local NS_ending = finals[identifying_ending]
	if NS_ending then
		NS = base:gsub(identifying_ending .. "$", NS_ending)
		DP = NS
	end
	
	if not (NS or DP) then
		local decl_type = args.decl_type
		
		local last1, last2 = usub(base, -1), usub(base, -2)
		local nom_base = usub(base, 1, -2)
		
		local is_neuter_noun = is_neuter and not adjective
		
		if identifying_ending == 'ντ' then
			-- May also add gender.
			NS, DP = handle_nt(base, decl_type, gender, is_neuter,
				is_neuter_noun, adjective, arg1, arg2)
		elseif ('τδθ'):find(identifying_ending) then
			DP = nom_base .. 'σ'
			if not adjective then
				if usub(base, -3) == 'τητ' or ('δθ'):find(last1) then
					if not gender[1] then
						gender = new_gender('F')
					end
				elseif last2 == 'ητ' or last2 == 'ωτ' then
					if not gender[1] then
						gender = new_gender('M')
					end
				elseif last2 == 'ᾰτ' or last2 == 'ᾱτ' then
					if not gender[1] then
						gender = new_gender('N')
					end
				end
			end
			if is_neuter then
				if last2 == 'οτ' then
					NS = nom_base .. 'ς'
				else
					NS = nom_base
				end
			else
				if last2 == 'οτ' then
					NS = base:gsub('οτ', 'ως')
				else
					NS = nom_base .. 'ς'
				end
			end
			if arg1:find('ς$') then
				table.insert(notes,
					'Nominative singular ' ..
							mention('-ς') ..
							' arose by reduction of the original cluster ' ..
							proto_mention(dental_lookup[last1]) .. '.')
			end
		elseif ('ρν'):find(identifying_ending) then
			local vowel = usub(base, -2, -2)
			if last1 == 'ρ' then
				DP = base .. 'σ'
			else
				DP = nom_base .. 'σ'
			end
			if is_neuter then
				NS = base
			elseif vowel == 'ε' then
				NS = usub(base, 1, -3) .. 'η' .. last1
			elseif vowel == 'ο' then
				NS = usub(base, 1, -3) .. 'ω' .. last1
			elseif vowel == 'ῑ' then --ῥίς etc.
				NS = nom_base .. 'ς'
			elseif (vowel == 'ᾰ' and gender[1] ~= 'N') then
				NS = usub(base, 1, -3) .. 'ᾱς'
			else
				NS = base
			end
			local last3 = usub(base, -3)
			if last3 == 'γον' or last3 == 'δον' then
				gender[1] = gender[1] or 'F'
			elseif ufind(last2, '[ᾰᾱα]ρ') then
				gender[1] = gender[1] or 'N'
			else
				gender[1] = gender[1] or 'M'
			end
		elseif identifying_ending == 'σ' then
			DP = base
			if is_neuter_noun then
				NS = nom_base .. 'ς'
			else
				NS = usub(base, 1, -3) .. 'ης'
			end
			DS = '$ῐ̈'
		elseif identifying_ending == 'ω' then
			-- m_paradigms.lp_prx.NS is nil. Is the condition correct?
			if NS == m_paradigms.lp_prx.NS then
				DS = nom_base .. 'ῳ/$ῐ̈'
				AS = '$/$ᾰ'
			else
				DS = '$ῐ̈́'
				AS = '$/$ᾰ́'
			end
			DP = nom_base .. 'ωσ'
			NS = nom_base .. 'ως'
			gender[1] = gender[1] or 'M'
		else
			error('Stem does not end in a consonant: ' .. base)
		end
	end
	
	-- This overrides any assignment done above. Better to improve the code
	-- above or prevent it from assigning NS.
	if not adjective then
		NS = arg1
	end
	if DP then
		if accent_alternating then
			DP = DP .. 'ῐ́(ν)'
		else
			DP = DP .. 'ῐ(ν)'
		end
	end
	
	ctable.DS, ctable.AS = DS, AS
	ctable.NS, ctable.DP = NS, DP
	args.gender = gender
end

local function dial_forms_third(args)
	if not args.ctable.DP then
		require('Module:debug').track('grc-decl/no dative plural')
		mw.log("no DP: " .. args[1] .. ", " .. args[2])
	end
	dial_form_multi(args, {
			{ 'DP', args.ctable['DP'] and args.ctable['DP'] .. '/$εσσῐ(ν)/$εσῐ(ν)' or '$εσσῐ(ν)/$εσῐ(ν)' },
			{ 'GD', '$οιῐν' },
		},
		'epi')
	
	dial_form_multi(args, {
		{ 'DP', '$εσσῐ(ν)', 'dor' },
	
		{ 'AS', '$ᾰν', 'kyp' },
	
		{ 'DP', '$εσσῐ(ν)', { 'les', 'the' } },
		{ 'DP', '$εσσῐ(ν)', 'boi' },
		{ 'DP', '$οις', { 'lok', 'ele' } },
	
		{ 'AP', '$ες', { 'ark', 'ele' } },
		{ 'AP', '$ᾰς/$ᾰνς', 'kre' }, })
end

local function dial_forms_third_oxy(args)
	dial_form_multi(args, {
			{ 'GD', '$οῖῐν' },
			{ 'DP', args.ctable['DP'] and args.ctable['DP'] .. '/$εσσῐ(ν)/$εσῐ(ν)' or '$εσσῐ(ν)/$εσῐ(ν)' },
		},
		'epi')
	
	dial_form_multi(args, {
		{ 'DP', '$εσσῐ(ν)', 'dor' },
	
		{ 'AS', '$ᾰν', 'kyp' },
	
		{ 'DP', '$εσσῐ(ν)', { 'les', 'the' } },
		{ 'DP', '$εσσῐ(ν)', 'boi' },
		{ 'DP', '$οῖς', { 'lok', 'ele' } },
	
		{ 'AP', '$ες', { 'ark', 'ele' } },
		{ 'AP', '$ᾰς/$ᾰνς', 'kre' }, })
end

-- For instance, [[μείζων]].
local function third_comparative(args, neuter)
	local short_stem, count = args.stem:gsub('ον$', '')
	if count ~= 1 then
		error('Stem failure.')
	end
	if neuter then
		dial_form(args, 'NP', '$ᾰ/' .. short_stem .. 'ω', 'att')
	else
		dial_form_multi(args, {
				{ 'AS', '$ᾰ/' .. short_stem .. 'ω'},
				{ 'NP', '$ες/' .. short_stem .. 'ους' },
				{ 'AP', '$ᾰς/' .. short_stem .. 'ους' },
			}, 'att')
	end
end

-- These nouns are monosyllabic, but unlike most they do not accent the ending
-- of the genitive–dative dual and genitive plural: παίδων, not *παιδῶν.
local monosyllabic_exceptions = {
	['παῖς'] = true,
	['δμώς'] = true,
	['θώς'] = true,
	['Τρώς'] = true,
	['δᾴς'] = true,
	['φῶς'] = true,
	['οὖς'] = true,
}
-- Other exceptions: contracted forms like ἔαρος > ἦρος (not *ἠρός).
-- The source for this is Smyth § 252.
local function third(neuter)
	return function (args)
		if args.decl_type:find('ντ$') and m_accent.syllables(args[1], 'eq', 1) then
			-- [[Special:WhatLinksHere/Wiktionary:Tracking/grc-decl/monosyllabic nt]]
			require('Module:debug').track('grc-decl/monosyllabic nt')
		end
		
		-- This tracks monosyllabic nouns or adjectives that have alternating accent
		-- position. Masculine and neuter participles (with stems in -ντ) and
		-- suffixes do not.
		local accent_alternating = not (args.suffix or args.stem:find('ντ$'))
			and m_accent.syllables(args[1], 'eq', 1)
		args.accent_alternating = accent_alternating
	
		local accent_on_ultima = accent_alternating and '' or '_prx'
		local suffix = accent_switch(args.accent.term, accent_on_ultima, accent_on_ultima, '_prx', nil, args.suffix)
		args.ctable = deep_copy(m_paradigms[(neuter and 'N_' or '') .. 'lp' .. suffix])
	
		if not (args.NS and args.DP) then
			third_nom(args, neuter, accent_alternating)
		end
		
		if args.comparative then
			third_comparative(args, neuter)
		end
		
		if not neuter then
			local last_two = usub(args.stem, -2)
			if last_two == 'ῐδ' or last_two == 'ῐτ' or last_two == 'ῑθ' then
				if args.accent.term ~= 'oxytone' then
					dial_form(args, 'AS', args.ctable['AS'], 'epi', 'ion') -- i.e. -χάριν; also poetic
					args.ctable['AS'] = args['AS'] or usub(args.stem, 1, -2) .. 'ν'
				end
				args.ctable['VS'] = args['VS'] or usub(args.stem, 1, -2)
			elseif last_two == 'ντ' and (args.decl_type == '1&3-εσσ' or not args.adjective) then
				args.ctable['VS'] = args['VS'] or usub(args.stem, 1, -2)
			elseif args.accent.term ~= 'oxytone' and args.accent.term ~= 'perispomenon' and
					ufind(args.stem, '[ρν]$') then
				args.ctable['VS'] = args['VS'] or args.stem
			-- What's an example of this?
			elseif usub(args.stem, -1) == 'ε' then
				args.ctable['VS'] = args['VS'] or args.stem .. 'ς'
			end
	
			if args.accent.term == 'perispomenon' then
				args.ctable['NS'] = args[1]
			end
		end
	
		if accent_alternating then
			dial_forms_third_oxy(args)
			if monosyllabic_exceptions[to_NFC(args[1])] then
				-- Remove accent marks: that is, make the form be accented on the
				-- stem.
				args.ctable.GD = m_accent.strip_tone(args.ctable.GD)
				args.ctable.GP = m_accent.strip_tone(args.ctable.GP)
			end
		else
			dial_forms_third(args)
		end
	end
end

export.inflections['3rd-cons'] = third(false)
export.inflections['3rd-N-cons'] = third(true)

local function dial_forms_es(args, neuter)
	dial_form_multi(args, {
		{ 'GS', '$εος', { 'nonIA' } },
		{ 'GS', '$ιος', 'buck9' },
		{ 'GS', '$εος/$ευς', { 'ion', 'epi' } },
		{ 'DS', '$ει/$εῐ̈', { 'epi', 'ion' } },
		{ 'ND', '$ει/$εε', { 'ion', 'epi' } },
		{ 'GD', '$έοιν', { 'nonIA', 'ion' } },
		{ 'GD', '$ίοιν', 'buck9' },
		{ 'NP', neuter and '$εᾰ' or '$εις/$εες', { 'ion', 'epi' } },
		{ 'GP', '$έων', { 'nonIA', 'ion', 'epi' } },
		{ 'GP', '$ίων', 'buck9' }, })
	
	if not neuter then
		dial_form_multi(args, {
			{ 'AS', '$εᾰ', { 'nonIA', 'ion', 'epi' } },
			{ 'AP', '$εᾰς', { 'nonIA', 'ion', 'epi' } },
			{ 'AP', '$ιᾰς', 'buck9' }, })
	end
end

local function dial_forms_es_oxy(args, neuter)
	dial_form_multi(args, {
		{ 'GS', '$έος', 'nonIA' },
		{ 'GS', '$ίος', 'buck9' },
		{ 'GS', '$έος/$εῦς', { 'ion', 'epi' } },
		{ 'DS', '$εῖ/$έῐ̈', { 'epi', 'ion' } },
		{ 'ND', '$εῖ/$έε', { 'ion', 'epi' } },
		{ 'GD', '$έοιν', { 'nonIA', 'ion' } },
		{ 'GD', '$ίοιν', 'buck9' },
		{ 'NP', neuter and '$έᾰ' or '$εῖς/$έες', { 'ion', 'epi' } },
		{ 'GP', '$έων', { 'nonIA', 'ion', 'epi' } },
		{ 'GP', '$ίων', 'buck9' }, })
	
	if not neuter then
		dial_form_multi(args, {
			{ 'AS', '$έᾰ', { 'nonIA', 'ion', 'epi' } },
			{ 'AS', '$ίᾰ', 'buck9' },
			{ 'AP', '$έᾰς', { 'nonIA', 'ion', 'epi' } },
			{ 'AP', '$ίᾰς', 'buck9' }, })
	end
end

-- Adjectives with these endings have persistent accent (Smyth 292c):
-- εὐώδης, εὐῶδες.
local persistent_es = {
	['ώδης'] = true,
	['ώλης'] = true,
	['ώρης'] = true,
	['ήρης'] = true,
}

-- How to deal with τριήρων, not *τριηρῶν (Smyth 264)?
local function es_adj(gender_code, open)
	return function(args)
		uncontracted_error(args.contracted or not open, args.dial, '-ης')
		local suffix = accent_switch(args.accent.term, '', nil, '_prx',
			'Perispomenon third-declension forms in -ης not supported.', args.suffix)
		args.ctable = deep_copy(m_paradigms[(gender_code or '') .. 'es_adj' ..
			suffix .. (open and '_open' or '')])
		
		-- Most adjectives of this type have recessive accent. Set accent to
		-- antepenult.
		if not args.suffix and not persistent_es[to_NFC(usub(args[1], -4))] then
			args.accent.position = -3
		end
		
		if args.accent.term == 'oxytone' then
			dial_forms_es_oxy(args, gender_code == 'N_')
		else
			dial_forms_es(args, gender_code == 'N_')
		end
		
		if gender_code ~= 'N_' then
			dial_form(args, 'NS', '$εῖς', 'boi', 'the')
		end
	end
end


export.inflections['3rd-εσ'] = es_adj()

export.inflections['3rd-N-εσ'] = es_adj('N_')

export.inflections['3rd-εσ-open'] = es_adj(nil, true)

export.inflections['3rd-N-εσ-open'] = es_adj('N_', true)

local function neuter_os(open)
	return function(args)
		nonrecessive_error(args.accent.term,
			'Third-declension neuter nouns in -ος must be accented recessively')
		uncontracted_error(args.contracted or not open, args.dial, '-ος')
		
		args.ctable = deep_copy(m_paradigms['N_es_prx' .. (open and '_open' or '')])
		dial_forms_es(args, true)
	end
end

export.inflections['3rd-N-ος'] = neuter_os()

export.inflections['3rd-N-ος-open'] = neuter_os(true)

local function neuter_as(open)
	return function(args)
		nonrecessive_error(args.accent.term,
			'Third-declension neuter nouns in -ας must be accented recessively')
		uncontracted_error(args.contracted or not open, args.dial, '-ᾰς')
		args.ctable = deep_copy(m_paradigms['N_as_prx' .. (open and '_open' or '')])
	end
end

export.inflections['3rd-N-ᾰσ'] = neuter_as()

export.inflections['3rd-N-ᾰσ-open'] = neuter_as(true)

local function kles(open)
	return function(args)
		local dialect = args.dial
		uncontracted_error(args.contracted or not open, dialect, '-κλης')
		if dialect == 'ion' or dialect == 'epi' then
			open = true
		end
		
		args.ctable = deep_copy(m_paradigms['kles' .. (open and '_open' or '')])
		args.number = { 'S', S = true }
		args.recessive_VS = true
		
		if open then
			dial_form_multi(args, {
					{ 'GS', '$κλῆος/$κλέος' },
					{ 'DS', '$κλῆῐ̈/$κλέῐ̈' },
					{ 'AS', '$κλῆᾰ/$κλέᾱ' },
				},
				{ 'epi', 'ion' })
		else
			dial_form_multi(args, {
				{ 'GS', '$κλέος', 'nonIA' },
				{ 'GS', '$κλεῖος', 'boi' }, })
		end
	end
end
export.inflections['3rd-κλῆς'] = kles()

export.inflections['3rd-κλῆς-open'] = kles(true)

-- Needs dialectal forms.
local function weak_i(gender_code)
	return function(args)
		nonrecessive_error(args.accent.term,
			'Third-declension nouns with stem in short ῐ must be accented recessively.')
		args.ctable = deep_copy(m_paradigms[(gender_code or '') .. 'weak_i_prx'])
		args.synaeresis = true
		
		 -- Or Ionic or both?
		dial_form_multi(args, {
				{ 'DS', '$ει/$εῐ̈' },
				{ 'ND', '$ει/$εε' },
				{ 'NP', '$εις/$εες' },
			},
			'epi')
	end
end

export.inflections['3rd-weak-ι'] = weak_i()

export.inflections['3rd-N-weak-ι'] = weak_i('N_')

local function dial_forms_weak_u(args, neuter)
	dial_form_multi(args, {
		{ 'GS', '$έος', { 'epi', 'ion' } },
		{ 'GD', '$έοιῐν', 'epi' },
		{ 'DP', '$έεσσῐ(ν)/$έεσῐ(ν)/$εσῐ(ν)', 'epi' },
		{ 'DP', '$έεσσῐ(ν)', 'dor' }})
	
	if args.adjective then
		args.ctable['GS'] = '$έος'
		args.ctable['ND'] = '$έε'
	end
	
	if not neuter then
		dial_form_multi(args, {
			{ 'NP', '$έες', 'epi' }, -- What about nonIA and ion?
			{ 'AP', '$έᾰς', { 'nonIA', 'ion', 'epi' } }, })
	end
end

local function dial_forms_weak_u_prx(args, neuter)
	dial_form_multi(args, {
		{ 'GS', '$εος', { 'epi', 'ion' } },
		{ 'GD', '$έοιῐν', 'epi' },
		{ 'DP', '$έεσσῐ(ν)/$έεσῐ(ν)/$εσῐ(ν)', 'epi' },
		{ 'DP', '$έεσσῐ(ν)', 'dor' }, })
	
	if args.adjective then
		args.ctable['GS'] = '$εος'
		args.ctable['ND'] = '$εε'
		args.ctable['GP'] = '$έων'
	end
	
	if not neuter then
		dial_form_multi(args, {
			{ 'NP', '$εες' },
			{ 'AP', '$εᾰς' },
		}, 'epi')
	end
end

local function weak_u (gender)
	return function(args)
		local suffix = accent_switch(args.accent.term, '', nil, '_prx', nil, args.suffix)
		args.ctable = deep_copy(m_paradigms[(gender or '') .. 'weak_u' .. suffix])
		if not args.adjective then
			args.synaeresis = true
		end
		
		local neuter = gender == 'N_'
		if args.accent.term == 'oxytone' then
			dial_forms_weak_u(args, neuter)
		else
			dial_forms_weak_u_prx(args, neuter)
		end
	end
end

export.inflections['3rd-weak-υ'] = weak_u()

export.inflections['3rd-N-weak-υ'] = weak_u('N_')

export.inflections['3rd-pure-ι'] = function(args)
	nonrecessive_error("Nouns in -ῐς cannot be accented on the ultima")
	args.ctable = deep_copy(m_paradigms.pure_i_prx)
	dial_form_multi(args, {
			{ 'GS', '$ῐος/$ηος' },
			{ 'DS', '$ῐῐ/$ῑ/$ηῐ̈/$ει' },
			{ 'GD', '$ῐ́οιῐν' },
			{ 'NP', '$ῐες/$ηες' },
			{ 'DP', '$ῐ́εσσῐ(ν)/$εσῐ(ν)/$ῐσῐ(ν)' },
			{ 'AP', '$ῐᾰς/$ηᾰς/$ῑς' },
		},
		'epi')
	
	dial_form(args, 'DP', '$ῐ́εσσῐ(ν)', 'dor')
end

export.inflections['3rd-N-pure-ι-prx'] = function(args)
	args.ctable = deep_copy(m_paradigms.N_pure_i_prx)
end
export.inflections['3rd-N-pure-ι-pax'] = export.inflections['3rd-N-pure-ι-prx']

local function pure_u_short(gender_code)
	return function(args)
		-- No error message needed.
		local suffix = accent_switch(args.accent.term, '', nil, '_prx', nil, args.suffix)
		if gender_code == 'N_' and args.accent.term == 'oxytone' then
			error('Neuter nouns in short -ῠ must be accented recessively.')
		end
		args.ctable = deep_copy(m_paradigms[(gender_code or '') .. 'pure_u' .. suffix])
		dial_form_multi(args, {
				{ 'GD', '$ῠ́οιῐν' },
				{ 'DP', '$ῠ́εσσῐ(ν)/$ῠσῐ(ν)/$ῠσσῐ(ν)' },
			},
			'epi')
		dial_form(args, 'DP', '$ῠ́εσσῐ(ν)', 'dor')
	end
end
export.inflections['3rd-pure-υ'] = pure_u_short()

export.inflections['3rd-N-pure-υ'] = pure_u_short('N_')

export.inflections['3rd-pure-υ-long'] = function(args)
	local suffix = accent_switch(args.accent.term, '', '_con', '_prx', nil, args.suffix)
	args.ctable = deep_copy(m_paradigms['pure_u_long' .. suffix])
	
	dial_form(args, 'DP', '$ῠ́εσσῐ(ν)', 'dor')
	
	dial_form_multi(args, {
			{ 'DP', accent_switch(args.accent.term,
				'$ῠ́εσσῐ(ν)/$ῠσῐ(ν)/$ῠσσῐ(ν)',
				'$ῠ́εσσῐ(ν)',
				'$ῠ́εσσῐ(ν)/$ῡσῐ(ν)/$ῡσσῐ(ν)', nil, args.suffix) },
			{ 'GD', '$ῠ́οιῐν' },
		},
		'epi')
end

local function third_eus(contracted)
	return function(args)
		-- Maybe only Attic actually?
		local dialect = args.dial
		if contracted and not (dialect == 'att' or dialect == 'koi' or dialect == 'byz') then
			error('Only Attic, Koine, or Byzantine Greek have contracted declined forms for nouns in -ευς.')
		end
		
		if dialect == 'kyp' or dialect == 'boi' then
			args.ctable = deep_copy(m_paradigms.eus_hwos)
		elseif dialect == 'les' or dialect == 'epi' then
			args.ctable = deep_copy(m_paradigms.eus_hos)
			
			dial_form_multi(args, {
					{ 'GS', '$ῆος/$έος' },
					{ 'DS', '$ῆῐ̈/$έῐ̈' },
					{ 'AS', '$ῆᾰ/$έᾰ' },
					{ 'GD', '$ήοιῐν' },
					{ 'DP', '$ήεσσῐ(ν)/$εῦσῐ(ν)' },
				},
				'epi')
		elseif dialect == 'the' or dialect == 'ele' then
			args.ctable = deep_copy(m_paradigms.eus_eios)
		else
			if contracted then
				args.ctable = deep_copy(m_paradigms.eus_con)
				table.insert(args.titleapp, '[[Appendix:Ancient Greek contraction|contracted]]')
			else
				args.ctable = deep_copy(m_paradigms.eus)
			end
			
			dial_form_multi(args, {
				{ 'DP', '$έεσσῐ(ν)', 'dor' }, -- this is somewhat conjectured
			
				{ 'GS', '$έος', { 'nonIA', 'ion' } },
			
				{ 'DS', '$εῖ', 'att' },
			
				{ 'AS', '$έᾰ', { 'nonIA', 'ion' } },
				{ 'AS', '$ῆ', { 'doric', 'del' } },
				{ 'AS', '$έᾰ', { 'lok', 'kre' } },
			
				{ 'NP', '$έες/$εῖς', 'ion' },
				{ 'NP', '$εῖς', 'nonIA' },
				{ 'NP', '$ῆς', { 'koa', 'lak' } },
				{ 'NP', '$ῆς', 'ara' },
				{ 'NP', '$έες', 'kre' },
				{ 'NP', '$εῖς', 'late' },
			
				{ 'AP', '$έᾰς', { 'nonIA', 'ion' } },
			
				{ 'NS', '$ής', 'ara' },
				{ 'AS', '$ήν', 'ara' }, })
			
			--[[
				Contraction only for nouns with vowel before -ευς.
			]]
			local diacritics = mw.loadData('Module:grc-utilities/data').diacritics
			local macron, breve = diacritics.macron, diacritics.breve
			if ufind(args.stem, '[αεηιουω][' .. macron .. breve .. ']?$') then
				dial_form_multi(args, {
						{ 'GS', '$έως/$ῶς' },
						{ 'AS', '$έᾱ/$ᾶ' },
						{ 'GP', '$έων/$ῶν' },
						{ 'AP', '$έᾱς/$ᾶς' },
					},
					'att')
				table.insert(args.notes, 'The first form is uncontracted, the second [[Appendix:Ancient Greek contraction|contracted]].')
			elseif contracted then
				error('Forms with a stem ending in ' ..
						require('Module:grc-utilities').tag(usub(args.stem, -1)) ..
						' do not have contracted forms.')
			end
		end
	end
end

export.inflections['3rd-ευς'] = third_eus(false)

export.inflections['3rd-ευς-con'] = third_eus(true)

export.inflections['3rd-οι'] = function(args)
	args.ctable = deep_copy(m_paradigms.oi)
	args.number = { 'S', S = true }
	dial_form_multi(args, {
		{ 'GS', '$ῶς', 'sever' },
		{ 'AS', '$οῦν', 'ion' }, })
end

-- irregular masculine or feminine nouns
export.inflections['irreg'] = function(args)
	local params = m_decl_static_data.irregular.noun.masculine_feminine
	if args.gender.N then
		return export.inflections['irregN'](args)
	end
	local noun = not args.adjective
	-- Would DP (dual–plural) ever be used?
	local number = args.number
	local param_code
	if number.F then
		param_code = 'full'
	elseif number.S then
		if number.P then
			param_code = 'SP'
		else
			param_code = 'S'
		end
	elseif number.D then
		if number.P then
			param_code = 'DP'
		else
			param_code = 'D'
		end
	elseif number.P then
		param_code = 'P'
	else
		error('Could not decide which table of forms to use.')
	end
	local forms = params[param_code]
	
	args.declheader = 'irreg'
	local ctable = {}
	local found_forms = false
	for i = 2, args.maxindex do
		local code = forms[i]
		local arg1 = ine(args[i])
		if code then
			local arg2 = ine(args[code])
			if arg1 and arg2 then
				error('Two forms specified for ' .. code .. ': ' .. arg1 .. ' and ' .. arg2 '.')
			end
			local number_code = code:sub(2, 2)
			if arg1 or arg2 then
				if number[number_code] then
					found_forms = true
				else
					error('The number ' .. number_code .. ' is not specified in the form parameter.')
				end
			end
			ctable[code] = arg1 or arg2
		elseif arg1 then
			local abbr = {
				S = 'singular',
				D = 'dual',
				P = 'plural',
			}
			local numbers = mw.text.listToText(require('Module:fun').map(function(code)
					return abbr[code] or code
				end,
				number
			))
			local singular = not number[2]
			error('Parameter ' .. i .. ' has been given, but only ' .. #forms ..
				' parameters are needed because the number' .. (singular and '' or 's') ..
				' specified in the form parameter ' .. (singular and 'is ' or 'are ') ..
				 numbers .. '.')
		end
	end
	if not found_forms then
		error('No displayable forms found.')
	end
	args.ctable = ctable
end

-- irregular neuter nouns
export.inflections['irregN'] = function(args) --neuter irregular nouns
	local params = m_decl_static_data.irregular.noun.neuter
	local number = args.number
	local param_code
	if number.F then
		param_code = 'full'
	elseif number.P then
		if number.S then
			param_code = 'SP'
		elseif number.D then
			param_code = 'DP'
		else
			param_code = 'P'
		end
	elseif number.D then
		param_code = 'D'
	elseif number.S then
		param_code = 'S'
	else
		error('Could not decide which table of forms to use.')
	end
	local forms = params[param_code]
	
	local ctable = {}
	
	for i = 2, args.maxindex do
		local code = forms[i]
		local arg1 = args[i]
		if code then
			local arg2 = args[code]
			if arg1 and arg2 then
				error('Two forms given for form ' .. code .. ': ' .. arg1 ..
					' in parameter ' .. i .. ' and ' .. arg2 ..
					' in parameter ' .. code ..
					'. Choose one or put both in the same parameter separated by slashes.')
			end
			
			ctable[code] = arg1 or arg2
		elseif arg1 then
			error('Parameter ' .. i .. ', ' .. arg1 .. ', does not have a case and number assigned to it.')
		end
	end
	for form, source in pairs(forms.redirects) do
		ctable[form] = args[form] or ctable[source]
	end
	args.ctable = ctable
end

-- irregular adjectives
export.inflections['irreg-adj'] = function(args)
	local params = m_decl_static_data.irregular.adjective
	local number = args.number
	local param_code
	if number.F then
		param_code = 'full'
	elseif number.P then
		if number.S then
			param_code = 'SP'
		else
			param_code = 'P'
		end
	elseif number.S then
		param_code = 'S'
	else
		error('Could not decide which table of forms to use.')
	end
	local forms = params[param_code]
	
	local atable = {}
	for i = 2, args.maxindex do
		local code = forms[i]
		local arg1 = ine(args[i])
		if code then
			local arg2 = ine(args[code])
			if arg1 and arg2 then
				error('Two forms given for form ' .. code .. ': ' .. arg1 ..
					' in parameter ' .. i .. ' and ' .. arg2 ..
					' in parameter ' .. code ..
					'. Choose one or put both in the same parameter separated by slashes.')
			end
			atable[code] = arg1 or arg2
		else
			if arg1 then
				error('No gender, case, and number assigned to parameter ' ..
						i .. ': ' .. tostring(arg1) .. '. Only ' ..
						#forms .. ' parameters are needed.')
			end
		end
	end
	for form, source in pairs(forms.redirects) do
		atable[form] = args[form] or atable[source]
	end
	if number.D then
		for target_case, source_case in pairs { A = 'N', V = 'N', D = 'G' } do
			for _, gender in ipairs { 'M', 'F', 'N' } do
				local target = gender .. target_case .. 'D'
				atable[target] = args[target] or atable[target] or atable[gender .. source_case .. 'D']
				
				local target = gender .. 'VP'
				atable[target] = args[target] or atable[target] or atable[gender .. 'NP']
			end
		end
	end
	args.atable = atable
end

export.inflections['indecl'] = function(args)
	args.ctable = deep_copy(m_paradigms.indecl)
	args.stem = { args[2] }
end

return export