Module:la-verb

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

This module is used for the various Latin verb inflection tables.


-- TODO:
-- 1. (DONE) detect_decl_and_subtypes doesn't do anything with perf_stem or supine_stem.
-- 2. (DONE) Should error on bad subtypes.
-- 3. Make sure Google Books link still works.
-- 4. (DONE) Add 4++ that has alternative perfects -īvī/-iī.
-- 5. (DONE) If sup but no perf, allow passive perfect forms.
-- 6. (DONE) Remove no-actv-perf.
-- 7. (DONE) Support plural prefix/suffix and plural passive prefix/suffix
-- 8. Delegate title line generation to ].
-- 9. Automatic forms of slots which depend on the values of other slots should take into account overrides on the other slot:
--		futr_actv_ptc: determines futr_actv_inf and perp_actv_inf
--		perf_pasv_ptc: determines perf_pasv_indc, plup_pasv_indc, futp_pasv_indc, perf_pasv_inf and futp_pasv_inf (with changes if deponent/semideponent etc)
--		acc_sup: determines futr_pasv_inf (and futr_actv_inf if there's no futr_pasv_ptc)
-- 10. Handle "revertor"-type: deponent in the present, but not in the perfect.

-- If enabled, compare this module with new version of module to make
-- sure all conjugations are the same.
local export = {}

local test_new_la_verb_module = false

local m_la_headword = require("Module:la-headword")
local m_la_nominal = require("Module:la-nominal")
local m_la_utilities = require("Module:la-utilities")
local m_links = require("Module:links")
local m_table = require("Module:table")
local m_utilities = require("Module:utilities")

local lang = require("Module:languages").getByCode("la")
local sc = require("Module:scripts").getByCode("Latn")
local allowed_subtypes = mw.loadData("Module:la-verb/data").allowed_subtypes

local PAGENAME = mw.loadData("Module:headword/data").pagename
local NAMESPACE = mw.title.getCurrentTitle().namespace
if NAMESPACE == 118 then -- Reconstruction
	PAGENAME = "*" .. PAGENAME
end

-- Conjugations are the functions that do the actual
-- conjugating by creating the forms of a basic verb.
-- They are defined further down.
local make_conjugation = {}

local add_form = m_la_utilities.add_form
local concat = table.concat
local do_generate_noun_forms = m_la_nominal.do_generate_noun_forms
local do_generate_adj_forms = m_la_nominal.do_generate_adj_forms
local dump = mw.dumpObject
local flatten = m_table.flatten
local form_contains = m_la_utilities.form_contains
local form_is_empty = m_la_utilities.form_is_empty
local forms_equal = m_la_utilities.forms_equal
local full_link = m_links.full_link
local insert = table.insert
local insert_if_not = m_table.insertIfNot
local ipairs = ipairs
local pairs = pairs
local pattern_escape = require("Module:string utilities").pattern_escape
local remove = table.remove
local remove_forms = m_la_utilities.remove_forms
local remove_links = m_links.remove_links
local split = mw.text.split
local table_len = m_table.length
local toNFC = mw.ustring.toNFC
local toNFD = mw.ustring.toNFD
local tostring = tostring
local type = type
local u = mw.ustring.char
local ugsub = mw.ustring.gsub
local umatch = mw.ustring.match
local unpack = unpack or table.unpack -- Lua 5.2 compatibility
local usub = mw.ustring.sub

local MACRON = u(0x304)
local BREVE = u(0x306)
local TREMA = u(0x308)
local LENGTH = MACRON .. "?" .. BREVE .. "?" .. TREMA .. "?"
local vowel = ""

local generic_slots = {}
local non_generic_slots = {}
local potential_lemma_slots = {
	"1s_pres_actv_indc", -- regular
	"3s_pres_actv_indc", -- impersonal
	"1s_perf_actv_indc", -- "coepī"
	"3s_perf_actv_indc", -- doesn't occur?
	"2s_pres_actv_impr", -- "cedo"
}

local persons = {"1s", "2s", "3s", "1p", "2p", "3p"}
local persons_2 = {"2s", "2p"}
local persons_23 = {"2s", "3s", "2p", "3p"}

local function handle_person(t, v, mood)
	local non_pers_slot = t .. "_" .. v .. "_" .. mood
	insert(generic_slots, non_pers_slot)
	for _, p in ipairs(persons) do
		insert(non_generic_slots, p .. "_" .. non_pers_slot)
	end
end

local voices = {"actv", "pasv"}
local indc_tenses = {"pres", "impf", "futr", "perf", "plup", "futp", "sigf"}
local subj_tenses = {"pres", "impf", "perf", "plup", "siga"}
local impr_tenses = {"pres", "futr"}
local nonfin_tenses = {"pres", "futr", "perf", "futp", "perp"}

for _, v in ipairs(voices) do
	for _, t in ipairs(indc_tenses) do
		handle_person(t, v, "indc")
	end
	for _, t in ipairs(subj_tenses) do
		handle_person(t, v, "subj")
	end
	for _, t in ipairs(impr_tenses) do
		handle_person(t, v, "impr")
	end
	for _, f in ipairs{"inf", "ptc"} do
		for _, t in ipairs(nonfin_tenses) do
			insert(non_generic_slots, t .. "_" .. v .. "_" .. f)
		end
	end
end

insert(non_generic_slots, "sigm_actv_inf")

local ger_cases = {"gen", "dat", "acc", "abl"}

for _, c in ipairs(ger_cases) do
	insert(non_generic_slots, c .. "_ger")
end

local sup_cases = {"acc", "abl"}

for _, c in ipairs(sup_cases) do
	insert(non_generic_slots, c .. "_sup")
end

local function track(page)
	require("Module:debug").track("la-verb/" .. page)
end

-- Constant version of :find()
local function cfind(str, text)
	return str:find(text, nil, true)
end

-- Used for testing differences between production and sandbox module; see `test_new_la_verb_module` arm in show().
-- Do not delete!
local function concat_vals(val)
	if type(val) == "table" then
		return table.concat(val, ",")
	else
		return val
	end
end

local function make_link(page, display, face, accel)
	return full_link({term = page, alt = display, lang = lang, sc = sc, accel = accel}, face)
end

-- Iterate over all the "slots" associated with a verb declension, where a slot
-- is e.g. 1s_pres_actv_indc (a non-generic slot), pres_actv_indc (a generic slot),
-- or linked_1s_pres_actv_indc (a linked slot). Only include the generic and/or linked
-- slots if called for.
local function iter_slots(include_generic, include_linked)
	-- stage == 1: non-generic slots
	-- stage == 2: generic slots
	-- stage == 3: linked slots
	local stage = 1
	local slotnum = 0
	local max_slotnum = table_len(non_generic_slots)
	return function()
		slotnum = slotnum + 1
		if slotnum > max_slotnum then
			slotnum = 1
			stage = stage + 1
			if stage == 2 then
				if include_generic then
					max_slotnum = table_len(generic_slots)
				else
					stage = stage + 1
				end
			end
			if stage == 3 then
				if include_linked then
					max_slotnum = table_len(potential_lemma_slots)
				else
					stage = stage + 1
				end
			end
			if stage > 3 then
				return nil
			end
		end
		if stage == 1 then
			return non_generic_slots
		elseif stage == 2 then
			return generic_slots
		end
		return "linked_" .. potential_lemma_slots
	end
end

local function ine(val)
	if val == "" then
		return nil
	end
	return val
end

-- Construct a simple one- or two-part link, which will be put through full_link later.
local function make_raw_link(page, display)
	if page and display then
		return "]"
	elseif page then
		return "]"
	end
	return display
end

local function split_prefix_and_base(lemma, main_verbs)
	lemma = toNFD(lemma)
	for _, main in ipairs(main_verbs) do
		local orig_main = main
		main = toNFD(main)
		local main_pattern = pattern_escape(main)
		local prefix = umatch(lemma, "^(.*)" .. main_pattern .. "$")
		-- If `main` starts with a vowel and there's no macron or trema, try it with a trema iff it's preceded by a vowel.
		if (
			not prefix and
			main:match("^" .. vowel) and
			not umatch(main, "^.")
		) then
			main_pattern = pattern_escape((main:gsub("^.", "%0" .. TREMA)))
			prefix = umatch(lemma, "^(.*" .. vowel .. LENGTH .. ")" .. main_pattern .. "$")
		end
		if prefix then
			return toNFC(prefix), orig_main
		end
	end
	require("Module:collation").sort(main_verbs, lang)
	error(("Conjugation %s may only be used with %s, or a derived verb that ends with one of them"):format(
		dump("irreg"), m_table.serialCommaJoin(main_verbs, {conj = "or", dontTag = true, dump = true})
	))
end

-- Given an ending (or possibly a full regex matching the entire lemma, if
-- a regex group is present), return the base minus the ending, or nil if
-- the ending doesn't match.
local function extract_base(lemma, ending)
	if cfind(ending, "(") then
		return umatch(lemma, ending)
	end
	return umatch(lemma, "^(.*)" .. ending .. "$")
end

local function check_spec(lemma, spec, specified_subtypes)
	local ending, subtypes, incompatible = unpack(spec)
	if incompatible then
		for _, subtype in ipairs(incompatible) do
			if specified_subtypes then
				return
			end
		end
	end
	for _, subtype in ipairs(subtypes) do
		-- A cancelled subtype will have the value false.
		if specified_subtypes == false then
			return
		end
	end
	local base = extract_base(lemma, ending)
	if base then
		return base, subtypes
	end
end

-- Given ENDINGS_AND_SUBTYPES (a list of pairs of endings with associated
-- subtypes, where each pair consists of a single ending spec and a list of
-- subtypes), check each ending in turn against LEMMA. If it matches, return
-- the pair BASE, SUBTYPES where BASE is the remainder of LEMMA minus the
-- ending, and SUBTYPES is the subtypes associated with the ending. If no
-- endings match, throw an error if DECLTYPE is non-nil, mentioning the
-- DECLTYPE (the user-specified declension); but if DECLTYPE is nil, just
-- return the pair nil, nil.
--
-- The ending spec in ENDINGS_AND_SUBTYPES is one of the following:
--
-- 1. A simple string, e.g. "ātur", specifying an ending.
-- 2. A regex that should match the entire lemma (it should be anchored at
--    the beginning with ^ and at the end with $), and contains a single
--    capturing group to match the base.
local function get_subtype_by_ending(lemma, conj, specified_subtypes, specs)
	for _, spec in ipairs(specs) do
		local base, subtypes = check_spec(lemma, spec, specified_subtypes)
		if base then
			return base, subtypes
		end
	end
	if conj then
		error("Unrecognized ending for conjugation " .. conj .. " verb: " .. lemma)
	end
	return nil, nil
end

local genders = {"m", "f", "n", "mp", "fp", "np"}

local irreg_verbs = {
	"aiō",
	"aiiō",
	"aijō",
	"ajō",
	"cedo", -- final "o" not "ō"
	"dīcō",
	"dūcō",
	"edō", -- FIXME: Will praedō cause problems?
	"dō", -- list after edō
	"eō",
	"faciō",
	"ferō",
	"fīō",
	"inquam",
	"mālō",
	"nōlō",
	"possum",
	"sum",
	"volō",
}

local irreg_verbs_conj = {
	 = "3rd-io",
	 = "3rd-io",
	 = "3rd-io",
	 = "3rd-io",
	 = "irreg",
	 = "3rd",
	 = "1st",
	 = "3rd",
	 = "3rd",
	 = "irreg",
	 = "3rd-io",
	 = "3rd",
	 = "3rd",
	 = "irreg",
	 = "irreg",
	 = "irreg",
	 = "irreg",
	 = "irreg",
	 = "irreg",
}

local function detect_decl_and_subtypes(args)
	local subtypes, specs = {}, split(args or "", "%.")
	local conj_arg = remove(specs, 1)
	for _, spec in ipairs(specs) do
		subtypes = spec:sub(1, 1) ~= "-"
	end

	local orig_lemma = toNFC(args or PAGENAME)
	local lemma = remove_links(orig_lemma)
	local base, conj, irreg, detected_subtypes
	local base_conj_arg, auto_perf_supine = conj_arg:match("^()(%+%+?)$")
	if base_conj_arg then
		if auto_perf_supine == "++" and base_conj_arg ~= "4" then
			error("Conjugation types 1++ and 2++ not allowed")
		end
		conj_arg = base_conj_arg
	end
	if orig_lemma:sub(1, 1) == "-" then
		subtypes.suffix = true
	end
	local auto_perf, auto_supine, auto_sigm
	if subtypes.sigmpasv then
		subtypes.sigm = true
	end
	if subtypes.sigm then
		auto_sigm = true
	end

	if conj_arg == "1" then
		conj = "1st"
		base, detected_subtypes = get_subtype_by_ending(lemma, "1", subtypes, {
			{"ō", {}},
			{"or", {"depon"}},
			{"at", {"impers"}},
			{"ātur", {"depon", "impers"}},
			{"ī", {"nopres"}},
			{"it", {"nopres", "impers"}},
			{"ā", {"imponly"}},
			{"āre", {"depon", "imponly"}},
		})
		if auto_perf_supine then
			if subtypes.nopres or subtypes.perfaspres or detected_subtypes.nopres then
				auto_perf = base
			else
				auto_perf = base .. "āv"
				auto_supine = base .. "āt"
			end
		end
		if auto_sigm then
			auto_sigm = base .. "āss"
		end
		if subtypes.suffix then
			subtypes.p3inf = true
		end
	elseif conj_arg == "2" then
		conj = "2nd"
		base, detected_subtypes = get_subtype_by_ending(lemma, "2", subtypes, {
			{"eō", {}},
			{"eor", {"depon"}},
			{"et", {"impers"}},
			{"ētur", {"depon", "impers"}},
			{"ī", {"nopres"}},
			{"it", {"nopres", "impers"}},
			{"ē", {"imponly"}},
			{"ēre", {"depon", "imponly"}},
		})
		if auto_perf_supine then
			if subtypes.nopres or subtypes.perfaspres or detected_subtypes.nopres then
				auto_perf = base
			else
				auto_perf = base .. "u"
				auto_supine = base .. "it"
			end
		end
		if auto_sigm then
			auto_sigm = base .. "ēss"
		end
	elseif conj_arg == "3" then
		base, detected_subtypes = get_subtype_by_ending(lemma, nil, subtypes, {
			{"iō", {"i"}},
			{"ior", {"depon", "i"}},
		})
		if base then
			conj = "3rd-io"
		else
			base, detected_subtypes = get_subtype_by_ending(lemma, "3", subtypes, {
				{"ō", {}},
				{"or", {"depon"}},
				{"it", {"impers"}, {"nopres"}},
				{"itur", {"depon", "impers"}},
				{"ī", {"nopres"}},
				{"it", {"nopres", "impers"}},
				{"e", {"imponly"}, {"depon"}},
				{"ere", {"depon", "imponly"}},
			})
			if subtypes.i then
				conj = "3rd-io"
			else
				conj = "3rd"
			end
		end
		if subtypes.nopres or subtypes.perfaspres or detected_subtypes.nopres then
			auto_perf = base
		end
		if subtypes.suffix then
			auto_perf = "-"
			auto_supine = "-"
			auto_sigm = "-"
		end
	elseif conj_arg == "4" then
		conj = "4th"
		base, detected_subtypes = get_subtype_by_ending(lemma, "4", subtypes, {
			{"iō", {}},
			{"ior", {"depon"}},
			{"it", {"impers"}, {"nopres"}},
			{"ītur", {"depon", "impers"}},
			{"ī", {"nopres"}, {"imponly"}},
			{"it", {"nopres", "impers"}},
			{"ī", {"imponly"}},
			{"īre", {"depon", "imponly"}},
		})
		if subtypes.nopres or subtypes.perfaspres or detected_subtypes.nopres then
			auto_perf = base
		elseif auto_perf_supine == "++" then
			auto_perf = base .. "īv/" .. base .. "i"
			auto_supine = base .. "īt"
		elseif auto_perf_supine == "+" then
			auto_perf = base .. "īv"
			auto_supine = base .. "īt"
		end
		if auto_sigm then
			auto_sigm = base .. "īss"
		end
	elseif conj_arg == "irreg" then
		irreg = true
		local prefix
		prefix, base = split_prefix_and_base(lemma, irreg_verbs)
		conj = irreg_verbs_conj
		args = base
		args = prefix
		-- args and args are used by ferō and sum and stay where they are
		detected_subtypes = {}
	else
		error("Unrecognized conjugation: " .. dump(conj_arg))
	end

	for _, detected_subtype in ipairs(detected_subtypes) do
		if detected_subtype == "impers" and subtypes then
			-- 3only overrides impers
		elseif detected_subtype == "nopres" and subtypes.perfaspres then
			-- perfaspres overrides nopres
		else
			subtypes = true
		end
	end
	
	-- Get the gender of the verb's complement.
	local gender
	for _, g in ipairs(genders) do
		if subtypes then
			if gender then
				error(("Only one of the subtypes %s may be specified"):format(
					m_table.serialCommaJoin(genders, {conj = "or", dontTag = true, dump = true})
				))
			end
			gender = g
		end
	end
	if not gender then
		gender = (subtypes.impers or subtypes.passimpers or subtypes or subtypes) and "n" or "m"
	end
	
	if subtypes.nopres or subtypes.perfaspres then
		subtypes.noger = true
	end

	if not irreg then
		args = base
		local perf_stem, supine_stem
		if subtypes.depon or subtypes.semidepon or subtypes.nopres or subtypes.perfaspres then
			supine_stem = args or auto_supine
			if supine_stem == "-" and not subtypes.suffix then
				supine_stem = nil
			end
			if not supine_stem then
				if subtypes.depon or subtypes.semidepon then
					subtypes.noperf = true
				end
				subtypes.nosup = true
			end
			if subtypes.sigm then
				local sigm_stem = args or auto_sigm
				if sigm_stem == "-" and not subtypes.suffix then
					sigm_stem = nil
				end
				args = sigm_stem
			end
			args = supine_stem
			args = nil
		else
			perf_stem = args or auto_perf
			if perf_stem == "-" and not subtypes.suffix then
				perf_stem = nil
			end
			if not perf_stem then
				subtypes.noperf = true
			end
			supine_stem = args or auto_supine
			if supine_stem == "-" and not subtypes.suffix then
				supine_stem = nil
			end
			if not supine_stem then
				subtypes.nosup = true
			end
			if subtypes.sigm then
				local sigm_stem = args or auto_sigm
				if sigm_stem == "-" and not subtypes.suffix then
					sigm_stem = nil
				end
				args = sigm_stem
			end
			args = perf_stem
			args = supine_stem
		end
		args = nil
	end
	
	if auto_sigm and subtypes.depon then
		subtypes.sigmpasv = true
	end

	for subtype in pairs(subtypes) do
		if (
			subtype == "i" and conj_arg ~= "3" or
			not allowed_subtypes
		) then
			error(("Unrecognized verb subtype %s for this conjugation"):format(dump(subtype)))
		end
	end

	return conj, irreg, subtypes, orig_lemma, lemma, gender
end

local function _add_forms(forms, keytype, persons, stem, ...)
	for i, p in ipairs(persons) do
		local suf = select(i, ...)
		if suf ~= nil then
			add_form(forms, p .. "_" .. keytype, stem, suf)
		end
	end
end

-- Add a value to all persons/numbers of a given tense/voice/mood, e.g.
-- "pres_actv_indc" (specified by KEYTYPE). If a value is already present
-- in a key, it won't be added again.
--
-- The value for a given person/number combination is formed by concatenating
-- STEM and the appropriate suffix for that person/number, e.g. SUF1S. The
-- suffix can be a list, in which case STEM will be concatenated in turn to
-- each value in the list and all the resulting forms added to the key. To
-- not add a value for a specific person/number, specify nil or {} for the
-- suffix for the person/number.
local function add_forms(forms, keytype, stem, ...)
	return _add_forms(forms, keytype, persons, stem, ...)
end

-- Add a value to the 2nd person (singular and plural) of a given
-- tense/voice/mood. This works like add_forms().
local function add_2_forms(forms, keytype, stem, ...)
	return _add_forms(forms, keytype, persons_2, stem, ...)
end

-- Add a value to the 2nd and 3rd persons (singular and plural) of a given
-- tense/voice/mood. This works like add_forms().
local function add_23_forms(forms, keytype, stem, ...)
	return _add_forms(forms, keytype, persons_23, stem, ...)
end

local function override(args, data)
	local forms = data.forms
	for slot in iter_slots(true, false) do
		if args then
			forms = split(args, "/")
		end
	end
end

local function set_linked_forms(data, typeinfo)
	-- Generate linked variants of slots that may be the lemma.
	-- If the form is the same as the lemma (with links removed),
	-- substitute the original lemma (with links included).
	local forms = data.forms
	for _, slot in ipairs(potential_lemma_slots) do
		local formval = forms
		local linked_forms = {}
		if formval then
			if type(formval) ~= "table" then
				formval = {formval}
			end
			for _, form in ipairs(formval) do
				if form == typeinfo.lemma then
					insert(linked_forms, typeinfo.orig_lemma)
				else
					insert(linked_forms, form)
				end
			end
		end
		forms = linked_forms
	end
end

local function link_words_if_unlinked(affix)
	return affix:match("%]") and affix or ugsub(affix, "%w+", "]")
end

local function get_affix(data, key)
	local affix = data or ""
	return link_words_if_unlinked(affix), remove_links(affix)
end

-- Add prefixes and suffixes to non-generic slots. The generic slots (e.g.
-- perf_pasv_indc, whose text indicates to use the past passive participle +
-- the present active indicative of ]), handle prefixes and suffixes
-- themselves in make_perfect_passive().
local function add_prefix_suffix(data)
	if not data.prefix and not data.suffix then
		return
	end
	
	local active_prefix, active_prefix_no_links = get_affix(data, "prefix")
	local passive_prefix, passive_prefix_no_links = get_affix(data, "passive_prefix")
	local plural_prefix, plural_prefix_no_links = get_affix(data, "plural_prefix")
	local plural_passive_prefix, plural_passive_prefix_no_links = get_affix(data, "plural_passive_prefix")

	local active_suffix, active_suffix_no_links = get_affix(data, "suffix")
	local passive_suffix, passive_suffix_no_links = get_affix(data, "passive_suffix")
	local plural_suffix, plural_suffix_no_links = get_affix(data, "plural_suffix")
	local plural_passive_suffix, plural_passive_suffix_no_links = get_affix(data, "plural_passive_suffix")

	local forms = data.forms
	for slot in iter_slots(false, true) do
		local prefix, suffix, prefix_no_links, suffix_no_links
		if cfind(slot, "pasv") and slot:match("p") then
			prefix = plural_passive_prefix
			suffix = plural_passive_suffix
			prefix_no_links = plural_passive_prefix_no_links
			suffix_no_links = plural_passive_suffix_no_links
		elseif cfind(slot, "pasv") and not cfind(slot, "_inf") then
			prefix = passive_prefix
			suffix = passive_suffix
			prefix_no_links = passive_prefix_no_links
			suffix_no_links = passive_suffix_no_links
		elseif slot:match("p") then
			prefix = plural_prefix
			suffix = plural_suffix
			prefix_no_links = plural_prefix_no_links
			suffix_no_links = plural_suffix_no_links
		else
			prefix = active_prefix
			suffix = active_suffix
			prefix_no_links = active_prefix_no_links
			suffix_no_links = active_suffix_no_links
		end
		local formval = forms
		if not form_is_empty(formval) then
			local affixed_forms = {}
			if type(formval) ~= "table" then
				formval = {formval}
			end
			for _, form in ipairs(formval) do
				if form_is_empty(form) then
					insert(affixed_forms, form)
				elseif slot:match("^linked") then
					-- If we're dealing with a linked slot, include the original
					-- links in the prefix/suffix and also add a link around the
					-- form itself if links aren't already present. (Note, above
					-- we early-exited if there was no prefix and no suffix.)
					if not form:match("%]") then
						form = "]"
					end
					insert(affixed_forms, prefix .. form .. suffix)
				elseif form:match("%]") then
					-- If not dealing with a linked slot, but there are links
					-- in the slot, include the original, potentially linked
					-- versions of the prefix and suffix (e.g. in perfect
					-- passive forms).
					insert(affixed_forms, prefix .. form .. suffix)
				else
					-- Otherwise, use the non-linking versions of the prefix and
					-- suffix so that the whole term (including prefix/suffix)
					-- gets linked.
					insert(affixed_forms, prefix_no_links .. form .. suffix_no_links)
				end
			end
			forms = affixed_forms
		end
	end
