Module:grc-decl/decl/data

From Wiktionary, the free dictionary
Jump to navigation Jump to search
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_table = require('Module:table')
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 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',
}

-- Copy tables loaded with mw.loadData, removing metatables.
local function deepcopy(orig)
	return m_table.deepcopy(orig, true)
end

-- 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 = deepcopy(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 = deepcopy(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 = deepcopy(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 = deepcopy(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 = deepcopy(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 = deepcopy(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 = deepcopy(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 = deepcopy(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 = deepcopy(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 = deepcopy(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 = deepcopy(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 = deepcopy(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 = deepcopy(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 = deepcopy(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 = deepcopy(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 = deepcopy(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 = deepcopy(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 = deepcopy(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 = deepcopy(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 = deepcopy(m_paradigms.eus_hwos)
		elseif dialect == 'les' or dialect == 'epi' then
			args.ctable = deepcopy(m_paradigms.eus_hos)
			
			dial_form_multi(args, {
					{ 'GS', '$ῆος/$έος' },
					{ 'DS', '$ῆῐ̈/$έῐ̈' },
					{ 'AS', '$ῆᾰ/$έᾰ' },
					{ 'GD', '$ήοιῐν' },
					{ 'DP', '$ήεσσῐ(ν)/$εῦσῐ(ν)' },
				},
				'epi')
		elseif dialect == 'the' or dialect == 'ele' then
			args.ctable = deepcopy(m_paradigms.eus_eios)
		else
			if contracted then
				args.ctable = deepcopy(m_paradigms.eus_con)
				table.insert(args.titleapp, '[[Appendix:Ancient Greek contraction|contracted]]')
			else
				args.ctable = deepcopy(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 = deepcopy(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 = require('Module:table').serialCommaJoin(require('Module:fun').map(function(code)
						return abbr[code] or code
					end,
					number),
				{ dontTag = true })
			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 = deepcopy(m_paradigms.indecl)
	args.stem = { args[2] }
end

return export