end

local function notes_override(args, data)
	local notes = {args, args, args}

	for n, note in pairs(notes) do
		if note == "-" then
			data.footnotes = nil
		elseif note == "p3inf" then
			data.footnotes = "The present passive infinitive in ''-ier'' is a rare poetic form which is attested."
		elseif note == "poetsyncperf" then
			data.footnotes = "At least one rare poetic syncopated perfect form is attested."
		elseif note == "sigm" then
			data.footnotes = "At least one use of the Old Latin \"sigmatic future\" and \"sigmatic aorist\" tenses is attested, which are used by ] writers; most notably ] and ]. The sigmatic future is generally ascribed a future or future perfect meaning, while the sigmatic aorist expresses a possible desire (\"might want to\")."
		elseif note == "sigmpasv" then
			data.footnotes = "At least one use of the Old Latin \"sigmatic future\" and \"sigmatic aorist\" tenses is attested, which are used by ] writers; most notably ] and ]. The sigmatic future is generally ascribed a future or future perfect meaning, while the sigmatic aorist expresses a possible desire (\"might want to\"). It is also attested as having a rare sigmatic future passive indicative form (\"will have been\"), which is not attested in the plural for any verb."
		elseif note == "sigmdepon" then
			data.footnotes = "At least one use of the Old Latin \"sigmatic future\" tense is attested, which is used by ] writers; most notably ] and ]. The sigmatic future is generally ascribed a future or future perfect meaning, and, as the verb is deponent, takes the form of what would otherwise be the rare sigmatic future passive indicative tense (which is not attested in the plural for any verb)."
		elseif note then
			data.footnotes = note
		end
	end

	if args == "-" then
		data.footnotes = {}
	end
end

-- Space added by default; hyphens unchanged; underscore represents no space.
local affix_joining_chars = {
	 = " ",
	 = "-",
	 = "",
}

local function normalize_prefix(prefix)
	return prefix and prefix:gsub("?$", affix_joining_chars, 1)
end

local function normalize_suffix(suffix)
	return suffix and suffix:gsub("^?", affix_joining_chars, 1)
end

local function handle_subtype_affix(args, affix, subtype_affix, normalize_func)
	if args then
		if not args then
			error(("Can't specify %s= without %s="):format(subtype_affix, affix))
		end
		return normalize_func(args)
	end
end

local function handle_affix(args, data, affix, normalize_func)
	data = normalize_func(args)
	for _, subtype in ipairs{"passive", "plural"} do
		local subtype_affix = subtype .. "_" .. affix
		data = handle_subtype_affix(args, affix, subtype_affix, normalize_func) or data
	end
	-- First fall back to the passive (e.g. "poenās dare", where the plural noun
	-- is used with both singular and plural verbs, but there's a separate
	-- passive form ''poenae datur''), then to the plural prefix, then to the
	-- base prefix.
	local subtype_affix = "plural_passive_" .. affix
	data = handle_subtype_affix(args, affix, subtype_affix, normalize_func) or
		data or data or data
end

function export.make_data(parent_args, from_headword, def1, def2)
	local params = {
		 = {required = true, default = def1 or "1+"},
		 = {required = true, default = def2 or "amō"},
		 = true,
		 = true,
		 = true,
		prefix = true,
		passive_prefix = true,
		plural_prefix = true,
		plural_passive_prefix = true,
		suffix = true,
		passive_suffix = true,
		plural_suffix = true,
		plural_passive_suffix = true,
		label = true,
		note1= true,
		note2= true,
		note3= true,
		notes= true,
		-- examined directly in export.show()
		search = true,
		json = {type = "boolean"},
	}
	for slot in iter_slots(true, false) do
		params = true
	end

	if from_headword then
		local list = {list = true}
		params.lemma = list
		params.id = true
		params.cat = list
	end

	local args = require("Module:parameters").process(parent_args, params)
	local conj, irreg, subtypes, orig_lemma, lemma, gender = detect_decl_and_subtypes(args)

	-- note: the addition of red superscripted footnotes ('<sup class="roa-red-superscript">' ... </sup>) is only implemented for the three form printing loops in which it is used
	local data = {
		forms = {},
		title = {},
		categories = args.cat and m_table.deepCopy(args.cat) or {},
		form_footnote_indices = {},
		footnotes = {},
		id = args.id,
		overriding_lemma = args.lemma,
		gender = gender,
	}

	local typeinfo = {
		lemma = lemma,
		orig_lemma = orig_lemma,
		conj = conj,
		irreg = irreg,
		subtypes = subtypes,
	}
	
	handle_affix(args, data, "prefix", normalize_prefix)
	handle_affix(args, data, "suffix", normalize_suffix)

	-- Generate the verb forms
	make_conjugation(args, data, typeinfo)

	-- Generate the title text
	m_la_headword.verb_title(data.title, typeinfo, export.get_lemma_forms(data))

	-- Override with user-set forms
	override(args, data)

	-- Set linked_* forms
	set_linked_forms(data, typeinfo)

	-- Prepend any prefixes, append any suffixes
	add_prefix_suffix(data)

	if args then
		insert_if_not(data.title, args)
	end

	notes_override(args, data)

	-- If json=1 specified, return a JSON string; used by bots
	if args.json then
		return require("Module:JSON").toJSON {
			data = data,
			typeinfo = typeinfo
		}
	end
	return data, typeinfo
end

local ptc_lemma_form = {
	 = "sg_m",
	 = "sg_f",
	 = "sg_n",
	 = "pl_m",
	 = "pl_f",
	 = "pl_n",
}

-- Make the gerund and gerundive/future passive participle. For the forms
-- labeled "gerund", we generate both gerund and gerundive variants if there's
-- a case-specific prefix or suffix for the case in question; otherwise we
-- generate only the gerund per se. BASE is the stem (ending in -nd).
-- UND_VARIANT, if true, means that a gerundive in -und should be generated
-- along with a gerundive in -end.
local function make_gerund(data, typeinfo, base, und_variant)
	local subtypes = typeinfo.subtypes
	if subtypes.noger then
		return
	end
	
	local forms, gender = data.forms, data.gender
	
	local slot = "nom_" .. ptc_lemma_form
	add_form(forms, "futr_pasv_ptc", "", do_generate_adj_forms({base .. "us<1&2+>"}, "participles", "bare").forms)

	if und_variant and not (subtypes.nound or base:match("end$") or umatch(base, "" .. LENGTH .. "end$")) then
		-- Per Lane's grammar section 899: "Verbs in -ere and -īre often have
		-- -undus, when not preceded by u or v, especially in formal style"
		-- There is also an optional exclusion if -undus is not attested
		add_form(forms, "futr_pasv_ptc", "", do_generate_adj_forms({base:gsub("end$", "und") .. "us<1&2+>"}, "participles", "bare").forms)
	end
	
	local ger_forms = do_generate_noun_forms({base .. "um<2>"}, "nouns", "bare").forms
	
	for _, case in ipairs(ger_cases) do
		for _, form in ipairs(ger_forms) do
			add_form(forms, case .. "_ger", "", form)
		end
	end
end

-- Conjugation functions

local function get_regular_stems(args, typeinfo)
	local subtypes = typeinfo.subtypes
	-- Get the parameters
	if subtypes.nopres or subtypes.perfaspres then
		typeinfo.pres_stem = nil
		typeinfo.perf_stem = ine(args)
		typeinfo.supine_stem = ine(args)
	elseif subtypes.depon or subtypes.semidepon then
		-- Deponent and semi-deponent verbs don't have the perfective principal part.
		-- But optionally semi-deponent verbs do.
		typeinfo.pres_stem = ine(args)
		typeinfo.perf_stem = nil
		typeinfo.supine_stem = ine(args)
	else
		typeinfo.pres_stem = ine(args)
		typeinfo.perf_stem = ine(args)
		typeinfo.supine_stem = ine(args)
	end
	if subtypes.sigm then
		typeinfo.sigm_stem = ine(args)
	end

	-- Prepare stems
	if not (typeinfo.pres_stem or subtypes.nopres or subtypes.perfaspres) then -- Template:
		error("Present stem has not been provided")
	end

	if typeinfo.perf_stem then
		typeinfo.perf_stem = split(typeinfo.perf_stem, "/")
	else
		typeinfo.perf_stem = {}
	end

	if typeinfo.supine_stem then
		typeinfo.supine_stem = split(typeinfo.supine_stem, "/")
	else
		typeinfo.supine_stem = {}
	end

	if typeinfo.sigm_stem then
		typeinfo.sigm_stem = split(typeinfo.sigm_stem, "/")
	else
		typeinfo.sigm_stem = {}
	end
end

local function set_conj_categories(ord, data, typeinfo)
	local categories, subtypes = data.categories, typeinfo.subtypes
	
	insert_if_not(categories, "Latin " .. ord .. " conjugation verbs")
	
	if subtypes.depon then
		insert_if_not(categories, "Latin " .. ord .. " conjugation deponent verbs")
	end
	
	if subtypes.noperf then
		insert_if_not(categories, "Latin " .. ord .. " conjugation verbs with missing perfect stem")
	end
	
	if subtypes.nosup then
		insert_if_not(categories, "Latin " .. ord .. " conjugation verbs with missing supine stem")
	elseif subtypes.supfutractvonly then
		insert_if_not(categories, "Latin " .. ord .. " conjugation verbs with missing supine stem except in the future active participle‎ ")
	end
	
	local pres_stem = typeinfo.pres_stem
	if not pres_stem then
		return
	end
	pres_stem = pres_stem and pres_stem:gsub("u", "\1")
	for _, perf_stem in ipairs(typeinfo.perf_stem) do
		perf_stem = perf_stem:gsub("u", "\1")
		if perf_stem == pres_stem .. "āv" then
			insert_if_not(categories, "Latin " .. ord .. " conjugation verbs with perfect in -āv-")
		elseif perf_stem == pres_stem .. "ēv" then
			insert_if_not(categories, "Latin " .. ord .. " conjugation verbs with perfect in -ēv-")
		elseif perf_stem == pres_stem .. "īv" then
			insert_if_not(categories, "Latin " .. ord .. " conjugation verbs with perfect in -īv-")
		elseif perf_stem == pres_stem .. "i" then
			insert_if_not(categories, "Latin " .. ord .. " conjugation verbs with perfect in -i-")
		elseif perf_stem == pres_stem .. "u" then
			insert_if_not(categories, "Latin " .. ord .. " conjugation verbs with perfect in -u-")
		elseif perf_stem == pres_stem then
			insert_if_not(categories, "Latin " .. ord .. " conjugation verbs with suffixless perfect")
		elseif perf_stem:match("$") then
			insert_if_not(categories, "Latin " .. ord .. " conjugation verbs with perfect in -s- or -x-")
		else
			insert_if_not(categories, "Latin " .. ord .. " conjugation verbs with irregular perfect")
		end
	end
end

-- Form-generating functions
local make_pres = {}

make_pres = function(data, typeinfo, pres_stem)
	local forms = data.forms
	local a = typeinfo.subtypes.shorta and "a" or "ā"
	-- Active imperfective indicative
	add_forms(forms, "pres_actv_indc", pres_stem, "ō", "ās", "at", a .. "mus", a .. "tis", "ant")
	add_forms(forms, "impf_actv_indc", pres_stem, a .. "bam", a .. "bās", a .. "bat", a .. "bāmus", a .. "bātis", a .. "bant")
	add_forms(forms, "futr_actv_indc", pres_stem, a .. "bō", a .. "bis", a .. "bit", a .. "bimus", a .. "bitis", a .. "bunt")

	-- Passive imperfective indicative
	add_forms(forms, "pres_pasv_indc", pres_stem, "or", {a .. "ris", a .. "re"}, a .. "tur", a .. "mur", a .. "minī", "antur")
	add_forms(forms, "impf_pasv_indc", pres_stem, a .. "bar", {a .. "bāris", a .. "bāre"}, a .. "bātur", a .. "bāmur", a .. "bāminī", a .. "bantur")
	add_forms(forms, "futr_pasv_indc", pres_stem, a .. "bor", {a .. "beris", a .. "bere"}, a .. "bitur", a .. "bimur", a .. "biminī", a .. "buntur")

	-- Active imperfective subjunctive
	add_forms(forms, "pres_actv_subj", pres_stem, "em", "ēs", "et", "ēmus", "ētis", "ent")
	add_forms(forms, "impf_actv_subj", pres_stem, a .. "rem", a .. "rēs", a .. "ret", a .. "rēmus", a .. "rētis", a .. "rent")

	-- Passive imperfective subjunctive
	add_forms(forms, "pres_pasv_subj", pres_stem, "er", {"ēris", "ēre"}, "ētur", "ēmur", "ēminī", "entur")
	add_forms(forms, "impf_pasv_subj", pres_stem, a .. "rer", {a .. "rēris", a .. "rēre"}, a .. "rētur", a .. "rēmur", a .. "rēminī", a .. "rentur")

	-- Imperative
	add_2_forms(forms, "pres_actv_impr", pres_stem, "ā", a .. "te")
	add_23_forms(forms, "futr_actv_impr", pres_stem, a .. "tō", a .. "tō", a .. "tōte", "antō")

	add_2_forms(forms, "pres_pasv_impr", pres_stem, a .. "re", a .. "minī")
	add_23_forms(forms, "futr_pasv_impr", pres_stem, a .. "tor", a .. "tor", nil, "antor")

	-- Present infinitives
	forms = pres_stem .. a .. "re"
	forms = pres_stem .. a .. "rī"

	-- Imperfective participles
	forms = pres_stem .. "āns"

	-- Gerund
	make_gerund(data, typeinfo, pres_stem .. "and")
end

make_pres = function(data, typeinfo, pres_stem)
	local forms = data.forms
	-- Active imperfective indicative
	add_forms(forms, "pres_actv_indc", pres_stem, "eō", "ēs", "et", "ēmus", "ētis", "ent")
	add_forms(forms, "impf_actv_indc", pres_stem, "ēbam", "ēbās", "ēbat", "ēbāmus", "ēbātis", "ēbant")
	add_forms(forms, "futr_actv_indc", pres_stem, "ēbō", "ēbis", "ēbit", "ēbimus", "ēbitis", "ēbunt")

	-- Active imperfective subjunctive
	add_forms(forms, "pres_actv_subj", pres_stem, "eam", "eās", "eat", "eāmus", "eātis", "eant")
	add_forms(forms, "impf_actv_subj", pres_stem, "ērem", "ērēs", "ēret", "ērēmus", "ērētis", "ērent")

	-- Active imperative
	add_2_forms(forms, "pres_actv_impr", pres_stem, "ē", "ēte")
	add_23_forms(forms, "futr_actv_impr", pres_stem, "ētō", "ētō", "ētōte", "entō")

	-- Passive imperfective indicative
	add_forms(forms, "pres_pasv_indc", pres_stem, "eor", {"ēris", "ēre"}, "ētur", "ēmur", "ēminī", "entur")
	add_forms(forms, "impf_pasv_indc", pres_stem, "ēbar", {"ēbāris", "ēbāre"}, "ēbātur", "ēbāmur", "ēbāminī", "ēbantur")
	add_forms(forms, "futr_pasv_indc", pres_stem, "ēbor", {"ēberis", "ēbere"}, "ēbitur", "ēbimur", "ēbiminī", "ēbuntur")

	-- Passive imperfective subjunctive
	add_forms(forms, "pres_pasv_subj", pres_stem, "ear", {"eāris", "eāre"}, "eātur", "eāmur", "eāminī", "eantur")
	add_forms(forms, "impf_pasv_subj", pres_stem, "ērer", {"ērēris", "ērēre"}, "ērētur", "ērēmur", "ērēminī", "ērentur")

	-- Passive imperative
	add_2_forms(forms, "pres_pasv_impr", pres_stem, "ēre", "ēminī")
	add_23_forms(forms, "futr_pasv_impr", pres_stem, "ētor", "ētor", nil, "entor")

	-- Present infinitives
	forms = pres_stem .. "ēre"
	forms = pres_stem .. "ērī"

	-- Imperfective participles
	forms = pres_stem .. "ēns"

	-- Gerund
	make_gerund(data, typeinfo, pres_stem .. "end")
end

make_pres = function(data, typeinfo, pres_stem)
	local forms = data.forms
	-- Active imperfective indicative
	add_forms(forms, "pres_actv_indc", pres_stem, "ō", "is", "it", "imus", "itis", "unt")
	add_forms(forms, "impf_actv_indc", pres_stem, "ēbam", "ēbās", "ēbat", "ēbāmus", "ēbātis", "ēbant")
	add_forms(forms, "futr_actv_indc", pres_stem, "am", "ēs", "et", "ēmus", "ētis", "ent")

	-- Passive imperfective indicative
	add_forms(forms, "pres_pasv_indc", pres_stem, "or", {"eris", "ere"}, "itur", "imur", "iminī", "untur")
	add_forms(forms, "impf_pasv_indc", pres_stem, "ēbar", {"ēbāris", "ēbāre"}, "ēbātur", "ēbāmur", "ēbāminī", "ēbantur")
	add_forms(forms, "futr_pasv_indc", pres_stem, "ar", {"ēris", "ēre"}, "ētur", "ēmur", "ēminī", "entur")

	-- Active imperfective subjunctive
	add_forms(forms, "pres_actv_subj", pres_stem, "am", "ās", "at", "āmus", "ātis", "ant")
	add_forms(forms, "impf_actv_subj", pres_stem, "erem", "erēs", "eret", "erēmus", "erētis", "erent")

	-- Passive imperfective subjunctive
	add_forms(forms, "pres_pasv_subj", pres_stem, "ar", {"āris", "āre"}, "ātur", "āmur", "āminī", "antur")
	add_forms(forms, "impf_pasv_subj", pres_stem, "erer", {"erēris", "erēre"}, "erētur", "erēmur", "erēminī", "erentur")

	-- Imperative
	add_2_forms(forms, "pres_actv_impr", pres_stem, "e", "ite")
	add_23_forms(forms, "futr_actv_impr", pres_stem, "itō", "itō", "itōte", "untō")

	add_2_forms(forms, "pres_pasv_impr", pres_stem, "ere", "iminī")
	add_23_forms(forms, "futr_pasv_impr", pres_stem, "itor", "itor", nil, "untor")

	-- Present infinitives
	forms = pres_stem .. "ere"
	forms = pres_stem .. "ī"

	-- Imperfective participles
	forms = pres_stem .. "ēns"

	-- Gerund
	make_gerund(data, typeinfo, pres_stem .. "end", "und-variant")
end

make_pres = function(data, typeinfo, pres_stem)
	local forms = data.forms
	-- Active imperfective indicative
	add_forms(forms, "pres_actv_indc", pres_stem, "iō", "is", "it", "imus", "itis", "iunt")
	add_forms(forms, "impf_actv_indc", pres_stem, "iēbam", "iēbās", "iēbat", "iēbāmus", "iēbātis", "iēbant")
	add_forms(forms, "futr_actv_indc", pres_stem, "iam", "iēs", "iet", "iēmus", "iētis", "ient")

	-- Active imperfective subjunctive
	add_forms(forms, "pres_actv_subj", pres_stem, "iam", "iās", "iat", "iāmus", "iātis", "iant")
	add_forms(forms, "impf_actv_subj", pres_stem, "erem", "erēs", "eret", "erēmus", "erētis", "erent")

	-- Active imperative
	add_2_forms(forms, "pres_actv_impr", pres_stem, "e", "ite")
	add_23_forms(forms, "futr_actv_impr", pres_stem, "itō", "itō", "itōte", "iuntō")

	-- Passive imperfective indicative
	add_forms(forms, "pres_pasv_indc", pres_stem, "ior", {"eris", "ere"}, "itur", "imur", "iminī", "iuntur")
	add_forms(forms, "impf_pasv_indc", pres_stem, "iēbar", {"iēbāris", "iēbāre"}, "iēbātur", "iēbāmur", "iēbāminī", "iēbantur")
	add_forms(forms, "futr_pasv_indc", pres_stem, "iar", {"iēris", "iēre"}, "iētur", "iēmur", "iēminī", "ientur")

	-- Passive imperfective subjunctive
	add_forms(forms, "pres_pasv_subj", pres_stem, "iar", {"iāris", "iāre"}, "iātur", "iāmur", "iāminī", "iantur")
	add_forms(forms, "impf_pasv_subj", pres_stem, "erer", {"erēris", "erēre"}, "erētur", "erēmur", "erēminī", "erentur")

	-- Passive imperative
	add_2_forms(forms, "pres_pasv_impr", pres_stem, "ere", "iminī")
	add_23_forms(forms, "futr_pasv_impr", pres_stem, "itor", "itor", nil, "iuntor")

	-- Present infinitives
	forms = pres_stem .. "ere"
	forms = pres_stem .. "ī"

	-- Imperfective participles
	forms = pres_stem .. "iēns"

	-- Gerund
	make_gerund(data, typeinfo, pres_stem .. "iend", "und-variant")
end

make_pres = function(data, typeinfo, pres_stem)
	local forms = data.forms
	-- Active imperfective indicative
	add_forms(forms, "pres_actv_indc", pres_stem, "iō", "īs", "it", "īmus", "ītis", "iunt")
	add_forms(forms, "impf_actv_indc", pres_stem, "iēbam", "iēbās", "iēbat", "iēbāmus", "iēbātis", "iēbant")
	add_forms(forms, "futr_actv_indc", pres_stem, "iam", "iēs", "iet", "iēmus", "iētis", "ient")

	-- Passive imperfective indicative
	add_forms(forms, "pres_pasv_indc", pres_stem, "ior", {"īris", "īre"}, "ītur", "īmur", "īminī", "iuntur")
	add_forms(forms, "impf_pasv_indc", pres_stem, "iēbar", {"iēbāris", "iēbāre"}, "iēbātur", "iēbāmur", "iēbāminī", "iēbantur")
	add_forms(forms, "futr_pasv_indc", pres_stem, "iar", {"iēris", "iēre"}, "iētur", "iēmur", "iēminī", "ientur")

	-- Active imperfective subjunctive
	add_forms(forms, "pres_actv_subj", pres_stem, "iam", "iās", "iat", "iāmus", "iātis", "iant")
	add_forms(forms, "impf_actv_subj", pres_stem, "īrem", "īrēs", "īret", "īrēmus", "īrētis", "īrent")

	-- Passive imperfective subjunctive
	add_forms(forms, "pres_pasv_subj", pres_stem, "iar", {"iāris", "iāre"}, "iātur", "iāmur", "iāminī", "iantur")
	add_forms(forms, "impf_pasv_subj", pres_stem, "īrer", {"īrēris", "īrēre"}, "īrētur", "īrēmur", "īrēminī", "īrentur")

	-- Imperative
	add_2_forms(forms, "pres_actv_impr", pres_stem, "ī", "īte")
	add_23_forms(forms, "futr_actv_impr", pres_stem, "ītō", "ītō", "ītōte", "iuntō")

	add_2_forms(forms, "pres_pasv_impr", pres_stem, "īre", "īminī")
	add_23_forms(forms, "futr_pasv_impr", pres_stem, "ītor", "ītor", nil, "iuntor")

	-- Present infinitives
	forms = pres_stem .. "īre"
	forms = pres_stem .. "īrī"

	-- Imperfective participles
	forms = pres_stem .. "iēns"

	-- Gerund
	make_gerund(data, typeinfo, pres_stem .. "iend", "und-variant")
end

local make_pres_mt = {}

function make_pres_mt:__call(data, typeinfo, pres_stem)
	if not pres_stem then
		return
	end
	return self(data, typeinfo, pres_stem)
end

setmetatable(make_pres, make_pres_mt)

local function make_perf(data, perf_stem, no_inf)
	if not perf_stem then
		return
	end
	local forms = data.forms
	
	if type(perf_stem) ~= "table" then
		perf_stem = {perf_stem}
	end

	for _, stem in ipairs(perf_stem) do
		-- Perfective indicative
		add_forms(forms, "perf_actv_indc", stem, "ī", "istī", "it", "imus", "istis", {"ērunt", "ēre"})
		add_forms(forms, "plup_actv_indc", stem, "eram", "erās", "erat", "erāmus", "erātis", "erant")
		add_forms(forms, "futp_actv_indc", stem, "erō", "eris", "erit", "erimus", "eritis", "erint")
		-- Perfective subjunctive
		add_forms(forms, "perf_actv_subj", stem, "erim", "erīs", "erit", "erīmus", "erītis", "erint")
		add_forms(forms, "plup_actv_subj", stem, "issem", "issēs", "isset", "issēmus", "issētis", "issent")

		-- Perfect infinitive
		if not no_inf then
			add_form(forms, "perf_actv_inf", stem, "isse")
		end
	end
end

local function make_true_supine(data, typeinfo, supine_stem)
	local subtypes = typeinfo.subtypes
	if subtypes.nosup or subtypes.supfutractvonly or not supine_stem then
		return
	end
	local forms = data.forms

	if type(supine_stem) ~= "table" then
		supine_stem = {supine_stem}
	end
	
	for _, stem in ipairs(supine_stem) do
		local supine_forms = do_generate_noun_forms({stem .. "us<4>"}, "nouns", "bare").forms
		add_form(forms, "acc_sup", "", supine_forms)
		add_form(forms, "abl_sup", "", supine_forms)
		add_form(forms, "futr_pasv_inf", forms, " īrī")
		if subtypes.nofutractvptc then
			add_form(forms, "futr_actv_inf", forms, " īre")
		end
	end
end

local function make_perf_pasv_ptc(data, typeinfo, supine_stem)
	local subtypes = typeinfo.subtypes
	if subtypes.nosup or subtypes.supfutractvonly or not supine_stem then
		return
	end
	local forms, gender = data.forms
	if subtypes.depon or subtypes.semidepon or subtypes.optsemidepon then
		gender = "m" -- perf_pasv_ptc becomes perf_actv_ptc, so the gender of the complement isn't relevant
	else
		gender = data.gender
	end

	if type(supine_stem) ~= "table" then
		supine_stem = {supine_stem}
	end
	
	for _, stem in ipairs(supine_stem) do
		-- Perfect passive participle
		local perf_pasv_ptc_forms = do_generate_adj_forms({stem .. "us<1&2+>"}, "participles", "bare").forms

		local form = perf_pasv_ptc_forms]
		add_form(forms, "perf_pasv_ptc", "", form)

		add_forms(forms, "perf_pasv_indc", form, " sum", " es", " est", nil, nil, nil)
		add_forms(forms, "plup_pasv_indc", form, " eram", " erās", " erat", nil, nil, nil)
		add_forms(forms, "futp_pasv_indc", form, " erō", " eris", " erit", nil, nil, nil)
		add_forms(forms, "perf_pasv_subj", form, " sim", " sīs", " sit", nil, nil, nil)
		add_forms(forms, "plup_pasv_subj", form, " essem", " essēs", " esset", nil, nil, nil)

		form = perf_pasv_ptc_forms:gsub("sg", "pl")]
		add_forms(forms, "perf_pasv_indc", form, nil, nil, nil, " sumus", " estis", " sunt")
		add_forms(forms, "plup_pasv_indc", form, nil, nil, nil, " erāmus", " erātis", " erant")
		add_forms(forms, "futp_pasv_indc", form, nil, nil, nil, " erimus", " eritis", " erint")
		add_forms(forms, "perf_pasv_subj", form, nil, nil, nil, " sīmus", " sītis", " sint")
		add_forms(forms, "plup_pasv_subj", form, nil, nil, nil, " essēmus", " essētis", " essent")

		form = perf_pasv_ptc_forms]
		add_form(forms, "perf_pasv_inf", form, " esse")
		add_form(forms, "futp_pasv_inf", form, " fore")
	end
end

-- A final "u" on the supine stem is omitted (e.g. "mortu" to "moritūrus").
local function make_futr_actv_ptc_stem(supine_stem)
	local futr_actv_ptc_stem = {}
	for _, stem in ipairs(supine_stem) do
		insert(futr_actv_ptc_stem, (stem:gsub("u?$", "")))
	end
	return futr_actv_ptc_stem
end

local function make_futr_actv_ptc(data, typeinfo, futr_actv_ptc_stem)
	local subtypes = typeinfo.subtypes
	if subtypes.nosup or subtypes.nofutractvptc or not futr_actv_ptc_stem then
		return
	end
	local forms = data.forms

	if type(futr_actv_ptc_stem) ~= "table" then
		futr_actv_ptc_stem = {futr_actv_ptc_stem}
	end

	for _, stem in ipairs(futr_actv_ptc_stem) do
		local futr_actv_ptc_forms = do_generate_adj_forms({stem .. "ūrus<1&2+>"}, "participles", "bare").forms
		add_form(forms, "futr_actv_ptc", "", futr_actv_ptc_forms)

		local form = futr_actv_ptc_forms
		add_form(forms, "futr_actv_inf", form, " esse")
		add_form(forms, "perp_actv_inf", form, " fuisse")
	end
end

local function make_supine(data, typeinfo, supine_stem, futr_actv_ptc_stem)
	if typeinfo.subtypes.nosup or not supine_stem then
		return
	end
	make_true_supine(data, typeinfo, supine_stem)
	make_perf_pasv_ptc(data, typeinfo, supine_stem)

	if type(supine_stem) ~= "table" then
		supine_stem = {supine_stem}
	end

	if futr_actv_ptc_stem == nil then
		futr_actv_ptc_stem = make_futr_actv_ptc_stem(supine_stem)
	end

	make_futr_actv_ptc(data, typeinfo, futr_actv_ptc_stem)
end

local function make_sigm(data, typeinfo, sigm_stem)
	local subtypes = typeinfo.subtypes
	if not subtypes.sigm then
		return
	end
	local forms = data.forms
	if type(sigm_stem) ~= "table" then
		sigm_stem = {sigm_stem}
	end
	local noteindex = table_len(data.footnotes) + 1
	-- Deponent verbs use the passive form
	for _, stem in ipairs(sigm_stem) do
		-- Sigmatic future active indicative
		add_forms(forms, "sigf_actv_indc", stem, "ō", "is", "it", "imus", "itis", "int")
		-- Sigmatic future passive indicative
		if subtypes.sigmpasv then
			add_forms(forms, "sigf_pasv_indc", stem, "or", "eris", "itur", nil, nil, nil) -- Plurals not attested.
		end
		-- Sigmatic future active subjunctive
		add_forms(forms, "siga_actv_subj", stem, "im", "īs", "īt", "īmus", "ītis", "int")
		-- Perfect infinitive
		if not no_inf then
			add_form(forms, "sigm_actv_inf", stem, "ere")
		end
	end
	data.form_footnote_indices = noteindex
	if subtypes.depon then
		data.footnotes = 'At least one use of the Old Latin \"sigmatic future\" tense is attested, which is used by ] writers; most notably ] and ]. The sigmatic future is generally ascribed a future or future perfect meaning, and, as the verb is deponent, takes the form of what would otherwise be the rare sigmatic future passive indicative tense (which is not attested in the plural for any verb).'
	else
		data.footnotes = 'At least one use of the Old Latin \"sigmatic future\" and \"sigmatic aorist\" tenses is attested, which are used by ] writers; most notably ] and ]. The sigmatic future is generally ascribed a future or future perfect meaning, while the sigmatic aorist expresses a possible desire (\"might want to\").'
		if subtypes.sigmpasv then
			data.footnotes = data.footnotes .. ' It is also attested as having a rare sigmatic future passive indicative form (\"will have been\"), which is not attested in the plural for any verb.'
		end
	end
end

make_conjugation = function(args, data, typeinfo)
	set_conj_categories("first", data, typeinfo)

	local pres_stem = typeinfo.pres_stem
	make_pres(data, typeinfo, pres_stem)
	make_perf(data, typeinfo.perf_stem)
	make_supine(data, typeinfo, typeinfo.supine_stem)
	make_sigm(data, typeinfo, typeinfo.sigm_stem)

	-- Additional forms in specific cases.
	local forms = data.forms
	if pres_stem == "dīlapid" then
		add_form(forms, "3p_sigf_actv_indc", "", "dīlapidāssunt", 2)
	elseif pres_stem == "invol" then
		add_form(forms, "3s_sigf_actv_indc", "", "involāsit", 2)
	elseif pres_stem == "viol" then
		local noteindex = table_len(data.footnotes) + 1
		add_form(forms, "3p_futp_actv_indc", "", "violārint", 2)
		add_form(forms, "3p_perf_actv_subj", "", "violārint", 2)
		add_form(forms, "3s_sigf_actv_indc", "", "violāsit", 2)
		data.form_footnote_indices = tostring(noteindex)
		data.form_footnote_indices = tostring(noteindex)
		data.footnotes = "Old Latin."
	end
end

make_conjugation = function(args, data, typeinfo)
	set_conj_categories("second", data, typeinfo)

	local pres_stem = typeinfo.pres_stem
	make_pres(data, typeinfo, pres_stem)
	make_perf(data, typeinfo.perf_stem)
	make_supine(data, typeinfo, typeinfo.supine_stem)
	make_sigm(data, typeinfo, typeinfo.sigm_stem)

	-- Additional forms in specific cases.
	if pres_stem == "noc" then
		add_form(data.forms, "3s_siga_actv_subj", "", "noxsīt", 2)
	end
end

make_conjugation = function(args, data, typeinfo)
	set_conj_categories("third", data, typeinfo)

	local pres_stem = typeinfo.pres_stem
	make_pres(data, typeinfo, pres_stem)
	make_perf(data, typeinfo.perf_stem)
	make_supine(data, typeinfo, typeinfo.supine_stem)
	make_sigm(data, typeinfo, typeinfo.sigm_stem)

	-- Additional forms in specific cases.
	if not pres_stem then
		return
	elseif toNFD(pres_stem):match(vowel .. MACRON .. "sc$") then
		insert_if_not(data.categories, "Latin inchoative verbs")
	end
	if pres_stem:match("nōsc$") then
		typeinfo.subtypes.poetsyncperf = true
	elseif pres_stem == "ulcīsc" then
		local forms = data.forms
		local formval = forms
		if type(formval) ~= "table" then
			formval = {formval}
		end
		forms = formval
		local noteindex = table_len(data.footnotes) + 1
		for i, form in ipairs(formval) do
			if form == "ulsō" then
				add_form(forms, "1s_sigf_actv_indc", "ull", "ō", i + 1)
				data.form_footnote_indices = tostring(noteindex)
				data.footnotes = 'The form \"ullō\" may have resulted from a later, erroneous misreading of \"ulsō\".'
				break
			end
		end
	end
end

make_conjugation = function(args, data, typeinfo)
	set_conj_categories("third", data, typeinfo)

	local supine_stem, futr_actv_ptc_stem = typeinfo.supine_stem
	if supine_stem then
		local subtypes = typeinfo.subtypes
		futr_actv_ptc_stem = make_futr_actv_ptc_stem(supine_stem)
		for i, stem in ipairs(futr_actv_ptc_stem) do
			-- TODO: generalise this as a subtype which is automatic for 3rd-io verbs with -rt in the supine
			local unsync_stem = stem:gsub("rt$", "rit")
			futr_actv_ptc_stem = unsync_stem
			if unsync_stem ~= stem then
				subtypes.syncfutractvptc = true
			end
		end
		typeinfo.futr_actv_ptc_stem = futr_actv_ptc_stem
	end

	make_pres(data, typeinfo, typeinfo.pres_stem)
	make_perf(data, typeinfo.perf_stem)
	make_supine(data, typeinfo, supine_stem, futr_actv_ptc_stem)
	make_sigm(data, typeinfo, typeinfo.sigm_stem)
end

local function ivi_ive(form)
	form = form:gsub("īvī", "iī")
	form = form:gsub("īvi", "ī")
	form = form:gsub("īve", "ī")
	form = form:gsub("īvē", "ē")
	return form
end

make_conjugation = function(args, data, typeinfo)
	set_conj_categories("fourth", data, typeinfo)

	local pres_stem = typeinfo.pres_stem
	make_pres(data, typeinfo, pres_stem)
	make_perf(data, typeinfo.perf_stem)
	make_supine(data, typeinfo, typeinfo.supine_stem)
	make_sigm(data, typeinfo, typeinfo.sigm_stem)

	local forms = data.forms
	if form_contains(forms, "serviō") or form_contains(forms, "saeviō") then
		add_forms(forms, "impf_actv_indc", pres_stem,
			{"iēbam", "ībam"},
			{"iēbās", "ībās"},
			{"iēbat", "ībat"},
			{"iēbāmus", "ībāmus"},
			{"iēbātis", "ībātis"},
			{"iēbant", "ībant"}
		)

		add_forms(forms, "futr_actv_indc", pres_stem,
			{"iam", "ībō"},
			{"iēs", "ībis"},
			{"iet", "ībit"},
			{"iēmus", "ībimus"},
			{"iētis", "ībitis"},
			{"ient", "ībunt"}
		)
	end
	
	local subtypes = typeinfo.subtypes
	if not (subtypes.alwayssyncperf or subtypes.optsyncperf) then
		return
	end
	for key, formval in pairs(forms) do
		if cfind(key, "perf") or cfind(key, "plup") or cfind(key, "futp") then
			if type(formval) ~= "table" then
				formval = {formval}
			end
			forms = {}
			for _, f in ipairs(formval) do
				if subtypes.optsyncperf then
					insert_if_not(forms, f)
				end
				insert_if_not(forms, ivi_ive(f))
			end
		end
	end
end

-- Irregular conjugations.
local irreg_conjugations = {}

make_conjugation = function(args, data, typeinfo)
	local verb = ine(args)
	local prefix = ine(args)

	if verb == nil then
		error("The verb to be conjugated has not been specified.")
	elseif not irreg_conjugations then
		error(("Internal error: the irregular verb %s does not have a conjugation function specified."):format(dump(verb)))
	end

	typeinfo.prefix = prefix

	-- Generate the verb forms
	irreg_conjugations(args, data, typeinfo)
end

for _, verb in ipairs{"aiō", "aiiō", "aijō", "ajō"} do
	local stem = usub(verb, 1, -2)
	local i_stem = stem:gsub("j$", "i")
	
	irreg_conjugations = function(args, data, typeinfo)
		local categories, forms, subtypes = data.categories, data.forms, typeinfo.subtypes
		
		insert_if_not(categories, "Latin third conjugation verbs")
		insert_if_not(categories, "Latin irregular verbs")
		insert_if_not(categories, "Latin active-only verbs")
		insert_if_not(categories, "Latin defective verbs")

		subtypes.highlydef = true
		subtypes.nopass = true
		subtypes.nosup = true

		local prefix = typeinfo.prefix or ""
		forms = prefix .. stem .. "ō"
		forms = prefix .. i_stem .. "s"
		forms = prefix .. i_stem .. "t"
		forms = prefix .. stem .. "unt"

		forms = prefix .. stem .. "ēbam"
		forms = prefix .. stem .. "ēbās"
		forms = prefix .. stem .. "ēbat"
		forms = prefix .. stem .. "ēbāmus"
		forms = prefix .. stem .. "ēbātis"
		forms = prefix .. stem .. "ēbant"

		forms = prefix .. i_stem .. "stī"
		forms = prefix .. i_stem .. "t"
		forms = prefix .. stem .. "ērunt"

		forms = prefix .. stem .. "ās"
		forms = prefix .. stem .. "at"
		forms = prefix .. stem .. "ant"

		forms = prefix .. i_stem

		forms = prefix .. stem .. "ere"
		forms = prefix .. stem .. "ēns"
	end
end

irreg_conjugations = function(args, data, typeinfo)
	local categories, forms, subtypes = data.categories, data.forms, typeinfo.subtypes

	insert_if_not(categories, "Latin irregular verbs")
	insert_if_not(categories, "Latin defective verbs")

	subtypes.imponly = true
	subtypes.nofut = true
	subtypes.nopass = true
	subtypes.noperf = true
	subtypes.nosup = true

	local prefix = typeinfo.prefix or ""
	forms = prefix .. "cedo"
	forms = {prefix .. "cette", prefix .. "cedite"}
end

irreg_conjugations = function(args, data, typeinfo)
	local categories, forms = data.categories, data.forms
	
	insert_if_not(categories, "Latin third conjugation verbs")
	insert_if_not(categories, "Latin irregular verbs")

	local prefix = typeinfo.prefix or ""
	make_pres(data, typeinfo, prefix .. "dīc")
	make_perf(data, prefix .. "dīx")
	make_supine(data, typeinfo, prefix .. "dict")
	make_sigm(data, typeinfo, prefix .. "dīx")

	-- Old Latin regular imperative.
	local noteindex = table_len(data.footnotes) + 1
	add_form(forms, "2s_pres_actv_impr", prefix, "dīc", 1)
	data.form_footnote_indices = tostring(noteindex)

	-- Old Latin future forms.
	if prefix == "" then
		add_form(forms, "1s_futr_actv_indc", "", "dīcēbō", 2)
		add_form(forms, "3s_futr_actv_indc", "", "dīcēbit", 2)
		data.form_footnote_indices = tostring(noteindex)
		data.form_footnote_indices = tostring(noteindex)
	end
	data.footnotes = "Old Latin."
end

irreg_conjugations = function(args, data, typeinfo)
	local categories, forms = data.categories, data.forms
	
	insert_if_not(categories, "Latin first conjugation verbs")
	insert_if_not(categories, "Latin irregular verbs")

	local prefix = typeinfo.prefix or ""
	typeinfo.subtypes.shorta = true
	
	make_pres(data, typeinfo, prefix .. "d")
	make_perf(data, prefix .. "ded")
	make_supine(data, typeinfo, prefix .. "dat")
end

irreg_conjugations = function(args, data, typeinfo)
	local categories, forms = data.categories, data.forms
	
	insert_if_not(categories, "Latin third conjugation verbs")
	insert_if_not(categories, "Latin irregular verbs")

	local prefix = typeinfo.prefix or ""
	make_pres(data, typeinfo, prefix .. "dūc")
	make_perf(data, prefix .. "dūx")
	make_supine(data, typeinfo, prefix .. "duct")
	make_sigm(data, typeinfo, prefix .. "dūx")

	add_form(forms, "2s_pres_actv_impr", prefix, "dūc", 1)
end

irreg_conjugations = function(args, data, typeinfo)
	local categories, forms = data.categories, data.forms
	
	insert_if_not(categories, "Latin third conjugation verbs")
	insert_if_not(categories, "Latin irregular verbs")

	local prefix = typeinfo.prefix or ""
	make_pres(data, typeinfo, prefix .. "ed")
	make_perf(data, prefix .. "ēd")
	make_supine(data, typeinfo, prefix .. "ēs")

	-- Active imperfective indicative
	add_forms(forms, "pres_actv_indc", prefix, nil, "ēs", "ēst", nil, "ēstis", nil)

	-- Passive imperfective indicative
	add_form(forms, "3s_pres_pasv_indc", prefix, "ēstur")

	-- Active imperfective subjunctive
	add_forms(forms, "pres_actv_subj", prefix, "edim", "edīs", "edit", "edīmus", "edītis", "edint")
	add_forms(forms, "impf_actv_subj", prefix, "ēssem", "ēssēs", "ēsset", "ēssēmus", "ēssētis", "ēssent")

	-- Active imperative
	add_2_forms(forms, "pres_actv_impr", prefix, "ēs", "ēste")
	add_23_forms(forms, "futr_actv_impr", prefix, "ēstō", "ēstō", "ēstōte", nil)

	-- Present infinitives
	add_form(forms, "pres_actv_inf", prefix, "ēsse")
end

irreg_conjugations = function(args, data, typeinfo)
	local forms = data.forms
	
	insert_if_not(data.categories, "Latin irregular verbs")

	local prefix = typeinfo.prefix or ""
	make_perf(data, prefix .. "i")
	make_supine(data, typeinfo, prefix .. "it")

	-- Active imperfective indicative
	add_forms(forms, "pres_actv_indc", prefix, "eō", "īs", "it", "īmus", "ītis",
		prefix == "prōd" and {"eunt", "īnunt"} or "eunt")
	add_forms(forms, "impf_actv_indc", prefix, "ībam", "ībās", "ībat", "ībāmus", "ībātis", "ībant")
	add_forms(forms, "futr_actv_indc", prefix, "ībō", "ībis", "ībit", "ībimus", "ībitis", "ībunt")

	-- Active perfective indicative
	add_form(forms, "1s_perf_actv_indc", prefix, "īvī")
	forms = {prefix .. "īstī", prefix .. "īvistī"}
	add_form(forms, "3s_perf_actv_indc", prefix, "īvit")
	forms = prefix .. "īstis"

	-- Passive imperfective indicative
	add_forms(forms, "pres_pasv_indc", prefix, "eor", {"īris", "īre"}, "ītur", "īmur", "īminī", "euntur")
	add_forms(forms, "impf_pasv_indc", prefix, "ībar", {"ībāris", "ībāre"}, "ībātur", "ībāmur", "ībāminī", "ībantur")
	add_forms(forms, "futr_pasv_indc", prefix, "ībor", {"īberis", "ībere"}, "ībitur", "ībimur", "ībiminī", "ībuntur")

	-- Active imperfective subjunctive
	add_forms(forms, "pres_actv_subj", prefix, "eam", "eās", "eat", "eāmus", "eātis", "eant")
	add_forms(forms, "impf_actv_subj", prefix, "īrem", "īrēs", "īret", "īrēmus", "īrētis", "īrent")

	-- Active perfective subjunctive
	forms = prefix .. "īssem"
	forms = prefix .. "īssēs"
	forms = prefix .. "īsset"
	forms = prefix .. "īssēmus"
	forms = prefix .. "īssētis"
	forms = prefix .. "īssent"

	-- Passive imperfective subjunctive
	add_forms(forms, "pres_pasv_subj", prefix, "ear", {"eāris", "eāre"}, "eātur", "eāmur", "eāminī", "eantur")
	add_forms(forms, "impf_pasv_subj", prefix, "īrer", {"īrēris", "īrēre"}, "īrētur", "īrēmur", "īrēminī", "īrentur")
	-- Imperative
	add_2_forms(forms, "pres_actv_impr", prefix, "ī", "īte")
	add_23_forms(forms, "futr_actv_impr", prefix, "ītō", "ītō", "ītōte", "euntō")
	add_2_forms(forms, "pres_pasv_impr", prefix, "īre", "īminī")
	add_23_forms(forms, "futr_pasv_impr", prefix, "ītor", "ītor", nil, "euntor")
	-- Present infinitives
	forms = prefix .. "īre"
	forms = prefix .. "īrī"
	-- Perfect/future infinitives
	forms = prefix .. "īsse"
	-- Imperfective participles
	forms = prefix .. "iēns"
	-- Gerund
	make_gerund(data, typeinfo, prefix .. "eund")
end

local function fio(forms, prefix, voice)
	-- Active/passive imperfective indicative
	add_forms(forms, "pres_" .. voice .. "_indc", prefix,
		"fīō", "fīs", "fit", "fīmus", "fītis", "fīunt")
	add_forms(forms, "impf_" .. voice .. "_indc", prefix .. "fīēb",
		"am", "ās", "at", "āmus", "ātis", "ant")
	add_forms(forms, "futr_" .. voice .. "_indc", prefix .. "fī",
		"am", "ēs", "et", "ēmus", "ētis", "ent")

	-- Active/passive imperfective subjunctive
	add_forms(forms, "pres_" .. voice .. "_subj", prefix .. "fī",
		"am", "ās", "at", "āmus", "ātis", "ant")
	add_forms(forms, "impf_" .. voice .. "_subj", prefix .. "fier",
		"em", "ēs", "et", "ēmus", "ētis", "ent")

	-- Active/passive imperative
	add_2_forms(forms, "pres_" .. voice .. "_impr", prefix .. "fī", "", "te")
	add_23_forms(forms, "futr_" .. voice .. "_impr", prefix .. "fī", "tō", "tō", "tōte", "untō")

	-- Active/passive present infinitive
	add_form(forms, "pres_" .. voice .. "_inf", prefix, "fierī")
end

irreg_conjugations = function(args, data, typeinfo)
	local categories, forms, subtypes = data.categories, data.forms, typeinfo.subtypes
	
	insert_if_not(categories, "Latin third conjugation verbs")
	insert_if_not(categories, "Latin irregular verbs")
	insert_if_not(categories, "Latin suppletive verbs")
	
	subtypes.suppl = true

	local prefix = typeinfo.prefix or ""
	make_pres(data, typeinfo, prefix .. "fac")
	-- Remove passive forms, except the gerund and future passive participle.
	remove_forms(forms, {"pasv_indc", "pasv_subj", "pasv_impr", "pasv_inf"})
	make_perf(data, prefix .. "fēc")
	make_supine(data, typeinfo, prefix .. "fact")
	make_sigm(data, typeinfo, prefix .. "fax")

	if prefix == "" then
		local noteindex = table_len(data.footnotes) + 1
		add_form(forms, "3s_perf_actv_indc", "", "fēced")
		data.form_footnote_indices = tostring(noteindex)
		data.footnotes = "Old Latin."
		-- Active imperative
		add_form(forms, "2s_pres_actv_impr", "", "fac", 1)
		-- Sigmatic forms
		add_form(forms, "1s_sigf_actv_indc", "", {"faxsō", "facsō", "faxiō"})
		add_form(forms, "2s_sigf_actv_indc", "", {"faxsis", "facsis", "facxis", "facxsis"})
		add_form(forms, "3s_sigf_actv_indc", "", "faxsit")
		add_form(forms, "1p_sigf_actv_indc", "", "faxsimus")
		add_form(forms, "2p_sigf_actv_indc", "", "faxsitis")
		add_form(forms, "3p_sigf_actv_indc", "", "faxsint")
		add_form(forms, "1s_siga_actv_subj", "", {"faxsim", "faxēm"})
		add_form(forms, "2s_siga_actv_subj", "", {"faxsīs", "faxseis", "faxeis", "faxēs"})
		add_form(forms, "3s_siga_actv_subj", "", {"faxsīt", "faxeit", "faxēt"})
		add_form(forms, "1p_siga_actv_subj", "", {"faxsīmus", "faxeimus"})
		add_form(forms, "2p_siga_actv_subj", "", {"faxsītis", "faxeitis"})
		add_form(forms, "3p_siga_actv_subj", "", {"faxsint", "faxēnt"})
	end

	fio(forms, prefix, "pasv")
end

irreg_conjugations = function(args, data, typeinfo)
	local categories, forms, subtypes = data.categories, data.forms, typeinfo.subtypes
	
	insert_if_not(categories, "Latin third conjugation verbs")
	insert_if_not(categories, "Latin irregular verbs")
	insert_if_not(categories, "Latin suppletive verbs")
	
	subtypes.suppl = true

	local prefix_pres = typeinfo.prefix or ""
	local prefix_perf = ine(args)
	local prefix_supine = ine(args)

	prefix_perf = prefix_perf or prefix_pres
	prefix_supine = prefix_supine or prefix_pres

	make_pres(data, typeinfo, prefix_pres .. "fer")
	if prefix_perf == "" then
		make_perf(data, {"tul", "tetul"})
	else
		make_perf(data, prefix_perf .. "tul")
	end
	make_supine(data, typeinfo, prefix_supine .. "lāt")

	-- Active imperfective indicative
	forms = prefix_pres .. "fers"
	forms = prefix_pres .. "fert"
	forms = prefix_pres .. "fertis"

	-- Passive imperfective indicative
	forms = prefix_pres .. "fertur"

	-- Active imperfective subjunctive
	forms = prefix_pres .. "ferrem"
	forms = prefix_pres .. "ferrēs"
	forms = prefix_pres .. "ferret"
	forms = prefix_pres .. "ferrēmus"
	forms = prefix_pres .. "ferrētis"
	forms = prefix_pres .. "ferrent"

	-- Passive present indicative
	forms = {prefix_pres .. "ferris", prefix_pres .. "ferre"}

	-- Passive imperfective subjunctive
	forms = prefix_pres .. "ferrer"
	forms = {prefix_pres .. "ferrēris", prefix_pres .. "ferrēre"}
	forms = prefix_pres .. "ferrētur"
	forms = prefix_pres .. "ferrēmur"
	forms = prefix_pres .. "ferrēminī"
	forms = prefix_pres .. "ferrentur"

	-- Imperative
	forms = prefix_pres .. "fer"
	forms = prefix_pres .. "ferte"

	forms = prefix_pres .. "fertō"
	forms = prefix_pres .. "fertō"
	forms = prefix_pres .. "fertōte"

	forms = prefix_pres .. "ferre"

	forms = prefix_pres .. "fertor"
	forms = prefix_pres .. "fertor"

	-- Present infinitives
	forms = prefix_pres .. "ferre"
	forms = prefix_pres .. "ferrī"
	
	if prefix_perf == "" then
		local noteindex = table_len(data.footnotes) + 1
		for slot, form in pairs(forms) do
			if type(form) == "table" then
				for _, f in ipairs(form) do
					if f:match("^tetul") then
						data.form_footnote_indices = tostring(noteindex)
						data.footnotes = "Old Latin."
						break
					end
				end
			end
		end
	end
end

irreg_conjugations = function(args, data, typeinfo)
	local categories, forms, subtypes = data.categories, data.forms, typeinfo.subtypes
	
	insert_if_not(categories, "Latin third conjugation verbs")
	insert_if_not(categories, "Latin irregular verbs")
	insert_if_not(categories, "Latin suppletive verbs")

	subtypes.semidepon = true
	subtypes.nofutractvptc = true
	
	if not subtypes.nosup then
		subtypes.suppl = true
	end

	local prefix = typeinfo.prefix or ""
	fio(forms, prefix, "actv")

	make_supine(data, typeinfo, prefix .. "fact")
	forms = forms

	-- Imperfective participles
	forms = nil

	-- Gerund
	make_gerund(data, typeinfo, prefix .. "fiend", "und-variant")
end

irreg_conjugations = function(args, data, typeinfo)
	local categories, forms, subtypes = data.categories, data.forms, typeinfo.subtypes
	
	insert_if_not(categories, "Latin irregular verbs")
	insert_if_not(categories, "Latin defective verbs")

	subtypes.highlydef = true

	forms = "inquam"
	forms = "inquis"
	forms = "inquit"
	forms = "inquimus"
	forms = "inquitis"
	forms = "inquiunt"

	forms = "inquiēs"
	forms = "inquiet"
	forms = "inquient"

	forms = "inquiēbat"

	forms = "inquiī"
	forms = "inquistī"
	forms = "inquit"

	forms = "inquiās"
	forms = "inquiat"
	forms = "inquiant"

	forms = "inque"
	forms = "inquitō"
	forms = "inquitō"

	forms = "inquiēns"

	local noteindex = table_len(data.footnotes) + 1
	data.footnotes = "Medieval Latin."
	noteindex = tostring(noteindex)
	data.form_footnote_indices = noteindex
	data.form_footnote_indices = noteindex
	data.form_footnote_indices = noteindex
end

-- Handle most forms of volō, mālō, nōlō.
local function volo_malo_nolo(forms, indc_stem, subj_stem, subtypes)
	-- Present active indicative needs to be done individually as each
	-- verb is different.
	add_forms(forms, "impf_actv_indc", indc_stem .. "ēb", "am", "ās", "at", "āmus", "ātis", "ant")
	add_forms(forms, "futr_actv_indc", indc_stem, "am", "ēs", "et", "ēmus", "ētis", "ent")

	-- Active imperfective subjunctive
	add_forms(forms, "pres_actv_subj", subj_stem, "im", "īs", "it", "īmus", "ītis", "int")
	add_forms(forms, "impf_actv_subj", subj_stem .. "l", "em", "ēs", "et", "ēmus", "ētis", "ent")

	-- Present infinitives
	forms = subj_stem .. "le"

	-- Imperfective participles
	forms = indc_stem .. "ēns"
	
	subtypes.suppl = true
end

irreg_conjugations = function(args, data, typeinfo)
	local categories, forms, subtypes = data.categories, data.forms, typeinfo.subtypes
	
	insert_if_not(categories, "Latin irregular verbs")
	insert_if_not(categories, "Latin suppletive verbs")

	subtypes.nopass = true
	subtypes.noimp = true
	make_perf(data, "mālu")

	-- Active imperfective indicative
	add_forms(forms, "pres_actv_indc", "", "mālō", "māvīs", "māvult", "mālumus", "māvultis", "mālunt")
	volo_malo_nolo(forms, "māl", "māl", subtypes)
end

irreg_conjugations = function(args, data, typeinfo)
	local categories, forms, subtypes = data.categories, data.forms, typeinfo.subtypes
	
	insert_if_not(categories, "Latin irregular verbs")
	insert_if_not(categories, "Latin suppletive verbs")

	subtypes.nopass = true
	make_perf(data, "nōlu")

	-- Active imperfective indicative
	add_forms(forms, "pres_actv_indc", "", "nōlō", "nōn vīs", "nōn vult", "nōlumus", "nōn vultis", "nōlunt")
	add_forms(forms, "impf_actv_indc", "nōlēb", "am", "ās", "at", "āmus", "ātis", "ant")
	volo_malo_nolo(forms, "nōl", "nōl", subtypes)

	-- Imperative
	add_2_forms(forms, "pres_actv_impr", "nōlī", "", "te")
	add_23_forms(forms, "futr_actv_impr", "nōl", "itō", "itō", "itōte", "untō")
	
	-- Additional Old Latin forms.
	local noteindex = table_len(data.footnotes) + 1
	data.footnotes = "Old Latin."
	noteindex = tostring(noteindex)
	
	add_forms(forms, "pres_actv_indc", "", nil, "nēvīs", {"nōn volt", "nēvolt"}, nil, {"nōn voltis", "nōltis"}, nil)
	data.form_footnote_indices = noteindex
	data.form_footnote_indices = noteindex
	data.form_footnote_indices = noteindex
end

irreg_conjugations = function(args, data, typeinfo)
	local categories, forms, subtypes = data.categories, data.forms, typeinfo.subtypes

	insert_if_not(categories, "Latin irregular verbs")
	insert_if_not(categories, "Latin suppletive verbs")

	subtypes.suppl = true
	subtypes.passimpers = true
	subtypes.passimpersold = true
	make_perf(data, "potu")

	-- Active imperfective indicative, impersonal passive
	add_forms(forms, "pres_actv_indc", "", "possum", "potes", "potest", "possumus", "potestis", "possunt")
	add_forms(forms, "impf_actv_indc", "poter", "am", "ās", "at", "āmus", "ātis", "ant")
	add_forms(forms, "futr_actv_indc", "poter", "ō", {"is", "e"}, "it", "imus", "itis", "unt")
	forms = "potestur"
	forms = "poterātur"
	forms = "poteritur"

	-- Active imperfective subjunctive, impersonal passive
	add_forms(forms, "pres_actv_subj", "poss", "im", "īs", "it", "īmus", "ītis", "int")
	add_forms(forms, "impf_actv_subj", "poss", "em", "ēs", "et", "ēmus", "ētis", "ent")
	forms = "possītur"
	forms = "possētur"

	-- Present infinitives
	forms = {"posse", "potesse"}

	-- Imperfective participles
	forms = "potēns"
	
	local noteindex = table_len(data.footnotes) + 1
	data.footnotes = "Old Latin. The passive forms are only used to govern a passive infinitive; e.g. " .. mw.getCurrentFrame():preprocess("{{uxi|la|nec retrahi '''potestur''' imperiis|t=\"nor '''can it''' be withdrawn by commands\"}}") .. "."
	noteindex = tostring(noteindex)
	for slot in pairs(data.forms) do
		if cfind(slot, "pasv") then
			data.form_footnote_indices = noteindex
		end
	end
	data.form_footnote_indices = noteindex
end

-- The vowel of the prefix is lengthened if it ends in -n and the next word begins with f- or s-.
local function shorten_prefix(prefix)
	return toNFC((ugsub(toNFD(prefix), "(" .. vowel .. ")" .. MACRON .. "n$", "%1n")))
end

local function lengthen_prefix(prefix)
	return toNFC((ugsub(toNFD(prefix), "(" .. vowel .. ")" .. LENGTH .. "n$", "%1" .. MACRON .. "n")))
end

irreg_conjugations = function(args, data, typeinfo)
	local categories, forms, subtypes = data.categories, data.forms, typeinfo.subtypes

	insert_if_not(categories, "Latin irregular verbs")
	insert_if_not(categories, "Latin suppletive verbs")

	local prefix = typeinfo.prefix or ""
	local prefix_e = ine(args) or shorten_prefix(prefix)
	local prefix_f = ine(args) or lengthen_prefix(prefix)
	local prefix_s = lengthen_prefix(prefix)

	subtypes.suppl = true
	subtypes.nopass = true
	subtypes.supfutractvonly = true
	make_perf(data, prefix_f .. "fu")
	make_supine(data, typeinfo, prefix_f .. "fut")

	-- Active imperfective indicative
	add_forms(forms, "pres_actv_indc", prefix_s, "sum", nil, nil, "sumus", nil, "sunt")
	add_forms(forms, "pres_actv_indc", prefix_e, nil, "es", "est", nil, "estis", nil)

	add_forms(forms, "impf_actv_indc", prefix_e, "eram", "erās", "erat", "erāmus", "erātis", "erant")
	add_forms(forms, "futr_actv_indc", prefix_e, "erō", {"eris", "ere"}, "erit", "erimus", "eritis", "erunt")

	-- Active imperfective subjunctive
	add_forms(forms, "pres_actv_subj", prefix_s, "sim", "sīs", "sit", "sīmus", "sītis", "sint")
	add_forms(forms, "pres_actv_subj", prefix_s, "siem", "siēs", "siet", "siēmus", "siētis", "sient")
	add_forms(forms, "pres_actv_subj", prefix_f, "fuam", "fuās", "fuat", "fuāmus", "fuātis", "fuant")
	local noteindex = table_len(data.footnotes) + 1
	for _, p in ipairs(persons) do
		data.form_footnote_indices = tostring(noteindex)
	end
	data.footnotes = "Old Latin or in poetry."
		
	if prefix_s == "ad" then
		local noteindex = table_len(data.footnotes) + 1
		add_form(forms, "3p_pres_actv_subj", "", "adessint", 2)
		data.form_footnote_indices = tostring(noteindex)
		data.footnotes = "Old Latin."
	end

	add_forms(forms, "impf_actv_subj", prefix_e, "essem", "essēs", "esset", "essēmus", "essētis", "essent")
	add_forms(forms, "impf_actv_subj", prefix_f, "forem", "forēs", "foret", "forēmus", "forētis", "forent")

	-- Imperative
	add_2_forms(forms, "pres_actv_impr", prefix_e, "es", "este")
	add_23_forms(forms, "futr_actv_impr", prefix_e, "estō", "estō", "estōte", nil)
	add_form(forms, "3p_futr_actv_impr", prefix_s, "suntō")

	-- Infinitives
	add_form(forms, "pres_actv_inf", prefix_e, "esse")
	add_form(forms, "futr_actv_inf", prefix_f, "fore")

	-- Imperfective participles
	if prefix == "ab" or prefix == "cōn" or prefix == "prae" or prefix == "præ" then
		add_form(forms, "pres_actv_ptc", prefix_s, "sēns")
--		forms = prefix_s .. "sēns"
	end
end

irreg_conjugations = function(args, data, typeinfo)
	local categories, forms, subtypes = data.categories, data.forms, typeinfo.subtypes
	
	insert_if_not(categories, "Latin irregular verbs")
	insert_if_not(categories, "Latin suppletive verbs")

	local prefix = typeinfo.prefix or ""

	subtypes.nopass = true
	subtypes.noimp = true
	subtypes.supfutractvonly = true
	make_perf(data, prefix .. "volu")
	make_supine(data, typeinfo, prefix .. "volit")

	-- Active imperfective indicative
	add_forms(forms, "pres_actv_indc", prefix, "volō", "vīs", "vult", "volumus", "vultis", "volunt")
	volo_malo_nolo(forms, prefix .. "vol", prefix .. "vel", subtypes)
	
	-- Additional Old Latin forms.
	if prefix == "" then
		local noteindex = table_len(data.footnotes) + 1
		data.footnotes = "Old Latin."
		noteindex = tostring(noteindex)
		
		add_forms(forms, "pres_actv_indc", prefix, nil, nil, "volt", nil, "voltis", nil)
		data.form_footnote_indices = noteindex
		data.form_footnote_indices = noteindex
		
		-- vol- in the present subjunctive.
		add_forms(forms, "pres_actv_subj", prefix .. "vol", "im", "īs", "it", "īmus", "ītis", "int")
		for slot in pairs(data.forms) do
			if cfind(slot, "pres_actv_subj") then
				data.form_footnote_indices = noteindex
			end
		end
	end
end

local function make_perfect_passive(data)
	local forms = data.forms
	local ppp = forms
	if type(ppp) ~= "table" then
		ppp = {ppp}
	end
	local ppplinks = {}
	for _, pppform in ipairs(ppp) do
		insert(ppplinks, make_link(pppform, nil, "term"))
	end
	local ppplink = concat(ppplinks, " or ")
	local sumlink = make_link("sum", nil, "term")
	local text_for_slot = {
		perf_pasv_indc = "present active indicative",
		futp_pasv_indc = "future active indicative",
		plup_pasv_indc = "imperfect active indicative",
		perf_pasv_subj = "present active subjunctive",
		plup_pasv_subj = "imperfect active subjunctive"
	}
	local passive_prefix, passive_suffix = data.passive_prefix, data.passive_suffix
	local prefix_joiner = passive_prefix and passive_prefix:match(" $") and "+ " or ""
	local suffix_joiner = passive_suffix and passive_suffix:match("^ ") and " +" or ""
	for slot, text in pairs(text_for_slot) do
		forms = (passive_prefix or "") .. prefix_joiner .. ppplink .. " + " ..
			text .. " of " .. sumlink .. suffix_joiner .. (passive_suffix or "")
	end
	ppp = forms
	if type(ppp) ~= "table" then
		ppp = {ppp}
	end
end

local function handle_deponent(data, subtypes)
	local forms = data.forms
	-- Generate a list of form types to remove.
	local to_remove = {"pres_pasv_ptc", "futr_pasv_inf"}
	if subtypes.depon then
		insert(to_remove, "actv")
	else
		insert(to_remove, "pres_pasv")
		insert(to_remove, "impf_pasv")
		insert(to_remove, "futr_pasv")
		if subtypes.semidepon then
			insert(to_remove, "perf_actv")
			insert(to_remove, "plup_actv")
			insert(to_remove, "futp_actv")
		end
	end
	-- Remove forms.
	for slot in pairs(forms) do
		if not (
			slot == "futr_actv_inf" or
			slot == "perp_actv_inf" or
			slot == "pres_actv_ptc" or
			slot == "futr_actv_ptc" or
			slot == "futr_pasv_ptc"
		) then
			for _, part in ipairs(to_remove) do
				if cfind(slot, part) then
					forms = nil
					break
				end
			end
		end
	end
	-- Change passive to active, except for the future passive participle.
	local changes = {}
	for slot in pairs(forms) do
		if cfind(slot, "pasv") and slot ~= "futr_pasv_ptc" then
			changes = slot:gsub("pasv", "actv")
		end
	end
	for old, new in pairs(changes) do
		add_form(forms, new, "", forms)
		forms = nil
	end
end

local function add_sync_perf(data, form, suf, note, noteindex)
	local forms = data.forms
	local formval = forms
	if type(formval) ~= "table" then
		formval = {formval}
	end
	local i = 0
	while true do
		i = i + 1
		local stem = formval
		if not stem then
			break
		end
		stem = stem:match("^(.*)" .. pattern_escape(suf) .. "$")
		-- Can only syncopate "vi", "ve", "vē", or any one of them spelled with a "u" after a vowel.
		if stem then
			local new_stem = toNFD(stem)
			local vowel, loc, v, ei, diacritic = umatch(new_stem, "(" .. vowel .. "?)" .. LENGTH .. "()()()(" .. LENGTH .. ")$")
			if loc and (v == "v" or vowel ~= "") and (ei == "e" or diacritic == "" or cfind(diacritic, BREVE)) then
				new_stem = usub(new_stem, 1, loc - 1)
				-- Can't be omitted if it's part of the first syllable.
				if new_stem:match(vowel) then
					i = i + 1
					add_form(forms, form, new_stem, suf, i)
					data.form_footnote_indices = tostring(noteindex)
					data.footnotes = note
				end
			end
		end
	end
end

local function has_no_infinitive(forms)
	for slot in pairs(forms) do
		if cfind(slot, "inf") and not form_is_empty(forms) then
			return false
		end
	end
	return true
end

local function has_no_gerund(forms)
	if not form_is_empty(forms) then
		return false
	end
	for _, case in ipairs(ger_cases) do
		if not form_is_empty(forms) then
			return false
		end
	end
	return true
end

local make_conjugation_mt = {}

function make_conjugation_mt:__call(args, data, typeinfo)
	if typeinfo.irreg then
		self.irreg(args, data, typeinfo)
	else
		get_regular_stems(args, typeinfo)
		self(args, data, typeinfo)
	end
	
	local categories, forms, subtypes = data.categories, data.forms, typeinfo.subtypes
	
	if subtypes.nopres or subtypes.perfaspres then
		insert_if_not(categories, "Latin verbs with missing present stem")
		insert_if_not(categories, "Latin defective verbs")
	end
	
	-- Maybe clear out the supine-derived forms (except maybe for the
	-- future active participle). Do this first because some code below
	-- looks at the perfect participle to derive other forms.
	if subtypes.nosup then
		-- Some verbs have no supine forms or forms derived from the supine
		insert_if_not(categories, "Latin verbs with missing supine stem")
		insert_if_not(categories, "Latin defective verbs")
		forms = nil
		forms = nil
		forms = nil
		forms = nil
		forms = nil
		forms = nil
		forms = nil
		forms = nil
		forms = nil
		if subtypes.depon or subtypes.semidepon or subtypes.optsemidepon then
			forms = nil
		end
		for _, case in ipairs(sup_cases) do
			forms = nil
		end
	elseif subtypes.nofutractvptc then
		-- Some verbs have a missing future active participle
		insert_if_not(categories, "Latin verbs with supine stem with missing future active participle")
		insert_if_not(categories, "Latin defective verbs")
		forms = nil
		forms = nil
	elseif subtypes.supfutractvonly then
		-- Some verbs have no supine forms or forms derived from the supine,
		-- except for the future active infinitive/participle
		insert_if_not(categories, "Latin verbs with missing supine stem except in the future active participle")
		insert_if_not(categories, "Latin defective verbs")
		forms = nil
		forms = nil
		forms = nil
		forms = nil
		forms = nil
		forms = nil
		if subtypes.depon or subtypes.semidepon or subtypes.optsemidepon then
			forms = nil
		end
		for _, case in ipairs(sup_cases) do
			forms = nil
		end
	end

	-- Add information for the passive perfective forms
	if not (
		form_is_empty(forms) or
		subtypes.optsemidepon or
		subtypes.impers or
		subtypes.passimpers and not (subtypes.depon or subtypes.semidepon or subtypes.optsemidepon) or
		subtypes or
		subtypes.pass3only and not (subtypes.depon or subtypes.semidepon or subtypes.optsemidepon)
	) then
		make_perfect_passive(data)
	end

	-- Perfect forms as present tense.
	if subtypes.perfaspres then
		insert_if_not(categories, "Latin verbs with perfect forms used in the present tense")
		-- Remove present active, imperfect active and future active forms and passive forms.
		-- Change perfect forms to non-perfect forms.
		local new_forms = {}
		forms = nil
		for slot, form in pairs(forms) do
			if not (
				cfind(slot, "pres") or
				cfind(slot, "impf") or
				cfind(slot, "futr") and not (cfind(slot, "inf") or cfind(slot, "ptc")) or
				slot:match("_ger$")
			) then
				-- If semi-deponent, the perfect passive forms will be converted to perfect active.
				if not (cfind(slot, "pasv") and (subtypes.semidepon or subtypes.optsemidepon)) then
					slot = slot:gsub("perf", "pres")
						:gsub("plup", "impf")
						:gsub("futp", "futr")
				end
				new_forms = form
			end
		end
		data.forms, forms = new_forms, new_forms
	-- No present stem.
	elseif subtypes.nopres then
		for slot in pairs(forms) do
			if (
				cfind(slot, "pres") or
				cfind(slot, "impf") or
				cfind(slot, "futr") and not (cfind(slot, "inf") or cfind(slot, "ptc")) or
				slot:match("_ger$")
			) then
				forms = nil
			end
		end
	end

	-- Types of irregularity related primarily to the active.
	-- These could in theory be combined with those related to the passive and imperative,
	-- i.e. there's no reason there couldn't be an impersonal deponent verb with no imperatives.

	-- Deponent verbs use passive forms with active meaning.
	if subtypes.depon then
		insert_if_not(categories, "Latin deponent verbs")
		handle_deponent(data, subtypes)
	-- Semi-deponent verbs use perfective passive forms with active meaning,
	-- and have no imperfective passive.
	elseif subtypes.semidepon then
		insert_if_not(categories, "Latin semi-deponent verbs")
		handle_deponent(data, subtypes)
	-- Optional semi-deponent verbs use perfective passive forms with active
	-- meaning, but also have perfect active forms with the same meaning,
	-- and have no imperfective passive.
	elseif subtypes.optsemidepon then
		insert_if_not(categories, "Latin semi-deponent verbs")
		insert_if_not(categories, "Latin optionally semi-deponent verbs")
		handle_deponent(data, subtypes)
	elseif subtypes.noperf then
		-- Some verbs have no perfect stem (e.g. inalbēscō, -ěre)
		insert_if_not(categories, "Latin defective verbs")
		if not subtypes.depon then
			insert_if_not(categories, "Latin verbs with missing perfect stem")
		end
		-- Remove all active perfect forms (passive perfect forms may
		-- still exist as they are formed with the supine stem)
		for slot in pairs(forms) do
			if cfind(slot, "actv") and (
				cfind(slot, "perf") or
				cfind(slot, "plup") or
				cfind(slot, "futp")
			) and slot ~= "futp_actv_inf" then
				forms = nil
			end
		end
	end

	-- Handle the removal of any passive forms, including any impersonal/3rd-
	-- person-only passives.
	
	-- Third-person only.
	if subtypes then
		insert_if_not(categories, "Latin third-person-only verbs")
		for slot in pairs(forms) do
			if slot:match("") then
				forms = nil
			end
		end
	-- Impersonal, so remove all non-third-person singular forms.
	elseif subtypes.impers then
		insert_if_not(categories, "Latin impersonal verbs")
		for slot in pairs(forms) do
			if slot:match("") or (
				-- Keep third-person plural forms if third-person passive is set.
				cfind(slot, "3p") and
				not (subtypes.pass3only and cfind(slot, "pasv"))
			) then
				forms = nil
			end
		end
	end
	
	-- No passive forms.
	if subtypes.nopass then
		insert_if_not(categories, "Latin active-only verbs")
		for slot in pairs(forms) do
			if cfind(slot, "pasv") then
				forms = nil
			end
		end
	-- Only third person in the passive. For phrasal verbs with a plural
	-- complement, also need to erase the 3s forms.
	-- FIXME: handling of mp, fp and np needs to be done separately.
	elseif subtypes.pass3only then
		insert_if_not(categories, "Latin verbs with third-person passive")
		local gender = data.gender
		for slot in pairs(forms) do
			if cfind(slot, "pasv") and (
				slot:match("") or
				(gender == "mp" or gender == "fp" or gender == "np") and cfind(slot, "3s")
			) then
				forms = nil
			end
		end
	-- Impersonal in the passive, so remove all passive non-third person forms.
	elseif subtypes.passimpers then
		insert_if_not(categories, "Latin verbs with impersonal passive")
		for slot in pairs(forms) do
			if cfind(slot, "pasv") and (
				slot:match("") or
				cfind(slot, "3p")
			) then
				forms = nil
			end
		end
	end

	-- Handle certain irregularities in the imperative
	if subtypes.noimp then
		-- Some verbs have no imperatives
		insert_if_not(categories, "Latin verbs with missing imperative")
		insert_if_not(categories, "Latin defective verbs")
		-- Remove all imperative forms
		for slot in pairs(forms) do
			if cfind(slot, "impr") then
				forms = nil
			end
		end
	end

	-- Handle certain irregularities in the future
	if subtypes.nofut then
		-- Some verbs (e.g. soleō) have no future
		insert_if_not(categories, "Latin verbs with missing future")
		insert_if_not(categories, "Latin defective verbs")
		-- Remove all future forms
		for slot in pairs(forms) do
			if cfind(slot, "fut") then -- handles futr = future and futp = future perfect
				forms = nil
			end
		end
	end

	local pres_stem = typeinfo.pres_stem
	-- Add the ancient future_passive_participle of certain verbs
	-- if pres_stem == "lāb" then
	-- 	forms = "lābundus"
	-- elseif pres_stem == "collāb" then
	-- 	forms = "collābundus"
	-- elseif pres_stem == "illāb" then
	-- 	forms = "illābundus"
	-- elseif pres_stem == "relāb" then
	-- 	forms = "relābundus"
	-- end

	-- Add the poetic present passive infinitive forms of certain verbs
	if subtypes.p3inf then
		local form = "pres_" .. (subtypes.depon and "actv" or "pasv") .. "_inf"
		local noteindex = table_len(data.footnotes) + 1
		local formval = forms
		if type(formval) ~= "table" then
			formval = {formval}
		end
		for i = 1, table_len(formval) do
			insert(formval, usub(formval, 1, -2) .. "ier")
		end
		forms = formval
		data.form_footnote_indices = tostring(noteindex)
		data.footnotes = "The present passive infinitive in ''-ier'' is a rare poetic form which is attested."
	end
	
	--[==[
	if subtypes.old2sgimp then
		local form = "2s_pres_" .. (subtypes.depon and "actv" or "pasv") .. "_impr"
		local formval = forms
		if type(formval) ~= "table" then
			formval = {formval}
		end
		local newvals = mw.clone(formval)
	end]==]
	
	-- Add the syncopated perfect forms, omitting the separately handled fourth conjugation cases.
	if subtypes.poetsyncperf then
		local noteindex, note = table_len(data.footnotes) + 1
		if pres_stem and pres_stem:match("nōsc$") then
			note = 'The verb "nōscō" and its compounds frequently drop the syllables "vi" and "ve" from their perfect, pluperfect and future perfect conjugations.'
		else
			note = "At least one rare poetic syncopated perfect form is attested."
		end
		-- infinitive
		add_sync_perf(data, "perf_actv_inf", "sse", note, noteindex)
		-- perfect actives
		add_sync_perf(data, "2s_perf_actv_indc", "stī", note, noteindex)
		add_sync_perf(data, "3s_perf_actv_indc", "t", note, noteindex)
		add_sync_perf(data, "1p_perf_actv_indc", "mus", note, noteindex)
		add_sync_perf(data, "2p_perf_actv_indc", "stis", note, noteindex)
		add_sync_perf(data, "3p_perf_actv_indc", "runt", note, noteindex)
		-- pluperfect actives
		add_sync_perf(data, "1s_plup_actv_indc", "ram", note, noteindex)
		add_sync_perf(data, "2s_plup_actv_indc", "rās", note, noteindex)
		add_sync_perf(data, "3s_plup_actv_indc", "rat", note, noteindex)
		add_sync_perf(data, "1p_plup_actv_indc", "rāmus", note, noteindex)
		add_sync_perf(data, "2p_plup_actv_indc", "rātis", note, noteindex)
		add_sync_perf(data, "3p_plup_actv_indc", "rant", note, noteindex)
		-- future perfect actives
		add_sync_perf(data, "1s_futp_actv_indc", "rō", note, noteindex)
		add_sync_perf(data, "2s_futp_actv_indc", "ris", note, noteindex)
		add_sync_perf(data, "3s_futp_actv_indc", "rit", note, noteindex)
		add_sync_perf(data, "1p_futp_actv_indc", "rimus", note, noteindex)
		add_sync_perf(data, "2p_futp_actv_indc", "ritis", note, noteindex)
		add_sync_perf(data, "3p_futp_actv_indc", "rint", note, noteindex)
		-- perfect subjunctives
		add_sync_perf(data, "1s_perf_actv_subj", "rim", note, noteindex)
		add_sync_perf(data, "2s_perf_actv_subj", "rīs", note, noteindex)
		add_sync_perf(data, "3s_perf_actv_subj", "rit", note, noteindex)
		add_sync_perf(data, "1p_perf_actv_subj", "rīmus", note, noteindex)
		add_sync_perf(data, "2p_perf_actv_subj", "rītis", note, noteindex)
		add_sync_perf(data, "3p_perf_actv_subj", "rint", note, noteindex)
		-- pluperfect subjunctives
		add_sync_perf(data, "1s_plup_actv_subj", "ssem", note, noteindex)
		add_sync_perf(data, "2s_plup_actv_subj", "ssēs", note, noteindex)
		add_sync_perf(data, "3s_plup_actv_subj", "sset", note, noteindex)
		add_sync_perf(data, "1p_plup_actv_subj", "ssēmus", note, noteindex)
		add_sync_perf(data, "2p_plup_actv_subj", "ssētis", note, noteindex)
		add_sync_perf(data, "3p_plup_actv_subj", "ssent", note, noteindex)
	end

	if subtypes.syncfutractvptc then
		-- TODO
	end

	-- Add category for sigmatic forms
	if subtypes.sigm then
		insert_if_not(categories, "Latin verbs with sigmatic forms")
		-- Add subcategory for (very rare) passive sigmatic forms
		if subtypes.sigmpasv then
			insert_if_not(categories, "Latin verbs with passive sigmatic forms")
		end
	end

	if subtypes.noinf then
		-- Some verbs (e.g. inquam) have no infinitives
		for slot in pairs(forms) do
			if cfind(slot, "inf") then
				forms = nil
			end
		end
	end
	if has_no_infinitive(forms) then
		subtypes.noinf = true
		insert_if_not(categories, "Latin verbs with missing infinitive")
		insert_if_not(categories, "Latin defective verbs")
	end

	if subtypes.noger then
		-- Some verbs (e.g. libet) have no gerund
		forms = nil
		for _, case in ipairs(ger_cases) do
			forms = nil
		end
	end
	-- Catch verbs which don't have .noger explicitly specified.
	if has_no_gerund(forms) then
		subtypes.noger = true
		insert_if_not(categories, "Latin verbs with missing gerund")
		insert_if_not(categories, "Latin defective verbs")
	end

	if subtypes.imponly then
		-- Some verbs only have imperatives
		insert_if_not(categories, "Latin imperative-only verbs")
		insert_if_not(categories, "Latin defective verbs")
		-- Remove all non-imperative forms
		for slot in pairs(forms) do
			if not cfind(slot, "impr") then
				forms = nil
			end
		end
	end
end

setmetatable(make_conjugation, make_conjugation_mt)

-- Functions for generating the inflection table

-- Convert FORM (one or more forms) to a string of links. If the form is empty
-- (see form_is_empty), the return value will be "&mdash;".
local function show_form(formval, accel)
	if not formval then
		return "&mdash;"
	elseif type(formval) ~= "table" then
		formval = {formval}
	elseif table_len(formval) == 0 then
		return "&mdash;"
	end

	for slot, form in ipairs(formval) do
		if form_is_empty(form) then
			form = "&mdash;"
		elseif form:match("%]") then
			-- Don't put accelerators on forms already containing links such as
			-- the perfect passive infinitive and future active infinitive, or
			-- the participles wrongly get tagged as infinitives as well as
			-- participles.
			form = make_link(form)
		else
			form = make_link(form, nil, nil, accel)
		end
		formval = form
	end

	return concat(formval, ",<br> ")
end

local parts_to_tags = {
	 = {'1', 's'},
	 = {'2', 's'},
	 = {'3', 's'},
	 = {'1', 'p'},
	 = {'2', 'p'},
	 = {'3', 'p'},
	 = {'act'},
	 = {'pass'},
	 = {'pres'},
	 = {'impf'},
	 = {'fut'},
	 = {'perf'},
	 = {'plup'},
	 = {'futp'},
	 = {'perf', 'potn'},
	 = {'sigm'},
	 = {'sigm', 'fut'},
	 = {'sigm', 'aor'},
	 = {'ind'},
	 = {'sub'},
	 = {'imp'},
	 = {'inf'},
	 = {'part'},
	 = {'ger'},
	 = {'sup'},
	 = {'nom'},
	 = {'gen'},
	 = {'dat'},
	 = {'acc'},
	 = {'abl'},
}

-- Call show_form() the forms in each non-generic slot (where a
-- generic slot is something like pres_actv_indc that covers a whole
-- row of slots), converting the forms to a string consisting of
-- comma-separated links with accelerators in them.
local function convert_forms_into_links(data)
	local accel_lemma, forms = data.actual_lemma, data.forms
	for slot in iter_slots(false, false) do
		local slot_parts = split(slot, "_")
		local tags = {}
		for _, part in ipairs(slot_parts) do
			for _, tag in ipairs(parts_to_tags) do
				insert(tags, tag)
			end
		end
		-- put the case first for verbal nouns
		local accel = {form = concat(tags, "|"), lemma = accel_lemma}
		forms = show_form(forms, accel)
	end
end

function export.get_valid_forms(raw_forms)
	local valid_forms = {}
	if raw_forms then
		if type(raw_forms) ~= "table" then
			raw_forms = {raw_forms}
		end
		for _, subform in ipairs(raw_forms) do
			if not form_is_empty(subform) then
				insert(valid_forms, subform)
			end
		end
	end
	return valid_forms
end

function export.get_lemma_forms(data, do_linked)
	local linked_prefix, forms = do_linked and "linked_" or "", data.forms
	for _, slot in ipairs(potential_lemma_slots) do
		local lemma_forms = export.get_valid_forms(forms)
		if table_len(lemma_forms) > 0 then
			return lemma_forms
		end
	end

	return nil
end

local function get_displayable_lemma(lemma_forms)
	if not lemma_forms then
		return "&mdash;"
	end
	local lemma_links = {}
	for _, subform in ipairs(lemma_forms) do
		insert(lemma_links, make_link(nil, subform, "term"))
	end
	return concat(lemma_links, ", ")
end

local tenses_display = {
	 = "present",
	 = "imperfect",
	 = "future",
	 = "perfect",
	 = "pluperfect",
	 = "future&nbsp;perfect",
	 = "perfect&nbsp;potential",
	 = "sigmatic&nbsp;future",
	 = "sigmatic&nbsp;aorist"
}

local voices_display = {
	 = "active",
	 = "passive",
}


local moods_display = {
	 = "indicative",
	 = "subjunctive",
	 = "imperative",
}

--[[
local nonfins = {
	 = "infinitives",
	 = "participles",
}
--]]

--[[
local verbalnouns = {
	 = "gerund",
	 = "supine",
}
--]]

--[[
local cases = {
	 = "nominative",
	 = "genitive",
	 = "dative",
	 = "accusative",
	 = "ablative",
}
--]]

local function make_row(data, prefixes, headspan, cellspan, rowslot, rowname, class, fn)
	local row = {}
	if rowname then
		insert(row, ('\n! class="%s"%s | %s%s'):format(class, headspan == 1 and "" or (' colspan="%d"'):format(headspan), rowname, fn or ""))
	end
	local has_row, forms = false, data.forms
	if not form_is_empty(forms) then
		if type(forms) == "table" then
			if table_len(forms) > 1 then
				error("Row slot " .. rowslot .. " cannot have more than one value.")
			end
			forms = forms
		end
		insert(row, '\n! colspan="12" class="roa-compound-row" |' .. forms)
		has_row = true
	else
		for _, prefix in ipairs(prefixes) do
			local slot = prefix .. (rowslot and "_" .. rowslot or "")
			insert(row, ('\n| colspan="%d" | '):format(cellspan) .. forms .. (
				data.form_footnote_indices == nil and "" or
				'<sup class="roa-red-superscript">' .. data.form_footnote_indices .. "</sup>"
			))
			if not form_is_empty(forms) then
				has_row = true
			end
		end
	end
	return has_row and concat(row) or false
end

local function make_group(data, _tenses, voice, mood, class)
	local group = {}
	for _, tense in ipairs(_tenses) do
		local fn = ""
		if tense == "sigf" or tense == "siga" then
			fn = data.form_footnote_indices
			fn = fn and ('<sup class="roa-red-superscript">' .. fn .. "</sup>") or ""
		end
		local row = make_row(data, persons, 1, 2, tense .. "_" .. voice .. "_" .. mood, tenses_display, class, fn)
		if row then
			insert(group, row)
		end
	end
	return #group > 0 and ('\n|-\n! rowspan="' .. tostring(#group) .. '" class="' .. class .. '" | ' .. voices_display .. concat(group, "\n|-")) or false
end

local function make_section(data, mood, _tenses)
	local section, name = {}, moods_display
	local class = "roa-" .. name .. "-left-rail"
	for _, voice in ipairs(voices) do
		local group = make_group(data, _tenses, voice, mood, class)
		if group then
			insert(section, group)
		end
	end
	if #section == 0 then
		return ""
	end
	return (([=[

|-
! colspan="2" rowspan="2" class="%CLASS" | %NAME
! colspan="6" class="%CLASS" | ''singular''
! colspan="6" class="%CLASS" | ''plural''
|-
! colspan="2" class="%CLASS" style="width:12.5%;" | ]
! colspan="2" class="%CLASS" style="width:12.5%;" | ]
! colspan="2" class="%CLASS" style="width:12.5%;" | ]
! colspan="2" class="%CLASS" style="width:12.5%;" | ]
! colspan="2" class="%CLASS" style="width:12.5%;" | ]
! colspan="2" class="%CLASS" style="width:12.5%;" | ]]=]):gsub("%%%u+", function(m)
		return m == "%NAME" and name or m == "%CLASS" and class or m
	end)) .. concat(section)
end

local function make_indc_rows(data)
	return make_section(data, "indc", indc_tenses)
end

local function make_subj_rows(data)
	return make_section(data, "subj", subj_tenses)
end

local function make_impr_rows(data)
	return make_section(data, "impr", impr_tenses)
end

local function get_nonfin_cells(t)
	local cells = {}
	for _, f in ipairs{"inf", "ptc"} do
		for _, v in ipairs(voices) do
			insert(cells, t .. "_" .. v .. "_" .. f)
		end
	end
	return cells
end

local function make_nonfin_rows(data)
	local nonfin = {}
	
	for _, t in ipairs(nonfin_tenses) do
		local row = make_row(data, get_nonfin_cells(t), 2, 3, nil, tenses_display, "roa-nonfinite-header")
		if row then
			insert(nonfin, row)
		end
	end
	if #nonfin == 0 then
		return ""
	end
	return [=[

|-
! colspan="2" rowspan="2" class="roa-nonfinite-header" | non-finite forms
! colspan="6" class="roa-nonfinite-header" | infinitive
! colspan="6" class="roa-nonfinite-header" | participle
|-
! colspan="3" class="roa-nonfinite-header" style="width:18.75%;" | active
! colspan="3" class="roa-nonfinite-header" style="width:18.75%;" | passive
! colspan="3" class="roa-nonfinite-header" style="width:18.75%;" | active
! colspan="3" class="roa-nonfinite-header" style="width:18.75%;" | passive
|-]=] .. concat(nonfin, "\n|-")
end

local function make_vn_rows(data)
	local vn = make_row(data, {"gen_ger", "dat_ger", "acc_ger", "abl_ger", "acc_sup", "abl_sup"}, nil, 2, nil, nil, "roa-nonfinite-header")
	if not vn then
		return ""
	end
	return [=[

|-
! colspan="2" rowspan="3" class="roa-nonfinite-header" | verbal nouns
! colspan="8" class="roa-nonfinite-header" | gerund
! colspan="4" class="roa-nonfinite-header" | supine
|-
! colspan="2" class="roa-nonfinite-header" style="width:12.5%;" | genitive
! colspan="2" class="roa-nonfinite-header" style="width:12.5%;" | dative
! colspan="2" class="roa-nonfinite-header" style="width:12.5%;" | accusative
! colspan="2" class="roa-nonfinite-header" style="width:12.5%;" | ablative
! colspan="2" class="roa-nonfinite-header" style="width:12.5%;" | accusative
! colspan="2" class="roa-nonfinite-header" style="width:12.5%;" | ablative
|-]=] .. vn
end

local function make_footnotes(data)
	local tbl, i = {}, 0
	for k, v in pairs(data.footnotes) do
		i = i + 1
		tbl = '<sup class="roa-red-superscript">' .. tostring(k) .. '</sup>' .. v .. '<br>'
	end
	return concat(tbl)
end

-- Make the table
local function make_table(data)
	data.actual_lemma = export.get_lemma_forms(data)
	convert_forms_into_links(data)
	local title = data.title
	return require("Module:TemplateStyles")("Module:roa-verb/style.css") .. [=[
<div class="NavFrame">
<div class="NavHead"> &nbsp;&nbsp;&nbsp;Conjugation of ]=] .. get_displayable_lemma(data.actual_lemma) .. (table_len(title) > 0 and " (" .. concat(title, ", ") .. ")" or "") .. [=[</div>
<div class="NavContent">
{| class="roa-inflection-table" data-toggle-category="inflection"
]=] .. make_indc_rows(data) .. make_subj_rows(data) .. make_impr_rows(data) .. make_nonfin_rows(data) .. make_vn_rows(data) .. [=[

|}</div></div>]=].. make_footnotes(data)
end

local function checkexist(data)
	if NAMESPACE ~= 0 then
		return
	end
	local outerbreak = false
	local categories = data.categories
	for _, conjugation in pairs(data.forms) do
		if conjugation then
			if type(conjugation) ~= "table" then
				conjugation = {conjugation}
			end
			for _, conj in ipairs(conjugation) do
				if not cfind(conj, " ") then
					local title = (lang:makeEntryName(conj))
					local t = mw.title.new(title)
					if t and not t.exists then
						insert_if_not(categories, "Latin verbs with red links in their inflection tables")
						outerbreak = true
						break
					end
				end
			end
		end
		if outerbreak then
			break
		end
	end
end

-- functions for creating external search hyperlinks

local function cleanup(x)
	x:gsub("]", "")
		:gsub(" ", "+")
	return toNFC((ugsub(toNFD(x), "+", "")))
end

local function flatten_values(t)
	t = flatten(t)
	local i = 1
	while true do
		local v = t
		if v == nil then
			return t
		elseif cfind(v, "<") then
			remove(t, i)
		else
			t = cleanup(v)
			i = i + 1
		end
	end
end

local function link_google_books(verb, forms, domain)
	local function partition_XS_into_N(XS, N)
		local count = 0
		local mensae = {}
		for _, v in pairs(XS) do
			if count % N == 0 then
				mensae = {}
			end
			count = count + 1
			mensae)+1] = v
		end
		return mensae
	end
	
	local function forms_N_to_link(fs, N, args, site)
		return ''
	end
	
	local function make_links_txt(fs, N, site)
		local args = site == "Books" and "tbm=bks&lr=lang_la" or ""
		local links = {}
		for k,v in pairs(partition_XS_into_N(fs, N)) do
			links = forms_N_to_link(v,k,args,site=="Books" and "" or site)
		end
		return concat(links, ' - ')
	end
	
	return "Google "..domain.." forms of "..verb.." : "..make_links_txt(forms, 30, domain)
end

-- The main new entry point.
function export.show(frame)
	local parent_args = frame:getParent().args
	local data, typeinfo = export.make_data(parent_args)
	if type(data) == "string" then -- json=1 specified
		return data
	end
	local domain = frame:getParent().args
	-- Test code to compare existing module to new one.
	if test_new_la_verb_module then
		local m_new_la_verb = require("Module:User:Benwing2/la-verb")
		local miscdata = {
			title = data.title,
			categories = data.categories,
		}
		local new_parent_args = frame:getParent().args
		local newdata = m_new_la_verb.make_data(new_parent_args)
		local newmiscdata = {
			title = newdata.title,
			categories = newdata.categories,
		}
		local all_verb_props = {"forms", "form_footnote_indices", "footnotes", "miscdata"}
		local difconj = false
		for _, prop in ipairs(all_verb_props) do
			local table = prop == "miscdata" and miscdata or data
			local newtable = prop == "miscdata" and newmiscdata or newdata
			for key, val in pairs(table) do
				local newval = newtable
				if not forms_equal(val, newval) then
					-- Uncomment this to display the particular key and
					-- differing forms.
					-- error(key .. " " .. (val and concat_vals(val) or "nil") .. " || " .. (newval and concat_vals(newval) or "nil"))
					difconj = true
					break
				end
			end
			if difconj then
				break
			end
			-- Do the comparison the other way as well in case of extra keys
			-- in the new table.
			for key, newval in pairs(newtable) do
				local val = table
				if not forms_equal(val, newval) then
					-- Uncomment this to display the particular key and
					-- differing forms.
					-- error(key .. " " .. (val and concat_vals(val) or "nil") .. " || " .. (newval and concat_vals(newval) or "nil"))
					difconj = true
					break
				end
			end
			if difconj then
				break
			end
		end
		track(difconj and "different-conj" or "same-conj")
	end

	if typeinfo.subtypes.suffix then
		data.categories = {}
	end

	-- Check if the links to the verb forms exist
	-- Has to happen after other categories are removed for suffixes
	checkexist(data)

	if domain == nil then
		return make_table(data) .. m_utilities.format_categories(data.categories, lang)
	end
	local verb = data ~= nil and ('), '+', ""))) .. '|' .. data .. ']]') or 'verb'
	return link_google_books(verb, flatten_values(data), domain)
end

return export