Module:la-nominal: Difference between revisions

From Acadēmīa Latīnitātis
No edit summary
No edit summary
Tag: Reverted
Line 1: Line 1:
local export = {}
local m_utilities = require("Module:utilities")
 
local m_table = require("Module:table")
 
local m_links = require("Module:links")
--[=[
local make_link = m_links.full_link
 
local m_la_headword = require("Module:la-headword")
Authorship: Ben Wing <benwing2>, with many ideas and a little code coming from
local m_la_utilities = require("Module:la-utilities")
the old [[Module:la-decl-multi]] by KC Kenny Lau.
local m_para = require("Module:parameters")
 
]=]


-- TODO:
-- TODO:
-- (DONE) Eliminate specification of noteindex from la-adj/data
-- 1. (DONE) detect_decl_and_subtypes doesn't do anything with perf_stem or supine_stem.
-- (DONE?) Finish autodetection of adjectives
-- 2. (DONE) Should error on bad subtypes.
-- (DONE) Remove old noun code
-- 3. Make sure Google Books link still works.
-- (DONE) Implement <.sufn>
-- 4. (DONE) Add 4++ that has alternative perfects -īvī/-iī.
-- (DONE) Look into adj voc=false
-- 5. (DONE) If sup but no perf, allow passive perfect forms unless no-pasv-perf.
-- (DONE) Handle loc in adjectives
-- 6. (DONE) Remove no-actv-perf.
-- Error on bad subtypes
-- 7. (DONE) Support plural prefix/suffix and plural passive prefix/suffix
-- Make sure Google Books link still works.
--
-- (DONE) Make sure .sufn triggers insertion of 'with m optionally -> n in compounds' in title.
-- If enabled, compare this module with new version of module to make
-- (DONE) Make sure title returned to la-adj lowercases the first letter even with a custom title.
-- sure all conjugations are the same.
local test_new_la_verb_module = false


--[=[
local export = {}


TERMINOLOGY:
local lang = require("Module:languages").getByCode("la")
 
-- "slot" = A particular case/number combination (for nouns) or
case/number/gender combination (for adjectives). Example slot names are
"abl_sg" (for noun) or "acc_pl_f" (for adjectives). Each slot is filled
with zero or more forms.
 
-- "form" = The declined Latin form representing the value of a given slot.
For example, rēge is a form, representing the value of the abl_sg slot of
the lemma rēx.
 
-- "lemma" = The dictionary form of a given Latin term. For nouns, it's
generally the nominative singular, but will be the nominative plural of
plurale tantum nouns (e.g. [[castra]]), and may occasionally be another
form (e.g. the genitive singular) if the nominative singular is missing.
For adjectives, it's generally the masculine nominative singular, but
will be the masculine nominative plural of plurale tantum adjectives
(e.g. [[dēnī]]).


-- "plurale tantum" (plural "pluralia tantum") = A noun or adjective that
local title = mw.title.getCurrentTitle()
exists only in the plural. Examples are castra "army camp", faucēs "throat",
local NAMESPACE = title.nsText
and dēnī "ten each" (used for counting pluralia tantum nouns).
local PAGENAME = title.text


-- "singulare tantum" (plural "singularia tantum") = A noun or adjective that
-- Conjugations are the functions that do the actual
exists only in the singular. Examples are geōlogia "geology" (and in
-- conjugating by creating the forms of a basic verb.
general most non-count nouns) and the adjective ūnus "one".
-- They are defined further down.
local conjugations = {}


]=]
-- Check if this verb is reconstructed
-- i.e. the pagename is Reconstruction:Latin/...
local reconstructed = NAMESPACE == "Reconstruction" and PAGENAME:find("^Latin/")


local lang = require("Module:languages").getByCode("la")
-- Forward functions
local m_links = require("Module:links")
local m_utilities = require("Module:utilities")
local m_table = require("Module:table")
local m_string_utilities = require("Module:string utilities")
local m_para = require("Module:parameters")


local current_title = mw.title.getCurrentTitle()
local postprocess
local NAMESPACE = current_title.nsText
local make_pres_1st
local PAGENAME = current_title.text
local make_pres_2nd
local make_pres_3rd
local make_pres_3rd_io
local make_pres_4th
local make_perf_and_supine
local make_perf
local make_deponent_perf
local make_supine
local make_sigm
local make_table
local make_indc_rows
local make_subj_rows
local make_impr_rows
local make_nonfin_rows
local make_vn_rows
local make_footnotes
local override
local checkexist
local checkirregular
local flatten_values
local link_google_books


local m_noun_decl = require("Module:la-noun/data")
local split = mw.text.split
local m_noun_table = require("Module:la-noun/table")
local find = mw.ustring.find
local m_adj_decl = require("Module:la-adj/data")
local len = mw.ustring.len
local m_adj_table = require("Module:la-adj/table")
local match = mw.ustring.match
local m_la_utilities = require("Module:la-utilities")
local sub = mw.ustring.sub
 
local gsub = mw.ustring.gsub
local rsplit = mw.text.split
local toNFD = mw.ustring.toNFD
local rfind = mw.ustring.find
local rmatch = mw.ustring.match
local rgmatch = mw.ustring.gmatch
local rsubn = mw.ustring.gsub
local ulen = mw.ustring.len
local uupper = mw.ustring.upper


-- version of rsubn() that discards all but the first return value
-- version of gsub() that discards all but the first return value
local function rsub(term, foo, bar)
local function gsub1(term, foo, bar)
local retval = rsubn(term, foo, bar)
local retval = gsub(term, foo, bar)
return retval
return retval
end
end


local ligatures = {
local function cfind(str, text)
['Ae'] = 'Æ',
-- Constant version of :find()
['ae'] = 'æ',
return str:find(text, nil, true)
['Oe'] = 'Œ',
['oe'] = 'œ',
}
 
local cases = {
"nom", "gen", "dat", "acc", "abl", "voc", "loc"
}
 
local nums = {
"sg", "pl"
}
 
local genders = {
"m", "f", "n"
}
 
local irreg_noun_to_decl = {
["bōs"] = "3",
["cherub"] = "irreg",
["deus"] = "2",
["Deus"] = "2",
["domus"] = "4,2",
["Iēsus"] = "4",
["Jēsus"] = "4",
["Iēsūs"] = "4",
["Jēsūs"] = "4",
["iūgerum"] = "2,3",
["jūgerum"] = "2,3",
["sūs"] = "3",
["ēthos"] = "3",
["Athōs"] = "2",
["lexis"] = "3",
["vēnum"] = "4,2",
["vīs"] = "3",
}
 
local irreg_adj_to_decl = {
["duo"] = "irreg+",
["ambō"] = "irreg+",
["mīlle"] = "3-1+",
["plūs"] = "3-1+",
["is"] = "1&2+",
["īdem"] = "1&2+",
["ille"] = "1&2+",
["ipse"] = "1&2+",
["iste"] = "1&2+",
["quis"] = "irreg+",
["quī"] = "irreg+",
["quisquis"] = "irreg+",
}
 
local declension_to_english = {
["1"] = "first",
["2"] = "second",
["3"] = "third",
["4"] = "fourth",
["5"] = "fifth",
}
 
local number_to_english = {
"one", "two", "three", "four", "five"
}
local linked_prefixes = {
"", "linked_"
}
 
-- List of adjective slots for which we generate linked variants. Include
-- feminine and neuter variants because they will be needed if the adjective
-- is part of a multiword feminine or neuter noun.
local potential_adj_lemma_slots = {
"nom_sg_m",
"nom_pl_m",
"nom_sg_f",
"nom_pl_f",
"nom_sg_n",
"nom_pl_n"
}
 
local linked_to_non_linked_adj_slots = {}
for _, slot in ipairs(potential_adj_lemma_slots) do
linked_to_non_linked_adj_slots["linked_" .. slot] = slot
end
end


local potential_noun_lemma_slots = {
local function form_is_empty(form)
"nom_sg",
return not form or form == "" or form == "-" or form == "" or form == "&mdash;" or (
"nom_pl"
type(form) == "table" and (form[1] == "" or form[1] == "-" or form[1] == "" or form[1] == "&mdash;")
}
)
 
local linked_to_non_linked_noun_slots = {}
for _, slot in ipairs(potential_noun_lemma_slots) do
linked_to_non_linked_noun_slots["linked_" .. slot] = slot
end
end


-- Iterate over all the "slots" associated with a noun declension, where a slot
local function initialize_slots()
-- is a particular case/number combination. If overridable_only, don't include the
local generic_slots = {}
-- "linked_" variants (linked_nom_sg, linked_nom_pl), which aren't overridable.
local non_generic_slots = {}
local function iter_noun_slots(overridable_only)
local function handle_slot(slot, generic)
local case = 1
if generic then
local num = 1
table.insert(generic_slots, slot)
local linked_variant = 0
else
local function iter()
table.insert(non_generic_slots, slot)
linked_variant = linked_variant + 1
end
local max_linked_variant = overridable_only and 1 or cases[case] == "nom" and 2 or 1
end
if linked_variant > max_linked_variant then
for _, v in ipairs({"actv", "pasv"}) do
linked_variant = 1
local function handle_tense(t, mood)
num = num + 1
local non_pers_slot = t .. "_" .. v .. "_" .. mood
if num > #nums then
handle_slot(non_pers_slot, true)
num = 1
for _, p in ipairs({"1s", "2s", "3s", "1p", "2p", "3p"}) do
case = case + 1
handle_slot(p .. "_" .. non_pers_slot, false)
if case > #cases then
return nil
end
end
end
end
end
return linked_prefixes[linked_variant] .. cases[case] .. "_" .. nums[num]
for _, t in ipairs({"pres", "impf", "futr", "perf", "plup", "futp", "sigf"}) do
handle_tense(t, "indc")
end
for _, t in ipairs({"pres", "impf", "perf", "plup", "siga"}) do
handle_tense(t, "subj")
end
for _, t in ipairs({"pres", "futr"}) do
handle_tense(t, "impr")
end
end
end
return iter
for _, f in ipairs({"inf", "ptc"}) do
for _, t in ipairs({"pres_actv", "perf_actv", "futr_actv", "pres_pasv", "perf_pasv", "futr_pasv"}) do
handle_slot(t .. "_" .. f, false)
end
end
for _, n in ipairs({"ger_gen", "ger_dat", "ger_acc", "ger_abl", "sup_acc", "sup_abl"}) do
handle_slot(n, false)
end
return non_generic_slots, generic_slots
end
end


-- Iterate over all the "slots" associated with an adjective declension, where a slot
local non_generic_slots, generic_slots = initialize_slots()
-- is a particular case/number/gender combination. If overridable_only, don't include the
 
-- "linked_" variants (linked_nom_sg_m, linked_nom_pl_m, etc.), which aren't overridable.
local potential_lemma_slots = {
local function iter_adj_slots(overridable_only)
"1s_pres_actv_indc", -- regular
local case = 1
"3s_pres_actv_indc", -- impersonal
local num = 1
"1s_perf_actv_indc", -- coepī
local gen = 1
"3s_perf_actv_indc", -- doesn't occur?
local linked_variant = 0
}
 
-- 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 = #non_generic_slots
local function iter()
local function iter()
linked_variant = linked_variant + 1
slotnum = slotnum + 1
local max_linked_variant = overridable_only and 1 or cases[case] == "nom" and genders[gen] == "m" and 2 or 1
if slotnum > max_slotnum then
if linked_variant > max_linked_variant then
slotnum = 1
linked_variant = 1
stage = stage + 1
gen = gen + 1
if stage == 2 then
if gen > #genders then
if include_generic then
gen = 1
max_slotnum = #generic_slots
num = num + 1
else
if num > #nums then
stage = stage + 1
num = 1
end
case = case + 1
end
if case > #cases then
if stage == 3 then
return nil
if include_linked then
end
max_slotnum = #potential_lemma_slots
else
stage = stage + 1
end
end
end
end
if stage > 3 then
return nil
end
end
if stage == 1 then
return non_generic_slots[slotnum]
elseif stage == 2 then
return generic_slots[slotnum]
else
return "linked_" .. potential_lemma_slots[slotnum]
end
end
return linked_prefixes[linked_variant] .. cases[case] .. "_" .. nums[num] .. "_" .. genders[gen]
end
end
return iter
return iter
end
end


-- Iterate over all the "slots" associated with a noun or adjective declension (depending on
local function ine(val)
-- the value of IS_ADJ), where a slot is a particular case/number combination (in the case of
if val == "" then
-- nouns) or case/number/gender combination (in the case of adjectives). If OVERRIDABLE_ONLY
return nil
-- is specified, only include overridable slots (not including linked_ variants).
local function iter_slots(is_adj, overridable_only)
if is_adj then
return iter_adj_slots(overridable_only)
else
else
return iter_noun_slots(overridable_only)
return val
end
end
end
end


local function concat_forms_in_slot(forms)
local function track(page)
if forms and forms ~= "" and forms ~= "" and #forms > 0 then
require("Module:debug").track("la-verb/" .. page)
local new_vals = {}
return true
for _, v in ipairs(forms) do
table.insert(new_vals, rsub(v, "|", "<!>"))
end
return table.concat(new_vals, ",")
else
return nil
end
end
end


local function glossary_link(anchor, text)
-- For a given form, we allow either strings (a single form) or lists of forms,
text = text or anchor
-- and treat strings equivalent to one-element lists.
return "[[Appendix:Glossary#" .. anchor .. "|" .. text .. "]]"
local function forms_equal(form1, form2)
if type(form1) ~= "table" then
form1 = {form1}
end
if type(form2) ~= "table" then
form2 = {form2}
end
return m_table.deepEquals(form1, form2)
end
end


local function track(page)
local function concat_vals(val)
require("Module:debug").track("la-nominal/" .. page)
if type(val) == "table" then
return true
return table.concat(val, ",")
end
else
 
return val
local function set_union(sets)
local union = {}
for _, set in ipairs(sets) do
for key, _ in pairs(set) do
union[key] = true
end
end
end
return union
end
end


local function set_difference(set1, set2)
-- Construct a one- or two-part link. For reasons I don't understand, if we're in
local diff = {}
-- the reconstructed namespace (e.g. for the page [[Reconstruction:Latin/nuo]]), we
for key, _ in pairs(set1) do
-- need to construct a special type of link; full_link() doesn't handle this
if not set2[key] then
-- correctly (although it tries ...). Ideally we should fix full_link() instead of
diff[key] = true
-- doing this.
end
local function make_raw_link(page, display)
if reconstructed then
display = display or page
page = lang:makeEntryName(page)
return "[[" .. NAMESPACE .. ":Latin/" .. page .. "|" .. display .. "]]"
elseif display then
return "[[" .. lang:makeEntryName(page) .. "|" .. display .. "]]"
else
return "[[" .. page .. "]]"
end
end
return diff
end
end


-- If a form is set as '*', that means its unattested
local function split_prefix_and_base(lemma, main_verbs)
-- but should still be generated
for _, main in ipairs(main_verbs) do
local function unattested_forms(data, args, is_adj)
local prefix = match(lemma, "^(.*)" .. main .. "$")
data.unattested = {}
if prefix then
for slot in iter_slots(is_adj) do
return prefix, main
if args[slot] == '*' then
data.unattested[slot] = true
args[slot] = nil
end
end
end
end
error("Argument " .. lemma .. " doesn't end in any of " .. table.concat(main_verbs, ","))
end
end


-- Make a link only if the form is attested
-- Given an ending (or possibly a full regex matching the entire lemma, if
local function link_if_attested(form, accel, is_unattested)
-- a regex group is present), return the base minus the ending, or nil if
if is_unattested then
-- the ending doesn't match.
return m_links.full_link({ lang = lang, alt = '*' .. form })
local function extract_base(lemma, ending)
if ending:find("%(") then
return match(lemma, ending)
else
else
return m_links.full_link({ lang = lang, alt = '' .. form })
return match(lemma, "^(.*)" .. ending .. "$")
end
end
end
end


local function process_noun_forms_and_overrides(data, args)
-- Given ENDINGS_AND_SUBTYPES (a list of pairs of endings with associated
local redlink = false
-- subtypes, where each pair consists of a single ending spec and a list of
unattested_forms(data, args, is_adj);
-- 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
-- Process overrides and canonicalize forms.
-- ending, and SUBTYPES is the subtypes associated with the ending. If no
for slot in iter_noun_slots() do
-- endings match, throw an error if DECLTYPE is non-nil, mentioning the
local val = nil
-- DECLTYPE (the user-specified declension); but if DECLTYPE is nil, just
if args[slot] then
-- return the pair nil, nil.
val = args[slot]
--
data.user_specified[slot] = true
-- The ending spec in ENDINGS_AND_SUBTYPES is one of the following:
else
--
-- Overridding nom_sg etc. should override linked_nom_sg so that
-- 1. A simple string, e.g. "ātur", specifying an ending.
-- the correct value gets displayed in the headword, which uses
-- 2. A regex that should match the entire lemma (it should be anchored at
-- linked_nom_sg.
--   the beginning with ^ and at the end with $), and contains a single
local non_linked_equiv_slot = linked_to_non_linked_noun_slots[slot]
--    capturing group to match the base.
if non_linked_equiv_slot and args[non_linked_equiv_slot] then
local function get_subtype_by_ending(lemma, conjtype, specified_subtypes,
val = args[non_linked_equiv_slot]
endings_and_subtypes)
data.user_specified[slot] = true
for _, ending_and_subtypes in ipairs(endings_and_subtypes) do
else
local ending = ending_and_subtypes[1]
val = data.forms[slot]
local subtypes = ending_and_subtypes[2]
not_this_subtype = false
for _, subtype in ipairs(subtypes) do
-- A subtype is directly canceled by specifying -SUBTYPE.
if specified_subtypes["-" .. subtype] then
not_this_subtype = true
break
end
end
end
end
if val then
if not not_this_subtype then
if type(val) == "string" then
local base = extract_base(lemma, ending)
val = mw.text.split(val, "/")
if base then
end
return base, subtypes
if (data.num == "pl" and slot:find("sg")) or (data.num == "sg" and slot:find("pl")) then
data.forms[slot] = ""
elseif val[1] == "" or val[1] == "-" or val[1] == "—" then
data.forms[slot] = "—"
else
data.forms[slot] = val
end
end
end
end
end
end
 
if conjtype then
-- Compute the lemma for accelerators. Do this after processing
error("Unrecognized ending for conjugation-" .. conjtype .. " verb: " .. lemma)
-- overrides in case we overrode the lemma form(s).
local accel_lemma
if data.num and data.num ~= "" then
accel_lemma = data.forms["nom_" .. data.num]
else
accel_lemma = data.forms["nom_sg"]
end
if type(accel_lemma) == "table" then
accel_lemma = accel_lemma[1]
end
end
return nil, nil
end


-- Set the accelerators, and determine if there are red links.
local irreg_verbs_to_conj_type = {
for slot in iter_noun_slots() do
["aiō"] = "3rd-io",
local val = data.forms[slot]
["aiiō"] = "3rd-io",
if val and val ~= "" and val ~= "" and #val > 0 then
["ajō"] = "3rd-io",
for i, form in ipairs(val) do
["dīcō"] = "3rd",
local accel_form = slot
["dūcō"] = "3rd",
accel_form = accel_form:gsub("_([sp])[gl]$", "|%1")
["faciō"] = "3rd-io",
["fīō"] = "3rd",
["ferō"] = "3rd",
["inquam"] = "irreg",
["libet"] = "2nd",
["lubet"] = "2nd",
["licet"] = "2nd",
["volō"] = "irreg",
["mālō"] = "irreg",
["nōlō"] = "irreg",
["possum"] = "irreg",
["piget"] = "2nd",
["coepī"] = "irreg",
["sum"] = "irreg",
["edō"] = "3rd",
["dō"] = "1st",
["eō"] = "irreg",
}


data.accel[slot] = {form = accel_form, lemma = accel_lemma}
local function detect_decl_and_subtypes(args)
if not redlink and NAMESPACE == '' then
local specs = split(args[1] or "", "%.")
local title = lang:makeEntryName(form)
local subtypes = {}
local t = mw.title.new(title)
local conj_arg
if t and not t.exists then
for i, spec in ipairs(specs) do
table.insert(data.categories, "Latin " .. data.pos .. " with red links in their inflection tables")
if i == 1 then
redlink = true
conj_arg = spec
end
else
end
local begins_with_hyphen = find(spec, "^%-")
spec = spec:gsub("%-", "")
if begins_with_hyphen then
spec = "-" .. spec
end
end
subtypes[spec] = true
end
end
end
end
end


local function process_adj_forms_and_overrides(data, args)
local orig_lemma = args[2] or mw.title.getCurrentTitle().subpageText
local redlink = false
orig_lemma = gsub1(orig_lemma, "o$", "ō")
unattested_forms(data, args, true)
local lemma = m_links.remove_links(orig_lemma)
local base, conjtype, conj_subtype, detected_subtypes
local base_conj_arg, auto_perf_supine = match(conj_arg, "^([124])(%+%+?)$")
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 sub(orig_lemma, 1, 1) == "-" then
subtypes.suffix = true
end
local auto_perf, auto_supine, auto_sigm
if subtypes.sigm or subtypes.sigmpasv or subtypes.suffix then
auto_sigm = true
end


-- Process overrides and canonicalize forms.
if conj_arg == "1" then
for slot in iter_adj_slots() do
conjtype = "1st"
-- If noneut=1 passed, clear out all neuter forms.
base, detected_subtypes = get_subtype_by_ending(lemma, "1", subtypes, {
if data.noneut and slot:find("_n") then
{"ō", {}},
data.forms[slot] = nil
{"or", {"depon"}},
{"at", {"impers"}},
{"ātur", {"depon", "impers"}},
{"ī", {"perfaspres"}},
})
if auto_perf_supine then
if subtypes.perfaspres then
auto_perf = base
else
auto_perf = base .. "āv"
auto_supine = base .. "āt"
end
end
end
-- If nomf=1 passed, clear out all masculine and feminine forms.
if auto_sigm then
if data.nomf and (slot:find("_m") or slot:find("_f")) then
auto_sigm = base .. "āss"
data.forms[slot] = nil
end
end
local val = nil
if subtypes.suffix then
if args[slot] then
subtypes.p3inf = true
val = args[slot]
subtypes.sigmpasv = true
data.user_specified[slot] = true
end
else
elseif conj_arg == "2" then
-- Overridding nom_sg_m etc. should override linked_nom_sg_m so that
conjtype = "2nd"
-- the correct value gets displayed in the headword, which uses
base, detected_subtypes = get_subtype_by_ending(lemma, "2", subtypes, {
-- linked_nom_sg_m.
{"eō", {}},
local non_linked_equiv_slot = linked_to_non_linked_adj_slots[slot]
{"eor", {"depon"}},
if non_linked_equiv_slot and args[non_linked_equiv_slot] then
{"et", {"impers"}},
val = args[non_linked_equiv_slot]
{"ētur", {"depon", "impers"}},
data.user_specified[slot] = true
{"ī", {"perfaspres"}},
})
if auto_perf_supine then
if subtypes.perfaspres then
auto_perf = base
else
else
val = data.forms[slot]
auto_perf = base .. "u"
auto_supine = base .. "it"
end
end
end
end
if val then
if auto_sigm then
if type(val) == "string" then
auto_sigm = "-/ēss"
val = mw.text.split(val, "/")
end
end
if subtypes.suffix then
if (data.num == "pl" and slot:find("sg")) or (data.num == "sg" and slot:find("pl")) then
subtypes.sigmpasv = true
data.forms[slot] = ""
end
elseif val[1] == "" or val[1] == "-" or val[1] == "" then
elseif conj_arg == "3" then
data.forms[slot] = ""
base, detected_subtypes = get_subtype_by_ending(lemma, nil, subtypes, {
{"iō", {"I"}},
{"ior", {"depon", "I"}},
})
if base then
conjtype = "3rd-io"
else
base, detected_subtypes = get_subtype_by_ending(lemma, "3", subtypes, {
{"ō", {}},
{"or", {"depon"}},
{"it", {"impers"}},
{"itur", {"depon", "impers"}},
{"ī", {"perfaspres"}},
})
if subtypes.I then
conjtype = "3rd-io"
else
else
data.forms[slot] = val
conjtype = "3rd"
end
end
end
end
if subtypes.perfaspres then
auto_perf = base
end
if subtypes.suffix then
auto_perf = "-"
auto_supine = "-"
auto_sigm = "-"
subtypes.sigmpasv = true
end
elseif conj_arg == "4" then
conjtype = "4th"
base, detected_subtypes = get_subtype_by_ending(lemma, "4", subtypes, {
{"iō", {}},
{"ior", {"depon"}},
{"it", {"impers"}},
{"ītur", {"depon", "impers"}},
{"ī", {"perfaspres"}},
})
if subtypes.perfaspres 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
if subtypes.suffix then
subtypes.sigm = true
end
elseif conj_arg == "irreg" then
conjtype = "irreg"
local prefix
prefix, base = split_prefix_and_base(lemma, {
"aiō",
"aiiō",
"ajō",
"dīcō",
"dūcō",
"faciō",
"fīō",
"ferō",
"inquam",
"libet",
"lubet",
"licet",
"volō",
"mālō",
"nōlō",
"possum",
"piget",
"coepī",
-- list sum after possum
"sum",
-- FIXME: Will praedō cause problems?
"edō",
-- list dō after edō
"dō",
"eō",
})
conj_subtype = irreg_verbs_to_conj_type[base]
args[1] = m_la_utilities.strip_macrons(base)
args[2] = prefix
-- args[3] and args[4] are used by ferō and sum and stay where they are
detected_subtypes = {}
else
error("Unrecognized conjugation '" .. conj_arg .. "'")
end
end


-- Compute the lemma for accelerators. Do this after processing
for _, detected_subtype in ipairs(detected_subtypes) do
-- overrides in case we overrode the lemma form(s).
if detected_subtype == "impers" and subtypes["3only"] then
local accel_lemma, accel_lemma_f
-- 3only overrides impers
if data.num and data.num ~= "" then
else
accel_lemma = data.forms["nom_" .. data.num .. "_m"]
subtypes[detected_subtype] = true
accel_lemma_f = data.forms["nom_" .. data.num .. "_f"]
end
else
accel_lemma = data.forms["nom_sg_m"]
accel_lemma_f = data.forms["nom_sg_f"]
end
if type(accel_lemma) == "table" then
accel_lemma = accel_lemma[1]
end
if type(accel_lemma_f) == "table" then
accel_lemma_f = accel_lemma_f[1]
end
end


-- Set the accelerators, and determine if there are red links.
if conjtype ~= "irreg" then
for slot in iter_adj_slots() do
args[1] = base
local val = data.forms[slot]
local perf_stem, supine_stem
if val and val ~= "" and val ~= "" and #val > 0 then
if subtypes.depon or subtypes.semidepon or subtypes.perfaspres then
for i, form in ipairs(val) do
supine_stem = args[3] or auto_supine
local accel_form = slot
if supine_stem == "-" and not subtypes.suffix then
accel_form = accel_form:gsub("_([sp])[gl]_", "|%1|")
supine_stem = nil
 
end
if data.noneut then
if not supine_stem then
-- If noneut=1, we're being asked to do a noun like
if subtypes.depon or subtypes.semidepon then
-- Aquītānus or Rōmānus that has masculine and feminine
subtypes.noperf = true
-- variants, not an adjective. In that case, make the
end
-- accelerators correspond to nominal case/number forms
subtypes.nosup = true
-- without the gender, and use the feminine as the
end
-- lemma for feminine forms.
if subtypes.sigm or subtypes.sigmpasv then
if slot:find("_f") then
local sigm_stem = args[5] or auto_sigm
data.accel[slot] = {form = accel_form:gsub("|f$", ""), lemma = accel_lemma_f}
if sigm_stem == "-" and not subtypes.suffix then
else
sigm_stem = nil
data.accel[slot] = {form = accel_form:gsub("|m$", ""), lemma = accel_lemma}
end
else
if not data.forms.nom_sg_n and not data.forms.nom_pl_n then
-- use multipart tags if called for
accel_form = accel_form:gsub("|m$", "|m//f//n")
elseif not data.forms.nom_sg_f and not data.forms.nom_pl_f then
accel_form = accel_form:gsub("|m$", "|m//f")
end
 
-- use the order nom|m|s, which is more standard than nom|s|m
accel_form = accel_form:gsub("|(.-)|(.-)$", "|%2|%1")
 
data.accel[slot] = {form = accel_form, lemma = accel_lemma}
end
end
if not redlink and NAMESPACE == '' then
args[5] = sigm_stem
local title = lang:makeEntryName(form)
end
local t = mw.title.new(title)
args[2] = supine_stem
if t and not t.exists then
args[3] = nil
table.insert(data.categories, "Latin " .. data.pos .. " with red links in their inflection tables")
else
redlink = true
perf_stem = args[3] or auto_perf
end
if perf_stem == "-" and not subtypes.suffix then
perf_stem = nil
end
if not perf_stem then
subtypes.noperf = true
end
supine_stem = args[4] 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 or subtypes.sigmpasv then
local sigm_stem = args[5] or auto_sigm
if sigm_stem == "-" and not subtypes.suffix then
sigm_stem = nil
end
end
args[5] = sigm_stem
end
end
args[2] = perf_stem
args[3] = supine_stem
end
args[4] = nil
end
for subtype, _ in pairs(subtypes) do
if not m_la_headword.allowed_subtypes[subtype] and
not m_la_headword.allowed_subtypes[sub(subtype, 2)] and
not (conjtype == "3rd" and subtype == "-I") and
not (conjtype == "3rd-io" and subtype == "I") and
not (subtype == "nound" or subtype == "sigm" or subtype == "sigmpasv" or subtype == "suffix") then
error("Unrecognized verb subtype " .. subtype)
end
end
end
end


-- See if the masculine and feminine/neuter are the same across all slots.
return conjtype, conj_subtype, subtypes, orig_lemma, lemma
-- If so, blank out the feminine/neuter so we use a table that combines
end
-- masculine and feminine, or masculine/feminine/neuter.
 
for _, gender in ipairs({"f", "n"}) do
-- The main new entry point.
local other_is_masc = true
function export.show(frame)
for _, case in ipairs(cases) do
local parent_args = frame:getParent().args
for _, num in ipairs(nums) do
local data, typeinfo = export.make_data(parent_args)
if not m_table.deepEquals(data.forms[case .. "_" .. num .. "_" .. gender],
local domain = frame:getParent().args['search']
data.forms[case .. "_" .. num .. "_m"]) then
-- Test code to compare existing module to new one.
other_is_masc = false
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, newtypeinfo = 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[prop]
local newtable = prop == "miscdata" and newmiscdata or newdata[prop]
for key, val in pairs(table) do
local newval = newtable[key]
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[key]
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
break
end
end
end
end
if not other_is_masc then
if difconj then
break
break
end
end
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)
else
local verb = data['forms']['1s_pres_actv_indc'] ~= nil and ('[['.. gsub(toNFD(data['forms']['1s_pres_actv_indc']),'[^%w]+',"")..'|'..data['forms']['1s_pres_actv_indc'].. ']]') or 'verb'
return link_google_books(verb, flatten_values(data['forms']), domain) end
end


if other_is_masc then
local function concat_forms(data, typeinfo, include_props)
for _, case in ipairs(cases) do
local ins_text = {}
for _, num in ipairs(nums) do
for key, val in pairs(data.forms) do
data.forms[case .. "_" .. num .. "_" .. gender] = nil
local ins_form = {}
end
if type(val) ~= "table" then
val = {val}
end
for _, v in ipairs(val) do
if not form_is_empty(v) then
table.insert(ins_form,
gsub1(gsub1(gsub1(v, "|", "<!>"), "=", "<->"), ",", "<.>")
)
end
end
end
if #ins_form > 0 then
table.insert(ins_text, key .. "=" .. table.concat(ins_form, ","))
end
end
end
end
if include_props then
table.insert(ins_text, "conj_type=" .. typeinfo.conj_type)
if typeinfo.conj_subtype then
table.insert(ins_text, "conj_subtype=" .. typeinfo.conj_subtype)
end
local subtypes = {}
for subtype, _ in pairs(typeinfo.subtypes) do
table.insert(subtypes, subtype)
end
table.insert(ins_text, "subtypes=" .. table.concat(subtypes, "."))
end
return table.concat(ins_text, "|")
end
end


-- Convert data.forms[slot] for all slots into displayable text. This is
-- The entry point for 'la-generate-verb-forms' and 'la-generate-verb-props'
-- an older function, still currently used for nouns but not for adjectives.
-- to generate all verb forms/props.
-- For adjectives, the adjective table module has special code to combine
function export.generate_forms(frame)
-- adjacent slots, and needs the original forms plus other text that will
local include_props = frame.args["include_props"]
-- go into the displayable text for the slot; this is handled below by
local parent_args = frame:getParent().args
-- partial_show_forms() and finish_show_form().
local data, typeinfo = export.make_data(parent_args)
local function show_forms(data, is_adj)
return concat_forms(data, typeinfo, include_props)
local noteindex = 1
end
local notes = {}
 
local seen_notes = {}
-- Add prefixes and suffixes to non-generic slots. The generic slots (e.g.
for slot in iter_slots(is_adj) do
-- perf_pasv_indc, whose text indicates to use the past passive participle +
local val = data.forms[slot]
-- the present active indicative of [[sum]]), handle prefixes and suffixes
if val and val ~= "" and val ~= "" then
-- themselves in make_perfect_passive().
for i, form in ipairs(val) do
local function add_prefix_suffix(data, typeinfo)
local link = link_if_attested(form, data.accel[slot], data.unattested[slot])
if not data.prefix and not data.suffix then
local this_notes = data.notes[slot .. i]
return
if this_notes and not data.user_specified[slot] then
end
if type(this_notes) == "string" then
 
this_notes = {this_notes}
local active_prefix = data.prefix or ""
end
local passive_prefix = data.passive_prefix or ""
local link_indices = {}
local plural_prefix = data.plural_prefix or ""
for _, this_note in ipairs(this_notes) do
local plural_passive_prefix = data.plural_passive_prefix or ""
local this_noteindex = seen_notes[this_note]
local active_prefix_no_links = m_links.remove_links(active_prefix)
if not this_noteindex then
local passive_prefix_no_links = m_links.remove_links(passive_prefix)
-- Generate a footnote index.
local plural_prefix_no_links = m_links.remove_links(plural_prefix)
this_noteindex = noteindex
local plural_passive_prefix_no_links = m_links.remove_links(plural_passive_prefix)
noteindex = noteindex + 1
 
table.insert(notes, '<sup style="color: red">' .. this_noteindex .. '</sup>' .. this_note)
local active_suffix = data.suffix or ""
seen_notes[this_note] = this_noteindex
local passive_suffix = data.passive_suffix or ""
local plural_suffix = data.plural_suffix or ""
local plural_passive_suffix = data.plural_passive_suffix or ""
local active_suffix_no_links = m_links.remove_links(active_suffix)
local passive_suffix_no_links = m_links.remove_links(passive_suffix)
local plural_suffix_no_links = m_links.remove_links(plural_suffix)
local plural_passive_suffix_no_links = m_links.remove_links(plural_passive_suffix)
 
for slot in iter_slots(false, true) do
if not slot:find("ger_") then
local prefix, suffix, prefix_no_links, suffix_no_links
if slot:find("pasv") and slot:find("[123]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 slot:find("pasv") and not slot:find("_inf") then
prefix = passive_prefix
suffix = passive_suffix
prefix_no_links = passive_prefix_no_links
suffix_no_links = passive_suffix_no_links
elseif slot:find("[123]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 forms = data.forms[slot]
if not form_is_empty(forms) then
local affixed_forms = {}
if type(forms) ~= "table" then
forms = {forms}
end
for _, form in ipairs(forms) do
if form_is_empty(form) then
table.insert(affixed_forms, form)
elseif slot:find("^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:find("[%[%]]") then
form = "[[" .. form .. "]]"
end
end
m_table.insertIfNot(link_indices, this_noteindex)
table.insert(affixed_forms, prefix .. form .. suffix)
elseif form:find("[%[%]]") 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).
table.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.
table.insert(affixed_forms, prefix_no_links .. form .. suffix_no_links)
end
end
val[i] = link .. '<sup style="color: red">' .. table.concat(link_indices, ",") .. '</sup>'
else
val[i] = link
end
end
data.forms[slot] = affixed_forms
end
end
-- FIXME, do we want this difference?
data.forms[slot] = table.concat(val, is_adj and ", " or "<br />")
end
end
end
end
for _, footnote in ipairs(data.footnotes) do
table.insert(notes, footnote)
end
data.footnotes = table.concat(notes, "<br />")
end
end


-- Generate the display form for a set of slots with identical content. We
local function notes_override(data, args)
-- verify that the slots are actually identical, and throw an assertion error
local notes = {args["note1"], args["note2"], args["note3"]}
-- if not. The display form is as in show_forms() but combines together all the
-- accelerator forms for all the slots.
for n, note in pairs(notes) do
local function finish_show_form(data, slots, is_adj)
if note == "-" then
assert(#slots > 0)
data.footnotes[n] = nil
local slot1 = slots[1]
elseif note == "p3inf" then
local forms = data.forms[slot1]
data.footnotes[n] = "The present passive infinitive in -ier is a rare poetic form which is attested."
local notetext = data.notetext[slot1]
elseif note == "poetsyncperf" then
for _, slot in ipairs(slots) do
data.footnotes[n] = "At least one rare poetic syncopated perfect form is attested."
if not m_table.deepEquals(data.forms[slot], forms) then
elseif note == "sigm" then
error("data.forms[" .. slot1 .. "] = " .. (concat_forms_in_slot(forms) or "nil") ..
data.footnotes[n] = "At least one use of the archaic \"sigmatic future\" and \"sigmatic aorist\" tenses is attested, which are used by [[Old Latin]] writers; most notably [[w:Plautus|Plautus]] and [[w:Terence|Terence]]. The sigmatic future is generally ascribed a future or future perfect meaning, while the sigmatic aorist expresses a possible desire (\"might want to\")."
", but data.forms[" .. slot .. "] = " .. (concat_forms_in_slot(data.forms[slot]) or "nil"))
elseif note == "sigmpasv" then
data.footnotes[n] = "At least one use of the archaic \"sigmatic future\" and \"sigmatic aorist\" tenses is attested, which are used by [[Old Latin]] writers; most notably [[w:Plautus|Plautus]] and [[w:Terence|Terence]]. 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[n] = "At least one use of the archaic \"sigmatic future\" tense is attested, which is used by [[Old Latin]] writers; most notably [[w:Plautus|Plautus]] and [[w:Terence|Terence]]. 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[n] = note
end
end
assert(m_table.deepEquals(data.notetext[slot], notetext))
end
end
if not forms then
return ""
if args["notes"] == "-" then
else
data.footnotes = {}
local accel_forms = {}
local accel_lemma = data.accel[slot1].lemma
for _, slot in ipairs(slots) do
assert(data.accel[slot].lemma == accel_lemma)
table.insert(accel_forms, data.accel[slot].form)
end
local combined_accel_form = table.concat(accel_forms, "|;|")
local accel = {form = combined_accel_form, lemma = accel_lemma}
local formtext = {}
for i, form in ipairs(forms) do
table.insert(formtext, link_if_attested(form, accel, data.unattested[slot1]) .. notetext[i])
end
-- FIXME, do we want this difference?
return table.concat(formtext, is_adj and ", " or "<br />")
end
end
end
end


-- Used by the adjective table module. This does some of the work of
local function set_linked_forms(data, typeinfo)
-- show_forms(); in particular, it converts all empty forms of any format
-- Generate linked variants of slots that may be the lemma.
-- (nil, "", "—") to nil and, if the forms aren't empty, generates the footnote
-- If the form is the same as the lemma (with links removed),
-- text associated with each form.
-- substitute the original lemma (with links included).
local function partial_show_forms(data, is_adj)
for _, slot in ipairs(potential_lemma_slots) do
local noteindex = 1
local forms = data.forms[slot]
local notes = {}
local linked_forms = {}
local seen_notes = {}
if forms then
data.notetext = {}
if type(forms) ~= "table" then
-- Store this function in DATA so that it can be called from the adjective
forms = {forms}
-- table module without needing to require this module, which will (or
end
-- could) lead to recursive module requiring.
for _, form in ipairs(forms) do
data.finish_show_form = finish_show_form
if form == typeinfo.lemma then
for slot in iter_slots(is_adj) do
table.insert(linked_forms, typeinfo.orig_lemma)
local val = data.forms[slot]
if not val or val == "" or val == "—" then
data.forms[slot] = nil
else
local notetext = {}
for i, form in ipairs(val) do
local this_notes = data.notes[slot .. i]
if this_notes and not data.user_specified[slot] then
if type(this_notes) == "string" then
this_notes = {this_notes}
end
local link_indices = {}
for _, this_note in ipairs(this_notes) do
local this_noteindex = seen_notes[this_note]
if not this_noteindex then
-- Generate a footnote index.
this_noteindex = noteindex
noteindex = noteindex + 1
table.insert(notes, '<sup style="color: red">' .. this_noteindex .. '</sup>' .. this_note)
seen_notes[this_note] = this_noteindex
end
m_table.insertIfNot(link_indices, this_noteindex)
end
table.insert(notetext, '<sup style="color: red">' .. table.concat(link_indices, ",") .. '</sup>')
else
else
table.insert(notetext, "")
table.insert(linked_forms, form)
end
end
end
end
data.notetext[slot] = notetext
end
end
data.forms["linked_" .. slot] = linked_forms
end
end
function export.make_data(parent_args, from_headword, def1, def2)
local params = {
[1] = {required = true, default = def1 or "1+"},
[2] = {required = true, default = def2 or "amō"},
[3] = {},
[4] = {},
[5] = {},
prefix = {},
passive_prefix = {},
plural_prefix = {},
plural_passive_prefix = {},
gen_prefix = {},
dat_prefix = {},
acc_prefix = {},
abl_prefix = {},
suffix = {},
passive_suffix = {},
plural_suffix = {},
plural_passive_suffix = {},
gen_suffix = {},
dat_suffix = {},
acc_suffix = {},
abl_suffix = {},
label = {},
note1= {},
note2= {},
note3= {},
notes= {},
-- examined directly in export.show()
search = {}
}
for slot in iter_slots(true, false) do
params[slot] = {}
end
if from_headword then
params.lemma = {list = true}
params.id = {}
params.cat = {list = true}
end
local args = m_para.process(parent_args, params)
local conj_type, conj_subtype, subtypes, orig_lemma, lemma =
detect_decl_and_subtypes(args)
if not conjugations[conj_type] then
error("Unknown conjugation type '" .. conj_type .. "'")
end
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,
}  --note: the addition of red superscripted footnotes ('<sup style="color: red">' ... </sup>) is only implemented for the three form printing loops in which it is used
local typeinfo = {
lemma = lemma,
orig_lemma = orig_lemma,
conj_type = conj_type,
conj_subtype = conj_subtype,
subtypes = subtypes,
}
if args.passive_prefix and not args.prefix then
error("Can't specify passive_prefix= without prefix=")
end
if args.plural_prefix and not args.prefix then
error("Can't specify plural_prefix= without prefix=")
end
end
for _, footnote in ipairs(data.footnotes) do
if args.plural_passive_prefix and not args.prefix then
table.insert(notes, footnote)
error("Can't specify plural_passive_prefix= without prefix=")
end
end
data.footnotes = table.concat(notes, "<br />")
end


local function make_noun_table(data)
if args.passive_suffix and not args.suffix then
if data.num == "sg" then
error("Can't specify passive_suffix= without suffix=")
return m_noun_table.make_table_sg(data)
end
elseif data.num == "pl" then
if args.plural_suffix and not args.suffix then
return m_noun_table.make_table_pl(data)
error("Can't specify plural_suffix= without suffix=")
else
end
return m_noun_table.make_table(data)
if args.plural_passive_suffix and not args.suffix then
error("Can't specify plural_passive_suffix= without suffix=")
end
end
end


local function concat_forms(data, is_adj, include_props)
local function normalize_prefix(prefix)
local ins_text = {}
if not prefix then
for slot in iter_slots(is_adj) do
return nil
local formtext = concat_forms_in_slot(data.forms[slot])
end
if formtext then
local no_space_prefix = match(prefix, "(.*)_$")
table.insert(ins_text, slot .. "=" .. formtext)
if no_space_prefix then
return no_space_prefix
elseif find(prefix, "%-$") then
return prefix
else
return prefix .. " "
end
end
end
end
if include_props then
 
if data.gender then
local function normalize_suffix(suffix)
table.insert(ins_text, "g=" .. mw.ustring.lower(data.gender))
if not suffix then
return nil
end
end
local num = data.num
local no_space_suffix = match(suffix, "^_(.*)$")
if not num or num == "" then
if no_space_suffix then
num = "both"
return no_space_suffix
elseif find(suffix, "^%-") then
return suffix
else
return " " .. suffix
end
end
table.insert(ins_text, "num=" .. num)
end
end
return table.concat(ins_text, "|")
 
data.prefix = normalize_prefix(args.prefix)
data.passive_prefix = normalize_prefix(args.passive_prefix) or data.prefix
data.plural_prefix = normalize_prefix(args.plural_prefix) or data.prefix
-- First fall back to the passive prefix (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.
data.plural_passive_prefix = normalize_prefix(args.plural_passive_prefix) or
normalize_prefix(args.passive_prefix) or data.plural_prefix
data.gen_prefix = normalize_prefix(args.gen_prefix)
data.dat_prefix = normalize_prefix(args.dat_prefix)
data.acc_prefix = normalize_prefix(args.acc_prefix)
data.abl_prefix = normalize_prefix(args.abl_prefix)
 
data.suffix = normalize_suffix(args.suffix)
data.passive_suffix = normalize_suffix(args.passive_suffix) or data.suffix
data.plural_suffix = normalize_suffix(args.plural_suffix) or data.suffix
-- Same as above for prefixes.
data.plural_passive_suffix = normalize_suffix(args.plural_passive_suffix) or
normalize_suffix(args.passive_suffix) or data.plural_suffix
data.gen_suffix = normalize_suffix(args.gen_suffix)
data.dat_suffix = normalize_suffix(args.dat_suffix)
data.acc_suffix = normalize_suffix(args.acc_suffix)
data.abl_suffix = normalize_suffix(args.abl_suffix)
 
-- Generate the verb forms
conjugations[conj_type](args, data, typeinfo)
 
-- Post-process the forms
postprocess(data, typeinfo)
 
-- Override with user-set forms
override(data, args)
 
-- Set linked_* forms
set_linked_forms(data, typeinfo)
 
-- Prepend any prefixes, append any suffixes
add_prefix_suffix(data)
if args["label"] then
m_table.insertIfNot(data.title, args["label"])
end
notes_override(data, args)
 
-- Check if the verb is irregular
if not conj_type == 'irreg' then checkirregular(args, data) end
return data, typeinfo
end
end


-- Given an ending (or possibly a full regex matching the entire lemma, if
local function form_contains(forms, form)
-- a regex group is present), return the base minus the ending, or nil if
if type(forms) == "string" then
-- the ending doesn't match.
return forms == form
local function extract_base(lemma, ending)
if ending:find("%(") then
return rmatch(lemma, ending)
else
else
return rmatch(lemma, "^(.*)" .. ending .. "$")
return m_table.contains(forms, form)
end
end
end
end


-- Given ENDINGS_AND_SUBTYPES (a list of pairs of endings with associated
-- Add a value to a given form key, e.g. "1s_pres_actv_indc". If the
-- subtypes, where each pair consists of a single ending spec and a list of
-- value is already present in the key, it won't be added again.
-- subtypes), check each ending in turn against LEMMA. If it matches, return
--
-- the pair BASE, STEM2, SUBTYPES where BASE is the remainder of LEMMA minus
-- The value is formed by concatenating STEM and SUF. SUF can be a list,
-- the ending, STEM2 is as passed in, and SUBTYPES is the subtypes associated
-- in which case STEM will be concatenated in turn to each value in the
-- with the ending. But don't return SUBTYPES if any of the subtypes in the
-- list and all the resulting forms added to the key.
-- list is specifically canceled in SPECIFIED_SUBTYPES (a set, i.e. a table
-- where the keys are strings and the value is always true); instead, consider
-- the next ending in turn. 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 nil, nil, nil.
--
--
-- The ending spec in ENDINGS_AND_SUBTYPES is one of the following:
-- POS is the position to insert the form(s) at; default is at the end.
-- To insert at the beginning specify 1 for POS.
local function add_form(data, key, stem, suf, pos)
if not suf then
return
end
if type(suf) ~= "table" then
suf = {suf}
end
for _, s in ipairs(suf) do
if not data.forms[key] then
data.forms[key] = {}
elseif type(data.forms[key]) == "string" then
data.forms[key] = {data.forms[key]}
end
m_table.insertIfNot(data.forms[key], stem .. s, pos)
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.
--
--
-- 1. A simple string, e.g. "tūdō", specifying an ending.
-- The value for a given person/number combination is formed by concatenating
-- 2. A regex that should match the entire lemma (it should be anchored at
-- STEM and the appropriate suffix for that person/number, e.g. SUF1S. The
--   the beginning with ^ and at the end with $), and contains a single
-- suffix can be a list, in which case STEM will be concatenated in turn to
--    capturing group to match the base.
-- each value in the list and all the resulting forms added to the key. To
-- 3. A pair {SIMPLE_STRING_OR_REGEX, STEM2_ENDING} where
-- not add a value for a specific person/number, specify nil or {} for the
--    SIMPLE_STRING_OR_REGEX is one of the previous two possibilities and
-- suffix for the person/number.
--   STEM2_ENDING is a string specifying the corresponding ending that must
local function add_forms(data, keytype, stem, suf1s, suf2s, suf3s, suf1p, suf2p, suf3p)
--    be present in STEM2. If this form is used, the combination of
add_form(data, "1s_" .. keytype, stem, suf1s)
--    base + STEM2_ENDING must exactly match STEM2 in order for this entry
add_form(data, "2s_" .. keytype, stem, suf2s)
--    to be considered a match. An example is {"is", ""}, which will match
add_form(data, "3s_" .. keytype, stem, suf3s)
--    lemma == "follis", stem2 == "foll", but not lemma == "lapis",
add_form(data, "1p_" .. keytype, stem, suf1p)
--    stem2 == "lapid".
add_form(data, "2p_" .. keytype, stem, suf2p)
local function get_noun_subtype_by_ending(lemma, stem2, decltype, specified_subtypes,
add_form(data, "3p_" .. keytype, stem, suf3p)
endings_and_subtypes)
end
for _, ending_and_subtypes in ipairs(endings_and_subtypes) do
 
local ending = ending_and_subtypes[1]
-- Add a value to the 2nd person (singular and plural) of a given
local subtypes = ending_and_subtypes[2]
-- tense/voice/mood. This works like add_forms().
not_this_subtype = false
local function add_2_forms(data, keytype, stem, suf2s, suf2p)
if specified_subtypes.pl and not m_table.contains(subtypes, "pl") then
add_form(data, "2s_" .. keytype, stem, suf2s)
-- We now require that plurale tantum terms specify a plural-form lemma.
add_form(data, "2p_" .. keytype, stem, suf2p)
-- The autodetected subtypes will include 'pl' for such lemmas; if not,
end
-- we fail this entry.
 
not_this_subtype = true
-- Add a value to the 2nd and 3rd persons (singular and plural) of a given
else
-- tense/voice/mood. This works like add_forms().
for _, subtype in ipairs(subtypes) do
local function add_23_forms(data, keytype, stem, suf2s, suf3s, suf2p, suf3p)
-- A subtype is directly canceled by specifying -SUBTYPE.
add_form(data, "2s_" .. keytype, stem, suf2s)
-- In addition, M or F as a subtype is canceled by N, and
add_form(data, "3s_" .. keytype, stem, suf3s)
-- vice-versa, but M doesn't cancel F or vice-versa; instead,
add_form(data, "2p_" .. keytype, stem, suf2p)
-- we simply ignore the conflicting gender specification when
add_form(data, "3p_" .. keytype, stem, suf3p)
-- constructing the combination of specified and inferred subtypes.
end
-- The reason for this is that neuters have distinct declensions
 
-- from masculines and feminines, but masculines and feminines have
-- Clear out all forms from a given key (e.g. "1s_pres_actv_indc").
-- the same declension, and various nouns in Latin that are
local function clear_form(data, key)
-- normally masculine are exceptionally feminine and vice-versa
data.forms[key] = nil
-- (nauta, agricola, fraxinus, malus "apple tree", manus, rēs,
end
-- etc.).
 
--
-- Clear out all forms from all persons/numbers a given tense/voice/mood
-- In addition, sg as a subtype is canceled by pl and vice-versa.
-- (e.g. "pres_actv_indc").
-- It's also possible to specify both, which will override sg but
local function clear_forms(data, keytype)
-- not cancel it (in the sense that it won't prevent the relevant
clear_form(data, "1s_" .. keytype)
-- rule from matching). For example, there's a rule specifying that
clear_form(data, "2s_" .. keytype)
-- lemmas beginning with a capital letter and ending in -ius take
clear_form(data, "3s_" .. keytype)
-- the ius.voci.sg subtypes. Specifying such a lemma with the
clear_form(data, "1p_" .. keytype)
-- subtype both will result in the ius.voci.both subtypes, whereas
clear_form(data, "2p_" .. keytype)
-- specifying such a lemma with the subtype pl will cause this rule
clear_form(data, "3p_" .. keytype)
-- not to match, and it will fall through to a less specific rule
end
-- that returns just the ius subtype, which will be combined with
 
-- the explicitly specified pl subtype to produce ius.pl.
local function make_perfect_passive(data)
if specified_subtypes["-" .. subtype] or
local ppp = data.forms["perf_pasv_ptc"]
subtype == "N" and (specified_subtypes.M or specified_subtypes.F) or
if type(ppp) ~= "table" then
(subtype == "M" or subtype == "F") and specified_subtypes.N or
ppp = {ppp}
subtype == "sg" and specified_subtypes.pl or
end
subtype == "pl" and specified_subtypes.sg then
local ppplinks = {}
not_this_subtype = true
for _, pppform in ipairs(ppp) do
break
table.insert(ppplinks, make_link({lang = lang, alt = '', term = pppform}, "term"))
end
end
end
local ppplink = table.concat(ppplinks, " or ")
local sumlink = make_link({lang = lang, alt = '', term = "sum"}, "term")
 
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 prefix_joiner = data.passive_prefix and data.passive_prefix:find(" $") and "+ " or ""
local suffix_joiner = data.passive_suffix and data.passive_suffix:find("^ ") and " +" or ""
for slot, text in pairs(text_for_slot) do
data.forms[slot] =
(data.passive_prefix or "") .. prefix_joiner .. ppplink .. " + " ..
text .. " of " .. sumlink .. suffix_joiner .. (data.passive_suffix or "")
end
ppp = data.forms["1s_pres_actv_indc"]
if type(ppp) ~= "table" then
ppp = {ppp}
end
if ppp[1] == "faciō" then
ppp = {"factum"}
ppplinks = {}
for _, pppform in ipairs(ppp) do
table.insert(ppplinks, make_link({lang = lang, alt = '', term = pppform}, "term"))
end
ppplink = table.concat(ppplinks, " or ")
sumlink = make_link({lang = lang, alt = '', term = "sum"}, "term")
for slot, text in pairs(text_for_slot) do
data.forms[slot] =
data.forms[slot] .. " or " .. ppplink .. " + " .. text .. " of " .. sumlink
end
end
if not not_this_subtype then
end
if type(ending) == "table" then
end
local lemma_ending = ending[1]
 
local stem2_ending = ending[2]
-- Make the gerund and gerundive/future passive participle. For the forms
local base = extract_base(lemma, lemma_ending)
-- labeled "gerund", we generate both gerund and gerundive variants if there's
if base and base .. stem2_ending == stem2 then
-- a case-specific prefix or suffix for the case in question; otherwise we
return base, stem2, subtypes
-- 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. NO_GERUND means to skip generating any
-- gerunds (and gerundive variants). NO_FUTR_PASV_PTC means to skip generating
-- the future passive participle.
local function make_gerund(data, typeinfo, base, und_variant, no_gerund, no_futr_pasv_ptc)
local neut_endings = {
nom = "um",
gen = "ī",
dat = "ō",
acc = "um",
abl = "ō",
}
 
local endings
if typeinfo.subtypes.f then
endings = {
nom = "a",
gen = "ae",
dat = "ae",
acc = "am",
abl = "ā",
}
elseif typeinfo.subtypes.n then
endings = neut_endings
elseif typeinfo.subtypes.mp then
endings = {
nom = "ī",
gen = "ōrum",
dat = "īs",
acc = "ōs",
abl = "īs",
}
elseif typeinfo.subtypes.fp then
endings = {
nom = "ae",
gen = "ārum",
dat = "īs",
acc = "ās",
abl = "īs",
}
elseif typeinfo.subtypes.np then
endings = {
nom = "a",
gen = "ōrum",
dat = "īs",
acc = "a",
abl = "īs",
}
else
endings = {
nom = "us",
gen = "ī",
dat = "ō",
acc = "um",
abl = "ō",
}
end
 
if find(base, "[uv]end$") or typeinfo.subtypes.nound 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
und_variant = false
end
local und_base = und_variant and base:gsub("end$", "und")
for case, ending in pairs(endings) do
if case == "nom" then
if not no_futr_pasv_ptc then
if typeinfo.subtypes.passimpers then
ending = "um"
end
end
else
add_form(data, "futr_pasv_ptc", "", base .. ending)
local base = extract_base(lemma, ending)
if und_base then
if base then
add_form(data, "futr_pasv_ptc", "", und_base .. ending)
return base, stem2, subtypes
end
end
end
elseif (data[case .. "_prefix"] or data[case .. "_suffix"]) and not no_gerund then
add_form(data, "ger_" .. case, "", (data[case .. "_prefix"] or "")
.. base .. ending .. (data[case .. "_suffix"] or ""))
if und_base then
add_form(data, "ger_" .. case, "", (data[case .. "_prefix"] or "")
.. und_base .. ending .. (data[case .. "_suffix"] or ""))
end
end
end
end
end
end
if decltype then
if not no_gerund then
error("Unrecognized ending for declension-" .. decltype .. " noun: " .. lemma)
for case, ending in pairs(neut_endings) do
add_form(data, "ger_" .. case, "",
(data.prefix or  "") .. base .. ending .. (data.suffix or  ""))
end
end
end
return nil, nil, nil
end
end


-- Autodetect the subtype of a noun given all the information specified by the
postprocess = function(data, typeinfo)
-- user: lemma, stem2, declension type and specified subtypes. Three values are
-- Maybe clear out the supine-derived forms (except maybe for the
-- returned: the lemma base (i.e. the stem of the lemma, as required by the
-- future active participle). Do this first because some code below
-- declension functions), the new stem2 and the autodetected subtypes. Note
-- looks at the perfect participle to derive other forms.
-- that this will not detect a given subtype if the explicitly specified
if typeinfo.subtypes.nosup then
-- subtypes are incompatible (i.e. if -SUBTYPE is specified for any subtype
-- Some verbs have no supine forms or forms derived from the supine
-- that would be returned; or if M or F is specified when N would be returned,
if typeinfo.subtypes.perfaspres == nil then
-- and vice-versa; or if pl is specified when sg would be returned, and
m_table.insertIfNot(data.title, "no [[supine]] stem")
-- vice-versa).
end
--
m_table.insertIfNot(data.categories, "Latin verbs with missing supine stem")
-- NOTE: This function has intimate knowledge of the way that the declension
m_table.insertIfNot(data.categories, "Latin defective verbs")
-- functions handle subtypes, particularly for the third declension.
 
local function detect_noun_subtype(lemma, stem2, typ, subtypes)
for key, _ in pairs(data.forms) do
local base, ending
if cfind(key, "sup") or (
key == "perf_actv_ptc" or key == "perf_pasv_ptc" or key == "perf_pasv_inf" or
key == "futr_actv_ptc" or key == "futr_actv_inf" or key == "futr_pasv_inf" or
(typeinfo.subtypes.depon or typeinfo.subtypes.semidepon or
typeinfo.subtypes.optsemidepon) and key == "perf_actv_inf"
) then
data.forms[key] = nil
end
end
elseif typeinfo.subtypes.supfutractvonly then
-- Some verbs have no supine forms or forms derived from the supine,
-- except for the future active infinitive/participle
if typeinfo.subtypes.perfaspres == nil then
m_table.insertIfNot(data.title, "no [[supine]] stem except in the [[future]] [[active]] [[participle]]")
end
m_table.insertIfNot(data.categories, "Latin verbs with missing supine stem except in the future active participle")
m_table.insertIfNot(data.categories, "Latin defective verbs")


if typ == "1" then
for key, _ in pairs(data.forms) do
return get_noun_subtype_by_ending(lemma, stem2, typ, subtypes, {
if cfind(key, "sup") or (
{"ām", {"F", "am"}},
key == "perf_actv_ptc" or key == "perf_pasv_ptc" or key == "perf_pasv_inf" or
{"ās", {"M", "Greek", "Ma"}},
key == "futr_pasv_inf"
{"ēs", {"M", "Greek", "Me"}},
) then
{"ē", {"F", "Greek"}},
data.forms[key] = nil
{"ae", {"F", "pl"}},
end
{"a", {"F"}},
end
})
end
elseif typ == "2" then
local detected_subtypes
-- Add information for the passive perfective forms
lemma, stem2, detected_subtypes = get_noun_subtype_by_ending(lemma, stem2, typ, subtypes, {
if data.forms["perf_pasv_ptc"] and not form_is_empty(data.forms["perf_pasv_ptc"]) then
{"^(.*r)$", {"M", "er"}},
if typeinfo.subtypes.passimpers then
{"^(.*v)os$", {"M", "vos"}},
-- this should always be a table because it's generated only in
{"^(.*v)om$", {"N", "vom"}},
-- make_supine()
-- If the lemma ends in -os and the user said N or -M, then the
local pppforms = data.forms["perf_pasv_ptc"]
-- following won't apply, and the second (neuter) -os will applly.
for _, ppp in ipairs(pppforms) do
{"os", {"M", "Greek"}},
if not form_is_empty(ppp) then
{"os", {"N", "Greek", "us"}},
-- make_supine() already generated the neuter form of the PPP.
{"on", {"N", "Greek"}},
local nns_ppp = make_raw_link(ppp)
-- -ius beginning with a capital letter is assumed a proper name,
add_form(data, "3s_perf_pasv_indc", nns_ppp, " [[est]]")
-- and takes the voci subtype (vocative in -ī) along with the ius
add_form(data, "3s_futp_pasv_indc", nns_ppp, " [[erit]]")
-- subtype and sg-only. Other nouns in -ius just take the ius
add_form(data, "3s_plup_pasv_indc", nns_ppp, " [[erat]]")
-- subtype. Explicitly specify "sg" so that if .pl is given,
add_form(data, "3s_perf_pasv_subj", nns_ppp, " [[sit]]")
-- this rule won't apply.
add_form(data, "3s_plup_pasv_subj", nns_ppp, {" [[esset]]", " [[foret]]"})
{"^([A-ZĀĒĪŌŪȲĂĔĬŎŬ].*)ius$", {"M", "ius", "voci", "sg"}},
{"ius", {"M", "ius"}},
{"ium", {"N", "ium"}},
-- If the lemma ends in -us and the user said N or -M, then the
-- following won't apply, and the second (neuter) -us will applly.
{"us", {"M"}},
{"us", {"N", "us"}},
{"um", {"N"}},
{"iī", {"M", "ius", "pl"}},
{"ia", {"N", "ium", "pl"}},
-- If the lemma ends in -ī and the user said N or -M, then the
-- following won't apply, and the second (neuter) -ī will applly.
{"ī", {"M", "pl"}},
{"ī", {"N", "us", "pl"}},
{"a", {"N", "pl"}},
})
stem2 = stem2 or lemma
return lemma, stem2, detected_subtypes
elseif typ == "3" then
if subtypes.pl then
if subtypes.Greek then
base = rmatch(lemma, "^(.*)erēs$")
if base then
return base .. "ēr", base .. "er", {"er"}
end
end
base = rmatch(lemma, "^(.*)ontēs$")
end
if base then
elseif typeinfo.subtypes.pass3only then
return base .. "ōn", base .. "ont", {"on"}
local pppforms = data.forms["perf_pasv_ptc"]
if type(pppforms) ~= "table" then
pppforms = {pppforms}
end
for _, ppp in ipairs(pppforms) do
if not form_is_empty(ppp) then
local ppp_s, ppp_p
if typeinfo.subtypes.mp then
ppp_p = make_raw_link(gsub1(ppp, "ī$", "us"), ppp)
elseif typeinfo.subtypes.fp then
ppp_p = make_raw_link(gsub1(ppp, "ae$", "us"), ppp)
elseif typeinfo.subtypes.np then
ppp_p = make_raw_link(gsub1(ppp, "a$", "us"), ppp)
elseif typeinfo.subtypes.f then
local ppp_lemma = gsub1(ppp, "a$", "us")
ppp_s = make_raw_link(ppp_lemma, ppp)
ppp_p = make_raw_link(ppp_lemma, gsub1(ppp, "a$", "ae"))
elseif typeinfo.subtypes.n then
local ppp_lemma = gsub1(ppp, "um$", "us")
ppp_s = make_raw_link(ppp_lemma, ppp)
ppp_p = make_raw_link(ppp_lemma, gsub1(ppp, "um$", "a"))
else
ppp_s = make_raw_link(ppp)
ppp_p = make_raw_link(ppp, gsub1(ppp, "us$", "ī"))
end
if not typeinfo.subtypes.mp and not typeinfo.subtypes.fp and not typeinfo.subtypes.np then
add_form(data, "3s_perf_pasv_indc", ppp_s, " [[est]]")
add_form(data, "3s_futp_pasv_indc", ppp_s, " [[erit]]")
add_form(data, "3s_plup_pasv_indc", ppp_s, " [[erat]]")
add_form(data, "3s_perf_pasv_subj", ppp_s, " [[sit]]")
add_form(data, "3s_plup_pasv_subj", ppp_s, {" [[esset]]", " [[foret]]"})
end
add_form(data, "3p_perf_pasv_indc", ppp_p, " [[sunt]]")
add_form(data, "3p_futp_pasv_indc", ppp_p, " [[erunt]]")
add_form(data, "3p_plup_pasv_indc", ppp_p, " [[erant]]")
add_form(data, "3p_perf_pasv_subj", ppp_p, " [[sint]]")
add_form(data, "3p_plup_pasv_subj", ppp_p, {" [[essent]]", " [[forent]]"})
end
end
base = rmatch(lemma, "^(.*)es$")
if base then
return "foo", stem2 or base, {}
end
error("Unrecognized ending for declension-3 plural Greek noun: " .. lemma)
end
end
base = rmatch(lemma, "^(.*)ia$")
else
if base then
make_perfect_passive(data)
return "foo", stem2 or base, {"N", "I", "pure"}
end
end
 
if typeinfo.subtypes.perfaspres then
-- Perfect forms as present tense
m_table.insertIfNot(data.title, "no [[present tense|present]] stem")
if typeinfo.subtypes.nosup then
m_table.insertIfNot(data.title, "no [[supine]] stem")
elseif typeinfo.subtypes.supfutractvonly then
m_table.insertIfNot(data.title, "no [[supine]] stem except in the [[future]] [[active]] [[participle]]")
end
m_table.insertIfNot(data.title, "active only")
m_table.insertIfNot(data.title, "[[perfect]] forms as present")
m_table.insertIfNot(data.title, "pluperfect as imperfect")
m_table.insertIfNot(data.title, "future perfect as future")
m_table.insertIfNot(data.categories, "Latin defective verbs")
m_table.insertIfNot(data.categories, "Latin active-only verbs")
m_table.insertIfNot(data.categories, "Latin verbs with missing present stem")
        m_table.insertIfNot(data.categories, "Latin verbs with perfect forms having imperfective meanings")
 
-- Change perfect passive participle to perfect active participle
data.forms["perf_actv_ptc"] = data.forms["perf_pasv_ptc"]
 
-- Change perfect active infinitive to present active infinitive
data.forms["pres_actv_inf"] = data.forms["perf_actv_inf"]
 
-- Remove passive forms
-- Remove present active, imperfect active and future active forms
for key, _ in pairs(data.forms) do
if key ~= "futr_actv_inf" and key ~= "futr_actv_ptc" and (
cfind(key, "pasv") or cfind(key, "pres") and key ~= "pres_actv_inf" or
cfind(key, "impf") or cfind(key, "futr")
) then
data.forms[key] = nil
end
end
 
-- Change perfect forms to non-perfect forms
for key, form in pairs(data.forms) do
if cfind(key, "perf") and key ~= "perf_actv_ptc" then
data.forms[key:gsub("perf", "pres")] = form
data.forms[key] = nil
elseif cfind(key, "plup") then
data.forms[key:gsub("plup", "impf")] = form
data.forms[key] = nil
elseif cfind(key, "futp") then
data.forms[key:gsub("futp", "futr")] = form
data.forms[key] = nil
elseif cfind(key, "ger") then
data.forms[key] = nil
end
end
 
data.forms["pres_actv_ptc"] = nil
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.
if typeinfo.subtypes.impers then
-- Impersonal verbs have only third-person singular forms.
m_table.insertIfNot(data.title, "[[impersonal]]")
m_table.insertIfNot(data.categories, "Latin impersonal verbs")
 
-- Remove all non-3sg forms
for key, _ in pairs(data.forms) do
if key:find("^[12][sp]") or key:find("^3p") then
data.forms[key] = nil
end
end
base = rmatch(lemma, "^(.*)a$")
end
if base then
elseif typeinfo.subtypes["3only"] then
return "foo", stem2 or base, {"N"}
m_table.insertIfNot(data.title, "[[third person]] only")
m_table.insertIfNot(data.categories, "Latin third-person-only verbs")
 
-- Remove all non-3sg forms
for key, _ in pairs(data.forms) do
if key:find("^[12][sp]") then
data.forms[key] = nil
end
end
base = rmatch(lemma, "^(.*)ēs$")
end
if base then
end
return "foo", stem2 or base, {}
 
if typeinfo.subtypes.nopasvperf and not typeinfo.subtypes.nosup and
not typeinfo.subtypes.supfutractvonly then
-- Some verbs have no passive perfect forms (e.g. ārēscō, -ěre).
-- Only do anything here if the verb has a supine; otherwise it
-- necessarily has no passive perfect forms.
m_table.insertIfNot(data.title, "no passive perfect forms")
m_table.insertIfNot(data.categories, "Latin defective verbs")
 
-- Remove all passive perfect forms
for key, _ in pairs(data.forms) do
if cfind(key, "pasv") and (cfind(key, "perf") or cfind(key, "plup") or cfind(key, "futp")) then
data.forms[key] = nil
end
end
error("Unrecognized ending for declension-3 plural noun: " .. lemma)
end
end
end
-- Handle certain irregularities in the passive
if typeinfo.subtypes.optsemidepon then
-- 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. We already generated the perfective
-- forms but need to clear out the imperfective passive.
m_table.insertIfNot(data.title, "optionally [[semi-deponent]]")
m_table.insertIfNot(data.categories, "Latin semi-deponent verbs")
m_table.insertIfNot(data.categories, "Latin optionally semi-deponent verbs")


stem2 = stem2 or m_la_utilities.make_stem2(lemma)
-- Remove imperfective passive forms
local detected_subtypes
for key, _ in pairs(data.forms) do
if subtypes.Greek then
if cfind(key, "pres_pasv") or cfind(key, "impf_pasv") or cfind(key, "futr_pasv") then
base, _, detected_subtypes = get_noun_subtype_by_ending(lemma, stem2, nil, subtypes, {
data.forms[key] = nil
{{"is", ""}, {"I"}},
{"ēr", {"er"}},
{"ōn", {"on"}},
})
if base then
return lemma, stem2, detected_subtypes
end
end
return lemma, stem2, {}
end
end
elseif typeinfo.subtypes.semidepon then
-- Semi-deponent verbs use perfective passive forms with active meaning,
-- and have no imperfective passive
m_table.insertIfNot(data.title, "[[semi-deponent]]")
m_table.insertIfNot(data.categories, "Latin semi-deponent verbs")


if not subtypes.N then
-- Remove perfective active and imperfective passive forms
base, _, detected_subtypes = get_noun_subtype_by_ending(lemma, stem2, nil, subtypes, {
for key, _ in pairs(data.forms) do
{{"^([A-ZĀĒĪŌŪȲĂĔĬŎŬ].*pol)is$", ""}, {"F", "polis", "sg", "loc"}},
if cfind(key, "perf_actv") or cfind(key, "plup_actv") or cfind(key, "futp_actv") or cfind(key, "pres_pasv") or cfind(key, "impf_pasv") or cfind(key, "futr_pasv") then
{{"tūdō", "tūdin"}, {"F"}},
data.forms[key] = nil
{{"tās", "tāt"}, {"F"}},
{{"tūs", "tūt"}, {"F"}},
{{"tiō", "tiōn"}, {"F"}},
{{"siō", "siōn"}, {"F"}},
{{"xiō", "xiōn"}, {"F"}},
{{"gō", "gin"}, {"F"}},
{{"or", "ōr"}, {"M"}},
{{"trīx", "trīc"}, {"F"}},
{{"trix", "trīc"}, {"F"}},
{{"is", ""}, {"I"}},
{{"^([a-zāēīōūȳăĕĭŏŭ].*)ēs$", ""}, {"I"}},
})
if base then
return lemma, stem2, detected_subtypes
end
end
end
end


base, _, detected_subtypes = get_noun_subtype_by_ending(lemma, stem2, nil, subtypes, {
-- Change perfective passive to active
{{"us", "or"}, {"N"}},
for key, form in pairs(data.forms) do
{{"us", "er"}, {"N"}},
if cfind(key, "perf_pasv") or cfind(key, "plup_pasv") or cfind(key, "futp_pasv") then
{{"ma", "mat"}, {"N"}},
data.forms[key:gsub("pasv", "actv")] = form
{{"men", "min"}, {"N"}},
data.forms[key] = nil
{{"^([A-ZĀĒĪŌŪȲĂĔĬŎŬ].*)e$", ""}, {"N", "sg"}},
end
{{"e", ""}, {"N", "I", "pure"}},
{{"al", "āl"}, {"N", "I", "pure"}},
{{"ar", "ār"}, {"N", "I", "pure"}},
})
if base then
return lemma, stem2, detected_subtypes
end
end
return lemma, stem2, {}
elseif typeinfo.subtypes.depon then
elseif typ == "4" then
-- Deponent verbs use passive forms with active meaning
if subtypes.echo or subtypes.argo or subtypes.Callisto then
m_table.insertIfNot(data.title, "[[deponent]]")
base = rmatch(lemma, "^(.*)ō$")
m_table.insertIfNot(data.categories, "Latin deponent verbs")
if not base then
 
error("Declension-4 noun of subtype .echo, .argo or .Callisto should end in -ō: " .. lemma)
-- Remove active forms and future passive infinitive
for key, _ in pairs(data.forms) do
if cfind(key, "actv") and key ~= "pres_actv_ptc" and key ~= "futr_actv_ptc" and key ~= "futr_actv_inf" and cfind(key, "sigf") == nil or key == "futr_pasv_inf" then
data.forms[key] = nil
end
end
if subtypes.Callisto then
end
return base, nil, {"F", "sg"}
 
else
-- Change passive to active
return base, nil, {"F"}
for key, form in pairs(data.forms) do
if cfind(key, "pasv") and key ~= "pres_pasv_ptc" and key ~= "futr_pasv_ptc" and key ~= "futr_pasv_inf" then
data.forms[key:gsub("pasv", "actv")] = form
data.forms[key] = nil
end
end
end
end
return get_noun_subtype_by_ending(lemma, stem2, typ, subtypes, {
{"us", {"M"}},
{"ū", {"N"}},
{"ūs", {"M", "pl"}},
{"ua", {"N", "pl"}},
})
elseif typ == "5" then
return get_noun_subtype_by_ending(lemma, stem2, typ, subtypes, {
{"iēs", {"F", "i"}},
{"iēs", {"F", "i", "pl"}},
{"ēs", {"F"}},
{"ēs", {"F", "pl"}},
})
elseif typ == "irreg" and lemma == "domus" then
-- [[domus]] auto-sets data.loc = true, but we need to know this
-- before declining the noun so we can propagate it to other segments.
return lemma, nil, {"loc"}
elseif typ == "indecl" or typ == "irreg" and (
lemma == "Deus" or lemma == "Iēsus" or lemma == "Jēsus" or
lemma == "Athōs" or lemma == "vēnum"
) then
-- Indeclinable nouns, and certain irregular nouns, set data.num = "sg",
-- but we need to know this before declining the noun so we can
-- propagate it to other segments.
return lemma, nil, {"sg"}
else
return lemma, nil, {}
end
end
end


function export.detect_noun_subtype(frame)
if typeinfo.subtypes.noperf then
local params = {
-- Some verbs have no perfect stem (e.g. inalbēscō, -ěre)
[1] = {required = true},
m_table.insertIfNot(data.title, "no [[perfect tense|perfect]] stem")
[2] = {},
m_table.insertIfNot(data.categories, "Latin verbs with missing perfect stem")
[3] = {},
m_table.insertIfNot(data.categories, "Latin defective verbs")
[4] = {},
 
}
-- Remove all active perfect forms (passive perfect forms may
local args = m_para.process(frame.args, params)
-- still exist as they are formed with the supine stem)
local specified_subtypes = {}
for key, _ in pairs(data.forms) do
if args[4] then
if cfind(key, "actv") and (cfind(key, "perf") or cfind(key, "plup") or cfind(key, "futp")) then
for _, subtype in ipairs(rsplit(args[4], ".")) do
data.forms[key] = nil
specified_subtypes[subtype] = true
end
end
end
end
end
local base, stem2, subtypes = detect_noun_subtype(args[1], args[2], args[3], specified_subtypes)
return base .. "|" .. (stem2 or "") .. "|" .. table.concat(subtypes, ".")
end


-- Given ENDINGS_AND_SUBTYPES (a list of four-tuples of ENDING, RETTYPE,
if typeinfo.subtypes.nopass then
-- SUBTYPES, PROCESS_RETVAL), check each ENDING in turn against LEMMA and
-- Remove all passive forms
-- STEM2. If it matches, return a four-tuple BASE, STEM2, RETTYPE, NEW_SUBTYPES
m_table.insertIfNot(data.title, "active only")
-- where BASE is normally the remainder of LEMMA minus the ending, STEM2 is
m_table.insertIfNot(data.categories, "Latin active-only verbs")
-- as passed in, RETTYPE is as passed in, and NEW_SUBTYPES is the same as
 
-- SUBTYPES minus any subtypes beginning with a hyphen. If no endings match,
-- Remove all non-3sg and passive forms
-- throw an error if DECLTYPPE is non-nil, mentioning the DECLTYPE
for key, _ in pairs(data.forms) do
-- (user-specified declension); but if DECLTYPE is nil, just return the tuple
if cfind(key, "pasv") then
-- nil, nil, nil, nil.
data.forms[key] = nil
--
end
-- In order for a given entry to match, ENDING must match and also the subtypes
end
-- in SUBTYPES (a list) must not be incompatible with the passed-in
elseif typeinfo.subtypes.pass3only then
-- user-specified subtypes SPECIFIED_SUBTYPES (a set, i.e. a table where the
-- Some verbs have only third-person forms in the passive
-- keys are strings and the value is always true). "Incompatible" means that
m_table.insertIfNot(data.title, "only third-person forms in passive")
-- a given SUBTYPE is specified in either one and -SUBTYPE in the other, or
m_table.insertIfNot(data.categories, "Latin verbs with third-person passive")
-- that "pl" is found in SPECIFIED_SUBTYPES and not in SUBTYPES.
 
--
-- Remove all non-3rd-person passive forms and all passive imperatives
-- The ending spec in ENDINGS_AND_SUBTYPES is one of the following:
for key, _ in pairs(data.forms) do
--
if cfind(key, "pasv") and (key:find("^[12][sp]") or cfind(key, "impr")) then
-- 1. A simple string, e.g. "tūdō", specifying an ending.
data.forms[key] = nil
-- 2. A regex that should match the entire lemma (it should be anchored at
end
--   the beginning with ^ and at the end with $), and contains a single
-- For phrasal verbs with a plural complement, also need to erase the
--    capturing group to match the base.
-- 3s forms.
-- 3. A pair {SIMPLE_STRING_OR_REGEX, STEM2_ENDING} where
if typeinfo.subtypes.mp or typeinfo.subtypes.fp or typeinfo.subtypes.np then
--    SIMPLE_STRING_OR_REGEX is one of the previous two possibilities and
if cfind(key, "pasv") and key:find("^3s") then
--    STEM2_ENDING is a string specifying the corresponding ending that must
data.forms[key] = nil
--    be present in STEM2. If this form is used, the combination of
--    base + STEM2_ENDING must exactly match STEM2 in order for this entry
--    to be considered a match. An example is {"is", ""}, which will match
--   lemma == "follis", stem2 == "foll", but not lemma == "lapis",
--    stem2 == "lapid".
--
-- If PROCESS_STEM2 is given and the returned STEM2 would be nil, call
-- process_stem2(BASE) to get the STEM2 to return.
local function get_adj_type_and_subtype_by_ending(lemma, stem2, decltype,
specified_subtypes, endings_and_subtypes, process_stem2)
for _, ending_and_subtypes in ipairs(endings_and_subtypes) do
local ending = ending_and_subtypes[1]
local rettype = ending_and_subtypes[2]
local subtypes = ending_and_subtypes[3]
local process_retval = ending_and_subtypes[4]
not_this_subtype = false
if specified_subtypes.pl and not m_table.contains(subtypes, "pl") then
-- We now require that plurale tantum terms specify a plural-form lemma.
-- The autodetected subtypes will include 'pl' for such lemmas; if not,
-- we fail this entry.
not_this_subtype = true
else
for _, subtype in ipairs(subtypes) do
-- A subtype is directly canceled by specifying -SUBTYPE.
if specified_subtypes["-" .. subtype] then
not_this_subtype = true
break
end
-- A subtype is canceled if the user specified SUBTYPE and
-- -SUBTYPE is given in the to-be-returned subtypes.
local must_not_be_present = rmatch(subtype, "^%-(.*)$")
if must_not_be_present and specified_subtypes[must_not_be_present] then
not_this_subtype = true
break
end
end
end
end
end
end
if not not_this_subtype then
elseif typeinfo.subtypes.passimpers then
local base
-- Some verbs are impersonal in the passive
if type(ending) == "table" then
m_table.insertIfNot(data.title, "[[impersonal]] in passive")
local lemma_ending = ending[1]
m_table.insertIfNot(data.categories, "Latin verbs with impersonal passive")
local stem2_ending = ending[2]
 
base = extract_base(lemma, lemma_ending)
-- Remove all non-3sg passive forms
if base and base .. stem2_ending ~= stem2 then
for key, _ in pairs(data.forms) do
base = nil
if cfind(key, "pasv") and (key:find("^[12][sp]") or key:find("^3p") or cfind(key, "impr")) or cfind(key, "futr_pasv_inf") then
end
data.forms[key] = nil
else
end
base = extract_base(lemma, ending)
end
end
 
-- Handle certain irregularities in the imperative
if typeinfo.subtypes.noimp then
-- Some verbs have no imperatives
m_table.insertIfNot(data.title, "no [[imperative]]s")
m_table.insertIfNot(data.categories, "Latin verbs with missing imperative")
m_table.insertIfNot(data.categories, "Latin defective verbs")
 
-- Remove all imperative forms
for key, _ in pairs(data.forms) do
if cfind(key, "impr") then
data.forms[key] = nil
end
end
end
 
-- Handle certain irregularities in the future
if typeinfo.subtypes.nofut then
-- Some verbs (e.g. soleō) have no future
m_table.insertIfNot(data.title, "no [[future]]")
m_table.insertIfNot(data.categories, "Latin verbs with missing future")
m_table.insertIfNot(data.categories, "Latin defective verbs")
 
-- Remove all future forms
for key, _ in pairs(data.forms) do
if cfind(key, "fut") then -- handles futr = future and futp = future perfect
data.forms[key] = nil
end
end
end
 
-- Add the ancient future_passive_participle of certain verbs
-- if typeinfo.pres_stem == "lāb" then
-- data.forms["futr_pasv_ptc"] = "lābundus"
-- elseif typeinfo.pres_stem == "collāb" then
-- data.forms["futr_pasv_ptc"] = "collābundus"
-- elseif typeinfo.pres_stem == "illāb" then
-- data.forms["futr_pasv_ptc"] = "illābundus"
-- elseif typeinfo.pres_stem == "relāb" then
-- data.forms["futr_pasv_ptc"] = "relābundus"
-- end
 
-- Add the poetic present passive infinitive forms of certain verbs
if typeinfo.subtypes.p3inf then
local is_depon = typeinfo.subtypes.depon
local form = "pres_" .. (is_depon and "actv" or "pasv") .. "_inf"
local noteindex = #(data.footnotes) + 1
local formval = data.forms[form]
if type(formval) ~= "table" then
formval = {formval}
end
local newvals = mw.clone(formval)
for _, fv in ipairs(formval) do
table.insert(newvals, sub(fv, 1, -2) .. "ier")
end
data.forms[form] = newvals
data.form_footnote_indices[form] = tostring(noteindex)
data.footnotes[noteindex] = 'The present passive infinitive in -ier is a rare poetic form which is attested.'
end
 
--Add the syncopated perfect forms, omitting the separately handled fourth conjugation cases
 
if typeinfo.subtypes.poetsyncperf then
local sss = {
--infinitive
{'perf_actv_inf', 'sse'},
--perfect actives
    {'2s_perf_actv_indc', 'stī'},
    {'3s_perf_actv_indc', 't'},
    {'1p_perf_actv_indc', 'mus'},
{'2p_perf_actv_indc', 'stis'},
{'3p_perf_actv_indc', 'runt'},
--pluperfect actives
    {'1s_plup_actv_indc', 'ram'},
    {'2s_plup_actv_indc', 'rās'},
    {'3s_plup_actv_indc', 'rat'},
    {'1p_plup_actv_indc', 'rāmus'},
{'2p_plup_actv_indc', 'rātis'},
{'3p_plup_actv_indc', 'rant'},
--future perfect actives
    {'1s_futp_actv_indc', 'rō'},
    {'2s_futp_actv_indc', 'ris'},
    {'3s_futp_actv_indc', 'rit'},
    {'1p_futp_actv_indc', 'rimus'},
{'2p_futp_actv_indc', 'ritis'},
{'3p_futp_actv_indc', 'rint'},
--perfect subjunctives
    {'1s_perf_actv_subj', 'rim'},
{'2s_perf_actv_subj', 'rīs'},
{'3s_perf_actv_subj', 'rit'},
{'1p_perf_actv_subj', 'rīmus'},
{'2p_perf_actv_subj', 'rītis'},
{'3p_perf_actv_subj', 'rint'},
--pluperfect subjunctives
    {'1s_plup_actv_subj', 'ssem'},
{'2s_plup_actv_subj', 'ssēs'},
{'3s_plup_actv_subj', 'sset'},
{'1p_plup_actv_subj', 'ssēmus'},
{'2p_plup_actv_subj', 'ssētis'},
{'3p_plup_actv_subj', 'ssent'}
}
local noteindex = #(data.footnotes)+1
function add_sync_perf(form, suff_sync)
local formval = data.forms[form]
if type(formval) ~= "table" then
formval = {formval}
end
end
if base then
local newvals = mw.clone(formval)
-- Remove subtypes of the form -SUBTYPE from the subtypes
for _, fv in ipairs(formval) do
-- to be returned.
-- Can only syncopate 'vi', 've', 'vē', or any one of them spelled with a 'u' after a vowel
local new_subtypes = {}
if fv:find('v[ieē]' .. suff_sync .. '$') or fv:find('vē' .. suff_sync .. '$') or find(fv, '[aeiouyāēīōūȳăĕĭŏŭ]u[ieē]' .. suff_sync.. '$') or find(fv, '[aeiouyāēīōūȳăĕĭŏŭ]uē' .. suff_sync.. '$') then
for _, subtype in ipairs(subtypes) do
m_table.insertIfNot(newvals, sub(fv, 1, -len(suff_sync) - 3) .. suff_sync)
if not rfind(subtype, "^%-") then
table.insert(new_subtypes, subtype)
end
end
end
if process_retval then
base, stem2 = process_retval(base, stem2)
end
if process_stem2 then
stem2 = stem2 or process_stem2(base)
end
return base, stem2, rettype, new_subtypes
end
end
data.forms[form] = newvals
data.form_footnote_indices[form] = noteindex
end
for _, v in ipairs(sss) do
add_sync_perf(v[1], v[2])
end
end
data.footnotes[noteindex] = "At least one rare poetic syncopated perfect form is attested." end
-- Add category for sigmatic forms
if typeinfo.subtypes.sigm or typeinfo.subtypes.sigmpasv then
m_table.insertIfNot(data.categories, "Latin verbs with sigmatic forms")
end
end
if not decltype then
-- Add subcategory for passive sigmatic forms
return nil, nil, nil, nil
if typeinfo.subtypes.sigmpasv or (typeinfo.subtypes.sigm and typeinfo.subtypes.depon) then
elseif decltype == "" then
m_table.insertIfNot(data.categories, "Latin verbs with passive sigmatic forms")
error("Unrecognized ending for adjective: " .. lemma)
else
error("Unrecognized ending for declension-" .. decltype .. " adjective: " .. lemma)
end
end
end
end


-- Autodetect the type and subtype of an adjective given all the information
--[=[
-- specified by the user: lemma, stem2, declension type and specified subtypes.
Conjugation functions
-- Four values are returned: the lemma base (i.e. the stem of the lemma, as
]=]--
-- required by the declension functions), the value of stem2 to pass to the
 
-- declension function, the declension type and the autodetected subtypes.
local function get_regular_stems(args, typeinfo)
-- Note that this will not detect a given subtype if -SUBTYPE is specified for
-- Get the parameters
-- any subtype that would be returned, or if SUBTYPE is specified and -SUBTYPE
if typeinfo.subtypes.depon or typeinfo.subtypes.semidepon then
-- is among the subtypes that would be returned (such subtypes are filtered out
-- Deponent and semi-deponent verbs don't have the perfective principal part.
-- of the returned subtypes).
-- But optionally semi-deponent verbs do.
local function detect_adj_type_and_subtype(lemma, stem2, typ, subtypes)
typeinfo.pres_stem = ine(args[1])
if not rfind(typ, "^[0123]") and not rfind(typ, "^irreg") then
typeinfo.perf_stem = nil
subtypes = mw.clone(subtypes)
typeinfo.supine_stem = ine(args[2])
subtypes[typ] = true
elseif typeinfo.subtypes.perfaspres then
typ = ""
typeinfo.perf_stem = ine(args[1])
typeinfo.supine_stem = ine(args[2])
else
typeinfo.pres_stem = ine(args[1])
typeinfo.perf_stem = ine(args[2])
typeinfo.supine_stem = ine(args[3])
end
if typeinfo.subtypes.sigm or typeinfo.subtypes.sigmpasv then
typeinfo.sigm_stem = ine(args[5])
end
end


local function base_as_stem2(base, stem2)
-- Prepare stems
return "foo", base
if not typeinfo.pres_stem then
if NAMESPACE == "Template" or typeinfo.subtypes.perfaspres then
typeinfo.pres_stem = "-"
else
error("Present stem has not been provided")
end
end
end


local function constant_base(baseval)
if typeinfo.perf_stem then
return function(base, stem2)
typeinfo.perf_stem = split(typeinfo.perf_stem, "/")
return baseval, nil
else
end
typeinfo.perf_stem = {}
end
end


local function decl12_stem2(base)
if typeinfo.supine_stem then
return base
typeinfo.supine_stem = split(typeinfo.supine_stem, "/")
else
typeinfo.supine_stem = {}
end
end
local function decl3_stem2(base)
if typeinfo.sigm_stem then
return m_la_utilities.make_stem2(base)
typeinfo.sigm_stem = split(typeinfo.sigm_stem, "/")
else
typeinfo.sigm_stem = {}
end
end
end
local decl12_entries = {
 
{"us", "1&2", {}},
local function has_perf_in_s_or_x(pres_stem, perf_stem)
{"a", "1&2", {}},
if pres_stem == perf_stem then
{"um", "1&2", {}},
return false
{"ī", "1&2", {"pl"}},
end
{"ae", "1&2", {"pl"}},
 
{"a", "1&2", {"pl"}},
return perf_stem and perf_stem:find("[sx]$") ~= nil
-- Nearly all -os adjectives are greekA
end
{"os", "1&2", {"greekA", "-greekE"}},
 
{"os", "1&2", {"greekE", "-greekA"}},
conjugations["1st"] = function(args, data, typeinfo)
{"ē", "1&2", {"greekE", "-greekA"}},
get_regular_stems(args, typeinfo)
{"on", "1&2", {"greekA", "-greekE"}},
{"on", "1&2", {"greekE", "-greekA"}},
{"^(.*er)$", "1&2", {"er"}},
{"^(.*ur)$", "1&2", {"er"}},
{"^(h)ic$", "1&2", {"ic"}},
}


local decl3_entries = {
table.insert(data.title, "[[Appendix:Latin first conjugation|first conjugation]]")
{"^(.*er)$", "3-3", {}},
table.insert(data.categories, "Latin first conjugation verb")
{"is", "3-2", {}},
{"e", "3-2", {}},
{"^(.*[ij])or$", "3-C", {}},
{"^(min)or$", "3-C", {}},
-- Detect -ēs as 3-1 without auto-inferring .pl if .pl
-- not specified. If we don't do this, the later entry for
-- -ēs will auto-infer .pl whenever -ēs is specified (which
-- won't work for adjectives like quadripēs, volucripēs).
-- Essentially, for declension-3 adjectives, we require that
-- .pl is given if the lemma is plural.
--
-- Most 3-1 adjectives are i-stem (e.g. audāx) so we require -I
-- to be given with non-i-stem adjectives. The first entry below
-- will apply when -I isn't given, the second when it is given.
{"^(.*ēs)$", "3-1", {"I"}},
{"^(.*ēs)$", "3-1", {"par"}},
{"^(.*[ij])ōrēs$", "3-C", {"pl"}},
{"^(min)ōrēs$", "3-C", {"pl"}},
-- If .pl with -ēs, we don't know if the adjective is 3-1, 3-2
-- or 3-3. Since 3-2 is probably the most common, we infer it
-- (as well as the fact that these adjectives *are* in a sense
-- 3-2 since they have a distinct neuter in -(i)a. Note that
-- we have two entries here; the first one will apply unless
-- -I is given, and will infer an i-stem adjective; the second
-- one will apply otherwise (and infer a non-i-stem 3-1 adjective).
{"ēs", "3-2", {"pl", "I"}},
{"ēs", "3-1", {"pl", "par"}, base_as_stem2},
-- Same for neuters.
{"ia", "3-2", {"pl", "I"}},
{"a", "3-1", {"pl", "par"}, base_as_stem2},
-- As above for -ēs but for miscellaneous singulars.
{"", "3-1", {"I"}},
{"", "3-1", {"par"}},
}


if typ == "" then
for _, perf_stem in ipairs(typeinfo.perf_stem) do
local base, new_stem2, rettype, new_subtypes =
if perf_stem == typeinfo.pres_stem .. "āv" then
get_adj_type_and_subtype_by_ending(lemma, stem2, nil, subtypes,
table.insert(data.categories, "Latin first conjugation verb with perfect in -av-")
decl12_entries, decl12_stem2)
elseif perf_stem == typeinfo.pres_stem .. "u" then
if base then
table.insert(data.categories, "Latin first conjugation verb with perfect in -u-")
return base, new_stem2, rettype, new_subtypes
elseif perf_stem == typeinfo.pres_stem then
table.insert(data.categories, "Latin first conjugation verb with suffixless perfect")
else
else
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ,
table.insert(data.categories, "Latin first conjugation verb with irregular perfect")
subtypes, decl3_entries, decl3_stem2)
end
end
elseif typ == "0" then
end
return lemma, nil, "0", {}
 
elseif typ == "3" then
make_pres_1st(data, typeinfo, typeinfo.pres_stem)
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes,
make_perf_and_supine(data, typeinfo)
decl3_entries, decl3_stem2)
make_sigm(data, typeinfo, typeinfo.sigm_stem)
elseif typ == "1&2" then
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes,
--Additional forms in specific cases
decl12_entries, decl12_stem2)
if typeinfo.pres_stem == "dīlapid" then
elseif typ == "1-1" then
add_form(data, "3p_sigf_actv_indc", "", "dīlapidāssunt", 2 )
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes, {
elseif typeinfo.pres_stem == "invol" then
{"a", "1-1", {}},
add_form(data, "3s_sigf_actv_indc", "", "involāsit", 2 )
{"ae", "1-1", {"pl"}},
elseif typeinfo.pres_stem == "viol" then
})
local noteindex = #(data.footnotes) + 1
elseif typ == "2-2" then
add_form(data, "3p_futp_actv_indc", "", "violārint", 2 )
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes, {
add_form(data, "3p_perf_actv_subj", "", "violārint", 2 )
{"us", "2-2", {}},
add_form(data, "3s_sigf_actv_indc", "", "violāsit", 2 )
{"um", "2-2", {}},
data.form_footnote_indices["3p_futp_actv_indc"] = tostring(noteindex)
{"ī", "2-2", {"pl"}},
data.form_footnote_indices["3p_perf_actv_subj"] = tostring(noteindex)
{"a", "2-2", {"pl"}},
data.footnotes[noteindex] = 'Archaic.'
{"os", "2-2", {"greek"}},
{"on", "2-2", {"greek"}},
{"oe", "2-2", {"greek", "pl"}},
})
elseif typ == "3-1" then
-- This will cancel out the I if -I is specified in subtypes, and the
-- resulting lack of I will get converted to "par".
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes, {
-- Detect -ēs as 3-1 without auto-inferring .pl if .pl
-- not specified. If we don't do this, the later entry for
-- -ēs will auto-infer .pl whenever -ēs is specified.
-- Essentially, for declension-3 adjectives, we require that
-- .pl is given if the lemma is plural.
-- We have two entries here; the first one will apply unless
-- -I is given, and will infer an i-stem adjective; the second
-- one will apply otherwise.
{"^(.*ēs)$", "3-1", {"I"}},
{"^(.*ēs)$", "3-1", {"par"}},
{"ēs", "3-1", {"pl", "I"}, base_as_stem2},
{"ēs", "3-1", {"pl", "par"}, base_as_stem2},
{"ia", "3-1", {"pl", "I"}, base_as_stem2},
{"a", "3-1", {"pl", "par"}, base_as_stem2},
{"", "3-1", {"I"}},
{"", "3-1", {"par"}},
}, decl3_stem2)
elseif typ == "3-2" then
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes, {
{"is", "3-2", {}},
{"e", "3-2", {}},
-- Detect -ēs as 3-2 without auto-inferring .pl if .pl
-- not specified. If we don't do this, the later entry for
-- -ēs will auto-infer .pl whenever -ēs is specified (which
-- won't work for adjectives like isoscelēs). Essentially,
-- for declension-3 adjectives, we require that .pl is given
-- if the lemma is plural.
{"ēs", "3-2", {}},
{"ēs", "3-2", {"pl"}},
{"ia", "3-2", {"pl"}},
}, decl3_stem2)
elseif typ == "3-C" then
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes, {
{"^(.*[ij])or$", "3-C", {}},
{"^(min)or$", "3-C", {}},
{"^(.*[ij])ōrēs$", "3-C", {"pl"}},
{"^(min)ōrēs$", "3-C", {"pl"}},
}, decl3_stem2)
elseif typ == "irreg" then
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes, {
{"^(duo)$", typ, {"pl"}},
{"^(ambō)$", typ, {"pl"}},
{"^(mīll?ia)$", typ, {"N", "pl"}, constant_base("mīlle")},
-- match ea
{"^(ea)$", typ, {}, constant_base("is")},
-- match id
{"^(id)$", typ, {}, constant_base("is")},
-- match plural eī, iī
{"^([ei]ī)$", typ, {"pl"}, constant_base("is")},
-- match plural ea, eae
{"^(eae?)$", typ, {"pl"}, constant_base("is")},
-- match eadem
{"^(eadem)$", typ, {}, constant_base("īdem")},
-- match īdem, idem
{"^([īi]dem)$", typ, {}, constant_base("īdem")},
-- match plural īdem
{"^(īdem)$", typ, {"pl"}},
-- match plural eadem, eaedem
{"^(eae?dem)$", typ, {"pl"}, constant_base("īdem")},
-- match illa, ipsa, ista; it doesn't matter if we overmatch because
-- we'll get an error as we use the stem itself in the returned base
{"^(i[lps][lst])a$", typ, {}, function(base, stem2) return base .. "e", nil end},
-- match illud, istud; as above, it doesn't matter if we overmatch
{"^(i[ls][lt])ud$", typ, {}, function(base, stem2) return base .. "e", nil end},
-- match ipsum
{"^(ipsum)$", typ, {}, constant_base("ipse")},
-- match plural illī, ipsī, istī; as above, it doesn't matter if we
-- overmatch
{"^(i[lps][lst])ī$", typ, {"pl"}, function(base, stem2) return base .. "e", nil end},
-- match plural illa, illae, ipsa, ipsae, ista, istae; as above, it
-- doesn't matter if we overmatch
{"^(i[lps][lst])ae?$", typ, {"pl"}, function(base, stem2) return base .. "e", nil end},
-- Detect quī as non-plural unless .pl specified.
{"^(quī)$", typ, {}},
-- Otherwise detect quī as plural.
{"^(quī)$", typ, {"pl"}},
-- Same for quae.
{"^(quae)$", typ, {}, constant_base("quī")},
{"^(quae)$", typ, {"pl"}, constant_base("quī")},
{"^(quid)$", typ, {}, constant_base("quis")},
{"^(quod)$", typ, {}, constant_base("quī")},
{"^(qui[cd]quid)$", typ, {}, constant_base("quisquis")},
{"^(quīquī)$", typ, {"pl"}, constant_base("quisquis")},
{"^(quaequae)$", typ, {"pl"}, constant_base("quisquis")},
-- match all remaining lemmas in lemma form
{"", typ, {}},
})
else -- 3-3 or 3-P
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes, {
{"ēs", typ, {"pl"}, base_as_stem2},
{"ia", typ, {"pl"}, base_as_stem2},
{"", typ, {}},
}, decl3_stem2)
end
end
end
end


-- Parse a segment (e.g. "lūna<1>", "aegis/aegid<3.Greek>", "bōs<irreg.F>",
conjugations["2nd"] = function(args, data, typeinfo)
-- bonus<+>", or "[[vetus]]/veter<3+.-I>"), consisting of a lemma (or optionally
get_regular_stems(args, typeinfo)
-- a lemma/stem) and declension+subtypes, where a + in the declension indicates
 
-- an adjective. Brackets can be present to indicate links, for use in
table.insert(data.title, "[[Appendix:Latin second conjugation|second conjugation]]")
-- {{la-noun}} and {{la-adj}}. The return value is a table, e.g.:
table.insert(data.categories, "Latin second conjugation verb")
-- {
 
--  decl = "1",
for _, perf_stem in ipairs(typeinfo.perf_stem) do
--  headword_decl = "1",
local pres_stem = typeinfo.pres_stem
--  is_adj = false,
pres_stem = pres_stem:gsub("qu", "1")
--  orig_lemma = "lūna",
perf_stem = perf_stem:gsub("qu", "1")
--  lemma = "lūna",
if perf_stem == pres_stem .. "ēv" then
--  stem2 = nil,
table.insert(data.categories, "Latin second conjugation verb with perfect in -ev-")
--  gender = "F",
elseif perf_stem == pres_stem .. "u" then
--  types = {["F"] = true},
table.insert(data.categories, "Latin second conjugation verb with perfect in -u-")
--  args = {"lūn"}
elseif perf_stem == pres_stem then
-- }
table.insert(data.categories, "Latin second conjugation verb with suffixless perfect")
--
elseif has_perf_in_s_or_x(pres_stem, perf_stem) then
-- or
table.insert(data.categories, "Latin second conjugation verb with perfect in -s- or -x-")
--
else
-- {
table.insert(data.categories, "Latin second conjugation verb with irregular perfect")
--  decl = "3",
end
--  headword_decl = "3",
end
--  is_adj = false,
--  orig_lemma = "aegis",
--  lemma = "aegis",
--  stem2 = "aegid",
--   gender = nil,
--  types = {["Greek"] = true},
--  args = {"aegis", "aegid"}
-- }
--
-- or
--
-- {
--  decl = "irreg",
--  headword_decl = "irreg/3",
--  is_adj = false,
--  orig_lemma = "bōs",
--   lemma = "bōs",
--  stem2 = nil,
--  gender = "F",
--  types = {["F"] = true},
--  args = {"bōs"}
-- }
-- or
--
-- {
--  decl = "1&2",
--  headword_decl = "1&2+",
--  is_adj = true,
--  orig_lemma = "bonus",
--  lemma = "bonus",
--  stem2 = nil,
--  gender = nil,
--  types = {},
--  args = {"bon"}
-- }
--
-- or
--
-- {
--  decl = "3-1",
--  headword_decl = "3-1+",
--  is_adj = true,
--  orig_lemma = "[[vetus]]",
--  lemma = "vetus",
--  stem2 = "veter",
--  gender = nil,
--  types = {},
--  args = {"vetus", "veter"}
-- }
local function parse_segment(segment)
local stem_part, spec_part = rmatch(segment, "^(.*)<(.-)>$")
local stems = rsplit(stem_part, "/", true)
local specs = rsplit(spec_part, ".", true)


local types = {}
make_pres_2nd(data, typeinfo, typeinfo.pres_stem)
local num = nil
make_perf_and_supine(data, typeinfo)
local loc = false
make_sigm(data, typeinfo, typeinfo.sigm_stem)
--Additional forms in specific cases
if typeinfo.pres_stem == "noc" then
add_form(data, "3s_siga_actv_subj", "", "noxsīt", 2 )
end
end


local args = {}
local function set_3rd_conj_categories(data, typeinfo)
table.insert(data.categories, "Latin third conjugation verb")


local decl
for _, perf_stem in ipairs(typeinfo.perf_stem) do
for j, spec in ipairs(specs) do
local pres_stem = typeinfo.pres_stem
if j == 1 then
pres_stem = pres_stem:gsub("qu", "1")
decl = spec
perf_stem = perf_stem:gsub("qu", "1")
if perf_stem == pres_stem .. "āv" then
table.insert(data.categories, "Latin third conjugation verb with perfect in -av-")
elseif perf_stem == pres_stem .. "ēv" then
table.insert(data.categories, "Latin third conjugation verb with perfect in -ev-")
elseif perf_stem == pres_stem .. "īv" then
table.insert(data.categories, "Latin third conjugation verb with perfect in -iv-")
elseif perf_stem == pres_stem .. "i" then
table.insert(data.categories, "Latin third conjugation verb with perfect in -i-")
elseif perf_stem == pres_stem .. "u" then
table.insert(data.categories, "Latin third conjugation verb with perfect in -u-")
elseif perf_stem == pres_stem then
table.insert(data.categories, "Latin third conjugation verb with suffixless perfect")
elseif has_perf_in_s_or_x(pres_stem, perf_stem) then
table.insert(data.categories, "Latin third conjugation verb with perfect in -s- or -x-")
else
else
local begins_with_hyphen
table.insert(data.categories, "Latin third conjugation verb with irregular perfect")
begins_with_hyphen, spec = rmatch(spec, "^(%-?)(.-)$")
spec = begins_with_hyphen .. spec:gsub("%-", "_")
types[spec] = true
end
end
end
end
conjugations["3rd"] = function(args, data, typeinfo)
get_regular_stems(args, typeinfo)
table.insert(data.title, "[[Appendix:Latin third conjugation|third conjugation]]")
set_3rd_conj_categories(data, typeinfo)
if typeinfo.pres_stem and match(typeinfo.pres_stem,"[āēīōū]sc$") then
table.insert(data.categories, "Latin inchoative verbs")
end
end


local orig_lemma = stems[1]
make_pres_3rd(data, typeinfo, typeinfo.pres_stem)
if not orig_lemma or orig_lemma == "" then
make_perf_and_supine(data, typeinfo)
orig_lemma = current_title.subpageText
make_sigm(data, typeinfo, typeinfo.sigm_stem)
--Additional forms in specific cases
--FIXME: needs to be cleared up
if match(typeinfo.pres_stem, "nōsc") then
local noteindex = #(data.footnotes) + 1
add_form(data, "2s_perf_actv_indc", "", sub(typeinfo.perf_stem[1],1,-2) .. "stī", 2 )
add_form(data, "1p_perf_actv_indc", "", sub(typeinfo.perf_stem[1],1,-2) .. "mus", 2 )
add_form(data, "2p_perf_actv_indc", "", sub(typeinfo.perf_stem[1],1,-2) .. "stis", 2 )
add_form(data, "3p_perf_actv_indc", "", sub(typeinfo.perf_stem[1],1,-2) .. "runt", 3 )
add_form(data, "1s_plup_actv_indc", "", sub(typeinfo.perf_stem[1],1,-2) .. "ram", 2 )
add_form(data, "2s_plup_actv_indc", "", sub(typeinfo.perf_stem[1],1,-2) .. "rās", 2 )
add_form(data, "3s_plup_actv_indc", "", sub(typeinfo.perf_stem[1],1,-2) .. "rat", 2 )
add_form(data, "1p_plup_actv_indc", "", sub(typeinfo.perf_stem[1],1,-2) .. "rāmus", 2 )
add_form(data, "2p_plup_actv_indc", "", sub(typeinfo.perf_stem[1],1,-2) .. "rātis", 2 )
add_form(data, "3p_plup_actv_indc", "", sub(typeinfo.perf_stem[1],1,-2) .. "rant", 2 )
add_form(data, "1s_futp_actv_indc", "", sub(typeinfo.perf_stem[1],1,-2) .. "rō", 2 )
add_form(data, "2s_futp_actv_indc", "", sub(typeinfo.perf_stem[1],1,-2) .. "ris", 2 )
add_form(data, "3s_futp_actv_indc", "", sub(typeinfo.perf_stem[1],1,-2) .. "rit", 2 )
add_form(data, "1p_futp_actv_indc", "", sub(typeinfo.perf_stem[1],1,-2) .. "rimus", 2 )
add_form(data, "2p_futp_actv_indc", "", sub(typeinfo.perf_stem[1],1,-2) .. "ritis", 2 )
add_form(data, "3p_futp_actv_indc", "", sub(typeinfo.perf_stem[1],1,-2) .. "rint", 2 )
add_form(data, "1s_perf_actv_subj", "", sub(typeinfo.perf_stem[1],1,-2) .. "rim", 2 )
add_form(data, "2s_perf_actv_subj", "", sub(typeinfo.perf_stem[1],1,-2) .. "rīs", 2 )
add_form(data, "3s_perf_actv_subj", "", sub(typeinfo.perf_stem[1],1,-2) .. "rit", 2 )
add_form(data, "1p_perf_actv_subj", "", sub(typeinfo.perf_stem[1],1,-2) .. "rīmus", 2 )
add_form(data, "2p_perf_actv_subj", "", sub(typeinfo.perf_stem[1],1,-2) .. "rītis", 2 )
add_form(data, "3p_perf_actv_subj", "", sub(typeinfo.perf_stem[1],1,-2) .. "rint", 2 )
add_form(data, "1s_plup_actv_subj", "", sub(typeinfo.perf_stem[1],1,-2) .. "ssem", 2 )
add_form(data, "2s_plup_actv_subj", "", sub(typeinfo.perf_stem[1],1,-2) .. "ssēs", 2 )
add_form(data, "3s_plup_actv_subj", "", sub(typeinfo.perf_stem[1],1,-2) .. "sset", 2 )
add_form(data, "1p_plup_actv_subj", "", sub(typeinfo.perf_stem[1],1,-2) .. "ssēmus", 2 )
add_form(data, "2p_plup_actv_subj", "", sub(typeinfo.perf_stem[1],1,-2) .. "ssētis", 2 )
add_form(data, "3p_plup_actv_subj", "", sub(typeinfo.perf_stem[1],1,-2) .. "ssent", 2 )
add_form(data, "perf_actv_inf", "", sub(typeinfo.perf_stem[1],1,-2) .. "sse", 2 )
data.form_footnote_indices["2s_perf_actv_indc"] = tostring(noteindex)
data.form_footnote_indices["1p_perf_actv_indc"] = tostring(noteindex)
data.form_footnote_indices["2p_perf_actv_indc"] = tostring(noteindex)
data.form_footnote_indices["3p_perf_actv_indc"] = tostring(noteindex)
data.form_footnote_indices["1s_plup_actv_indc"] = tostring(noteindex)
data.form_footnote_indices["2s_plup_actv_indc"] = tostring(noteindex)
data.form_footnote_indices["3s_plup_actv_indc"] = tostring(noteindex)
data.form_footnote_indices["1p_plup_actv_indc"] = tostring(noteindex)
data.form_footnote_indices["2p_plup_actv_indc"] = tostring(noteindex)
data.form_footnote_indices["3p_plup_actv_indc"] = tostring(noteindex)
data.form_footnote_indices["1s_futp_actv_indc"] = tostring(noteindex)
data.form_footnote_indices["2s_futp_actv_indc"] = tostring(noteindex)
data.form_footnote_indices["3s_futp_actv_indc"] = tostring(noteindex)
data.form_footnote_indices["1p_futp_actv_indc"] = tostring(noteindex)
data.form_footnote_indices["2p_futp_actv_indc"] = tostring(noteindex)
data.form_footnote_indices["3p_futp_actv_indc"] = tostring(noteindex)
data.form_footnote_indices["1s_perf_actv_subj"] = tostring(noteindex)
data.form_footnote_indices["2s_perf_actv_subj"] = tostring(noteindex)
data.form_footnote_indices["3s_perf_actv_subj"] = tostring(noteindex)
data.form_footnote_indices["1p_perf_actv_subj"] = tostring(noteindex)
data.form_footnote_indices["2p_perf_actv_subj"] = tostring(noteindex)
data.form_footnote_indices["3p_perf_actv_subj"] = tostring(noteindex)
data.form_footnote_indices["1s_plup_actv_subj"] = tostring(noteindex)
data.form_footnote_indices["2s_plup_actv_subj"] = tostring(noteindex)
data.form_footnote_indices["3s_plup_actv_subj"] = tostring(noteindex)
data.form_footnote_indices["1p_plup_actv_subj"] = tostring(noteindex)
data.form_footnote_indices["2p_plup_actv_subj"] = tostring(noteindex)
data.form_footnote_indices["3p_plup_actv_subj"] = tostring(noteindex)
data.form_footnote_indices["perf_actv_inf"] = tostring(noteindex)
data.footnotes[noteindex] = 'The verb \"nōscō\" and its compounds frequently drop the syllables \"vi\" and \"ve\" from their perfect, pluperfect and future perfect conjugations.'
end
end
local lemma = m_links.remove_links(orig_lemma)
if typeinfo.pres_stem == "ulcīsc" then
local stem2 = stems[2]
local noteindex = #(data.footnotes) + 1
if stem2 == "" then
add_form(data, "1s_sigf_actv_indc", "", "ullō", 2 )
stem2 = nil
data.form_footnote_indices["1s_sigf_actv_indc"] = tostring(noteindex)
data.footnotes[noteindex] = 'The form \"ullō\" may have resulted from a later, erroneous misreading of \"ulsō\".'
end
end
if #stems > 2 then
end
error("Too many stems, at most 2 should be given: " .. stem_part)
 
conjugations["3rd-io"] = function(args, data, typeinfo)
get_regular_stems(args, typeinfo)
 
table.insert(data.title, "[[Appendix:Latin third conjugation|third conjugation]] ''iō''-variant")
set_3rd_conj_categories(data, typeinfo)
 
make_pres_3rd_io(data, typeinfo, typeinfo.pres_stem)
make_perf_and_supine(data, typeinfo)
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
 
conjugations["4th"] = function(args, data, typeinfo)
get_regular_stems(args, typeinfo)
 
table.insert(data.title, "[[Appendix:Latin fourth conjugation|fourth conjugation]]")
table.insert(data.categories, "Latin fourth conjugation verb")
 
 
for _, perf_stem in ipairs(typeinfo.perf_stem) do
local pres_stem = typeinfo.pres_stem
pres_stem = pres_stem:gsub("qu", "1")
perf_stem = perf_stem:gsub("qu", "1")
if perf_stem == pres_stem .. "īv" then
table.insert(data.categories, "Latin fourth conjugation verb with perfect in -iv-")
elseif perf_stem == pres_stem .. "i" then
table.insert(data.categories, "Latin fourth conjugation verb with perfect in -i-")
elseif perf_stem == pres_stem .. "u" then
table.insert(data.categories, "Latin fourth conjugation verb with perfect in -u-")
elseif perf_stem == pres_stem then
table.insert(data.categories, "Latin fourth conjugation verb with suffixless perfect")
elseif has_perf_in_s_or_x(pres_stem, perf_stem) then
table.insert(data.categories, "Latin fourth conjugation verb with perfect in -s- or -x-")
else
table.insert(data.categories, "Latin fourth conjugation verb with irregular perfect")
end
end
end


local base, detected_subtypes
make_pres_4th(data, typeinfo, typeinfo.pres_stem)
local is_adj = false
make_perf_and_supine(data, typeinfo)
local gender = nil
make_sigm(data, typeinfo, typeinfo.sigm_stem)


if rfind(decl, "%+") then
if form_contains(data.forms["1s_pres_actv_indc"], "serviō") or form_contains(data.forms["1s_pres_actv_indc"], "saeviō") then
decl = decl:gsub("%+", "")
add_forms(data, "impf_actv_indc", typeinfo.pres_stem,
base, stem2, decl, detected_subtypes = detect_adj_type_and_subtype(
{"iēbam", "ībam"},
lemma, stem2, decl, types
{"iēbās", "ībās"},
{"iēbat", "ībat"},
{"iēbāmus", "ībāmus"},
{"iēbātis", "ībātis"},
{"iēbant", "ībant"}
)
)
is_adj = true


headword_decl = irreg_adj_to_decl[lemma] and "irreg/" .. irreg_adj_to_decl[lemma] or decl .. "+"
add_forms(data, "futr_actv_indc", typeinfo.pres_stem,
{"iam", "ībō"},
{"iēs", "ībis"},
{"iet", "ībit"},
{"iēmus", "ībimus"},
{"iētis", "ībitis"},
{"ient", "ībunt"}
)
end


for _, subtype in ipairs(detected_subtypes) do
if typeinfo.subtypes.alwayssyncperf or typeinfo.subtypes.optsyncperf then
if types["-" .. subtype] then
for key, form in pairs(data.forms) do
-- if a "cancel subtype" spec is given, remove the cancel spec
if cfind(key, "perf") or cfind(key, "plup") or cfind(key, "futp") then
-- and don't apply the subtype
local forms = data.forms[key]
types["-" .. subtype] = nil
if type(forms) ~= "table" then
else
forms = {forms}
types[subtype] = true
end
data.forms[key] = {}
for _, f in ipairs(forms) do
if typeinfo.subtypes.optsyncperf then
m_table.insertIfNot(data.forms[key], f)
end
m_table.insertIfNot(data.forms[key], ivi_ive(f))
end
end
end
end
end
else
end
base, stem2, detected_subtypes = detect_noun_subtype(lemma, stem2, decl, types)
end
 
-- Irregular conjugations
local irreg_conjugations = {}


headword_decl = irreg_noun_to_decl[lemma] and "irreg/" .. irreg_noun_to_decl[lemma] or decl
conjugations["irreg"] = function(args, data, typeinfo)
local verb = ine(args[1])
local prefix = ine(args[2])


for _, subtype in ipairs(detected_subtypes) do
if not verb then
if types["-" .. subtype] then
if NAMESPACE == "Template" then
-- if a "cancel subtype" spec is given, remove the cancel spec
verb = "sum"
-- and don't apply the subtype
else
types["-" .. subtype] = nil
error("The verb to be conjugated has not been specified.")
elseif (subtype == "M" or subtype == "F" or subtype == "N") and
(types.M or types.F or types.N) then
-- if gender already specified, don't create conflicting gender spec
elseif (subtype == "sg" or subtype == "pl" or subtype == "both") and
(types.sg or types.pl or types.both) then
-- if number restriction already specified, don't create conflicting
-- number restriction spec
else
types[subtype] = true
end
end
end
end


if not types.pl and not types.both and rfind(lemma, "^[A-ZĀĒĪŌŪȲĂĔĬŎŬ]") then
if not irreg_conjugations[verb] then
types.sg = true
error("The verb '" .. verb .. "' is not recognised as an irregular verb.")
end
end
end


if types.loc then
typeinfo.verb = verb
loc = true
typeinfo.prefix = prefix
types.loc = nil
 
-- Generate the verb forms
irreg_conjugations[verb](args, data, typeinfo)
end
 
irreg_conjugations["aio"] = function(args, data, typeinfo)
table.insert(data.title, "[[Appendix:Latin third conjugation|third conjugation]] iō-variant")
table.insert(data.title, "[[Appendix:Latin irregular verbs|irregular]]")
table.insert(data.title, "active only")
table.insert(data.title, "highly [[defective verb|defective]]")
table.insert(data.categories, "Latin third conjugation verb")
table.insert(data.categories, "Latin irregular verbs")
table.insert(data.categories, "Latin active-only verbs")
table.insert(data.categories, "Latin defective verbs")
 
-- Signal to {{la-verb}} to display the verb as irregular and highly defective
typeinfo.subtypes.irreg = true
typeinfo.subtypes.highlydef = true
 
local prefix = typeinfo.prefix or ""
 
data.forms["1s_pres_actv_indc"] = prefix .. "aiō"
data.forms["2s_pres_actv_indc"] = prefix .. "ais"
data.forms["3s_pres_actv_indc"] = prefix .. "ait"
data.forms["3p_pres_actv_indc"] = prefix .. "aiunt"
 
data.forms["1s_impf_actv_indc"] = prefix .. "aiēbam"
data.forms["2s_impf_actv_indc"] = prefix .. "aiēbās"
data.forms["3s_impf_actv_indc"] = prefix .. "aiēbat"
data.forms["1p_impf_actv_indc"] = prefix .. "aiēbāmus"
data.forms["2p_impf_actv_indc"] = prefix .. "aiēbātis"
data.forms["3p_impf_actv_indc"] = prefix .. "aiēbant"
 
data.forms["2s_perf_actv_indc"] = prefix .. "aistī"
data.forms["3s_perf_actv_indc"] = prefix .. "ait"
 
data.forms["2s_pres_actv_subj"] = prefix .. "aiās"
data.forms["3s_pres_actv_subj"] = prefix .. "aiat"
data.forms["3p_pres_actv_subj"] = prefix .. "aiant"
 
data.forms["2s_pres_actv_impr"] = prefix .. "ai"
 
data.forms["pres_actv_inf"] = prefix .. "aiere"
data.forms["pres_actv_ptc"] = prefix .. "aiēns"
end
 
irreg_conjugations["aiio"] = function(args, data, typeinfo)
table.insert(data.title, "[[Appendix:Latin third conjugation|third conjugation]] iō-variant")
table.insert(data.title, "[[Appendix:Latin irregular verbs|irregular]]")
table.insert(data.title, "active only")
table.insert(data.title, "highly [[defective verb|defective]]")
table.insert(data.categories, "Latin third conjugation verb")
table.insert(data.categories, "Latin irregular verbs")
table.insert(data.categories, "Latin active-only verbs")
table.insert(data.categories, "Latin defective verbs")
 
-- Signal to {{la-verb}} to display the verb as irregular and highly defective
typeinfo.subtypes.irreg = true
typeinfo.subtypes.highlydef = true
 
local prefix = typeinfo.prefix or ""
 
data.forms["1s_pres_actv_indc"] = prefix .. "aiiō"
data.forms["2s_pres_actv_indc"] = prefix .. "ais"
data.forms["3s_pres_actv_indc"] = prefix .. "ait"
data.forms["3p_pres_actv_indc"] = prefix .. "aiunt"
 
data.forms["1s_impf_actv_indc"] = prefix .. "aiiēbam"
data.forms["2s_impf_actv_indc"] = prefix .. "aiiēbās"
data.forms["3s_impf_actv_indc"] = prefix .. "aiiēbat"
data.forms["1p_impf_actv_indc"] = prefix .. "aiiēbāmus"
data.forms["2p_impf_actv_indc"] = prefix .. "aiiēbātis"
data.forms["3p_impf_actv_indc"] = prefix .. "aiiēbant"
 
data.forms["2s_perf_actv_indc"] = prefix .. "aistī"
data.forms["3s_perf_actv_indc"] = prefix .. "ait"
 
data.forms["2s_pres_actv_subj"] = prefix .. "aiiās"
data.forms["3s_pres_actv_subj"] = prefix .. "aiiat"
data.forms["3p_pres_actv_subj"] = prefix .. "aiiant"
 
data.forms["2s_pres_actv_impr"] = prefix .. "ai"
 
data.forms["pres_actv_inf"] = prefix .. "aiiere"
data.forms["pres_actv_ptc"] = prefix .. "aiiēns"
end
 
irreg_conjugations["ajo"] = function(args, data, typeinfo)
table.insert(data.title, "[[Appendix:Latin third conjugation|third conjugation]] iō-variant")
table.insert(data.title, "[[Appendix:Latin irregular verbs|irregular]]")
table.insert(data.title, "active only")
table.insert(data.title, "highly [[defective verb|defective]]")
table.insert(data.categories, "Latin third conjugation verb")
table.insert(data.categories, "Latin irregular verbs")
table.insert(data.categories, "Latin active-only verbs")
table.insert(data.categories, "Latin defective verbs")
 
-- Signal to {{la-verb}} to display the verb as irregular and highly defective
typeinfo.subtypes.irreg = true
typeinfo.subtypes.highlydef = true
 
local prefix = typeinfo.prefix or ""
 
data.forms["1s_pres_actv_indc"] = prefix .. "ajō"
data.forms["2s_pres_actv_indc"] = prefix .. "ais"
data.forms["3s_pres_actv_indc"] = prefix .. "ait"
data.forms["3p_pres_actv_indc"] = prefix .. "ajunt"
 
data.forms["1s_impf_actv_indc"] = prefix .. "ajēbam"
data.forms["2s_impf_actv_indc"] = prefix .. "ajēbās"
data.forms["3s_impf_actv_indc"] = prefix .. "ajēbat"
data.forms["1p_impf_actv_indc"] = prefix .. "ajēbāmus"
data.forms["2p_impf_actv_indc"] = prefix .. "ajēbātis"
data.forms["3p_impf_actv_indc"] = prefix .. "ajēbant"
 
data.forms["2s_perf_actv_indc"] = prefix .. "aistī"
data.forms["3s_perf_actv_indc"] = prefix .. "ait"
 
data.forms["2s_pres_actv_subj"] = prefix .. "ajās"
data.forms["3s_pres_actv_subj"] = prefix .. "ajat"
data.forms["3p_pres_actv_subj"] = prefix .. "ajant"
 
data.forms["2s_pres_actv_impr"] = prefix .. "ai"
 
data.forms["pres_actv_inf"] = prefix .. "ajere"
data.forms["pres_actv_ptc"] = prefix .. "ajēns"
end
 
irreg_conjugations["dico"] = function(args, data, typeinfo)
table.insert(data.title, "[[Appendix:Latin third conjugation|third conjugation]]")
table.insert(data.title, "[[Appendix:Latin irregular verbs|irregular]] short imperative")
table.insert(data.categories, "Latin third conjugation verb")
table.insert(data.categories, "Latin irregular verbs")
 
local prefix = typeinfo.prefix or ""
 
make_pres_3rd(data, typeinfo, prefix .. "dīc")
make_perf(data, prefix .. "dīx")
make_supine(data, typeinfo, prefix .. "dict")
make_sigm(data, typeinfo, prefix .. "dīx")
--Archaic regular imperative
local noteindex = #(data.footnotes) + 1
add_form(data, "2s_pres_actv_impr", prefix, "dīc", 1)
data.form_footnote_indices["2s_pres_actv_impr"] = tostring(noteindex)
--Archaic future forms
if prefix == "" then
add_form(data, "1s_futr_actv_indc", "", "dīcēbō", 2 )
add_form(data, "3s_futr_actv_indc", "", "dīcēbit", 2 )
data.form_footnote_indices["1s_futr_actv_indc"] = tostring(noteindex)
data.form_footnote_indices["3s_futr_actv_indc"] = tostring(noteindex)
end
end
data.footnotes[noteindex] = 'Archaic.'
end
irreg_conjugations["do"] = function(args, data, typeinfo)
table.insert(data.title, "[[Appendix:Latin first conjugation|first conjugation]]")
table.insert(data.title, "[[Appendix:Latin irregular verbs|irregular]] short ''a'' in most forms except " .. make_link({lang = lang, alt = '', alt = "dās"}, "term") .. " and " .. make_link({lang = lang, alt = '', alt = "dā"}, "term"))
table.insert(data.categories, "Latin first conjugation verb")
table.insert(data.categories, "Latin irregular verbs")
-- Signal to {{la-verb}} to display the verb as irregular
typeinfo.subtypes.irreg = true
local prefix = typeinfo.prefix or ""
make_perf(data, prefix .. "ded")
make_supine(data, typeinfo, prefix .. "dat")
-- Active imperfective indicative
add_forms(data, "pres_actv_indc", prefix, "dō", "dās", "dat", "damus", "datis", "dant")
add_forms(data, "impf_actv_indc", prefix, "dabam", "dabās", "dabat", "dabāmus", "dabātis", "dabant")
add_forms(data, "futr_actv_indc", prefix, "dabō", "dabis", "dabit", "dabimus", "dabitis", "dabunt")
-- Passive imperfective indicative
add_forms(data, "pres_pasv_indc", prefix, "dor", {"daris", "dare"}, "datur", "damur", "daminī", "dantur")
add_forms(data, "impf_pasv_indc", prefix, "dabar", {"dabāris", "dabāre"}, "dabātur", "dabāmur", "dabāminī", "dabantur")
add_forms(data, "futr_pasv_indc", prefix, "dabor", {"daberis", "dabere"}, "dabitur", "dabimur", "dabiminī", "dabuntur")
-- Active imperfective subjunctive
add_forms(data, "pres_actv_subj", prefix, "dem", "dēs", "det", "dēmus", "dētis", "dent")
add_forms(data, "impf_actv_subj", prefix, "darem", "darēs", "daret", "darēmus", "darētis", "darent")
-- Passive imperfective subjunctive
add_forms(data, "pres_pasv_subj", prefix, "der", {"dēris", "dēre"}, "dētur", "dēmur", "dēminī", "dentur")
add_forms(data, "impf_pasv_subj", prefix, "darer", {"darēris", "darēre"}, "darētur", "darēmur", "darēminī", "darentur")
-- Imperative
add_2_forms(data, "pres_actv_impr", prefix, "dā", "date")
add_23_forms(data, "futr_actv_impr", prefix, "datō", "datō", "datōte", "dantō")
add_2_forms(data, "pres_pasv_impr", prefix, "dare", "daminī")
-- no 2p form
add_23_forms(data, "futr_pasv_impr", prefix, "dator", "dator", {}, "dantor")
-- Present infinitives
data.forms["pres_actv_inf"] = prefix .. "dare"
data.forms["pres_pasv_inf"] = prefix .. "darī"
-- Imperfective participles
data.forms["pres_actv_ptc"] = prefix .. "dāns"
-- Gerund
make_gerund(data, typeinfo, prefix .. "dand")
end
irreg_conjugations["duco"] = function(args, data, typeinfo)
table.insert(data.title, "[[Appendix:Latin third conjugation|third conjugation]]")
table.insert(data.title, "[[Appendix:Latin irregular verbs|irregular]] short imperative")
table.insert(data.categories, "Latin third conjugation verb")
table.insert(data.categories, "Latin irregular verbs")
local prefix = typeinfo.prefix or ""
make_pres_3rd(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(data, "2s_pres_actv_impr", prefix, "dūc", 1)
end
irreg_conjugations["edo"] = function(args, data, typeinfo)
table.insert(data.title, "[[Appendix:Latin third conjugation|third conjugation]]")
table.insert(data.title, "some [[Appendix:Latin irregular verbs|irregular]] alternative forms")
table.insert(data.categories, "Latin third conjugation verb")
table.insert(data.categories, "Latin irregular verbs")
-- Signal to {{la-verb}} to display the verb as irregular
typeinfo.subtypes.irreg = true
local prefix = typeinfo.prefix or ""
make_pres_3rd(data, typeinfo, prefix .. "ed")
make_perf(data, prefix .. "ēd")
make_supine(data, typeinfo, prefix .. "ēs")
-- Active imperfective indicative
add_forms(data, "pres_actv_indc", prefix, {}, "ēs", "ēst", {}, "ēstis", {})
-- Passive imperfective indicative
add_form(data, "3s_pres_pasv_indc", prefix, "ēstur")
-- Active imperfective subjunctive
add_forms(data, "pres_actv_subj", prefix, "edim", "edīs", "edit", "edīmus", "edītis", "edint")
add_forms(data, "impf_actv_subj", prefix, "ēssem", "ēssēs", "ēsset", "ēssēmus", "ēssētis", "ēssent")
-- Active imperative
add_2_forms(data, "pres_actv_impr", prefix, "ēs", "ēste")
add_23_forms(data, "futr_actv_impr", prefix, "ēstō", "ēstō", "ēstōte", {})
-- Present infinitives
add_form(data, "pres_actv_inf", prefix, "ēsse")
end
irreg_conjugations["eo"] = function(args, data, typeinfo)
table.insert(data.title, "[[Appendix:Latin irregular verbs|irregular]]")
table.insert(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(data, "pres_actv_indc", prefix, "eō", "īs", "it", "īmus", "ītis",
prefix == "prōd" and {"eunt", "īnunt"} or "eunt")
add_forms(data, "impf_actv_indc", prefix, "ībam", "ībās", "ībat", "ībāmus", "ībātis", "ībant")
add_forms(data, "futr_actv_indc", prefix, "ībō", "ībis", "ībit", "ībimus", "ībitis", "ībunt")
-- Active perfective indicative
add_form(data, "1s_perf_actv_indc", prefix, "īvī")
data.forms["2s_perf_actv_indc"] = {prefix .. "īstī", prefix .. "īvistī"}
add_form(data, "3s_perf_actv_indc", prefix, "īvit")
data.forms["2p_perf_actv_indc"] = prefix .. "īstis"
-- Passive imperfective indicative
add_forms(data, "pres_pasv_indc", prefix, "eor", { "īris", "īre"}, "ītur", "īmur", "īminī", "euntur")
add_forms(data, "impf_pasv_indc", prefix, "ībar", {"ībāris", "ībāre"}, "ībātur", "ībāmur", "ībāminī", "ībantur")
add_forms(data, "futr_pasv_indc",  prefix, "ībor", {"īberis", "ībere"}, "ībitur", "ībimur", "ībiminī", "ībuntur")
-- Active imperfective subjunctive
add_forms(data, "pres_actv_subj", prefix, "eam", "eās", "eat", "eāmus", "eātis", "eant")
add_forms(data, "impf_actv_subj", prefix, "īrem", "īrēs", "īret", "īrēmus", "īrētis", "īrent")
-- Active perfective subjunctive
data.forms["1s_plup_actv_subj"] = prefix .. "īssem"
data.forms["2s_plup_actv_subj"] = prefix .. "īssēs"
data.forms["3s_plup_actv_subj"] = prefix .. "īsset"
data.forms["1p_plup_actv_subj"] = prefix .. "īssēmus"
data.forms["2p_plup_actv_subj"] = prefix .. "īssētis"
data.forms["3p_plup_actv_subj"] = prefix .. "īssent"
-- Passive imperfective subjunctive
add_forms(data, "pres_pasv_subj", prefix, "ear", {"eāris", "eāre"}, "eātur", "eāmur", "eāminī", "eantur")
add_forms(data, "impf_pasv_subj", prefix, "īrer", {"īrēris", "īrēre"}, "īrētur", "īrēmur", "īrēminī", "īrentur")
-- Imperative
add_2_forms(data, "pres_actv_impr", prefix, "ī", "īte")
add_23_forms(data, "futr_actv_impr", prefix, "ītō", "ītō", "ītōte", "euntō")
add_2_forms(data, "pres_pasv_impr", prefix, "īre", "īminī")
add_23_forms(data, "futr_pasv_impr", prefix, "ītor", "ītor", {}, "euntor")
-- Present infinitives
data.forms["pres_actv_inf"] = prefix .. "īre"
data.forms["pres_pasv_inf"] = prefix .. "īrī"
-- Perfect/future infinitives
data.forms["perf_actv_inf"] = prefix .. "īsse"
-- Imperfective participles
data.forms["pres_actv_ptc"] = prefix .. "iēns"
-- Gerund
make_gerund(data, typeinfo, prefix .. "eund")
end
local function fio(data, prefix, voice)
-- Active/passive imperfective indicative
add_forms(data, "pres_" .. voice .. "_indc", prefix,
"fīō", "fīs", "fit", "fīmus", "fītis", "fīunt")
add_forms(data, "impf_" .. voice .. "_indc", prefix .. "fīēb",
"am", "ās", "at", "āmus", "ātis", "ant")
add_forms(data, "futr_" .. voice .. "_indc", prefix .. "fī",
"am", "ēs", "et", "ēmus", "ētis", "ent")
-- Active/passive imperfective subjunctive
add_forms(data, "pres_" .. voice .. "_subj", prefix .. "fī",
"am", "ās", "at", "āmus", "ātis", "ant")
add_forms(data, "impf_" .. voice .. "_subj", prefix .. "fier",
"em", "ēs", "et", "ēmus", "ētis", "ent")
-- Active/passive imperative
add_2_forms(data, "pres_" .. voice .. "_impr", prefix .. "fī", "", "te")
add_23_forms(data, "futr_" .. voice .. "_impr", prefix .. "fī", "tō", "tō", "tōte", "untō")


if types.M then
-- Active/passive present infinitive
gender = "M"
add_form(data, "pres_" .. voice .. "_inf", prefix, "fierī")
elseif types.F then
end
gender = "F"
 
elseif types.N then
irreg_conjugations["facio"] = function(args, data, typeinfo)
gender = "N"
table.insert(data.title, "[[Appendix:Latin third conjugation|third conjugation]] ''iō''-variant")
table.insert(data.title, "[[Appendix:Latin irregular verbs|irregular]] and partially [[suppletive]] in the passive")
table.insert(data.categories, "Latin third conjugation verb")
table.insert(data.categories, "Latin irregular verbs")
table.insert(data.categories, "Latin suppletive verbs")
 
local prefix = typeinfo.prefix or ""
 
make_pres_3rd_io(data, typeinfo, prefix .. "fac", "nopass")
-- We said no passive, but we do want the future passive participle.
make_gerund(data, typeinfo, prefix .. "faciend", "und-variant", "no-gerund")
 
make_perf(data, prefix .. "fēc")
make_supine(data, typeinfo, prefix .. "fact")
make_sigm(data, typeinfo, prefix .. "fax")
 
if prefix == "" then
-- Active imperative
add_form(data, "2s_pres_actv_impr", "", "fac", 1)
-- Sigmatic forms
add_form(data, "1s_sigf_actv_indc", "", {"faxsō", "facsō", "faxiō"}, 2)
add_form(data, "2s_sigf_actv_indc", "", {"faxsis", "facsis", "facxis", "facxsis"},  2)
add_form(data, "3s_sigf_actv_indc", "", "faxsit", 2)
add_form(data, "1p_sigf_actv_indc", "", "faxsimus", 2)
add_form(data, "2p_sigf_actv_indc", "", "faxsitis", 2)
add_form(data, "3p_sigf_actv_indc", "", "faxsint", 2)
add_form(data, "1s_siga_actv_subj", "", {"faxsim", "faxēm"}, 2)
add_form(data, "2s_siga_actv_subj", "", {"faxsīs", "faxseis", "faxeis", "faxēs"}, 2)
add_form(data, "3s_siga_actv_subj", "", {"faxsīt", "faxeit", "faxēt"},  2)
add_form(data, "1p_siga_actv_subj", "", {"faxsīmus", "faxeimus"}, 2)
add_form(data, "2p_siga_actv_subj", "", {"faxsītis", "faxeitis"}, 2)
add_form(data, "3p_siga_actv_subj", "", {"faxsint", "faxēnt"}, 2)
end
end


if types.pl then
fio(data, prefix, "pasv")
num = "pl"
end
types.pl = nil
 
elseif types.sg then
irreg_conjugations["fio"] = function(args, data, typeinfo)
num = "sg"
table.insert(data.title, "[[Appendix:Latin third conjugation|third conjugation]] ''iō''-variant")
types.sg = nil
table.insert(data.title, "[[Appendix:Latin irregular verbs|irregular]] long ''ī''")
if not typeinfo.subtypes.nosup then
table.insert(data.title, "[[suppletive]] in the supine stem")
end
end
table.insert(data.categories, "Latin third conjugation verb")
table.insert(data.categories, "Latin irregular verbs")
table.insert(data.categories, "Latin suppletive verbs")
local prefix = typeinfo.prefix or ""


args[1] = base
typeinfo.subtypes.semidepon = true
args[2] = stem2


return {
fio(data, prefix, "actv")
decl = decl,
 
headword_decl = headword_decl,
make_supine(data, typeinfo, prefix .. "fact")
is_adj = is_adj,
 
gender = gender,
-- Perfect/future infinitives
orig_lemma = orig_lemma,
data.forms["futr_actv_inf"] = data.forms["futr_pasv_inf"]
lemma = lemma,
 
stem2 = stem2,
-- Imperfective participles
types = types,
data.forms["pres_actv_ptc"] = nil
num = num,
data.forms["futr_actv_ptc"] = nil
loc = loc,
 
args = args,
-- Gerund
}
make_gerund(data, typeinfo, prefix .. "fiend", "und-variant")
end
end


-- Parse a segment run (i.e. a string with zero or more segments [see
irreg_conjugations["fero"] = function(args, data, typeinfo)
-- parse_segment] and optional surrounding text, e.g. "foenum<2>-graecum<2>"
table.insert(data.title, "[[Appendix:Latin third conjugation|third conjugation]]")
-- or "[[pars]]/part<3.abl-e-occ-i> [[oratio|ōrātiōnis]]"). The segment run
table.insert(data.title, "[[Appendix:Latin irregular verbs|irregular]]")
-- currently cannot contain any alternants (e.g. "((epulum<2.sg>,epulae<1>))").
table.insert(data.title, "[[suppletive]]")
-- The return value is a table of the following form:
table.insert(data.categories, "Latin third conjugation verb")
-- {
table.insert(data.categories, "Latin irregular verbs")
--  segments = PARSED_SEGMENTS (a list of parsed segments),
table.insert(data.categories, "Latin suppletive verbs")
--  loc = LOC (a boolean indicating whether any of the individual segments
 
--    has a locative),
-- Signal to {{la-verb}} to display the verb as irregular
--  num = NUM (the first specified value for a number restriction, or nil if
typeinfo.subtypes.irreg = true
--    no number restrictions),
 
--  gender = GENDER (the first specified or inferred gender, or nil if none),
local prefix_pres = typeinfo.prefix or ""
--  is_adj = IS_ADJ (true if all segments are adjective segments, false if
local prefix_perf = ine(args[3])
--    there's at least one noun segment, nil if only raw-text segments),
local prefix_supine = ine(args[4])
--  propses = PROPSES (list of per-word properties, where each element is an
 
--     object {
prefix_perf = prefix_perf or prefix_pres
--      decl = DECL (declension),
prefix_supine = prefix_supine or prefix_pres
--      headword_decl = HEADWORD_DECL (declension to be displayed in headword),
 
--      types = TYPES (set describing the subtypes of a given word),
make_pres_3rd(data, typeinfo, prefix_pres .. "fer")
--    }
if prefix_perf == "" then
-- }
make_perf(data, {"tul", "tetul"})
-- Each element in PARSED_SEGMENTS is as returned by parse_segment() but will
local noteindex = #(data.footnotes) + 1
-- have an additional .orig_prefix field indicating the text before the segment
for slot in iter_slots(false, false) do
-- (including bracketed links) and corresponding .prefix field indicating the text
if cfind(slot, "perf") or cfind(slot, "plup") or cfind(slot, "futp") then
-- with bracketed links resolved. If there is trailing text, the last element will
data.form_footnote_indices[slot] = tostring(noteindex)
-- have only .orig_prefix and .prefix fields containing that trailing text.
data.footnotes[noteindex] = 'Archaic.'
local function parse_segment_run(segment_run)
local loc = nil
local num = nil
local is_adj = nil
-- If the segment run begins with a hyphen, include the hyphen in the
-- set of allowed characters for a declined segment. This way, e.g. the
-- suffix [[-cen]] can be declared as {{la-ndecl|-cen/-cin<3>}} rather than
-- {{la-ndecl|-cen/cin<3>}}, which is less intuitive.
local is_suffix = rfind(segment_run, "^%-")
local segments = {}
local propses = {}
-- We want to not break up a bracketed link followed by <> even if it has a space or
-- hyphen in it. So we do an outer capturing split to find the bracketed links followed
-- by <>, then do inner capturing splits on all the remaining text to find the other
-- declined terms.
local bracketed_segments = m_string_utilities.capturing_split(segment_run, "(%[%[[^%[%]]-%]%]<.->)")
for i, bracketed_segment in ipairs(bracketed_segments) do
if i % 2 == 0 then
table.insert(segments, bracketed_segment)
else
for _, subsegment in ipairs(m_string_utilities.capturing_split(
bracketed_segment, is_suffix and "([^<> ,]+<.->)" or "([^<> ,%-]+<.->)"
)) do
table.insert(segments, subsegment)
end
end
end
end
else
make_perf(data, prefix_perf .. "tul")
end
make_supine(data, typeinfo, prefix_supine .. "lāt")
-- Active imperfective indicative
data.forms["2s_pres_actv_indc"] = prefix_pres .. "fers"
data.forms["3s_pres_actv_indc"] = prefix_pres .. "fert"
data.forms["2p_pres_actv_indc"] = prefix_pres .. "fertis"
-- Passive imperfective indicative
data.forms["3s_pres_pasv_indc"] = prefix_pres .. "fertur"
-- Active imperfective subjunctive
data.forms["1s_impf_actv_subj"] = prefix_pres .. "ferrem"
data.forms["2s_impf_actv_subj"] = prefix_pres .. "ferrēs"
data.forms["3s_impf_actv_subj"] = prefix_pres .. "ferret"
data.forms["1p_impf_actv_subj"] = prefix_pres .. "ferrēmus"
data.forms["2p_impf_actv_subj"] = prefix_pres .. "ferrētis"
data.forms["3p_impf_actv_subj"] = prefix_pres .. "ferrent"
-- Passive present indicative
data.forms["2s_pres_pasv_indc"] = {prefix_pres .. "ferris", prefix_pres .. "ferre"}
-- Passive imperfective subjunctive
data.forms["1s_impf_pasv_subj"] = prefix_pres .. "ferrer"
data.forms["2s_impf_pasv_subj"] = {prefix_pres .. "ferrēris", prefix_pres .. "ferrēre"}
data.forms["3s_impf_pasv_subj"] = prefix_pres .. "ferrētur"
data.forms["1p_impf_pasv_subj"] = prefix_pres .. "ferrēmur"
data.forms["2p_impf_pasv_subj"] = prefix_pres .. "ferrēminī"
data.forms["3p_impf_pasv_subj"] = prefix_pres .. "ferrentur"
-- Imperative
data.forms["2s_pres_actv_impr"] = prefix_pres .. "fer"
data.forms["2p_pres_actv_impr"] = prefix_pres .. "ferte"
data.forms["2s_futr_actv_impr"] = prefix_pres .. "fertō"
data.forms["3s_futr_actv_impr"] = prefix_pres .. "fertō"
data.forms["2p_futr_actv_impr"] = prefix_pres .. "fertōte"
data.forms["2s_pres_pasv_impr"] = prefix_pres .. "ferre"
data.forms["2s_futr_pasv_impr"] = prefix_pres .. "fertor"
data.forms["3s_futr_pasv_impr"] = prefix_pres .. "fertor"
-- Present infinitives
data.forms["pres_actv_inf"] = prefix_pres .. "ferre"
data.forms["pres_pasv_inf"] = prefix_pres .. "ferrī"
end
irreg_conjugations["inquam"] = function(args, data, typeinfo)
table.insert(data.title, "[[Appendix:Latin irregular verbs|irregular]]")
table.insert(data.title, "highly [[defective verb|defective]]")
table.insert(data.categories, "Latin irregular verbs")
table.insert(data.categories, "Latin defective verbs")
-- Signal to {{la-verb}} to display the verb as highly defective
-- (it already displays as irregular because conj == "irreg" and
-- subconj == "irreg")
typeinfo.subtypes.highlydef = true
data.forms["1s_pres_actv_indc"] = "inquam"
data.forms["2s_pres_actv_indc"] = "inquis"
data.forms["3s_pres_actv_indc"] = "inquit"
data.forms["1p_pres_actv_indc"] = "inquimus"
data.forms["2p_pres_actv_indc"] = "inquitis"
data.forms["3p_pres_actv_indc"] = "inquiunt"
data.forms["2s_futr_actv_indc"] = "inquiēs"
data.forms["3s_futr_actv_indc"] = "inquiet"
data.forms["3s_impf_actv_indc"] = "inquiēbat"
data.forms["1s_perf_actv_indc"] = "inquiī"
data.forms["2s_perf_actv_indc"] = "inquistī"
data.forms["3s_perf_actv_indc"] = "inquit"
data.forms["3s_pres_actv_subj"] = "inquiat"
data.forms["2s_pres_actv_impr"] = "inque"
data.forms["2s_futr_actv_impr"] = "inquitō"
data.forms["3s_futr_actv_impr"] = "inquitō"
data.forms["pres_actv_ptc"] = "inquiēns"
end
local function libet_lubet(data, typeinfo, stem)
table.insert(data.title, "[[Appendix:Latin second conjugation|second conjugation]]")
table.insert(data.title, "mostly [[impersonal]]")
table.insert(data.categories, "Latin second conjugation verb")
table.insert(data.categories, "Latin impersonal verbs")
typeinfo.subtypes.nopass = true
local prefix = typeinfo.prefix or ""
stem = prefix .. stem
-- Active imperfective indicative
data.forms["3s_pres_actv_indc"] = stem .. "et"
data.forms["3s_impf_actv_indc"] = stem .. "ēbat"
data.forms["3s_futr_actv_indc"] = stem .. "ēbit"
-- Active perfective indicative
data.forms["3s_perf_actv_indc"] = {stem .. "uit", "[[" .. stem .. "itum]] [[est]]"}
data.forms["3s_plup_actv_indc"] = {stem .. "uerat", "[[" .. stem .. "itum]] [[erat]]"}
data.forms["3s_futp_actv_indc"] = {stem .. "uerit", "[[" .. stem .. "itum]] [[erit]]"}
-- Active imperfective subjunctive
data.forms["3s_pres_actv_subj"] = stem .. "eat"
data.forms["3s_impf_actv_subj"] = stem .. "ēret"
-- Active perfective subjunctive
data.forms["3s_perf_actv_subj"] = {stem .. "uerit", "[[" .. stem .. "itum]] [[sit]]"}
data.forms["3s_plup_actv_subj"] = {stem .. "uisset", "[[" .. stem .. "itum]] [[esset]]"}
data.forms["3p_plup_actv_subj"] = stem .. "uissent"
-- Present infinitives
data.forms["pres_actv_inf"] = stem .. "ēre"
-- Perfect infinitive
data.forms["perf_actv_inf"] = {stem .. "uisse", "[[" .. stem .. "itum]] [[esse]]"}
-- Imperfective participles
data.forms["pres_actv_ptc"] = stem .. "ēns"
data.forms["perf_actv_ptc"] = stem .. "itum"
end
irreg_conjugations["libet"] = function(args, data, typeinfo)
libet_lubet(data, typeinfo, "lib")
end
irreg_conjugations["lubet"] = function(args, data, typeinfo)
libet_lubet(data, typeinfo, "lub")
end
irreg_conjugations["licet"] = function(args, data, typeinfo)
table.insert(data.title, "[[Appendix:Latin second conjugation|second conjugation]]")
table.insert(data.title, "mostly [[impersonal]]")
table.insert(data.categories, "Latin second conjugation verb")
table.insert(data.categories, "Latin impersonal verbs")
typeinfo.subtypes.nopass = true
-- Active imperfective indicative
data.forms["3s_pres_actv_indc"] = "licet"
data.forms["3p_pres_actv_indc"] = "licent"
data.forms["3s_impf_actv_indc"] = "licēbat"
data.forms["3p_impf_actv_indc"] = "licēbant"
data.forms["3s_futr_actv_indc"] = "licēbit"
-- Active perfective indicative
data.forms["3s_perf_actv_indc"] = {"licuit", "[[licitum]] [[est]]"}
data.forms["3s_plup_actv_indc"] = {"licuerat", "[[licitum]] [[erat]]"}
data.forms["3s_futp_actv_indc"] = {"licuerit", "[[licitum]] [[erit]]"}
-- Active imperfective subjunctive
data.forms["3s_pres_actv_subj"] = "liceat"
data.forms["3p_pres_actv_subj"] = "liceant"
data.forms["3s_impf_actv_subj"] = "licēret"
-- Perfective subjunctive
data.forms["3s_perf_actv_subj"] = {"licuerit", "[[licitum]] [[sit]]"}
data.forms["3s_plup_actv_subj"] = {"licuisset", "[[licitum]] [[esset]]"}
-- Imperative
data.forms["2s_futr_actv_impr"] = "licētō"
data.forms["3s_futr_actv_impr"] = "licētō"
-- Infinitives
data.forms["pres_actv_inf"] = "licēre"
data.forms["perf_actv_inf"] = {"licuisse", "[[licitum]] [[esse]]"}
data.forms["futr_actv_inf"] = "[[licitūrum]] [[esse]]"
-- Participles
data.forms["pres_actv_ptc"] = "licēns"
data.forms["perf_actv_ptc"] = "licitus"
data.forms["futr_actv_ptc"] = "licitūrus"
end
-- Handle most forms of volō, mālō, nōlō.
local function volo_malo_nolo(data, indc_stem, subj_stem)
-- Present active indicative needs to be done individually as each
-- verb is different.
add_forms(data, "impf_actv_indc", indc_stem .. "ēb", "am", "ās", "at", "āmus", "ātis", "ant")
add_forms(data, "futr_actv_indc", indc_stem, "am", "ēs", "et", "ēmus", "ētis", "ent")
-- Active imperfective subjunctive
add_forms(data, "pres_actv_subj", subj_stem, "im", "īs", "it", "īmus", "ītis", "int")
add_forms(data, "impf_actv_subj", subj_stem .. "l", "em", "ēs", "et", "ēmus", "ētis", "ent")
-- Present infinitives
data.forms["pres_actv_inf"] = subj_stem .. "le"
-- Imperfective participles
data.forms["pres_actv_ptc"] = indc_stem .. "ēns"
end
irreg_conjugations["volo"] = function(args, data, typeinfo)
table.insert(data.title, "[[Appendix:Latin irregular verbs|irregular]]")
table.insert(data.title, "[[suppletive]] in the second-person singular indicative present")
table.insert(data.categories, "Latin irregular verbs")
table.insert(data.categories, "Latin suppletive verbs")
local prefix = typeinfo.prefix or ""
typeinfo.subtypes.nopass = true
typeinfo.subtypes.noimp = true
make_perf(data, prefix .. "volu")
-- Active imperfective indicative
add_forms(data, "pres_actv_indc", prefix,
"volō", "vīs", prefix ~= "" and "vult" or {"vult", "volt"},
"volumus", prefix ~= "" and "vultis" or {"vultis", "voltis"}, "volunt")
volo_malo_nolo(data, prefix .. "vol", prefix .. "vel")
end
irreg_conjugations["malo"] = function(args, data, typeinfo)
table.insert(data.title, "[[Appendix:Latin irregular verbs|irregular]]")
table.insert(data.title, "[[suppletive]] in the second-person singular indicative present")
table.insert(data.categories, "Latin irregular verbs")
table.insert(data.categories, "Latin suppletive verbs")
typeinfo.subtypes.nopass = true
typeinfo.subtypes.noimp = true
make_perf(data, "mālu")
-- Active imperfective indicative
add_forms(data, "pres_actv_indc", "",
"mālō", "māvīs", "māvult", "mālumus", "māvultis", "mālunt")
volo_malo_nolo(data, "māl", "māl")
end
irreg_conjugations["nolo"] = function(args, data, typeinfo)
table.insert(data.title, "[[Appendix:Latin irregular verbs|irregular]]")
table.insert(data.title, "[[suppletive]] in the second-person singular indicative present")
table.insert(data.categories, "Latin irregular verbs")
table.insert(data.categories, "Latin suppletive verbs")
typeinfo.subtypes.nopass = true
make_perf(data, "nōlu")
-- Active imperfective indicative
add_forms(data, "pres_actv_indc", "",
"nōlō", "nōn vīs", "nōn vult", "nōlumus", "nōn vultis", "nōlunt")
add_forms(data, "impf_actv_indc", "nōlēb", "am", "ās", "at", "āmus", "ātis", "ant")
volo_malo_nolo(data, "nōl", "nōl")
-- Imperative
add_2_forms(data, "pres_actv_impr", "nōlī", "", "te")
add_23_forms(data, "futr_actv_impr", "nōl", "itō", "itō", "itōte", "untō")
end
irreg_conjugations["possum"] = function(args, data, typeinfo)
table.insert(data.title, "highly [[Appendix:Latin irregular verbs|irregular]]")
table.insert(data.title, "[[suppletive]]")
table.insert(data.categories, "Latin irregular verbs")
table.insert(data.categories, "Latin suppletive verbs")
typeinfo.subtypes.nopass = true
make_perf(data, "potu")
-- Active imperfective indicative
add_forms(data, "pres_actv_indc", "", "possum", "potes", "potest",
"possumus", "potestis", "possunt")
add_forms(data, "impf_actv_indc", "poter", "am", "ās", "at", "āmus", "ātis", "ant")
add_forms(data, "futr_actv_indc", "poter", "ō", {"is", "e"}, "it", "imus", "itis", "unt")
-- Active imperfective subjunctive
add_forms(data, "pres_actv_subj", "poss", "im", "īs", "it", "īmus", "ītis", "int")
add_forms(data, "impf_actv_subj", "poss", "em", "ēs", "et", "ēmus", "ētis", "ent")
-- Present infinitives
data.forms["pres_actv_inf"] = "posse"
-- Imperfective participles
data.forms["pres_actv_ptc"] = "potēns"
end
irreg_conjugations["piget"] = function(args, data, typeinfo)
table.insert(data.title, "[[Appendix:Latin second conjugation|second conjugation]]")
table.insert(data.title, "[[impersonal]]")
table.insert(data.title, "[[semi-deponent]]")
table.insert(data.categories, "Latin second conjugation verb")
table.insert(data.categories, "Latin impersonal verbs")
table.insert(data.categories, "Latin semi-deponent verbs")
table.insert(data.categories, "Latin defective verbs")
local prefix = typeinfo.prefix or ""
--[[
-- not used
local ppplink = make_link({lang = lang, alt = '', term = prefix .. "ausus"}, "term")
local sumlink = make_link({lang = lang, alt = '', term = "sum"}, "term")
--]]
data.forms["3s_pres_actv_indc"] = prefix .. "piget"
data.forms["3s_impf_actv_indc"] = prefix .. "pigēbat"
data.forms["3s_futr_actv_indc"] = prefix .. "pigēbit"
data.forms["3s_perf_actv_indc"] = {prefix .. "piguit", "[[" .. prefix .. "pigitum]] [[est]]"}
data.forms["3s_plup_actv_indc"] = {prefix .. "piguerat", "[[" .. prefix .. "pigitum]] [[erat]]"}
data.forms["3s_futp_actv_indc"] = {prefix .. "piguerit", "[[" .. prefix .. "pigitum]] [[erit]]"}
data.forms["3s_pres_actv_subj"] = prefix .. "pigeat"
data.forms["3s_impf_actv_subj"] = prefix .. "pigēret"
data.forms["3s_perf_actv_subj"] = {prefix .. "piguerit", "[[" .. prefix .. "pigitum]] [[sit]]"}
data.forms["3s_plup_actv_subj"] = {prefix .. "piguisset", "[[" .. prefix .. "pigitum]] [[esset]]"}
data.forms["pres_actv_inf"] = prefix .. "pigēre"
data.forms["perf_actv_inf"] = "[[" .. prefix .. "pigitum]] [[esse]]"
data.forms["pres_actv_ptc"] = prefix .. "pigēns"
data.forms["perf_actv_ptc"] = prefix .. "pigitum"
-- Gerund
make_gerund(data, typeinfo, prefix .. "pigend")
end
irreg_conjugations["coepi"] = function(args, data, typeinfo)
table.insert(data.title, "[[Appendix:Latin third conjugation|third conjugation]]")
table.insert(data.title, "no [[present tense|present]] stem")
table.insert(data.categories, "Latin third conjugation verb")
table.insert(data.categories, "Latin verbs with missing present stem")
table.insert(data.categories, "Latin defective verbs")
local prefix = typeinfo.prefix or ""
make_perf(data, prefix .. "coep")
make_supine(data, typeinfo, prefix .. "coept")
make_perfect_passive(data)
end
-- The vowel of the prefix is lengthened if it ends in -n and the next word begins with f- or s-.
local function lengthen_prefix(prefix)
return prefix:gsub("([aeiou]n)$", {["an"] = "ān", ["en"] = "ēn", ["in"] = "īn", ["on"] = "ōn", ["un"] = "ūn"})
end
irreg_conjugations["sum"] = function(args, data, typeinfo)
table.insert(data.title, "highly [[Appendix:Latin irregular verbs|irregular]]")
table.insert(data.title, "[[suppletive]]")
table.insert(data.categories, "Latin irregular verbs")
table.insert(data.categories, "Latin suppletive verbs")
local prefix = typeinfo.prefix or ""
local prefix_e = ine(args[3]) or prefix
local prefix_f = lengthen_prefix(ine(args[4]) or prefix)
local prefix_s = lengthen_prefix(prefix)
typeinfo.subtypes.nopass = true
typeinfo.subtypes.supfutractvonly = true
make_perf(data, prefix_f .. "fu")
make_supine(data, typeinfo, prefix_f .. "fut")
-- Active imperfective indicative
data.forms["1s_pres_actv_indc"] = prefix_s .. "sum"
data.forms["2s_pres_actv_indc"] = prefix_e .. "es"
data.forms["3s_pres_actv_indc"] = prefix_e .. "est"
data.forms["1p_pres_actv_indc"] = prefix_s .. "sumus"
data.forms["2p_pres_actv_indc"] = prefix_e .. "estis"
data.forms["3p_pres_actv_indc"] = prefix_s .. "sunt"
data.forms["1s_impf_actv_indc"] = prefix_e .. "eram"
data.forms["2s_impf_actv_indc"] = prefix_e .. "erās"
data.forms["3s_impf_actv_indc"] = prefix_e .. "erat"
data.forms["1p_impf_actv_indc"] = prefix_e .. "erāmus"
data.forms["2p_impf_actv_indc"] = prefix_e .. "erātis"
data.forms["3p_impf_actv_indc"] = prefix_e .. "erant"
data.forms["1s_futr_actv_indc"] = prefix_e .. "erō"
data.forms["2s_futr_actv_indc"] = {prefix_e .. "eris", prefix_e .. "ere"}
data.forms["3s_futr_actv_indc"] = prefix_e .. "erit"
data.forms["1p_futr_actv_indc"] = prefix_e .. "erimus"
data.forms["2p_futr_actv_indc"] = prefix_e .. "eritis"
data.forms["3p_futr_actv_indc"] = prefix_e .. "erunt"
-- Active imperfective subjunctive
data.forms["1s_pres_actv_subj"] = prefix_s .. "sim"
data.forms["2s_pres_actv_subj"] = prefix_s .. "sīs"
data.forms["3s_pres_actv_subj"] = prefix_s .. "sit"
data.forms["1p_pres_actv_subj"] = prefix_s .. "sīmus"
data.forms["2p_pres_actv_subj"] = prefix_s .. "sītis"
data.forms["3p_pres_actv_subj"] = prefix_s .. "sint"
if prefix_s == "ad" then
local noteindex = #(data.footnotes) + 1
add_form(data, "3p_pres_actv_subj", "", "adessint", 2 )
data.form_footnote_indices["3p_pres_actv_subj"] = tostring(noteindex)
data.footnotes[noteindex] = 'Archaic.'
end
data.forms["1s_impf_actv_subj"] = {prefix_e .. "essem", prefix_f .. "forem"}
data.forms["2s_impf_actv_subj"] = {prefix_e .. "essēs", prefix_f .. "forēs"}
data.forms["3s_impf_actv_subj"] = {prefix_e .. "esset", prefix_f .. "foret"}
data.forms["1p_impf_actv_subj"] = {prefix_e .. "essēmus", prefix_f .. "forēmus"}
data.forms["2p_impf_actv_subj"] = {prefix_e .. "essētis", prefix_f .. "forētis"}
data.forms["3p_impf_actv_subj"] = {prefix_e .. "essent", prefix_f .. "forent"}
-- Imperative
data.forms["2s_pres_actv_impr"] = prefix_e .. "es"
data.forms["2p_pres_actv_impr"] = prefix_e .. "este"
data.forms["2s_futr_actv_impr"] = prefix_e .. "estō"
data.forms["3s_futr_actv_impr"] = prefix_e .. "estō"
data.forms["2p_futr_actv_impr"] = prefix_e .. "estōte"
data.forms["3p_futr_actv_impr"] = prefix_s .. "suntō"
-- Present infinitives
data.forms["pres_actv_inf"] = prefix_e .. "esse"
-- Future infinitives
data.forms["futr_actv_inf"] = {"[[" .. prefix_f .. "futūrum]] [[esse]]", prefix_f .. "fore"}
-- Imperfective participles
if prefix == "ab" then
data.forms["pres_actv_ptc"] = "absēns"
elseif prefix == "prae" then
data.forms["pres_actv_ptc"] = "praesēns"
end
-- Gerund
data.forms["ger_gen"] = nil
data.forms["ger_dat"] = nil
data.forms["ger_acc"] = nil
data.forms["ger_abl"] = nil
-- Supine
data.forms["sup_acc"] = nil
data.forms["sup_abl"] = nil
end
-- Form-generating functions
make_pres_1st = function(data, typeinfo, pres_stem)
if not pres_stem then
return
end
-- Active imperfective indicative
add_forms(data, "pres_actv_indc", pres_stem, "ō", "ās", "at", "āmus", "ātis", "ant")
add_forms(data, "impf_actv_indc", pres_stem, "ābam", "ābās", "ābat", "ābāmus", "ābātis", "ābant")
add_forms(data, "futr_actv_indc", pres_stem, "ābō", "ābis", "ābit", "ābimus", "ābitis", "ābunt")
-- Passive imperfective indicative
add_forms(data, "pres_pasv_indc", pres_stem, "or", {"āris", "āre"}, "ātur", "āmur", "āminī", "antur")
add_forms(data, "impf_pasv_indc", pres_stem, "ābar", {"ābāris", "ābāre"}, "ābātur", "ābāmur", "ābāminī", "ābantur")
add_forms(data, "futr_pasv_indc", pres_stem, "ābor", {"āberis", "ābere"}, "ābitur", "ābimur", "ābiminī", "ābuntur")
-- Active imperfective subjunctive
add_forms(data, "pres_actv_subj", pres_stem, "em", "ēs", "et", "ēmus", "ētis", "ent")
add_forms(data, "impf_actv_subj", pres_stem, "ārem", "ārēs", "āret", "ārēmus", "ārētis", "ārent")
-- Passive imperfective subjunctive
add_forms(data, "pres_pasv_subj", pres_stem, "er", {"ēris", "ēre"}, "ētur", "ēmur", "ēminī", "entur")
add_forms(data, "impf_pasv_subj", pres_stem, "ārer", {"ārēris", "ārēre"}, "ārētur", "ārēmur", "ārēminī", "ārentur")
-- Imperative
add_2_forms(data, "pres_actv_impr", pres_stem, "ā", "āte")
add_23_forms(data, "futr_actv_impr", pres_stem, "ātō", "ātō", "ātōte", "antō")
add_2_forms(data, "pres_pasv_impr", pres_stem, "āre", "āminī")
add_23_forms(data, "futr_pasv_impr", pres_stem, "ātor", "ātor", {}, "antor")
-- Present infinitives
data.forms["pres_actv_inf"] = pres_stem .. "āre"
data.forms["pres_pasv_inf"] = pres_stem .. "ārī"
-- Imperfective participles
data.forms["pres_actv_ptc"] = pres_stem .. "āns"
-- Gerund
make_gerund(data, typeinfo, pres_stem .. "and")
end
make_pres_2nd = function(data, typeinfo, pres_stem, nopass, noimpr)
-- Active imperfective indicative
add_forms(data, "pres_actv_indc", pres_stem, "eō", "ēs", "et", "ēmus", "ētis", "ent")
add_forms(data, "impf_actv_indc", pres_stem, "ēbam", "ēbās", "ēbat", "ēbāmus", "ēbātis", "ēbant")
add_forms(data, "futr_actv_indc", pres_stem, "ēbō", "ēbis", "ēbit", "ēbimus", "ēbitis", "ēbunt")
-- Active imperfective subjunctive
add_forms(data, "pres_actv_subj", pres_stem, "eam", "eās", "eat", "eāmus", "eātis", "eant")
add_forms(data, "impf_actv_subj", pres_stem, "ērem", "ērēs", "ēret", "ērēmus", "ērētis", "ērent")
-- Active imperative
if not noimpr then
add_2_forms(data, "pres_actv_impr", pres_stem, "ē", "ēte")
add_23_forms(data, "futr_actv_impr", pres_stem, "ētō", "ētō", "ētōte", "entō")
end
end
local parsed_segments = {}
 
local gender = nil
if not nopass then
for i = 2, (#segments - 1), 2 do
-- Passive imperfective indicative
local parsed_segment = parse_segment(segments[i])
add_forms(data, "pres_pasv_indc", pres_stem, "eor", {"ēris", "ēre"}, "ētur", "ēmur", "ēminī", "entur")
-- Overall locative is true if any segments call for locative.
add_forms(data, "impf_pasv_indc", pres_stem, "ēbar", {"ēbāris", "ēbāre"}, "ēbātur", "ēbāmur", "ēbāminī", "ēbantur")
loc = loc or parsed_segment.loc
add_forms(data, "futr_pasv_indc", pres_stem, "ēbor", {"ēberis", "ēbere"}, "ēbitur", "ēbimur", "ēbiminī", "ēbuntur")
-- The first specified value for num is used becomes the overall value.
 
num = num or parsed_segment.num
-- Passive imperfective subjunctive
if is_adj == nil then
add_forms(data, "pres_pasv_subj", pres_stem, "ear", {"eāris", "eāre"}, "eātur", "eāmur", "eāminī", "eantur")
is_adj = parsed_segment.is_adj
add_forms(data, "impf_pasv_subj", pres_stem, "ērer", {"ērēris", "ērēre"}, "ērētur", "ērēmur", "ērēminī", "ērentur")
else
 
is_adj = is_adj and parsed_segment.is_adj
-- Passive imperative
if not noimpr then
add_2_forms(data, "pres_pasv_impr", pres_stem, "ēre", "ēminī")
add_23_forms(data, "futr_pasv_impr", pres_stem, "ētor", "ētor", {}, "entor")
end
end
gender = gender or parsed_segment.gender
parsed_segment.orig_prefix = segments[i - 1]
parsed_segment.prefix = m_links.remove_links(segments[i - 1])
table.insert(parsed_segments, parsed_segment)
local props = {
decl = parsed_segment.decl,
headword_decl = parsed_segment.headword_decl,
types = parsed_segment.types,
}
table.insert(propses, props)
end
end
if segments[#segments] ~= "" then
 
table.insert(parsed_segments, {
-- Present infinitives
orig_prefix = segments[#segments],
data.forms["pres_actv_inf"] = pres_stem .. "ēre"
prefix = m_links.remove_links(segments[#segments]),
if not nopass then
})
data.forms["pres_pasv_inf"] = pres_stem .. "ērī"
end
 
-- Imperfective participles
data.forms["pres_actv_ptc"] = pres_stem .. "ēns"
 
-- Gerund
make_gerund(data, typeinfo, pres_stem .. "end", nil, nil, nopass)
end
 
make_pres_3rd = function(data, typeinfo, pres_stem)
-- Active imperfective indicative
add_forms(data, "pres_actv_indc", pres_stem, "ō", "is", "it", "imus", "itis", "unt")
add_forms(data, "impf_actv_indc", pres_stem, "ēbam", "ēbās", "ēbat", "ēbāmus", "ēbātis", "ēbant")
add_forms(data, "futr_actv_indc", pres_stem, "am", "ēs", "et", "ēmus", "ētis", "ent")
 
-- Passive imperfective indicative
add_forms(data, "pres_pasv_indc", pres_stem, "or", {"eris", "ere"}, "itur", "imur", "iminī", "untur")
add_forms(data, "impf_pasv_indc", pres_stem, "ēbar", {"ēbāris", "ēbāre"}, "ēbātur", "ēbāmur", "ēbāminī", "ēbantur")
add_forms(data, "futr_pasv_indc", pres_stem, "ar", {"ēris", "ēre"}, "ētur", "ēmur", "ēminī", "entur")
 
-- Active imperfective subjunctive
add_forms(data, "pres_actv_subj", pres_stem, "am", "ās", "at", "āmus", "ātis", "ant")
add_forms(data, "impf_actv_subj", pres_stem, "erem", "erēs", "eret", "erēmus", "erētis", "erent")
 
-- Passive imperfective subjunctive
add_forms(data, "pres_pasv_subj", pres_stem, "ar", {"āris", "āre"}, "ātur", "āmur", "āminī", "antur")
add_forms(data, "impf_pasv_subj", pres_stem, "erer", {"erēris", "erēre"}, "erētur", "erēmur", "erēminī", "erentur")
 
-- Imperative
add_2_forms(data, "pres_actv_impr", pres_stem, "e", "ite")
add_23_forms(data, "futr_actv_impr", pres_stem, "itō", "itō", "itōte", "untō")
 
add_2_forms(data, "pres_pasv_impr", pres_stem, "ere", "iminī")
add_23_forms(data, "futr_pasv_impr", pres_stem, "itor", "itor", {}, "untor")
 
-- Present infinitives
data.forms["pres_actv_inf"] = pres_stem .. "ere"
data.forms["pres_pasv_inf"] = pres_stem .. "ī"
 
-- Imperfective participles
data.forms["pres_actv_ptc"] = pres_stem .. "ēns"
 
-- Gerund
make_gerund(data, typeinfo, pres_stem .. "end", "und-variant")
end
 
make_pres_3rd_io = function(data, typeinfo, pres_stem, nopass)
-- Active imperfective indicative
add_forms(data, "pres_actv_indc", pres_stem, "iō", "is", "it", "imus", "itis", "iunt")
add_forms(data, "impf_actv_indc", pres_stem, "iēbam", "iēbās", "iēbat", "iēbāmus", "iēbātis", "iēbant")
add_forms(data, "futr_actv_indc", pres_stem, "iam", "iēs", "iet", "iēmus", "iētis", "ient")
 
-- Active imperfective subjunctive
add_forms(data, "pres_actv_subj", pres_stem, "iam", "iās", "iat", "iāmus", "iātis", "iant")
add_forms(data, "impf_actv_subj", pres_stem, "erem", "erēs", "eret", "erēmus", "erētis", "erent")
 
-- Active imperative
add_2_forms(data, "pres_actv_impr", pres_stem, "e", "ite")
add_23_forms(data, "futr_actv_impr", pres_stem, "itō", "itō", "itōte", "iuntō")
 
-- Passive imperfective indicative
if not nopass then
add_forms(data, "pres_pasv_indc", pres_stem, "ior", {"eris", "ere"}, "itur", "imur", "iminī", "iuntur")
add_forms(data, "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(data, "futr_pasv_indc", pres_stem, "iar", {"iēris", "iēre"}, "iētur", "iēmur", "iēminī", "ientur")
 
-- Passive imperfective subjunctive
add_forms(data, "pres_pasv_subj", pres_stem, "iar", {"iāris", "iāre"}, "iātur", "iāmur", "iāminī", "iantur")
add_forms(data, "impf_pasv_subj", pres_stem, "erer", {"erēris", "erēre"}, "erētur", "erēmur", "erēminī", "erentur")
 
-- Passive imperative
add_2_forms(data, "pres_pasv_impr", pres_stem, "ere", "iminī")
add_23_forms(data, "futr_pasv_impr", pres_stem, "itor", "itor", {}, "iuntor")
end
 
-- Present infinitives
data.forms["pres_actv_inf"] = pres_stem .. "ere"
if not nopass then
data.forms["pres_pasv_inf"] = pres_stem .. "ī"
end
 
-- Imperfective participles
data.forms["pres_actv_ptc"] = pres_stem .. "iēns"
 
-- Gerund
make_gerund(data, typeinfo, pres_stem .. "iend", "und-variant", nil, nopass)
end
 
make_pres_4th = function(data, typeinfo, pres_stem)
-- Active imperfective indicative
add_forms(data, "pres_actv_indc", pres_stem, "iō", "īs", "it", "īmus", "ītis", "iunt")
add_forms(data, "impf_actv_indc", pres_stem, "iēbam", "iēbās", "iēbat", "iēbāmus", "iēbātis", "iēbant")
add_forms(data, "futr_actv_indc", pres_stem, "iam", "iēs", "iet", "iēmus", "iētis", "ient")
 
-- Passive imperfective indicative
add_forms(data, "pres_pasv_indc", pres_stem, "ior", {"īris", "īre"}, "ītur", "īmur", "īminī", "iuntur")
add_forms(data, "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(data, "futr_pasv_indc", pres_stem, "iar", {"iēris", "iēre"}, "iētur", "iēmur", "iēminī", "ientur")
 
-- Active imperfective subjunctive
add_forms(data, "pres_actv_subj", pres_stem, "iam", "iās", "iat", "iāmus", "iātis", "iant")
add_forms(data, "impf_actv_subj", pres_stem, "īrem", "īrēs", "īret", "īrēmus", "īrētis", "īrent")
 
-- Passive imperfective subjunctive
add_forms(data, "pres_pasv_subj", pres_stem, "iar", {"iāris", "iāre"}, "iātur", "iāmur", "iāminī", "iantur")
add_forms(data, "impf_pasv_subj", pres_stem, "īrer", {"īrēris", "īrēre"}, "īrētur", "īrēmur", "īrēminī", "īrentur")
 
-- Imperative
add_2_forms(data, "pres_actv_impr", pres_stem, "ī", "īte")
add_23_forms(data, "futr_actv_impr", pres_stem, "ītō", "ītō", "ītōte", "iuntō")
 
add_2_forms(data, "pres_pasv_impr", pres_stem, "īre", "īminī")
add_23_forms(data, "futr_pasv_impr", pres_stem, "ītor", "ītor", {}, "iuntor")
 
-- Present infinitives
data.forms["pres_actv_inf"] = pres_stem .. "īre"
data.forms["pres_pasv_inf"] = pres_stem .. "īrī"
 
-- Imperfective participles
data.forms["pres_actv_ptc"] = pres_stem .. "iēns"
 
-- Gerund
make_gerund(data, typeinfo, pres_stem .. "iend", "und-variant")
end
 
make_perf_and_supine = function(data, typeinfo)
if typeinfo.subtypes.optsemidepon then
make_perf(data, typeinfo.perf_stem, "noinf")
make_deponent_perf(data, typeinfo.supine_stem)
else
make_perf(data, typeinfo.perf_stem)
make_supine(data, typeinfo, typeinfo.supine_stem)
end
end
return {
segments = parsed_segments,
loc = loc,
num = num,
is_adj = is_adj,
gender = gender,
propses = propses,
}
end
end


-- Parse an alternant, e.g. "((epulum<2.sg>,epulae<1>))",
make_perf = function(data, perf_stem, no_inf)
-- "((Serapis<3>,Serapis/Serapid<3>))" or
if not perf_stem then
-- "((rēs<5>pūblica<1>,rēspūblica<1>))". The return value is a table of the form
return
-- {
end
--  alternants = PARSED_ALTERNANTS (a list of segment runs, each of which is a
if type(perf_stem) ~= "table" then
--    list of parsed segments as returned by parse_segment_run()),
perf_stem = {perf_stem}
--   loc = LOC (a boolean indicating whether any of the individual segment runs
end
--    has a locative),
 
--  num = NUM (the overall number restriction, one of "sg", "pl" or "both"),
for _, stem in ipairs(perf_stem) do
--  gender = GENDER (the first specified or inferred gender, or nil if none),
-- Perfective indicative
--  is_adj = IS_ADJ (true if all non-constant alternants are adjectives, false
add_forms(data, "perf_actv_indc", stem, "ī", "istī", "it", "imus", "istis", {"ērunt", "ēre"})
--    if all nouns, nil if only constant alternants; conflicting alternants
add_forms(data, "plup_actv_indc", stem, "eram", "erās", "erat", "erāmus", "erātis", "erant")
--    cause an error),
add_forms(data, "futp_actv_indc", stem, "erō", "eris", "erit", "erimus", "eritis", "erint")
--  propses = PROPSES (list of lists of per-word property objecs),
-- Perfective subjunctive
-- }
add_forms(data, "perf_actv_subj", stem, "erim", "erīs", "erit", "erīmus", "erītis", "erint")
local function parse_alternant(alternant)
add_forms(data, "plup_actv_subj", stem, "issem", "issēs", "isset", "issēmus", "issētis", "issent")
local parsed_alternants = {}
 
local alternant_spec = rmatch(alternant, "^%(%((.*)%)%)$")
-- Perfect infinitive
local alternants = rsplit(alternant_spec, ",")
if not no_inf then
local loc = false
add_form(data, "perf_actv_inf", stem, "isse")
local num = nil
local gender = nil
local is_adj = nil
local propses = {}
for i, alternant in ipairs(alternants) do
local parsed_run = parse_segment_run(alternant)
table.insert(parsed_alternants, parsed_run)
loc = loc or parsed_run.loc
-- First time through, set the overall num to the num of the first run,
-- even if nil. After that, if we ever see a run with a different value
-- of num, set the overall num to "both". That way, if all alternants
-- don't specify a num, we get an unspecified num, but if some do and
-- some don't, we get both, because an unspecified num defaults to
-- both.
if i == 1 then
num = parsed_run.num
elseif num ~= parsed_run.num then
-- FIXME, this needs to be rethought to allow for
-- adjective alternants.
num = "both"
end
end
gender = gender or parsed_run.gender
if is_adj == nil then
is_adj = parsed_run.is_adj
elseif parsed_run.is_adj ~= nil and parsed_run.is_adj ~= is_adj then
error("Saw both noun and adjective alternants; not allowed")
end
table.insert(propses, parsed_run.propses)
end
end
return {
alternants = parsed_alternants,
loc = loc,
num = num,
gender = gender,
is_adj = is_adj,
propses = propses,
}
end
end


-- Parse a segment run (see parse_segment_run()). Unlike for
make_deponent_perf = function(data, supine_stem)
-- parse_segment_run(), this can contain alternants such as
if not supine_stem then
-- "((epulum<2.sg>,epulae<1>))" or "((Serapis<3.sg>,Serapis/Serapid<3.sg>))"
return
-- embedded in it to indicate words composed of multiple declensions.
end
-- The return value is a table of the following form:
if type(supine_stem) ~= "table" then
-- {
supine_stem = {supine_stem}
--  segments = PARSED_SEGMENTS (a list of parsed segments),
end
--  loc = LOC (a boolean indicating whether any of the individual segments has
 
--    a locative),
-- Perfect/future infinitives
--  num = NUM (the first specified value for a number restriction, or nil if
for _, stem in ipairs(supine_stem) do
--    no number restrictions),
local stems = "[[" .. stem .. "us]] "
--  gender = GENDER (the first specified or inferred gender, or nil if none),
local stemp = "[[" .. stem .. "ī]] "
--  is_adj = IS_ADJ (true if all segments are adjective segments, false if
 
--    there's at least one noun segment, nil if only raw-text segments),
add_forms(data, "perf_actv_indc", stems, "[[sum]]", "[[es]]", "[[est]]", {}, {}, {})
--  propses = PROPSES (list of either per-word property objects or lists of
add_forms(data, "perf_actv_indc", stemp, {}, {}, {}, "[[sumus]]", "[[estis]]", "[[sunt]]")
-- lists of such objects),
 
-- }.
add_forms(data, "plup_actv_indc", stems, "[[eram]]", "[[erās]]", "[[erat]]", {}, {}, {})
-- Each element in PARSED_SEGMENTS is one of three types:
add_forms(data, "plup_actv_indc", stemp, {}, {}, {}, "[[erāmus]]", "[[erātis]]", "[[erant]]")
--
 
-- 1. A regular segment, as returned by parse_segment() but with additional
add_forms(data, "futp_actv_indc", stems, "[[erō]]", "[[eris]]", "[[erit]]", {}, {}, {})
--    .prefix and .orig_prefix fields indicating the text before the segment, as per
add_forms(data, "futp_actv_indc", stemp, {}, {}, {}, "[[erimus]]", "[[eritis]]", "[[erint]]")
--    the return value of parse_segment_run().
 
-- 2. A raw-text segment, i.e. a table with only .prefix and .orig_prefix fields
add_forms(data, "perf_actv_subj", stems, "[[sim]]", "[[sīs]]", "[[sit]]", {}, {}, {})
--    containing the raw text.
add_forms(data, "perf_actv_subj", stemp, {}, {}, {}, "[[sīmus]]", "[[sītis]]", "[[sint]]")
-- 3. An alternating segment, as returned by parse_alternant().
 
-- Note that each alternant is a segment run rather than a single parsed
add_forms(data, "plup_actv_subj", stems, "[[essem]]", "[[essēs]]", "[[esset]]", {}, {}, {})
-- segment to allow for alternants like "((rēs<5>pūblica<1>,rēspūblica<1>))".
add_forms(data, "plup_actv_subj", stemp, {}, {}, {}, "[[essēmus]]", "[[essētis]]", "[[essent]]")
-- The parsed segment runs in PARSED_SEGMENT_RUNS are tables as returned by
 
-- parse_segment_run() (of the same form as the overall return value of
add_form(data, "perf_actv_inf", "", "[[" .. stem .. "um]] [[esse]]")
-- parse_segment_run_allowing_alternants()).
add_form(data, "futr_actv_inf", "", "[[" .. stem .. "ūrum]] [[esse]]")
local function parse_segment_run_allowing_alternants(segment_run)
add_form(data, "perf_actv_ptc", stem, "us")
if rfind(segment_run, " ") then
add_form(data, "futr_actv_ptc", stem, "ūrus")
track("has-space")
 
-- Supine
add_form(data, "sup_acc", stem, "um")
add_form(data, "sup_abl", stem, "ū")
end
end
 
make_supine = function(data, typeinfo, supine_stem)
if not supine_stem then
return
end
end
if rfind(segment_run, "%(%(") then
if type(supine_stem) ~= "table" then
track("has-alternant")
supine_stem = {supine_stem}
end
end
local alternating_segments = m_string_utilities.capturing_split(segment_run, "(%(%(.-%)%))")
 
local parsed_segments = {}
-- Perfect/future infinitives
local loc = false
for _, stem in ipairs(supine_stem) do
local num = nil
local futr_actv_inf, perf_pasv_inf, futr_pasv_inf, futr_actv_ptc
local gender = nil
local perf_pasv_ptc_lemma, perf_actv_ptc, perf_actv_ptc_acc
local is_adj = nil
-- Perfect/future participles
local propses = {}
futr_actv_ptc = stem .. "ūrus"
for i = 1, #alternating_segments do
if typeinfo.subtypes.passimpers then
local alternating_segment = alternating_segments[i]
perf_pasv_ptc_lemma = stem .. "um"
if alternating_segment ~= "" then
perf_pasv_ptc = perf_pasv_ptc_lemma
local this_is_adj
perf_pasv_ptc_acc = perf_pasv_ptc_lemma
if i % 2 == 1 then
else
local parsed_run = parse_segment_run(alternating_segment)
perf_pasv_ptc_lemma = stem .. "us"
for _, parsed_segment in ipairs(parsed_run.segments) do
if typeinfo.subtypes.mp then
table.insert(parsed_segments, parsed_segment)
perf_pasv_ptc = stem .. "ī"
end
perf_pasv_ptc_acc = stem .. "ōs"
loc = loc or parsed_run.loc
elseif typeinfo.subtypes.fp then
num = num or parsed_run.num
perf_pasv_ptc = stem .. "ae"
gender = gender or parsed_run.gender
perf_pasv_ptc_acc = stem .. "ās"
this_is_adj = parsed_run.is_adj
elseif typeinfo.subtypes.np then
for _, props in ipairs(parsed_run.propses) do
perf_pasv_ptc = stem .. "a"
table.insert(propses, props)
perf_pasv_ptc_acc = perf_pasv_ptc
end
elseif typeinfo.subtypes.f then
perf_pasv_ptc = stem .. "a"
perf_pasv_ptc_acc = stem .. "am"
elseif typeinfo.subtypes.n then
perf_pasv_ptc = stem .. "um"
perf_pasv_ptc_acc = perf_pasv_ptc
else
else
local parsed_alternating_segment = parse_alternant(alternating_segment)
perf_pasv_ptc = perf_pasv_ptc_lemma
table.insert(parsed_segments, parsed_alternating_segment)
perf_pasv_ptc_acc = stem .. "um"
loc = loc or parsed_alternating_segment.loc
num = num or parsed_alternating_segment.num
gender = gender or parsed_alternating_segment.gender
this_is_adj = parsed_alternating_segment.is_adj
table.insert(propses, parsed_alternating_segment.propses)
end
if is_adj == nil then
is_adj = this_is_adj
elseif this_is_adj ~= nil then
is_adj = is_adj and this_is_adj
end
end
end
end
end


if #parsed_segments > 1 then
perf_pasv_inf = make_raw_link(perf_pasv_ptc_lemma,
track("multiple-segments")
perf_pasv_ptc_acc ~= perf_pasv_ptc_lemma and perf_pasv_ptc_acc or nil) .. " [[esse]]"
futr_pasv_inf = make_raw_link(stem .. "um") .. " [[īrī]]"
 
-- Exceptions
local mortu = {
["conmortu"] = true,
["commortu"] = true,
["dēmortu"] = true,
["ēmortu"] = true,
["inmortu"] = true,
["immortu"] = true,
["inēmortu"] = true,
["intermortu"] = true,
["permortu"] = true,
["praemortu"] = true,
["superēmortu"] = true
}
local ort = {
["ort"] = true,
["abort"] = true,
["adort"] = true,
["coort"] = true,
["exort"] = true,
["hort"] = true,
["obort"] = true
}
if mortu[stem] then
futr_actv_ptc = stem:gsub("mortu$", "moritūrus")
elseif ort[stem] then
futr_actv_ptc = stem:gsub("ort$", "oritūrus")
elseif stem == "mortu" then
-- FIXME, are we sure about this?
futr_actv_inf = {}
futr_actv_ptc = "moritūrus"
end
 
if not futr_actv_inf then
futr_actv_inf = make_raw_link(futr_actv_ptc, futr_actv_ptc:gsub("us$", "um")) .. " [[esse]]"
end
 
add_form(data, "futr_actv_inf", "", futr_actv_inf)
add_form(data, "perf_pasv_inf", "", perf_pasv_inf)
add_form(data, "futr_pasv_inf", "", futr_pasv_inf)
add_form(data, "futr_actv_ptc", "", futr_actv_ptc)
add_form(data, "perf_pasv_ptc", "", perf_pasv_ptc)
 
-- Supine itself
add_form(data, "sup_acc", stem, "um")
add_form(data, "sup_abl", stem, "ū")
end
end
return {
segments = parsed_segments,
loc = loc,
num = num,
gender = gender,
is_adj = is_adj,
propses = propses,
}
end
end


-- Combine each form in FORMS (a list of forms associated with a slot) with each
make_sigm = function(data, typeinfo, sigm_stem)
-- form in NEW_FORMS (either a single string for a single form, or a list of
if not (typeinfo.subtypes.sigm or typeinfo.subtypes.sigmpasv) then
-- forms) by concatenating EXISTING_FORM .. PREFIX .. NEW_FORM. Also combine
return
-- NOTES (a table specifying the footnotes associated with each existing form,
-- i.e. a map from form indices to lists of footnotes) with NEW_NOTES (new
-- footnotes associated with the new forms, in the same format as NOTES). Return
-- a pair NEW_FORMS, NEW_NOTES where either or both of FORMS and NOTES (but not
-- the sublists in NOTES) may be destructively modified to generate the return
-- values.
local function append_form(forms, notes, new_forms, new_notes, prefix)
new_forms = new_forms or ""
notes = notes or {}
new_notes = new_notes or {}
prefix = prefix or ""
if type(new_forms) == "table" and #new_forms == 1 then
new_forms = new_forms[1]
end
end
if type(new_forms) == "string" then
if type(sigm_stem) ~= "table" then
-- If there's only one new form, destructively modify the existing
sigm_stem = {sigm_stem}
-- forms and notes for this new form and its footnotes.
end
for i = 1, #forms do
local noteindex = #(data.footnotes) + 1
forms[i] = forms[i] .. prefix .. new_forms
if new_notes[1] then
for _, stem in ipairs(sigm_stem) do
if not notes[i] then
-- Deponent verbs use the passive form
notes[i] = new_notes[1]
if typeinfo.subtypes.depon then
else
add_form(data, "1s_sigf_actv_indc", stem, "or")
local combined_notes = m_table.deepcopy(notes[i])
add_form(data, "2s_sigf_actv_indc", stem, "eris")
for _, note in ipairs(new_notes[1]) do
add_form(data, "3s_sigf_actv_indc", stem, "itur")
table.insert(combined_notes, note)
else
end
for _, stem in ipairs(sigm_stem) do
notes[i] = combined_notes
-- Sigmatic future active indicative
add_forms(data, "sigf_actv_indc", stem, "ō", "is", "it", "imus", "itis", "int")
-- Sigmatic future passive indicative (option)
if typeinfo.subtypes.sigmpasv then
add_form(data, "1s_sigf_pasv_indc", stem, "or")
add_form(data, "2s_sigf_pasv_indc", stem, "eris")
add_form(data, "3s_sigf_pasv_indc", stem, "itur")
end
-- Sigmatic future active subjunctive
add_forms(data, "siga_actv_subj", stem, "im", "īs", "īt", "īmus", "ītis", "int")
-- Perfect infinitive
if not no_inf then
add_form(data, "sigm_actv_inf", stem, "ere")
end
end
end
end
end
end
return forms, notes
end
else
data.form_footnote_indices["sigm"] = noteindex
-- If there are multiple new forms, we need to loop over all
if (typeinfo.subtypes.sigm or typeinfo.subtypes.sigmpasv) and not (typeinfo.subtypes.depon) then
-- combinations of new and old forms. In that case, use new tables
data.footnotes[noteindex] = 'At least one use of the archaic \"sigmatic future\" and \"sigmatic aorist\" tenses is attested, which are used by [[Old Latin]] writers; most notably [[w:Plautus|Plautus]] and [[w:Terence|Terence]]. The sigmatic future is generally ascribed a future or future perfect meaning, while the sigmatic aorist expresses a possible desire (\"might want to\").'
-- for the combined forms and notes.
if typeinfo.subtypes.sigmpasv then
local ret_forms = {}
data.footnotes[noteindex] = data.footnotes[noteindex] .. ' 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.'
local ret_notes = {}
for i=1, #forms do
for j=1, #new_forms do
table.insert(ret_forms, forms[i] .. prefix .. new_forms[j])
if new_notes[j] then
if not notes[i] then
-- We are constructing a linearized matrix of size
-- NI x NJ where J is in the inner loop. If I and J
-- are zero-based, the linear index of (I, J) is
-- I * NJ + J. However, we are one-based, so the
-- same formula won't work. Instead, we effectively
-- need to convert to zero-based indices, compute
-- the zero-based linear index, and then convert it
-- back to a one-based index, i.e.
--
-- (I - 1) * NJ + (J - 1) + 1
--
-- i.e. (I - 1) * NJ + J.
ret_notes[(i - 1) * #new_forms + j] = new_notes[j]
else
local combined_notes = m_table.deepcopy(notes[i])
for _, note in ipairs(new_notes[j]) do
table.insert(combined_notes, note)
end
ret_notes[(i - 1) * #new_forms + j] = combined_notes
end
end
end
end
end
return ret_forms, ret_notes
elseif typeinfo.subtypes.depon then
data.footnotes[noteindex] = 'At least one use of the archaic \"sigmatic future\" tense is attested, which is used by [[Old Latin]] writers; most notably [[w:Plautus|Plautus]] and [[w:Terence|Terence]]. 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).'
end
end
end
end


-- Destructively modify any forms in FORMS (a map from a slot to a form or a
-- Functions for generating the inflection table
-- list of forms) by converting sequences of ae, oe, Ae or Oe to the
 
-- appropriate ligatures.
-- Convert FORM (one or more forms) to a string of links. If the form is empty
local function apply_ligatures(forms, is_adj)
-- (see form_is_empty), the return value will be "&mdash;".
for slot in iter_slots(is_adj) do
local function show_form(form, accel)
if type(forms[slot]) == "string" then
if not form then
forms[slot] = forms[slot]:gsub("[AaOo]e", ligatures)
return "&mdash;"
elseif type(forms[slot]) == "table" then
end
for i = 1, #forms[slot] do
 
forms[slot][i] = forms[slot][i]:gsub("[AaOo]e", ligatures)
if type(form) ~= "table" then
end
form = {form}
end
 
for key, subform in ipairs(form) do
if form_is_empty(subform) then
form[key] = "&mdash;"
elseif reconstructed and not subform:find(NAMESPACE .. ":Latin/") then
form[key] = make_link({lang = lang, alt = '', term = NAMESPACE .. ":Latin/" .. subform, alt = subform})
elseif subform:find("[%[%]]") 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[key] = make_link({ lang = lang, alt = '' .. subform })
else
form[key] = make_link({ lang = lang, alt = '' .. subform })
end
end
end
end
return table.concat(form, ",<br> ")
end
end


-- Modify any forms in FORMS (a map from a slot to a form or a list of forms) by
parts_to_tags = {
-- converting final m to optional n or m.
  ['1s'] = {'1', 's'},
local function apply_sufn(forms, is_adj)
  ['2s'] = {'2', 's'},
for slot in iter_slots(is_adj) do
  ['3s'] = {'3', 's'},
if type(forms[slot]) == "string" then
  ['1p'] = {'1', 'p'},
if forms[slot]:find("m$") then
  ['2p'] = {'2', 'p'},
forms[slot] = {forms[slot]:gsub("m$", "n"), forms[slot]}
  ['3p'] = {'3', 'p'},
end
  ['actv'] = {'act'},
elseif type(forms[slot]) == "table" then
  ['pasv'] = {'pass'},
-- See if any final m's.
  ['pres'] = {'pres'},
local final_m
  ['impf'] = {'impf'},
for i = 1, #forms[slot] do
  ['futr'] = {'fut'},
if forms[slot][i]:find("m$") then
  ['perf'] = {'perf'},
final_m = true
  ['plup'] = {'plup'},
break
  ['futp'] = {'futp'},
end
  ['sigm'] = {'sigm'},
end
  ['sigf'] = {'sigm', 'fut'},
if final_m then
  ['siga'] = {'sigm', 'aor'},
local newval = {}
  ['indc'] = {'ind'},
for i = 1, #forms[slot] do
  ['subj'] = {'sub'},
if forms[slot][i]:find("m$") then
  ['impr'] = {'imp'},
local val = forms[slot][i]:gsub("m$", "n") -- discard second retval
  ['inf'] = {'inf'},
table.insert(newval, val)
  ['ptc'] = {'part'},
end
  ['ger'] = {'ger'},
table.insert(newval, forms[slot][i])
  ['sup'] = {'sup'},
end
  ['nom'] = {'nom'},
forms[slot] = newval
  ['gen'] = {'gen'},
  ['dat'] = {'dat'},
  ['acc'] = {'acc'},
  ['abl'] = {'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 = data.actual_lemma[1]
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[part]) do
table.insert(tags, tag)
end
end
end
end
-- put the case first for verbal nouns
local accel_slot
if tags[1] == "sup" or tags[1] == "ger" then
accel_slot = tags[2] .. "|" .. tags[1]
else
accel_slot = table.concat(tags, "|")
end
local accel = {form = accel_slot, lemma = accel_lemma}
data.forms[slot] = show_form(data.forms[slot], accel)
end
end
end
end


-- If NUM == "sg", copy the singular forms to the plural ones; vice-versa if
function export.get_valid_forms(raw_forms)
-- NUM == "pl". This should allow for the equivalent of plural
local valid_forms = {}
-- "alpha and omega" formed from two singular nouns, and for the equivalent of
if raw_forms then
-- plural "St. Vincent and the Grenadines" formed from a singular noun and a
if type(raw_forms) ~= "table" then
-- plural noun. (These two examples actually occur in Russian, at least.)
raw_forms = {raw_forms}
local function propagate_number_restrictions(forms, num, is_adj)
end
if num == "sg" or num == "pl" then
for _, subform in ipairs(raw_forms) do
for slot in iter_slots(is_adj) do
if not form_is_empty(subform) then
if rfind(slot, num) then
table.insert(valid_forms, subform)
local other_num_slot = num == "sg" and slot:gsub("sg", "pl") or slot:gsub("pl", "sg")
forms[other_num_slot] = type(forms[slot]) == "table" and m_table.deepcopy(forms[slot]) or forms[slot]
end
end
end
end
end
end
return valid_forms
end
end


local function join_sentences(sentences, joiner)
function export.get_lemma_forms(data, do_linked)
-- Lowercase the first letter of all but the first sentence, and remove the
local linked_prefix = do_linked and "linked_" or ""
-- final period from all but the last sentence. Then join together with the
for _, slot in ipairs(potential_lemma_slots) do
-- joiner (e.g. " and " or " or ").
local lemma_forms = export.get_valid_forms(data.forms[linked_prefix .. slot])
-- FIXME: Should we join three or more as e.g. "foo, bar and baz"?
if #lemma_forms > 0 then
local sentences_to_join = {}
return lemma_forms
for i, sentence in ipairs(sentences) do
if i < #sentences then
sentence = rsub(sentence, "%.$", "")
end
end
if i > 1 then
sentence = m_string_utilities.lcfirst(sentence)
end
table.insert(sentences_to_join, sentence)
end
end
return table.concat(sentences_to_join, joiner)
 
return nil
end
end


-- Construct the declension of a parsed segment run of the form returned by
local function get_displayable_lemma(lemma_forms)
-- parse_segment_run() or parse_segment_run_allowing_alternants(). Return value
if not lemma_forms then
-- is a table
return "&mdash;"
-- {
end
--  forms = FORMS (keyed by slot, list of forms for that slot),
local lemma_links = {}
--  notes = NOTES (keyed by slot, map from form indices to lists of footnotes),
for _, subform in ipairs(lemma_forms) do
--  title = TITLE (list of titles for each segment in the run),
table.insert(lemma_links, make_link({lang = lang, alt = '', alt = subform}, "term"))
--  categories = CATEGORIES (combined categories for all segments),
end
--  voc = BOOLEAN (false if any adjective in the run has no vocative),
return table.concat(lemma_links, ", ")
-- }
end
local function decline_segment_run(parsed_run, pos, is_adj)
local declensions = {
-- For each possible slot (e.g. "abl_sg"), list of possible forms.
forms = {},
-- Keyed by slot (e.g. "abl_sg"). Value is a table indicating the footnotes
-- corresponding to the forms for that slot. Each such table maps indices
-- (the index of the corresponding form) to a list of one or more
-- footnotes.
notes = {},
title = {},
subtitleses = {},
orig_titles = {},
categories = {},
footnotes = {},
-- FIXME, do we really need to special-case this? Maybe the nonexistent vocative
-- form will automatically propagate up through the other forms.
voc = true,
-- May be set true if declining a 1-1 adjective
loc = false,
noneut = false,
nomf = false,
}


for slot in iter_slots(is_adj) do
-- Make the table
declensions.forms[slot] = {""}
make_table = function(data)
local pagename = PAGENAME
if reconstructed then
pagename = pagename:gsub("Latin/","")
end
end
data.actual_lemma = export.get_lemma_forms(data)
convert_forms_into_links(data)


for _, seg in ipairs(parsed_run.segments) do
return [=[
if seg.decl then -- not an alternant, not a constant segment
{| style="width: 100%; background: #EEE; border: 1px solid #AAA; font-size: 95%; text-align: center;" class="inflection-table vsSwitcher" data-toggle-category="inflection"
seg.loc = parsed_run.loc
|-
seg.num = seg.num or parsed_run.num
! colspan="8" class="vsToggleElement" style="background: #CCC; text-align: left;" | &nbsp;&nbsp;&nbsp;Conjugation of ]=] .. get_displayable_lemma(data.actual_lemma) .. (#data.title > 0 and " (" .. table.concat(data.title, ", ") .. ")" or "") .. [=[
seg.gender = seg.gender or parsed_run.gender
 
]=] .. make_indc_rows(data) .. make_subj_rows(data) .. make_impr_rows(data) .. make_nonfin_rows(data) .. make_vn_rows(data) .. [=[
 
|}]=].. make_footnotes(data)
 
end
 
local tenses = {
["pres"] = "present",
["impf"] = "imperfect",
["futr"] = "future",
["perf"] = "perfect",
["plup"] = "pluperfect",
["futp"] = "future&nbsp;perfect",
["sigf"] = "sigmatic&nbsp;future",
["siga"] = "sigmatic&nbsp;aorist"
}


local data
local voices = {
["actv"] = "active",
["pasv"] = "passive",
}


local potential_lemma_slots
--[[
local moods = {
["indc"] = "indicative",
["subj"] = "subjunctive",
["impr"] = "imperative",
}
--]]


if seg.is_adj then
local nonfins = {
if not m_adj_decl[seg.decl] then
["inf"] = "infinitives",
error("Unrecognized declension '" .. seg.decl .. "'")
["ptc"] = "participles",
end
}


potential_lemma_slots = potential_adj_lemma_slots
--[[
local verbalnouns = {
["ger"] = "gerund",
["sup"] = "supine",
}
--]]


data = {
--[[
subtitles = {},
local cases = {
num = seg.num or "",
["nom"] = "nominative",
gender = seg.gender,
["gen"] = "genitive",
voc = true,
["dat"] = "dative",
loc = seg.loc,
["acc"] = "accusative",
noneut = false,
["abl"] = "ablative",
nomf = false,
}
pos = is_adj and pos or "adjectives",
--]]
forms = {},
types = seg.types,
categories = {},
notes = {},
}
m_adj_decl[seg.decl](data, seg.args)
if not data.voc then
declensions.voc = false
end
if data.loc then
declensions.loc = true
end
if data.noneut then
declensions.noneut = true
end
if data.nomf then
declensions.nomf = true
end
-- Construct title out of "original title" and subtitles.
if data.types.sufn then
table.insert(data.subtitles, {"with", " ''m'' optionally → ''n'' in compounds"})
elseif data.types.not_sufn then
table.insert(data.subtitles, {"without", " ''m'' optionally → ''n'' in compounds"})
end
-- Record original title and subtitles for use in alternant title-constructing code.
table.insert(declensions.orig_titles, data.title)
if #data.subtitles > 0 then
local subtitles = {}
for _, subtitle in ipairs(data.subtitles) do
if type(subtitle) == "table" then
-- Occurs e.g. with ''idem'', ''quīdam''
table.insert(subtitles, table.concat(subtitle))
else
table.insert(subtitles, subtitle)
end
end
data.title = data.title .. " (" .. table.concat(subtitles, ", ") .. ")"
end
table.insert(declensions.subtitleses, data.subtitles)
else
if not m_noun_decl[seg.decl] then
error("Unrecognized declension '" .. seg.decl .. "'")
end


potential_lemma_slots = potential_noun_lemma_slots
make_indc_rows = function(data)
local indc = {}


data = {
for _, v in ipairs({"actv", "pasv"}) do
subtitles = {},
local group = {}
num = seg.num or "",
local nonempty = false
loc = seg.loc,
pos = pos,
for _, t in ipairs({"pres", "impf", "futr", "perf", "plup", "futp", "sigf"}) do
forms = {},
local row = {}
types = seg.types,
local notempty = false
categories = {},
notes = {},
}


m_noun_decl[seg.decl](data, seg.args)
if data.forms[t .. "_" .. v .. "_indc"] then
row = "\n! colspan=\"6\" style=\"background: #CCC\" |" .. data.forms[t .. "_" .. v .. "_indc"]
nonempty = true
notempty = true
else
for col, p in ipairs({"1s", "2s", "3s", "1p", "2p", "3p"}) do
local slot = p .. "_" .. t .. "_" .. v .. "_indc"
row[col] = "\n| " .. data.forms[slot] .. (
data.form_footnote_indices[slot] == nil and "" or
'<sup style="color: red">' .. data.form_footnote_indices[slot].."</sup>"
)


-- Construct title out of "original title" and subtitles.
-- show_form() already called so can just check for "&mdash;"
if not data.title then
if data.forms[slot] ~= "&mdash;" then
local apparent_decl = rmatch(seg.headword_decl, "^irreg/(.*)$")
nonempty = true
if apparent_decl then
notempty = true
if #data.subtitles == 0 then
table.insert(data.subtitles, glossary_link("irregular"))
end
else
apparent_decl = seg.headword_decl
end
end
if declension_to_english[apparent_decl] then
local english = declension_to_english[apparent_decl]
data.title = "[[Appendix:Latin " .. english .. " declension|" .. english .. "-declension]]"
elseif apparent_decl == "irreg" then
data.title = glossary_link("irregular")
elseif apparent_decl == "indecl" or apparent_decl == "0" then
data.title = glossary_link("indeclinable")
else
error("Internal error! Don't recognize noun declension " .. apparent_decl)
end
data.title = data.title .. " noun"
end
end
if data.types.sufn then
 
table.insert(data.subtitles, {"with", " ''m'' optionally → ''n'' in compounds"})
row = table.concat(row)
elseif data.types.not_sufn then
end
table.insert(data.subtitles, {"without", " ''m'' optionally → ''n'' in compounds"})
end
local fn
-- Record original title and subtitles for use in alternant title-constructing code.
if t == "sigf" and data.form_footnote_indices["sigm"] then
table.insert(declensions.orig_titles, data.title)
fn = '<sup style="color: red">' .. data.form_footnote_indices["sigm"].."</sup>"
if #data.subtitles > 0 then
else
local subtitles = {}
fn = ""
for _, subtitle in ipairs(data.subtitles) do
end
if type(subtitle) == "table" then
-- Occurs e.g. with 1st-declension ''-ābus'' ending where
if notempty then
-- we want a common prefix to be extracted out if possible
table.insert(group, "\n! style=\"background:#c0cfe4\" | " .. tenses[t] .. fn .. row)
-- in the alternant title-generating code.
table.insert(subtitles, table.concat(subtitle))
else
table.insert(subtitles, subtitle)
end
end
data.title = data.title .. " (" .. table.concat(subtitles, ", ") .. ")"
end
table.insert(declensions.subtitleses, data.subtitles)
end
end
end


-- Generate linked variants of slots that may be the lemma.
if nonempty and #group > 0 then
-- If the form is the same as the lemma (with links removed),
table.insert(indc, "\n|- class=\"vsHide\"\n! rowspan=\"" .. tostring(#group) .. "\" style=\"background:#c0cfe4\" | " .. voices[v] .. "\n" .. table.concat(group, "\n|- class=\"vsHide\""))
-- substitute the original lemma (with links included).
end
for _, slot in ipairs(potential_lemma_slots) do
end
local forms = data.forms[slot]
if forms then
local linked_forms = {}
if type(forms) ~= "table" then
forms = {forms}
end
for _, form in ipairs(forms) do
if form == seg.lemma then
table.insert(linked_forms, seg.orig_lemma)
else
table.insert(linked_forms, form)
end
end
data.forms["linked_" .. slot] = linked_forms
end
end


if seg.types.lig then
return
apply_ligatures(data.forms, is_adj)
[=[
end


if seg.types.sufn then
|- class="vsHide"
apply_sufn(data.forms, is_adj)
! colspan="2" rowspan="2" style="background:#c0cfe4" | indicative
end
! colspan="3" style="background:#c0cfe4" | ''singular''
! colspan="3" style="background:#c0cfe4" | ''plural''
|- class="vsHide"
! style="background:#c0cfe4;width:12.5%" | [[first person|first]]
! style="background:#c0cfe4;width:12.5%" | [[second person|second]]
! style="background:#c0cfe4;width:12.5%" | [[third person|third]]
! style="background:#c0cfe4;width:12.5%" | [[first person|first]]
! style="background:#c0cfe4;width:12.5%" | [[second person|second]]
! style="background:#c0cfe4;width:12.5%" | [[third person|third]]
]=] .. table.concat(indc)


propagate_number_restrictions(data.forms, seg.num, is_adj)
end


for slot in iter_slots(is_adj) do
make_subj_rows = function(data)
-- 1. Select the forms to append to the existing ones.
local subj = {}


local new_forms
for _, v in ipairs({"actv", "pasv"}) do
if is_adj then
local group = {}
if not seg.is_adj then
local nonempty = false
error("Can't decline noun '" .. seg.lemma .. "' when overall term is an adjective")
end
new_forms = data.forms[slot]
if not new_forms and slot:find("_[fn]$") then
new_forms = data.forms[slot:gsub("_[fn]$", "_m")]
end
elseif seg.is_adj then
if not seg.gender then
error("Declining modifying adjective " .. seg.lemma .. " but don't know gender of associated noun")
end
-- Select the appropriately gendered equivalent of the case/number
-- combination. Some adjectives won't have feminine or neuter
-- variants, though (e.g. 3-1 and 3-2 adjectives don't have a
-- distinct feminine), so in that case select the masculine.
new_forms = data.forms[slot .. "_" .. mw.ustring.lower(seg.gender)]
or data.forms[slot .. "_m"]
else
new_forms = data.forms[slot]
end


-- 2. Extract the new footnotes in the format we require, which is
for _, t in ipairs({"pres", "impf", "perf", "plup", "siga"}) do
-- different from the format passed in by the declension functions.
local row = {}
local notempty = false


local new_notes = {}
if data.forms[t .. "_" .. v .. "_subj"] then
row = "\n! colspan=\"6\" style=\"background: #CCC\" |" .. data.forms[t .. "_" .. v .. "_subj"]
nonempty = true
notempty = true
else
for col, p in ipairs({"1s", "2s", "3s", "1p", "2p", "3p"}) do
local slot = p .. "_" .. t .. "_" .. v .. "_subj"
row[col] = "\n| " .. data.forms[slot] .. (
data.form_footnote_indices[slot] == nil and "" or
'<sup style="color: red">' .. data.form_footnote_indices[slot].."</sup>"
)


if type(new_forms) == "string" and data.notes[slot .. "1"] then
-- show_form() already called so can just check for "&mdash;"
new_notes[1] = {data.notes[slot .. "1"]}
if data.forms[slot] ~= "&mdash;" then
elseif new_forms then
nonempty = true
for j = 1, #new_forms do
notempty = true
if data.notes[slot .. j] then
new_notes[j] = {data.notes[slot .. j]}
end
end
end
end
end


-- 3. Append new forms and footnotes to the existing ones.
row = table.concat(row)
end
local fn
if t == "siga" and data.form_footnote_indices["sigm"] then
fn = '<sup style="color: red">' .. data.form_footnote_indices["sigm"].."</sup>"
else
fn = ""
end


declensions.forms[slot], declensions.notes[slot] = append_form(
if notempty then
declensions.forms[slot], declensions.notes[slot], new_forms,
table.insert(group, "\n! style=\"background:#c0e4c0\" | " .. tenses[t] .. fn .. row)
new_notes, slot:find("linked") and seg.orig_prefix or seg.prefix)
end
end
end


if not seg.types.nocat and (is_adj or not seg.is_adj) then
if nonempty and #group > 0 then
for _, cat in ipairs(data.categories) do
table.insert(subj, "\n|- class=\"vsHide\"\n! rowspan=\"" .. tostring(#group) .. "\" style=\"background:#c0e4c0\" | " .. voices[v] .. "\n" .. table.concat(group, "\n|- class=\"vsHide\""))
m_table.insertIfNot(declensions.categories, cat)
end
end
end
end


if data.footnote then
return
table.insert(declensions.footnotes, data.footnote)
[=[
end


if seg.prefix ~= "" and seg.prefix ~= "-" and seg.prefix ~= " " then
|- class="vsHide"
table.insert(declensions.title, glossary_link("indeclinable") .. " portion")
! colspan="2" rowspan="2" style="background:#c0e4c0" | subjunctive
end
! colspan="3" style="background:#c0e4c0" | ''singular''
table.insert(declensions.title, data.title)
! colspan="3" style="background:#c0e4c0" | ''plural''
elseif seg.alternants then
|- class="vsHide"
local seg_declensions = nil
! style="background:#c0e4c0;width:12.5%" | [[first person|first]]
local seg_titles = {}
! style="background:#c0e4c0;width:12.5%" | [[second person|second]]
local seg_subtitleses = {}
! style="background:#c0e4c0;width:12.5%" | [[third person|third]]
local seg_stems_seen = {}
! style="background:#c0e4c0;width:12.5%" | [[first person|first]]
local seg_categories = {}
! style="background:#c0e4c0;width:12.5%" | [[second person|second]]
local seg_footnotes = {}
! style="background:#c0e4c0;width:12.5%" | [[third person|third]]
-- If all alternants have exactly one non-constant segment and all are
]=] .. table.concat(subj)
-- of the same declension, we use special code that displays the
-- differences in the subtitles. Otherwise we use more general code
-- that displays the full title and subtitles of each segment,
-- separating segment combined titles by "and" and the segment-run
-- combined titles by "or".
local title_the_hard_way = false
local alternant_decl = nil
local alternant_decl_title = nil
for _, this_parsed_run in ipairs(seg.alternants) do
local num_non_constant_segments = 0
for _, segment in ipairs(this_parsed_run.segments) do
if segment.decl then
if not alternant_decl then
alternant_decl = segment.decl
elseif alternant_decl ~= segment.decl then
title_the_hard_way = true
num_non_constant_segments = 500
break
end
num_non_constant_segments = num_non_constant_segments + 1
end
end
if num_non_constant_segments ~= 1 then
title_the_hard_way = true
break
end
end
if not title_the_hard_way then
-- If using the special-purpose code, find the subtypes that are
-- not present in a given alternant but are present in at least
-- one other, and record "negative" variants of these subtypes
-- so that the declension-construction code can record subtitles
-- for these negative variants (so we can construct text like
-- "i-stem or imparisyllabic non-i-stem").
local subtypeses = {}
for _, this_parsed_run in ipairs(seg.alternants) do
for _, segment in ipairs(this_parsed_run.segments) do
if segment.decl then
table.insert(subtypeses, segment.types)
m_table.insertIfNot(seg_stems_seen, segment.stem2)
end
end
end
local union = set_union(subtypeses)
for _, this_parsed_run in ipairs(seg.alternants) do
for _, segment in ipairs(this_parsed_run.segments) do
if segment.decl then
local neg_subtypes = set_difference(union, segment.types)
for neg_subtype, _ in pairs(neg_subtypes) do
segment.types["not_" .. neg_subtype] = true
end
end
end
end
end


for _, this_parsed_run in ipairs(seg.alternants) do
end
this_parsed_run.loc = seg.loc
this_parsed_run.num = this_parsed_run.num or seg.num
this_parsed_run.gender = this_parsed_run.gender or seg.gender
local this_declensions = decline_segment_run(this_parsed_run, pos, is_adj)
if not this_declensions.voc then
declensions.voc = false
end
if this_declensions.noneut then
declensions.noneut = true
end
if this_declensions.nomf then
declensions.nomf = true
end
-- If there's a number restriction on the segment run, blank
-- out the forms outside the restriction. This allows us to
-- e.g. construct heteroclites that decline one way in the
-- singular and a different way in the plural.
if this_parsed_run.num == "sg" or this_parsed_run.num == "pl" then
for slot in iter_slots(is_adj) do
if this_parsed_run.num == "sg" and rfind(slot, "pl") or
this_parsed_run.num == "pl" and rfind(slot, "sg") then
this_declensions.forms[slot] = {}
this_declensions.notes[slot] = nil
end
end
end
if not seg_declensions then
seg_declensions = this_declensions
else
for slot in iter_slots(is_adj) do
-- For a given slot, combine the existing and new forms.
-- We do this by checking to see whether a new form is
-- already present and not adding it if so; in the
-- process, we keep a map from indices in the new forms
-- to indices in the combined forms, for use in
-- combining footnotes below.
local curforms = seg_declensions.forms[slot] or {}
local newforms = this_declensions.forms[slot] or {}
local newform_index_to_new_index = {}
for newj, form in ipairs(newforms) do
local did_break = false
for j = 1, #curforms do
if curforms[j] == form then
newform_index_to_new_index[newj] = j
did_break = true
break
end
end
if not did_break then
table.insert(curforms, form)
newform_index_to_new_index[newj] = #curforms
end
end
seg_declensions.forms[slot] = curforms
-- Now combine the footnotes. Keep in mind that
-- each form may have its own set of footnotes, and
-- in some cases we didn't add a form from the new
-- list of forms because it already occurred in the
-- existing list of forms; in that case, we combine
-- footnotes from the two sources.
local curnotes = seg_declensions.notes[slot]
local newnotes = this_declensions.notes[slot]
if newnotes then
if not curnotes then
curnotes = {}
end
for index, notes in pairs(newnotes) do
local combined_index = newform_index_to_new_index[index]
if not curnotes[combined_index] then
curnotes[combined_index] = notes
else
local combined = mw.clone(curnotes[combined_index])
for _, note in ipairs(newnotes) do
m_table.insertIfNot(combined, newnotes)
end
curnotes[combined_index] = combined
end
end
end
end
end
for _, cat in ipairs(this_declensions.categories) do
m_table.insertIfNot(seg_categories, cat)
end
for _, footnote in ipairs(this_declensions.footnotes) do
m_table.insertIfNot(seg_footnotes, footnote)
end
m_table.insertIfNot(seg_titles, this_declensions.title)
for _, subtitles in ipairs(this_declensions.subtitleses) do
table.insert(seg_subtitleses, subtitles)
end
if not alternant_decl_title then
alternant_decl_title = this_declensions.orig_titles[1]
end
end


-- If overall run is singular, copy singular to plural, and
make_impr_rows = function(data)
-- vice-versa. See propagate_number_restrictions() for rationale;
local impr = {}
-- also, this should eliminate cases of empty forms, which will
local has_impr = false
-- cause the overall set of forms for that slot to be empty.
propagate_number_restrictions(seg_declensions.forms, parsed_run.num,
is_adj)


for slot in iter_slots(is_adj) do
for _, v in ipairs({"actv", "pasv"}) do
declensions.forms[slot], declensions.notes[slot] = append_form(
local group = {}
declensions.forms[slot], declensions.notes[slot],
local nonempty = false
seg_declensions.forms[slot], seg_declensions.notes[slot], nil)
end


if is_adj or not seg.is_adj then
for _, t in ipairs({"pres", "futr"}) do
for _, cat in ipairs(seg_categories) do
local row = {}
m_table.insertIfNot(declensions.categories, cat)
end
end
for _, footnote in ipairs(seg_footnotes) do
m_table.insertIfNot(declensions.footnotes, footnote)
end


local title_to_insert
if data.forms[t .. "_" .. v .. "_impr"] then
if title_the_hard_way then
row = "\n! colspan=\"6\" style=\"background: #CCC\" |" .. data.forms[t .. "_" .. v .. "_impr"]
title_to_insert = join_sentences(seg_titles, " or ")
nonempty = true
else
else
-- Special-purpose title-generation code, for the common
for col, p in ipairs({"1s", "2s", "3s", "1p", "2p", "3p"}) do
-- situation where each alternant has single-segment runs and
local slot = p .. "_" .. t .. "_" .. v .. "_impr"
-- all segments belong to the same declension.
row[col] = "\n| " .. data.forms[slot] .. (
--
data.form_footnote_indices[slot] == nil and "" or
-- 1. Find the initial subtitles common to all segments.
'<sup style="color: red">' .. data.form_footnote_indices[slot].."</sup>"
local first_subtitles = seg_subtitleses[1]
)
local num_common_subtitles = #first_subtitles
 
for i = 2, #seg_subtitleses do
-- show_form() already called so can just check for "&mdash;"
local this_subtitles = seg_subtitleses[i]
if data.forms[slot] ~= "&mdash;" then
for j = 1, num_common_subtitles do
nonempty = true
if not m_table.deepEquals(first_subtitles[j], this_subtitles[j]) then
num_common_subtitles = j - 1
break
end
end
end
end
end
-- 2. Construct the portion of the text based on the common subtitles.
 
local common_subtitles = {}
row = table.concat(row)
for i = 1, num_common_subtitles do
if type(first_subtitles[i]) == "table" then
table.insert(common_subtitles, table.concat(first_subtitles[i]))
else
table.insert(common_subtitles, first_subtitles[i])
end
end
local common_subtitle_portion = table.concat(common_subtitles, ", ")
local non_common_subtitle_portion
-- 3. Special-case the situation where there's one non-common
--    subtitle in each segment and a common prefix or suffix to
--    all of them.
local common_prefix, common_suffix
for i = 1, #seg_subtitleses do
local this_subtitles = seg_subtitleses[i]
if #this_subtitles ~= num_common_subtitles + 1 or
type(this_subtitles[num_common_subtitles + 1]) ~= "table" or
#this_subtitles[num_common_subtitles + 1] ~= 2 then
break
end
if i == 1 then
common_prefix = this_subtitles[num_common_subtitles + 1][1]
common_suffix = this_subtitles[num_common_subtitles + 1][2]
else
local this_prefix = this_subtitles[num_common_subtitles + 1][1]
local this_suffix = this_subtitles[num_common_subtitles + 1][2]
if this_prefix ~= common_prefix then
common_prefix = nil
end
if this_suffix ~= common_suffix then
common_suffix = nil
end
if not common_prefix and not common_suffix then
break
end
end
end
if common_prefix or common_suffix then
if common_prefix and common_suffix then
error("Something is wrong, first non-common subtitle is actually common to all segments")
end
if common_prefix then
local non_common_parts = {}
for i = 1, #seg_subtitleses do
table.insert(non_common_parts, seg_subtitleses[i][num_common_subtitles + 1][2])
end
non_common_subtitle_portion = common_prefix .. table.concat(non_common_parts, " or ")
else
local non_common_parts = {}
for i = 1, #seg_subtitleses do
table.insert(non_common_parts, seg_subtitleses[i][num_common_subtitles + 1][1])
end
non_common_subtitle_portion = table.concat(non_common_parts, " or ") .. common_suffix
end
else
-- 4. Join the subtitles that differ from segment to segment.
--    Record whether there are any such differing subtitles.
--    If some segments have differing subtitles and others don't,
--    we use the text "otherwise" for the segments without
--    differing subtitles.
local saw_non_common_subtitles = false
local non_common_subtitles = {}
for i = 1, #seg_subtitleses do
local this_subtitles = seg_subtitleses[i]
local this_non_common_subtitles = {}
for j = num_common_subtitles + 1, #this_subtitles do
if type(this_subtitles[j]) == "table" then
table.insert(this_non_common_subtitles, table.concat(this_subtitles[j]))
else
table.insert(this_non_common_subtitles, this_subtitles[j])
end
end
if #this_non_common_subtitles > 0 then
table.insert(non_common_subtitles, table.concat(this_non_common_subtitles, ", "))
saw_non_common_subtitles = true
else
table.insert(non_common_subtitles, "otherwise")
end
end
non_common_subtitle_portion =
saw_non_common_subtitles and table.concat(non_common_subtitles, " or ") or ""
end
-- 5. Combine the common and non-common subtitle portions.
local subtitle_portions = {}
if common_subtitle_portion ~= "" then
table.insert(subtitle_portions, common_subtitle_portion)
end
if non_common_subtitle_portion ~= "" then
table.insert(subtitle_portions, non_common_subtitle_portion)
end
if #seg_stems_seen > 1 then
table.insert(subtitle_portions,
(number_to_english[#seg_stems_seen] or "" .. #seg_stems_seen) .. " different stems"
)
end
local subtitle_portion =  table.concat(subtitle_portions, "; ")
if subtitle_portion ~= "" then
title_to_insert = alternant_decl_title .. " (" .. subtitle_portion .. ")"
else
title_to_insert = alternant_decl_title
end
end
end
-- Don't insert blank title (happens e.g. with "((ali))quis<irreg+>").
 
if title_to_insert ~= "" then
table.insert(group, "\n! style=\"background:#e4d4c0\" | " .. tenses[t] .. row)
table.insert(declensions.title, title_to_insert)
end
else
for slot in iter_slots(is_adj) do
declensions.forms[slot], declensions.notes[slot] = append_form(
declensions.forms[slot], declensions.notes[slot],
slot:find("linked") and seg.orig_prefix or seg.prefix)
end
table.insert(declensions.title, glossary_link("indeclinable") .. " portion")
end
end
end


-- First title is uppercase, remainder have an indefinite article, joined
if nonempty and #group > 0 then
-- using "with".
has_impr = true
local titles = {}
table.insert(impr, "\n|- class=\"vsHide\"\n! rowspan=\"" .. tostring(#group) .. "\" style=\"background:#e4d4c0\" | " .. voices[v] .. "\n" .. table.concat(group, "\n|- class=\"vsHide\""))
for i, title in ipairs(declensions.title) do
if i == 1 then
table.insert(titles, m_string_utilities.ucfirst(title))
else
table.insert(titles, m_string_utilities.add_indefinite_article(title))
end
end
end
end
declensions.title = table.concat(titles, " with ")
return declensions
end


local function construct_title(args_title, declensions_title, from_headword, parsed_run)
if not has_impr then
if args_title then
return ""
declensions_title = rsub(args_title, "<1>", "[[Appendix:Latin first declension|first declension]]")
declensions_title = rsub(declensions_title, "<1&2>", "[[Appendix:Latin first declension|first]]/[[Appendix:Latin second declension|second declension]]")
declensions_title = rsub(declensions_title, "<2>", "[[Appendix:Latin second declension|second declension]]")
declensions_title = rsub(declensions_title, "<3>", "[[Appendix:Latin third declension|third declension]]")
declensions_title = rsub(declensions_title, "<4>", "[[Appendix:Latin fourth declension|fourth declension]]")
declensions_title = rsub(declensions_title, "<5>", "[[Appendix:Latin fifth declension|fifth declension]]")
if from_headword then
declensions_title = m_string_utilities.lcfirst(rsub(declensions_title, "%.$", ""))
else
declensions_title = m_string_utilities.ucfirst(declensions_title)
end
else
local post_text_parts = {}
if parsed_run.loc then
table.insert(post_text_parts, ", with locative")
end
if parsed_run.num == "sg" then
table.insert(post_text_parts, ", singular only")
elseif parsed_run.num == "pl" then
table.insert(post_text_parts, ", plural only")
end
 
local post_text = table.concat(post_text_parts)
if from_headword then
declensions_title = m_string_utilities.lcfirst(declensions_title) .. post_text
else
declensions_title = m_string_utilities.ucfirst(declensions_title) .. post_text .. "."
end
end
end
return
[=[


return declensions_title
|- class="vsHide"
! colspan="2" rowspan="2" style="background:#e4d4c0" | imperative
! colspan="3" style="background:#e4d4c0" | ''singular''
! colspan="3" style="background:#e4d4c0" | ''plural''
|- class="vsHide"
! style="background:#e4d4c0;width:12.5%" | [[first person|first]]
! style="background:#e4d4c0;width:12.5%" | [[second person|second]]
! style="background:#e4d4c0;width:12.5%" | [[third person|third]]
! style="background:#e4d4c0;width:12.5%" | [[first person|first]]
! style="background:#e4d4c0;width:12.5%" | [[second person|second]]
! style="background:#e4d4c0;width:12.5%" | [[third person|third]]
]=] .. table.concat(impr)
end
end


function export.do_generate_noun_forms(parent_args, pos, from_headword, def, support_num_type)
make_nonfin_rows = function(data)
local params = {
local nonfin = {}
[1] = {required = true, default = def or "aqua<1>"},
footnote = {},
title = {},
num = {},
}
for slot in iter_noun_slots() do
params[slot] = {}
end
if from_headword then
params.lemma = {list = true}
params.id = {}
params.pos = {default = pos}
params.cat = {list = true}
params.indecl = {type = "boolean"}
params.m = {list = true}
params.f = {list = true}
params.g = {list = true}
end
if support_num_type then
params["type"] = {}
end


local args = m_para.process(parent_args, params)
for _, f in ipairs({"inf", "ptc"}) do
local row = {}


if args.title then
for col, t in ipairs({"pres_actv", "perf_actv", "futr_actv", "pres_pasv", "perf_pasv", "futr_pasv"}) do
track("overriding-title")
local slot = t .. "_" .. f
end
--row[col] = "\n| " .. data.forms[slot]
pos = args.pos or pos -- args.pos only set when from_headword
row[col] = "\n| " .. data.forms[slot] .. (
data.form_footnote_indices[slot] == nil and "" or
local parsed_run = parse_segment_run_allowing_alternants(args[1])
'<sup style="color: red">' .. data.form_footnote_indices[slot] .."</sup>"
parsed_run.loc = parsed_run.loc or not not (args.loc_sg or args.loc_pl)
)
parsed_run.num = args.num or parsed_run.num


local declensions = decline_segment_run(parsed_run, pos, false)
end


if not parsed_run.loc then
row = table.concat(row)
declensions.forms.loc_sg = nil
table.insert(nonfin, "\n|- class=\"vsHide\"\n! style=\"background:#e2e4c0\" colspan=\"2\" | " .. nonfins[f] .. row)
declensions.forms.loc_pl = nil
end
end


declensions.title = construct_title(args.title, declensions.title, false, parsed_run)
return
[=[


local all_data = {
|- class="vsHide"
title = declensions.title,
! colspan="2" rowspan="2" style="background:#e2e4c0" | non-finite forms
footnotes = {},
! colspan="3" style="background:#e2e4c0" | active
num = parsed_run.num or "",
! colspan="3" style="background:#e2e4c0" | passive
gender = parsed_run.gender,
|- class="vsHide"
propses = parsed_run.propses,
! style="background:#e2e4c0;width:12.5%" | present
forms = declensions.forms,
! style="background:#e2e4c0;width:12.5%" | perfect
categories = declensions.categories,
! style="background:#e2e4c0;width:12.5%" | future
notes = {},
! style="background:#e2e4c0;width:12.5%" | present
user_specified = {},
! style="background:#e2e4c0;width:12.5%" | perfect
accel = {},
! style="background:#e2e4c0;width:12.5%" | future
overriding_lemma = args.lemma,
]=] .. table.concat(nonfin)
id = args.id,
pos = pos,
cat = args.cat,
indecl = args.indecl,
m = args.m,
f = args.f,
overriding_genders = args.g,
num_type = args["type"],
}


if args.footnote then
end
m_table.insertIfNot(all_data.footnotes, args.footnote)
end
for _, footnote in ipairs(declensions.footnotes) do
m_table.insertIfNot(all_data.footnotes, footnote)
end
for slot in iter_noun_slots() do
if declensions.notes[slot] then
for index, notes in pairs(declensions.notes[slot]) do
all_data.notes[slot .. index] = notes
end
end
end


process_noun_forms_and_overrides(all_data, args)
make_vn_rows = function(data)
local vn = {}
local has_vn = false


return all_data
local row = {}
end


function export.do_generate_adj_forms(parent_args, pos, from_headword, def, support_num_type)
for col, slot in ipairs({"ger_gen", "ger_dat", "ger_acc", "ger_abl", "sup_acc", "sup_abl"}) do
local params = {
-- show_form() already called so can just check for "&mdash;"
[1] = {required = true, default = def or "bonus"},
if data.forms[slot] ~= "&mdash;" then
footnote = {},
has_vn = true
title = {},
end
num = {},
row[col] = "\n| " .. data.forms[slot] .. (
noneut = {type = "boolean"},
data.form_footnote_indices[slot] == nil and "" or
nomf = {type = "boolean"},
'<sup style="color: red">' .. data.form_footnote_indices[slot] .. "</sup>"
}
)
for slot in iter_adj_slots() do
params[slot] = {}
end
if from_headword then
params.lemma = {list = true}
params.comp = {list = true}
params.sup = {list = true}
params.adv = {list = true}
params.id = {}
params.pos = {default = pos}
params.cat = {list = true}
params.indecl = {type = "boolean"}
end
if support_num_type then
params["type"] = {}
end
end


local args = m_para.process(parent_args, params)
row = table.concat(row)


if args.title then
if has_vn then
track("overriding-title")
table.insert(vn, "\n|- class=\"vsHide\"" .. row)
end
end
pos = args.pos or pos -- args.pos only set when from_headword
local segment_run = args[1]
if not rfind(segment_run, "[<(]") then
-- If the segment run doesn't have any explicit declension specs or alternants,
-- add a default declension spec of <+> to it (or <0+> for indeclinable
-- adjectives). This allows the majority of adjectives to just specify
-- the lemma.
segment_run = segment_run .. (args.indecl and "<0+>" or "<+>")
end
local parsed_run = parse_segment_run_allowing_alternants(segment_run)
parsed_run.loc = parsed_run.loc or not not (
args.loc_sg_m or args.loc_sg_f or args.loc_sg_n or args.loc_pl_m or args.loc_pl_f or args.loc_pl_n
)
parsed_run.num = args.num or parsed_run.num
local overriding_voc = not not (
args.voc_sg_m or args.voc_sg_f or args.voc_sg_n or args.voc_pl_m or args.voc_pl_f or args.voc_pl_n
)
local declensions = decline_segment_run(parsed_run, pos, true)


if not parsed_run.loc then
if not has_vn then
declensions.forms.loc_sg_m = nil
return ""
declensions.forms.loc_sg_f = nil
declensions.forms.loc_sg_n = nil
declensions.forms.loc_pl_m = nil
declensions.forms.loc_pl_f = nil
declensions.forms.loc_pl_n = nil
end
end
return
[=[


-- declensions.voc is false if any component has no vocative (e.g. quī); in
|- class="vsHide"
-- that case, if the user didn't supply any vocative overrides, wipe out
! colspan="2" rowspan="3" style="background:#e0e0b0" | verbal nouns
-- any partially-generated vocatives
! colspan="4" style="background:#e0e0b0" | gerund
if not overriding_voc and not declensions.voc then
! colspan="2" style="background:#e0e0b0" | supine
declensions.forms.voc_sg_m = nil
|- class="vsHide"
declensions.forms.voc_sg_f = nil
! style="background:#e0e0b0;width:12.5%" | genitive
declensions.forms.voc_sg_n = nil
! style="background:#e0e0b0;width:12.5%" | dative
declensions.forms.voc_pl_m = nil
! style="background:#e0e0b0;width:12.5%" | accusative
declensions.forms.voc_pl_f = nil
! style="background:#e0e0b0;width:12.5%" | ablative
declensions.forms.voc_pl_n = nil
! style="background:#e0e0b0;width:12.5%" | accusative
end
! style="background:#e0e0b0;width:12.5%" | ablative]=] .. table.concat(vn)


declensions.title = construct_title(args.title, declensions.title, from_headword, parsed_run)
end


local all_data = {
make_footnotes = function(data)
title = declensions.title,
local tbl = {}
footnotes = {},
local i = 0
num = parsed_run.num or "",
for k,v in pairs(data.footnotes) do
propses = parsed_run.propses,
i = i + 1
forms = declensions.forms,
tbl[i] = '<sup style="color: red">'..tostring(k)..'</sup>'..v..'<br>' end
categories = declensions.categories,
return table.concat(tbl)
notes = {},
end
user_specified = {},
accel = {},
voc = declensions.voc,
loc = declensions.loc,
noneut = args.noneut or declensions.noneut,
nomf = args.nomf or declensions.nomf,
overriding_lemma = args.lemma,
comp = args.comp,
sup = args.sup,
adv = args.adv,
id = args.id,
pos = pos,
cat = args.cat,
indecl = args.indecl,
num_type = args["type"],
}


if args.footnote then
override = function(data, args)
m_table.insertIfNot(all_data.footnotes, args.footnote)
for slot in iter_slots(true, false) do
end
if args[slot] then
for _, footnote in ipairs(declensions.footnotes) do
data.forms[slot] = split(args[slot], "/")
m_table.insertIfNot(all_data.footnotes, footnote)
end
end
end
end


for slot in iter_adj_slots() do
checkexist = function(data)
if declensions.notes[slot] then
if NAMESPACE ~= '' then return end
for index, notes in pairs(declensions.notes[slot]) do
local outerbreak = false
all_data.notes[slot .. index] = notes
for _, conjugation in pairs(data.forms) do
if conjugation then
if type(conjugation) == "string" then
conjugation = {conjugation}
end
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
table.insert(data.categories, "Latin verbs with red links in their inflection tables")
outerbreak = true
break
end
end
end
end
if outerbreak then
break
end
end
end
end
end


process_adj_forms_and_overrides(all_data, args)
checkirregular = function(args,data)
 
local apocopic = sub(args[1],1,-2)
return all_data
apocopic = gsub(apocopic,'[^aeiouyāēīōūȳ]+$','')
if args[1] and args[2] and not find(args[2],'^'..apocopic) then
table.insert(data.categories,'Latin stem-changing verbs')
end
end
end


function export.show_noun(frame)
local parent_args = frame:getParent().args
local data = export.do_generate_noun_forms(parent_args, "nouns")


show_forms(data, false)


return make_noun_table(data)
end


function export.show_adj(frame)
local parent_args = frame:getParent().args
local data = export.do_generate_adj_forms(parent_args, "adjectives")


partial_show_forms(data, true)


return m_adj_table.make_table(data, data.noneut, data.nomf)
end


function export.generate_noun_forms(frame)
-- functions for creating external search hyperlinks
local include_props = frame.args["include_props"]
local parent_args = frame:getParent().args
local data = export.do_generate_noun_forms(parent_args, "nouns")


return concat_forms(data, false, include_props)
flatten_values = function(T)
function noaccents(x)
return gsub(toNFD(x),'[^%w]+',"")
end
function cleanup(x)
return noaccents(gsub(gsub(gsub(x, '%[', ''), '%]', ''), ' ', '+'))
end
local tbl = {}
for _, v in pairs(T) do
if type(v) == "table" then
local FT = flatten_values(v)
for _, V in pairs(FT) do
tbl[#tbl+1] = cleanup(V)
end
else
if find(v, '<') == nil then
tbl[#tbl+1] = cleanup(v)
end
end
end
return tbl
end
end


function export.generate_adj_forms(frame)
link_google_books = function(verb, forms, domain)
local include_props = frame.args["include_props"]
function partition_XS_into_N(XS, N)
local parent_args = frame:getParent().args
local count = 0
local data = export.do_generate_adj_forms(parent_args, "adjectives")
local mensae = {}
 
for _, v in pairs(XS) do
return concat_forms(data, true, include_props)
if count % N == 0 then mensae[#mensae+1] = {} end
count = count + 1
mensae[#mensae][#(mensae[#mensae])+1] = v end
return mensae end
function forms_N_to_link(fs, N, args, site)
return '[https://www.google.com/search?'..args..'&q='..site..'+%22'.. table.concat(fs, "%22+OR+%22") ..'%22 '..N..']' end
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[#links+1] = forms_N_to_link(v,k,args,site=="Books" and "" or site) end
return table.concat(links, ' - ') end
return "Google "..domain.." forms of "..verb.." : "..make_links_txt(forms, 30, domain)
end
end


return export
return export

Revision as of 18:43, 15 February 2023

Documentation for this module may be created at Module:la-nominal/doc

local m_utilities = require("Module:utilities")
local m_table = require("Module:table")
local m_links = require("Module:links")
local make_link = m_links.full_link
local m_la_headword = require("Module:la-headword")
local m_la_utilities = require("Module:la-utilities")
local m_para = require("Module:parameters")

-- 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 unless no-pasv-perf.
-- 6. (DONE) Remove no-actv-perf.
-- 7. (DONE) Support plural prefix/suffix and plural passive prefix/suffix
--
-- If enabled, compare this module with new version of module to make
-- sure all conjugations are the same.
local test_new_la_verb_module = false

local export = {}

local lang = require("Module:languages").getByCode("la")

local title = mw.title.getCurrentTitle()
local NAMESPACE = title.nsText
local PAGENAME = title.text

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

-- Check if this verb is reconstructed
-- i.e. the pagename is Reconstruction:Latin/...
local reconstructed = NAMESPACE == "Reconstruction" and PAGENAME:find("^Latin/")

-- Forward functions

local postprocess
local make_pres_1st
local make_pres_2nd
local make_pres_3rd
local make_pres_3rd_io
local make_pres_4th
local make_perf_and_supine
local make_perf
local make_deponent_perf
local make_supine
local make_sigm
local make_table
local make_indc_rows
local make_subj_rows
local make_impr_rows
local make_nonfin_rows
local make_vn_rows
local make_footnotes
local override
local checkexist
local checkirregular
local flatten_values
local link_google_books

local split = mw.text.split
local find = mw.ustring.find
local len = mw.ustring.len
local match = mw.ustring.match
local sub = mw.ustring.sub
local gsub = mw.ustring.gsub
local toNFD = mw.ustring.toNFD

-- version of gsub() that discards all but the first return value
local function gsub1(term, foo, bar)
	local retval = gsub(term, foo, bar)
	return retval
end

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

local function form_is_empty(form)
	return not form or form == "" or form == "-" or form == "—" or form == "&mdash;" or (
		type(form) == "table" and (form[1] == "" or form[1] == "-" or form[1] == "—" or form[1] == "&mdash;")
	)
end

local function initialize_slots()
	local generic_slots = {}
	local non_generic_slots = {}
	local function handle_slot(slot, generic)
		if generic then
			table.insert(generic_slots, slot)
		else
			table.insert(non_generic_slots, slot)
		end
	end
	for _, v in ipairs({"actv", "pasv"}) do
		local function handle_tense(t, mood)
			local non_pers_slot = t .. "_" .. v .. "_" .. mood
			handle_slot(non_pers_slot, true)
			for _, p in ipairs({"1s", "2s", "3s", "1p", "2p", "3p"}) do
				handle_slot(p .. "_" .. non_pers_slot, false)
			end
		end
		for _, t in ipairs({"pres", "impf", "futr", "perf", "plup", "futp", "sigf"}) do
			handle_tense(t, "indc")
		end
		for _, t in ipairs({"pres", "impf", "perf", "plup", "siga"}) do
			handle_tense(t, "subj")
		end
		for _, t in ipairs({"pres", "futr"}) do
			handle_tense(t, "impr")
		end
	end
	for _, f in ipairs({"inf", "ptc"}) do
		for _, t in ipairs({"pres_actv", "perf_actv", "futr_actv", "pres_pasv", "perf_pasv", "futr_pasv"}) do
			handle_slot(t .. "_" .. f, false)
		end
	end
	for _, n in ipairs({"ger_gen", "ger_dat", "ger_acc", "ger_abl", "sup_acc", "sup_abl"}) do
		handle_slot(n, false)
	end
	return non_generic_slots, generic_slots
end

local non_generic_slots, generic_slots = initialize_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?
}

-- 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 = #non_generic_slots
	local function iter()
		slotnum = slotnum + 1
		if slotnum > max_slotnum then
			slotnum = 1
			stage = stage + 1
			if stage == 2 then
				if include_generic then
					max_slotnum = #generic_slots
				else
					stage = stage + 1
				end
			end
			if stage == 3 then
				if include_linked then
					max_slotnum = #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[slotnum]
		elseif stage == 2 then
			return generic_slots[slotnum]
		else
			return "linked_" .. potential_lemma_slots[slotnum]
		end
	end
	return iter
end

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

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

-- For a given form, we allow either strings (a single form) or lists of forms,
-- and treat strings equivalent to one-element lists.
local function forms_equal(form1, form2)
	if type(form1) ~= "table" then
		form1 = {form1}
	end
	if type(form2) ~= "table" then
		form2 = {form2}
	end
	return m_table.deepEquals(form1, form2)
end

local function concat_vals(val)
	if type(val) == "table" then
		return table.concat(val, ",")
	else
		return val
	end
end

-- Construct a one- or two-part link. For reasons I don't understand, if we're in
-- the reconstructed namespace (e.g. for the page [[Reconstruction:Latin/nuo]]), we
-- need to construct a special type of link; full_link() doesn't handle this
-- correctly (although it tries ...). Ideally we should fix full_link() instead of
-- doing this.
local function make_raw_link(page, display)
	if reconstructed then
		display = display or page
		page = lang:makeEntryName(page)
		return "[[" .. NAMESPACE .. ":Latin/" .. page .. "|" .. display .. "]]"
	elseif display then
		return "[[" .. lang:makeEntryName(page) .. "|" .. display .. "]]"
	else
		return "[[" .. page .. "]]"
	end
end

local function split_prefix_and_base(lemma, main_verbs)
	for _, main in ipairs(main_verbs) do
		local prefix = match(lemma, "^(.*)" .. main .. "$")
		if prefix then
			return prefix, main
		end
	end
	error("Argument " .. lemma .. " doesn't end in any of " .. table.concat(main_verbs, ","))
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 ending:find("%(") then
		return match(lemma, ending)
	else
		return match(lemma, "^(.*)" .. ending .. "$")
	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, conjtype, specified_subtypes,
		endings_and_subtypes)
	for _, ending_and_subtypes in ipairs(endings_and_subtypes) do
		local ending = ending_and_subtypes[1]
		local subtypes = ending_and_subtypes[2]
		not_this_subtype = false
		for _, subtype in ipairs(subtypes) do
			-- A subtype is directly canceled by specifying -SUBTYPE.
			if specified_subtypes["-" .. subtype] then
				not_this_subtype = true
				break
			end
		end
		if not not_this_subtype then
			local base = extract_base(lemma, ending)
			if base then
				return base, subtypes
			end
		end
	end
	if conjtype then
		error("Unrecognized ending for conjugation-" .. conjtype .. " verb: " .. lemma)
	end
	return nil, nil
end

local irreg_verbs_to_conj_type = {
	["aiō"] = "3rd-io",
	["aiiō"] = "3rd-io",
	["ajō"] = "3rd-io",
	["dīcō"] = "3rd",
	["dūcō"] = "3rd",
	["faciō"] = "3rd-io",
	["fīō"] = "3rd",
	["ferō"] = "3rd",
	["inquam"] = "irreg",
	["libet"] = "2nd",
	["lubet"] = "2nd",
	["licet"] = "2nd",
	["volō"] = "irreg",
	["mālō"] = "irreg",
	["nōlō"] = "irreg",
	["possum"] = "irreg",
	["piget"] = "2nd",
	["coepī"] = "irreg",
	["sum"] = "irreg",
	["edō"] = "3rd",
	["dō"] = "1st",
	["eō"] = "irreg",
}

local function detect_decl_and_subtypes(args)
	local specs = split(args[1] or "", "%.")
	local subtypes = {}
	local conj_arg
	for i, spec in ipairs(specs) do
		if i == 1 then
			conj_arg = spec
		else
			local begins_with_hyphen = find(spec, "^%-")
			spec = spec:gsub("%-", "")
			if begins_with_hyphen then
				spec = "-" .. spec
			end
			subtypes[spec] = true
		end
	end

	local orig_lemma = args[2] or mw.title.getCurrentTitle().subpageText
	orig_lemma = gsub1(orig_lemma, "o$", "ō")
	local lemma = m_links.remove_links(orig_lemma)
	local base, conjtype, conj_subtype, detected_subtypes
	local base_conj_arg, auto_perf_supine = match(conj_arg, "^([124])(%+%+?)$")
	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 sub(orig_lemma, 1, 1) == "-" then
		subtypes.suffix = true
	end
	local auto_perf, auto_supine, auto_sigm
	if subtypes.sigm or subtypes.sigmpasv or subtypes.suffix then
		auto_sigm = true
	end

	if conj_arg == "1" then
		conjtype = "1st"
		base, detected_subtypes = get_subtype_by_ending(lemma, "1", subtypes, {
			{"ō", {}},
			{"or", {"depon"}},
			{"at", {"impers"}},
			{"ātur", {"depon", "impers"}},
			{"ī", {"perfaspres"}},
		})
		if auto_perf_supine then
			if subtypes.perfaspres 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
			subtypes.sigmpasv = true
		end
	elseif conj_arg == "2" then
		conjtype = "2nd"
		base, detected_subtypes = get_subtype_by_ending(lemma, "2", subtypes, {
			{"eō", {}},
			{"eor", {"depon"}},
			{"et", {"impers"}},
			{"ētur", {"depon", "impers"}},
			{"ī", {"perfaspres"}},
		})
		if auto_perf_supine then
			if subtypes.perfaspres then
				auto_perf = base
			else
				auto_perf = base .. "u"
				auto_supine = base .. "it"
			end
		end
		if auto_sigm then
			auto_sigm = "-/ēss"
		end
		if subtypes.suffix then
			subtypes.sigmpasv = true
		end
	elseif conj_arg == "3" then
		base, detected_subtypes = get_subtype_by_ending(lemma, nil, subtypes, {
			{"iō", {"I"}},
			{"ior", {"depon", "I"}},
		})
		if base then
			conjtype = "3rd-io"
		else
			base, detected_subtypes = get_subtype_by_ending(lemma, "3", subtypes, {
				{"ō", {}},
				{"or", {"depon"}},
				{"it", {"impers"}},
				{"itur", {"depon", "impers"}},
				{"ī", {"perfaspres"}},
			})
			if subtypes.I then
				conjtype = "3rd-io"
			else
				conjtype = "3rd"
			end
		end
		if subtypes.perfaspres then
			auto_perf = base
		end
		if subtypes.suffix then
			auto_perf = "-"
			auto_supine = "-"
			auto_sigm = "-"
			subtypes.sigmpasv = true
		end
	elseif conj_arg == "4" then
		conjtype = "4th"
		base, detected_subtypes = get_subtype_by_ending(lemma, "4", subtypes, {
			{"iō", {}},
			{"ior", {"depon"}},
			{"it", {"impers"}},
			{"ītur", {"depon", "impers"}},
			{"ī", {"perfaspres"}},
		})
		if subtypes.perfaspres 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
		if subtypes.suffix then
			subtypes.sigm = true
		end
	elseif conj_arg == "irreg" then
		conjtype = "irreg"
		local prefix
		prefix, base = split_prefix_and_base(lemma, {
			"aiō",
			"aiiō",
			"ajō",
			"dīcō",
			"dūcō",
			"faciō",
			"fīō",
			"ferō",
			"inquam",
			"libet",
			"lubet",
			"licet",
			"volō",
			"mālō",
			"nōlō",
			"possum",
			"piget",
			"coepī",
			-- list sum after possum
			"sum",
			-- FIXME: Will praedō cause problems?
			"edō",
			-- list dō after edō
			"dō",
			"eō",
		})
		conj_subtype = irreg_verbs_to_conj_type[base]
		args[1] = m_la_utilities.strip_macrons(base)
		args[2] = prefix
		-- args[3] and args[4] are used by ferō and sum and stay where they are
		detected_subtypes = {}
	else
		error("Unrecognized conjugation '" .. conj_arg .. "'")
	end

	for _, detected_subtype in ipairs(detected_subtypes) do
		if detected_subtype == "impers" and subtypes["3only"] then
			-- 3only overrides impers
		else
			subtypes[detected_subtype] = true
		end
	end

	if conjtype ~= "irreg" then
		args[1] = base
		local perf_stem, supine_stem
		if subtypes.depon or subtypes.semidepon or subtypes.perfaspres then
			supine_stem = args[3] 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 or subtypes.sigmpasv then
				local sigm_stem = args[5] or auto_sigm
				if sigm_stem == "-" and not subtypes.suffix then
					sigm_stem = nil
				end
				args[5] = sigm_stem
			end
			args[2] = supine_stem
			args[3] = nil
		else
			perf_stem = args[3] 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[4] 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 or subtypes.sigmpasv then
				local sigm_stem = args[5] or auto_sigm
				if sigm_stem == "-" and not subtypes.suffix then
					sigm_stem = nil
				end
				args[5] = sigm_stem
			end
			args[2] = perf_stem
			args[3] = supine_stem
		end
		args[4] = nil
	end

	for subtype, _ in pairs(subtypes) do
		if not m_la_headword.allowed_subtypes[subtype] and
			not m_la_headword.allowed_subtypes[sub(subtype, 2)] and
			not (conjtype == "3rd" and subtype == "-I") and
			not (conjtype == "3rd-io" and subtype == "I") and
			not (subtype == "nound" or subtype == "sigm" or subtype == "sigmpasv" or subtype == "suffix") then
			error("Unrecognized verb subtype " .. subtype)
		end
	end

	return conjtype, conj_subtype, subtypes, orig_lemma, lemma
end

-- The main new entry point.
function export.show(frame)
	local parent_args = frame:getParent().args
	local data, typeinfo = export.make_data(parent_args)
	local domain = frame:getParent().args['search']
	-- 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, newtypeinfo = 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[prop]
			local newtable = prop == "miscdata" and newmiscdata or newdata[prop]
			for key, val in pairs(table) do
				local newval = newtable[key]
				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[key]
				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)
	else
		local verb = data['forms']['1s_pres_actv_indc'] ~= nil and ('[['.. gsub(toNFD(data['forms']['1s_pres_actv_indc']),'[^%w]+',"")..'|'..data['forms']['1s_pres_actv_indc'].. ']]') or 'verb'
		return link_google_books(verb, flatten_values(data['forms']), domain) end
end

local function concat_forms(data, typeinfo, include_props)
	local ins_text = {}
	for key, val in pairs(data.forms) do
		local ins_form = {}
		if type(val) ~= "table" then
			val = {val}
		end
		for _, v in ipairs(val) do
			if not form_is_empty(v) then
				table.insert(ins_form,
					gsub1(gsub1(gsub1(v, "|", "<!>"), "=", "<->"), ",", "<.>")
				)
			end
		end
		if #ins_form > 0 then
			table.insert(ins_text, key .. "=" .. table.concat(ins_form, ","))
		end
	end
	if include_props then
		table.insert(ins_text, "conj_type=" .. typeinfo.conj_type)
		if typeinfo.conj_subtype then
			table.insert(ins_text, "conj_subtype=" .. typeinfo.conj_subtype)
		end
		local subtypes = {}
		for subtype, _ in pairs(typeinfo.subtypes) do
			table.insert(subtypes, subtype)
		end
		table.insert(ins_text, "subtypes=" .. table.concat(subtypes, "."))
	end
	return table.concat(ins_text, "|")
end

-- The entry point for 'la-generate-verb-forms' and 'la-generate-verb-props'
-- to generate all verb forms/props.
function export.generate_forms(frame)
	local include_props = frame.args["include_props"]
	local parent_args = frame:getParent().args
	local data, typeinfo = export.make_data(parent_args)
	return concat_forms(data, typeinfo, include_props)
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 [[sum]]), handle prefixes and suffixes
-- themselves in make_perfect_passive().
local function add_prefix_suffix(data, typeinfo)
	if not data.prefix and not data.suffix then
		return
	end

	local active_prefix = data.prefix or ""
	local passive_prefix = data.passive_prefix or ""
	local plural_prefix = data.plural_prefix or ""
	local plural_passive_prefix = data.plural_passive_prefix or ""
	local active_prefix_no_links = m_links.remove_links(active_prefix)
	local passive_prefix_no_links = m_links.remove_links(passive_prefix)
	local plural_prefix_no_links = m_links.remove_links(plural_prefix)
	local plural_passive_prefix_no_links = m_links.remove_links(plural_passive_prefix)

	local active_suffix = data.suffix or ""
	local passive_suffix = data.passive_suffix or ""
	local plural_suffix = data.plural_suffix or ""
	local plural_passive_suffix = data.plural_passive_suffix or ""
	local active_suffix_no_links = m_links.remove_links(active_suffix)
	local passive_suffix_no_links = m_links.remove_links(passive_suffix)
	local plural_suffix_no_links = m_links.remove_links(plural_suffix)
	local plural_passive_suffix_no_links = m_links.remove_links(plural_passive_suffix)

	for slot in iter_slots(false, true) do
		if not slot:find("ger_") then
			local prefix, suffix, prefix_no_links, suffix_no_links
			if slot:find("pasv") and slot:find("[123]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 slot:find("pasv") and not slot:find("_inf") then
				prefix = passive_prefix
				suffix = passive_suffix
				prefix_no_links = passive_prefix_no_links
				suffix_no_links = passive_suffix_no_links
			elseif slot:find("[123]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 forms = data.forms[slot]
			if not form_is_empty(forms) then
				local affixed_forms = {}
				if type(forms) ~= "table" then
					forms = {forms}
				end
				for _, form in ipairs(forms) do
					if form_is_empty(form) then
						table.insert(affixed_forms, form)
					elseif slot:find("^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:find("[%[%]]") then
							form = "[[" .. form .. "]]"
						end
						table.insert(affixed_forms, prefix .. form .. suffix)
					elseif form:find("[%[%]]") 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).
						table.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.
						table.insert(affixed_forms, prefix_no_links .. form .. suffix_no_links)
					end
				end
				data.forms[slot] = affixed_forms
			end
		end
	end
end

local function notes_override(data, args)
	local notes = {args["note1"], args["note2"], args["note3"]}
	
	for n, note in pairs(notes) do
		if note == "-" then
			data.footnotes[n] = nil
		elseif note == "p3inf" then
			data.footnotes[n] = "The present passive infinitive in -ier is a rare poetic form which is attested."
		elseif note == "poetsyncperf" then
			data.footnotes[n] = "At least one rare poetic syncopated perfect form is attested."
		elseif note == "sigm" then
			data.footnotes[n] = "At least one use of the archaic \"sigmatic future\" and \"sigmatic aorist\" tenses is attested, which are used by [[Old Latin]] writers; most notably [[w:Plautus|Plautus]] and [[w:Terence|Terence]]. 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[n] = "At least one use of the archaic \"sigmatic future\" and \"sigmatic aorist\" tenses is attested, which are used by [[Old Latin]] writers; most notably [[w:Plautus|Plautus]] and [[w:Terence|Terence]]. 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[n] = "At least one use of the archaic \"sigmatic future\" tense is attested, which is used by [[Old Latin]] writers; most notably [[w:Plautus|Plautus]] and [[w:Terence|Terence]]. 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[n] = note
		end
	end
	
	if args["notes"] == "-" then
		data.footnotes = {}
	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).
	for _, slot in ipairs(potential_lemma_slots) do
		local forms = data.forms[slot]
		local linked_forms = {}
		if forms then
			if type(forms) ~= "table" then
				forms = {forms}
			end
			for _, form in ipairs(forms) do
				if form == typeinfo.lemma then
					table.insert(linked_forms, typeinfo.orig_lemma)
				else
					table.insert(linked_forms, form)
				end
			end
		end
		data.forms["linked_" .. slot] = linked_forms
	end
end

function export.make_data(parent_args, from_headword, def1, def2)
	local params = {
		[1] = {required = true, default = def1 or "1+"},
		[2] = {required = true, default = def2 or "amō"},
		[3] = {},
		[4] = {},
		[5] = {},
		prefix = {},
		passive_prefix = {},
		plural_prefix = {},
		plural_passive_prefix = {},
		gen_prefix = {},
		dat_prefix = {},
		acc_prefix = {},
		abl_prefix = {},
		suffix = {},
		passive_suffix = {},
		plural_suffix = {},
		plural_passive_suffix = {},
		gen_suffix = {},
		dat_suffix = {},
		acc_suffix = {},
		abl_suffix = {},
		label = {},
		note1= {},
		note2= {},
		note3= {},
		notes= {},
		-- examined directly in export.show()
		search = {}
	}
	for slot in iter_slots(true, false) do
		params[slot] = {}
	end

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

	local args = m_para.process(parent_args, params)
	local conj_type, conj_subtype, subtypes, orig_lemma, lemma =
		detect_decl_and_subtypes(args)

	if not conjugations[conj_type] then
		error("Unknown conjugation type '" .. conj_type .. "'")
	end

	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,
	}  --note: the addition of red superscripted footnotes ('<sup style="color: red">' ... </sup>) is only implemented for the three form printing loops in which it is used
	local typeinfo = {
		lemma = lemma,
		orig_lemma = orig_lemma,
		conj_type = conj_type,
		conj_subtype = conj_subtype,
		subtypes = subtypes,
	}

	if args.passive_prefix and not args.prefix then
		error("Can't specify passive_prefix= without prefix=")
	end
	if args.plural_prefix and not args.prefix then
		error("Can't specify plural_prefix= without prefix=")
	end
	if args.plural_passive_prefix and not args.prefix then
		error("Can't specify plural_passive_prefix= without prefix=")
	end

	if args.passive_suffix and not args.suffix then
		error("Can't specify passive_suffix= without suffix=")
	end
	if args.plural_suffix and not args.suffix then
		error("Can't specify plural_suffix= without suffix=")
	end
	if args.plural_passive_suffix and not args.suffix then
		error("Can't specify plural_passive_suffix= without suffix=")
	end

	local function normalize_prefix(prefix)
		if not prefix then
			return nil
		end
		local no_space_prefix = match(prefix, "(.*)_$")
		if no_space_prefix then
			return no_space_prefix
		elseif find(prefix, "%-$") then
			return prefix
		else
			return prefix .. " "
		end
	end

	local function normalize_suffix(suffix)
		if not suffix then
			return nil
		end
		local no_space_suffix = match(suffix, "^_(.*)$")
		if no_space_suffix then
			return no_space_suffix
		elseif find(suffix, "^%-") then
			return suffix
		else
			return " " .. suffix
		end
	end

	data.prefix = normalize_prefix(args.prefix)
	data.passive_prefix = normalize_prefix(args.passive_prefix) or data.prefix
	data.plural_prefix = normalize_prefix(args.plural_prefix) or data.prefix
	-- First fall back to the passive prefix (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.
	data.plural_passive_prefix = normalize_prefix(args.plural_passive_prefix) or
		normalize_prefix(args.passive_prefix) or data.plural_prefix
	data.gen_prefix = normalize_prefix(args.gen_prefix)
	data.dat_prefix = normalize_prefix(args.dat_prefix)
	data.acc_prefix = normalize_prefix(args.acc_prefix)
	data.abl_prefix = normalize_prefix(args.abl_prefix)

	data.suffix = normalize_suffix(args.suffix)
	data.passive_suffix = normalize_suffix(args.passive_suffix) or data.suffix
	data.plural_suffix = normalize_suffix(args.plural_suffix) or data.suffix
	-- Same as above for prefixes.
	data.plural_passive_suffix = normalize_suffix(args.plural_passive_suffix) or
		normalize_suffix(args.passive_suffix) or data.plural_suffix
	data.gen_suffix = normalize_suffix(args.gen_suffix)
	data.dat_suffix = normalize_suffix(args.dat_suffix)
	data.acc_suffix = normalize_suffix(args.acc_suffix)
	data.abl_suffix = normalize_suffix(args.abl_suffix)

	-- Generate the verb forms
	conjugations[conj_type](args, data, typeinfo)

	-- Post-process the forms
	postprocess(data, typeinfo)

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

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

	-- Prepend any prefixes, append any suffixes
	add_prefix_suffix(data)
	
	if args["label"] then
		m_table.insertIfNot(data.title, args["label"])
	end
	
	notes_override(data, args)

	-- Check if the verb is irregular
	if not conj_type == 'irreg' then checkirregular(args, data) end
	return data, typeinfo
end

local function form_contains(forms, form)
	if type(forms) == "string" then
		return forms == form
	else
		return m_table.contains(forms, form)
	end
end

-- Add a value to a given form key, e.g. "1s_pres_actv_indc". If the
-- value is already present in the key, it won't be added again.
--
-- The value is formed by concatenating STEM and SUF. SUF 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.
--
-- POS is the position to insert the form(s) at; default is at the end.
-- To insert at the beginning specify 1 for POS.
local function add_form(data, key, stem, suf, pos)
	if not suf then
		return
	end
	if type(suf) ~= "table" then
		suf = {suf}
	end
	for _, s in ipairs(suf) do
		if not data.forms[key] then
			data.forms[key] = {}
		elseif type(data.forms[key]) == "string" then
			data.forms[key] = {data.forms[key]}
		end
		m_table.insertIfNot(data.forms[key], stem .. s, pos)
	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(data, keytype, stem, suf1s, suf2s, suf3s, suf1p, suf2p, suf3p)
	add_form(data, "1s_" .. keytype, stem, suf1s)
	add_form(data, "2s_" .. keytype, stem, suf2s)
	add_form(data, "3s_" .. keytype, stem, suf3s)
	add_form(data, "1p_" .. keytype, stem, suf1p)
	add_form(data, "2p_" .. keytype, stem, suf2p)
	add_form(data, "3p_" .. keytype, stem, suf3p)
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(data, keytype, stem, suf2s, suf2p)
	add_form(data, "2s_" .. keytype, stem, suf2s)
	add_form(data, "2p_" .. keytype, stem, suf2p)
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(data, keytype, stem, suf2s, suf3s, suf2p, suf3p)
	add_form(data, "2s_" .. keytype, stem, suf2s)
	add_form(data, "3s_" .. keytype, stem, suf3s)
	add_form(data, "2p_" .. keytype, stem, suf2p)
	add_form(data, "3p_" .. keytype, stem, suf3p)
end

-- Clear out all forms from a given key (e.g. "1s_pres_actv_indc").
local function clear_form(data, key)
	data.forms[key] = nil
end

-- Clear out all forms from all persons/numbers a given tense/voice/mood
-- (e.g. "pres_actv_indc").
local function clear_forms(data, keytype)
	clear_form(data, "1s_" .. keytype)
	clear_form(data, "2s_" .. keytype)
	clear_form(data, "3s_" .. keytype)
	clear_form(data, "1p_" .. keytype)
	clear_form(data, "2p_" .. keytype)
	clear_form(data, "3p_" .. keytype)
end

local function make_perfect_passive(data)
	local ppp = data.forms["perf_pasv_ptc"]
	if type(ppp) ~= "table" then
		ppp = {ppp}
	end
	local ppplinks = {}
	for _, pppform in ipairs(ppp) do
		table.insert(ppplinks, make_link({lang = lang, alt = '', term = pppform}, "term"))
	end
	local ppplink = table.concat(ppplinks, " or ")
	local sumlink = make_link({lang = lang, alt = '', term = "sum"}, "term")

	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 prefix_joiner = data.passive_prefix and data.passive_prefix:find(" $") and "+ " or ""
	local suffix_joiner = data.passive_suffix and data.passive_suffix:find("^ ") and " +" or ""
	for slot, text in pairs(text_for_slot) do
		data.forms[slot] =
			(data.passive_prefix or "") .. prefix_joiner .. ppplink .. " + " ..
			text .. " of " .. sumlink .. suffix_joiner .. (data.passive_suffix or "")
	end
	ppp = data.forms["1s_pres_actv_indc"]
	if type(ppp) ~= "table" then
		ppp = {ppp}
	end
	if ppp[1] == "faciō" then
		ppp = {"factum"}
		ppplinks = {}
		for _, pppform in ipairs(ppp) do
			table.insert(ppplinks, make_link({lang = lang, alt = '', term = pppform}, "term"))
		end
		ppplink = table.concat(ppplinks, " or ")
		sumlink = make_link({lang = lang, alt = '', term = "sum"}, "term")
		for slot, text in pairs(text_for_slot) do
			data.forms[slot] =
				data.forms[slot] .. " or " .. ppplink .. " + " .. text .. " of " .. sumlink
		end
	end
end

-- 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. NO_GERUND means to skip generating any
-- gerunds (and gerundive variants). NO_FUTR_PASV_PTC means to skip generating
-- the future passive participle.
local function make_gerund(data, typeinfo, base, und_variant, no_gerund, no_futr_pasv_ptc)
	local neut_endings = {
		nom = "um",
		gen = "ī",
		dat = "ō",
		acc = "um",
		abl = "ō",
	}

	local endings
	if typeinfo.subtypes.f then
		endings = {
			nom = "a",
			gen = "ae",
			dat = "ae",
			acc = "am",
			abl = "ā",
		}
	elseif typeinfo.subtypes.n then
		endings = neut_endings
	elseif typeinfo.subtypes.mp then
		endings = {
			nom = "ī",
			gen = "ōrum",
			dat = "īs",
			acc = "ōs",
			abl = "īs",
		}
	elseif typeinfo.subtypes.fp then
		endings = {
			nom = "ae",
			gen = "ārum",
			dat = "īs",
			acc = "ās",
			abl = "īs",
		}
	elseif typeinfo.subtypes.np then
		endings = {
			nom = "a",
			gen = "ōrum",
			dat = "īs",
			acc = "a",
			abl = "īs",
		}
	else
		endings = {
			nom = "us",
			gen = "ī",
			dat = "ō",
			acc = "um",
			abl = "ō",
		}
	end

	if find(base, "[uv]end$") or typeinfo.subtypes.nound 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
		und_variant = false
	end
	local und_base = und_variant and base:gsub("end$", "und")
	for case, ending in pairs(endings) do
		if case == "nom" then
			if not no_futr_pasv_ptc then
				if typeinfo.subtypes.passimpers then
					ending = "um"
				end
				add_form(data, "futr_pasv_ptc", "", base .. ending)
				if und_base then
					add_form(data, "futr_pasv_ptc", "", und_base .. ending)
				end
			end
		elseif (data[case .. "_prefix"] or data[case .. "_suffix"]) and not no_gerund then
			add_form(data, "ger_" .. case, "", (data[case .. "_prefix"] or "")
				.. base .. ending .. (data[case .. "_suffix"] or ""))
			if und_base then
				add_form(data, "ger_" .. case, "", (data[case .. "_prefix"] or "")
					.. und_base .. ending .. (data[case .. "_suffix"] or ""))
			end
		end
	end
	if not no_gerund then
		for case, ending in pairs(neut_endings) do
			add_form(data, "ger_" .. case, "",
				(data.prefix or  "") ..	base .. ending .. (data.suffix or  ""))
		end
	end
end

postprocess = function(data, typeinfo)
	-- 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 typeinfo.subtypes.nosup then
		-- Some verbs have no supine forms or forms derived from the supine
		if typeinfo.subtypes.perfaspres == nil then
			m_table.insertIfNot(data.title, "no [[supine]] stem")
		end
		m_table.insertIfNot(data.categories, "Latin verbs with missing supine stem")
		m_table.insertIfNot(data.categories, "Latin defective verbs")

		for key, _ in pairs(data.forms) do
			if cfind(key, "sup") or (
				key == "perf_actv_ptc" or key == "perf_pasv_ptc" or key == "perf_pasv_inf" or
				key == "futr_actv_ptc" or key == "futr_actv_inf" or key == "futr_pasv_inf" or
				(typeinfo.subtypes.depon or typeinfo.subtypes.semidepon or
				 typeinfo.subtypes.optsemidepon) and key == "perf_actv_inf"
			) then
				data.forms[key] = nil
			end
		end
	elseif typeinfo.subtypes.supfutractvonly then
		-- Some verbs have no supine forms or forms derived from the supine,
		-- except for the future active infinitive/participle
		if typeinfo.subtypes.perfaspres == nil then
			m_table.insertIfNot(data.title, "no [[supine]] stem except in the [[future]] [[active]] [[participle]]")
		end
		m_table.insertIfNot(data.categories, "Latin verbs with missing supine stem except in the future active participle")
		m_table.insertIfNot(data.categories, "Latin defective verbs")

		for key, _ in pairs(data.forms) do
			if cfind(key, "sup") or (
				key == "perf_actv_ptc" or key == "perf_pasv_ptc" or key == "perf_pasv_inf" or
				key == "futr_pasv_inf"
			) then
				data.forms[key] = nil
			end
		end
	end
	
	-- Add information for the passive perfective forms
	if data.forms["perf_pasv_ptc"] and not form_is_empty(data.forms["perf_pasv_ptc"]) then
		if typeinfo.subtypes.passimpers then
			-- this should always be a table because it's generated only in
			-- make_supine()
			local pppforms = data.forms["perf_pasv_ptc"]
			for _, ppp in ipairs(pppforms) do
				if not form_is_empty(ppp) then
					-- make_supine() already generated the neuter form of the PPP.
					local nns_ppp = make_raw_link(ppp)
					add_form(data, "3s_perf_pasv_indc", nns_ppp, " [[est]]")
					add_form(data, "3s_futp_pasv_indc", nns_ppp, " [[erit]]")
					add_form(data, "3s_plup_pasv_indc", nns_ppp, " [[erat]]")
					add_form(data, "3s_perf_pasv_subj", nns_ppp, " [[sit]]")
					add_form(data, "3s_plup_pasv_subj", nns_ppp, {" [[esset]]", " [[foret]]"})
				end
			end
		elseif typeinfo.subtypes.pass3only then
			local pppforms = data.forms["perf_pasv_ptc"]
			if type(pppforms) ~= "table" then
				pppforms = {pppforms}
			end
			for _, ppp in ipairs(pppforms) do
				if not form_is_empty(ppp) then
					local ppp_s, ppp_p
					if typeinfo.subtypes.mp then
						ppp_p = make_raw_link(gsub1(ppp, "ī$", "us"), ppp)
					elseif typeinfo.subtypes.fp then
						ppp_p = make_raw_link(gsub1(ppp, "ae$", "us"), ppp)
					elseif typeinfo.subtypes.np then
						ppp_p = make_raw_link(gsub1(ppp, "a$", "us"), ppp)
					elseif typeinfo.subtypes.f then
						local ppp_lemma = gsub1(ppp, "a$", "us")
						ppp_s = make_raw_link(ppp_lemma, ppp)
						ppp_p = make_raw_link(ppp_lemma, gsub1(ppp, "a$", "ae"))
					elseif typeinfo.subtypes.n then
						local ppp_lemma = gsub1(ppp, "um$", "us")
						ppp_s = make_raw_link(ppp_lemma, ppp)
						ppp_p = make_raw_link(ppp_lemma, gsub1(ppp, "um$", "a"))
					else
						ppp_s = make_raw_link(ppp)
						ppp_p = make_raw_link(ppp, gsub1(ppp, "us$", "ī"))
					end
					if not typeinfo.subtypes.mp and not typeinfo.subtypes.fp and not typeinfo.subtypes.np then
						add_form(data, "3s_perf_pasv_indc", ppp_s, " [[est]]")
						add_form(data, "3s_futp_pasv_indc", ppp_s, " [[erit]]")
						add_form(data, "3s_plup_pasv_indc", ppp_s, " [[erat]]")
						add_form(data, "3s_perf_pasv_subj", ppp_s, " [[sit]]")
						add_form(data, "3s_plup_pasv_subj", ppp_s, {" [[esset]]", " [[foret]]"})
					end
					add_form(data, "3p_perf_pasv_indc", ppp_p, " [[sunt]]")
					add_form(data, "3p_futp_pasv_indc", ppp_p, " [[erunt]]")
					add_form(data, "3p_plup_pasv_indc", ppp_p, " [[erant]]")
					add_form(data, "3p_perf_pasv_subj", ppp_p, " [[sint]]")
					add_form(data, "3p_plup_pasv_subj", ppp_p, {" [[essent]]", " [[forent]]"})
				end
			end
		else
			make_perfect_passive(data)
		end
	end

	if typeinfo.subtypes.perfaspres then
		-- Perfect forms as present tense
		m_table.insertIfNot(data.title, "no [[present tense|present]] stem")
		if typeinfo.subtypes.nosup then
			m_table.insertIfNot(data.title, "no [[supine]] stem")
		elseif typeinfo.subtypes.supfutractvonly then
			m_table.insertIfNot(data.title, "no [[supine]] stem except in the [[future]] [[active]] [[participle]]")
		end
		m_table.insertIfNot(data.title, "active only")
		m_table.insertIfNot(data.title, "[[perfect]] forms as present")
		m_table.insertIfNot(data.title, "pluperfect as imperfect")
		m_table.insertIfNot(data.title, "future perfect as future")
		m_table.insertIfNot(data.categories, "Latin defective verbs")
		m_table.insertIfNot(data.categories, "Latin active-only verbs")
		m_table.insertIfNot(data.categories, "Latin verbs with missing present stem")
        m_table.insertIfNot(data.categories, "Latin verbs with perfect forms having imperfective meanings")

		-- Change perfect passive participle to perfect active participle
		data.forms["perf_actv_ptc"] = data.forms["perf_pasv_ptc"]

		-- Change perfect active infinitive to present active infinitive
		data.forms["pres_actv_inf"] = data.forms["perf_actv_inf"]

		-- Remove passive forms
		-- Remove present active, imperfect active and future active forms
		for key, _ in pairs(data.forms) do
			if key ~= "futr_actv_inf" and key ~= "futr_actv_ptc" and (
				cfind(key, "pasv") or cfind(key, "pres") and key ~= "pres_actv_inf" or
				cfind(key, "impf") or cfind(key, "futr")
			) then
				data.forms[key] = nil
			end
		end

		-- Change perfect forms to non-perfect forms
		for key, form in pairs(data.forms) do
			if cfind(key, "perf") and key ~= "perf_actv_ptc" then
				data.forms[key:gsub("perf", "pres")] = form
				data.forms[key] = nil
			elseif cfind(key, "plup") then
				data.forms[key:gsub("plup", "impf")] = form
				data.forms[key] = nil
			elseif cfind(key, "futp") then
				data.forms[key:gsub("futp", "futr")] = form
				data.forms[key] = nil
			elseif cfind(key, "ger") then
				data.forms[key] = nil
			end
		end

		data.forms["pres_actv_ptc"] = nil
	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.
	if typeinfo.subtypes.impers then
		-- Impersonal verbs have only third-person singular forms.
		m_table.insertIfNot(data.title, "[[impersonal]]")
		m_table.insertIfNot(data.categories, "Latin impersonal verbs")

		-- Remove all non-3sg forms
		for key, _ in pairs(data.forms) do
			if key:find("^[12][sp]") or key:find("^3p") then
				data.forms[key] = nil
			end
		end
	elseif typeinfo.subtypes["3only"] then
		m_table.insertIfNot(data.title, "[[third person]] only")
		m_table.insertIfNot(data.categories, "Latin third-person-only verbs")

		-- Remove all non-3sg forms
		for key, _ in pairs(data.forms) do
			if key:find("^[12][sp]") then
				data.forms[key] = nil
			end
		end
	end

	if typeinfo.subtypes.nopasvperf and not typeinfo.subtypes.nosup and
			not typeinfo.subtypes.supfutractvonly then
		-- Some verbs have no passive perfect forms (e.g. ārēscō, -ěre).
		-- Only do anything here if the verb has a supine; otherwise it
		-- necessarily has no passive perfect forms.
		m_table.insertIfNot(data.title, "no passive perfect forms")
		m_table.insertIfNot(data.categories, "Latin defective verbs")

		-- Remove all passive perfect forms
		for key, _ in pairs(data.forms) do
			if cfind(key, "pasv") and (cfind(key, "perf") or cfind(key, "plup") or cfind(key, "futp")) then
				data.forms[key] = nil
			end
		end
	end

	-- Handle certain irregularities in the passive
	if typeinfo.subtypes.optsemidepon then
		-- 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. We already generated the perfective
		-- forms but need to clear out the imperfective passive.
		m_table.insertIfNot(data.title, "optionally [[semi-deponent]]")
		m_table.insertIfNot(data.categories, "Latin semi-deponent verbs")
		m_table.insertIfNot(data.categories, "Latin optionally semi-deponent verbs")

		-- Remove imperfective passive forms
		for key, _ in pairs(data.forms) do
			if cfind(key, "pres_pasv") or cfind(key, "impf_pasv") or cfind(key, "futr_pasv") then
				data.forms[key] = nil
			end
		end
	elseif typeinfo.subtypes.semidepon then
		-- Semi-deponent verbs use perfective passive forms with active meaning,
		-- and have no imperfective passive
		m_table.insertIfNot(data.title, "[[semi-deponent]]")
		m_table.insertIfNot(data.categories, "Latin semi-deponent verbs")

		-- Remove perfective active and imperfective passive forms
		for key, _ in pairs(data.forms) do
			if cfind(key, "perf_actv") or cfind(key, "plup_actv") or cfind(key, "futp_actv") or cfind(key, "pres_pasv") or cfind(key, "impf_pasv") or cfind(key, "futr_pasv") then
				data.forms[key] = nil
			end
		end

		-- Change perfective passive to active
		for key, form in pairs(data.forms) do
			if cfind(key, "perf_pasv") or cfind(key, "plup_pasv") or cfind(key, "futp_pasv") then
				data.forms[key:gsub("pasv", "actv")] = form
				data.forms[key] = nil
			end
		end
	elseif typeinfo.subtypes.depon then
		-- Deponent verbs use passive forms with active meaning
		m_table.insertIfNot(data.title, "[[deponent]]")
		m_table.insertIfNot(data.categories, "Latin deponent verbs")

		-- Remove active forms and future passive infinitive
		for key, _ in pairs(data.forms) do
			if cfind(key, "actv") and key ~= "pres_actv_ptc" and key ~= "futr_actv_ptc" and key ~= "futr_actv_inf" and cfind(key, "sigf") == nil or key == "futr_pasv_inf" then
				data.forms[key] = nil
			end
		end

		-- Change passive to active
		for key, form in pairs(data.forms) do
			if cfind(key, "pasv") and key ~= "pres_pasv_ptc" and key ~= "futr_pasv_ptc" and key ~= "futr_pasv_inf" then
				data.forms[key:gsub("pasv", "actv")] = form
				data.forms[key] = nil
			end
		end
	end

	if typeinfo.subtypes.noperf then
		-- Some verbs have no perfect stem (e.g. inalbēscō, -ěre)
		m_table.insertIfNot(data.title, "no [[perfect tense|perfect]] stem")
		m_table.insertIfNot(data.categories, "Latin verbs with missing perfect stem")
		m_table.insertIfNot(data.categories, "Latin defective verbs")

		-- Remove all active perfect forms (passive perfect forms may
		-- still exist as they are formed with the supine stem)
		for key, _ in pairs(data.forms) do
			if cfind(key, "actv") and (cfind(key, "perf") or cfind(key, "plup") or cfind(key, "futp")) then
				data.forms[key] = nil
			end
		end
	end

	if typeinfo.subtypes.nopass then
		-- Remove all passive forms
		m_table.insertIfNot(data.title, "active only")
		m_table.insertIfNot(data.categories, "Latin active-only verbs")

		-- Remove all non-3sg and passive forms
		for key, _ in pairs(data.forms) do
			if cfind(key, "pasv") then
				data.forms[key] = nil
			end
		end
	elseif typeinfo.subtypes.pass3only then
		-- Some verbs have only third-person forms in the passive
		m_table.insertIfNot(data.title, "only third-person forms in passive")
		m_table.insertIfNot(data.categories, "Latin verbs with third-person passive")

		-- Remove all non-3rd-person passive forms and all passive imperatives
		for key, _ in pairs(data.forms) do
			if cfind(key, "pasv") and (key:find("^[12][sp]") or cfind(key, "impr")) then
				data.forms[key] = nil
			end
			-- For phrasal verbs with a plural complement, also need to erase the
			-- 3s forms.
			if typeinfo.subtypes.mp or typeinfo.subtypes.fp or typeinfo.subtypes.np then
				if cfind(key, "pasv") and key:find("^3s") then
					data.forms[key] = nil
				end
			end
		end
	elseif typeinfo.subtypes.passimpers then
		-- Some verbs are impersonal in the passive
		m_table.insertIfNot(data.title, "[[impersonal]] in passive")
		m_table.insertIfNot(data.categories, "Latin verbs with impersonal passive")

		-- Remove all non-3sg passive forms
		for key, _ in pairs(data.forms) do
			if cfind(key, "pasv") and (key:find("^[12][sp]") or key:find("^3p") or cfind(key, "impr")) or cfind(key, "futr_pasv_inf") then
				data.forms[key] = nil
			end
		end
	end

	-- Handle certain irregularities in the imperative
	if typeinfo.subtypes.noimp then
		-- Some verbs have no imperatives
		m_table.insertIfNot(data.title, "no [[imperative]]s")
		m_table.insertIfNot(data.categories, "Latin verbs with missing imperative")
		m_table.insertIfNot(data.categories, "Latin defective verbs")

		-- Remove all imperative forms
		for key, _ in pairs(data.forms) do
			if cfind(key, "impr") then
				data.forms[key] = nil
			end
		end
	end

	-- Handle certain irregularities in the future
	if typeinfo.subtypes.nofut then
		-- Some verbs (e.g. soleō) have no future
		m_table.insertIfNot(data.title, "no [[future]]")
		m_table.insertIfNot(data.categories, "Latin verbs with missing future")
		m_table.insertIfNot(data.categories, "Latin defective verbs")

		-- Remove all future forms
		for key, _ in pairs(data.forms) do
			if cfind(key, "fut") then -- handles futr = future and futp = future perfect
				data.forms[key] = nil
			end
		end
	end

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

	-- Add the poetic present passive infinitive forms of certain verbs
	if typeinfo.subtypes.p3inf then
			local is_depon = typeinfo.subtypes.depon
			local form = "pres_" .. (is_depon and "actv" or "pasv") .. "_inf"
			local noteindex = #(data.footnotes) + 1
			local formval = data.forms[form]
			if type(formval) ~= "table" then
				formval = {formval}
			end
			local newvals = mw.clone(formval)
			for _, fv in ipairs(formval) do
				table.insert(newvals, sub(fv, 1, -2) .. "ier")
			end
			data.forms[form] = newvals
			data.form_footnote_indices[form] = tostring(noteindex)
			data.footnotes[noteindex] = 'The present passive infinitive in -ier is a rare poetic form which is attested.'
	end

	--Add the syncopated perfect forms, omitting the separately handled fourth conjugation cases

	if typeinfo.subtypes.poetsyncperf then
		local sss = {
			--infinitive
			{'perf_actv_inf', 'sse'},
			--perfect actives
		    {'2s_perf_actv_indc', 'stī'},
		    {'3s_perf_actv_indc', 't'},
		    {'1p_perf_actv_indc', 'mus'},
			{'2p_perf_actv_indc', 'stis'},
			{'3p_perf_actv_indc', 'runt'},
			--pluperfect actives
		    {'1s_plup_actv_indc', 'ram'},
		    {'2s_plup_actv_indc', 'rās'},
		    {'3s_plup_actv_indc', 'rat'},
		    {'1p_plup_actv_indc', 'rāmus'},
			{'2p_plup_actv_indc', 'rātis'},
			{'3p_plup_actv_indc', 'rant'},
			--future perfect actives
		    {'1s_futp_actv_indc', 'rō'},
		    {'2s_futp_actv_indc', 'ris'},
		    {'3s_futp_actv_indc', 'rit'},
		    {'1p_futp_actv_indc', 'rimus'},
			{'2p_futp_actv_indc', 'ritis'},
			{'3p_futp_actv_indc', 'rint'},
			--perfect subjunctives
		    {'1s_perf_actv_subj', 'rim'},
			{'2s_perf_actv_subj', 'rīs'},
			{'3s_perf_actv_subj', 'rit'},
			{'1p_perf_actv_subj', 'rīmus'},
			{'2p_perf_actv_subj', 'rītis'},
			{'3p_perf_actv_subj', 'rint'},
			--pluperfect subjunctives
		    {'1s_plup_actv_subj', 'ssem'},
			{'2s_plup_actv_subj', 'ssēs'},
			{'3s_plup_actv_subj', 'sset'},
			{'1p_plup_actv_subj', 'ssēmus'},
			{'2p_plup_actv_subj', 'ssētis'},
			{'3p_plup_actv_subj', 'ssent'}
		}
		local noteindex = #(data.footnotes)+1
		function add_sync_perf(form, suff_sync)
			local formval = data.forms[form]
			if type(formval) ~= "table" then
				formval = {formval}
			end
			local newvals = mw.clone(formval)
			for _, fv in ipairs(formval) do
				-- Can only syncopate 'vi', 've', 'vē', or any one of them spelled with a 'u' after a vowel
				if fv:find('v[ieē]' .. suff_sync .. '$') or fv:find('vē' .. suff_sync .. '$') or find(fv, '[aeiouyāēīōūȳăĕĭŏŭ]u[ieē]' .. suff_sync.. '$') or find(fv, '[aeiouyāēīōūȳăĕĭŏŭ]uē' .. suff_sync.. '$') then
					m_table.insertIfNot(newvals, sub(fv, 1, -len(suff_sync) - 3) .. suff_sync)
				end
			end
			data.forms[form] = newvals
			data.form_footnote_indices[form] = noteindex
		end
		for _, v in ipairs(sss) do
			add_sync_perf(v[1], v[2])
		end
		data.footnotes[noteindex] = "At least one rare poetic syncopated perfect form is attested." end
		
	-- Add category for sigmatic forms
	if typeinfo.subtypes.sigm or typeinfo.subtypes.sigmpasv then
		m_table.insertIfNot(data.categories, "Latin verbs with sigmatic forms")
	end
	-- Add subcategory for passive sigmatic forms
	if typeinfo.subtypes.sigmpasv or (typeinfo.subtypes.sigm and typeinfo.subtypes.depon) then
		m_table.insertIfNot(data.categories, "Latin verbs with passive sigmatic forms")
	end

end

--[=[
	Conjugation functions
]=]--

local function get_regular_stems(args, typeinfo)
	-- Get the parameters
	if typeinfo.subtypes.depon or typeinfo.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[1])
		typeinfo.perf_stem = nil
		typeinfo.supine_stem = ine(args[2])
	elseif typeinfo.subtypes.perfaspres then
		typeinfo.perf_stem = ine(args[1])
		typeinfo.supine_stem = ine(args[2])
	else
		typeinfo.pres_stem = ine(args[1])
		typeinfo.perf_stem = ine(args[2])
		typeinfo.supine_stem = ine(args[3])
	end
	if typeinfo.subtypes.sigm or typeinfo.subtypes.sigmpasv then
		typeinfo.sigm_stem = ine(args[5])
	end

	-- Prepare stems
	if not typeinfo.pres_stem then
		if NAMESPACE == "Template" or typeinfo.subtypes.perfaspres then
			typeinfo.pres_stem = "-"
		else
			error("Present stem has not been provided")
		end
	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 has_perf_in_s_or_x(pres_stem, perf_stem)
	if pres_stem == perf_stem then
		return false
	end

	return perf_stem and perf_stem:find("[sx]$") ~= nil
end

conjugations["1st"] = function(args, data, typeinfo)
	get_regular_stems(args, typeinfo)

	table.insert(data.title, "[[Appendix:Latin first conjugation|first conjugation]]")
	table.insert(data.categories, "Latin first conjugation verb")

	for _, perf_stem in ipairs(typeinfo.perf_stem) do
		if perf_stem == typeinfo.pres_stem .. "āv" then
			table.insert(data.categories, "Latin first conjugation verb with perfect in -av-")
		elseif perf_stem == typeinfo.pres_stem .. "u" then
			table.insert(data.categories, "Latin first conjugation verb with perfect in -u-")
		elseif perf_stem == typeinfo.pres_stem then
			table.insert(data.categories, "Latin first conjugation verb with suffixless perfect")
		else
			table.insert(data.categories, "Latin first conjugation verb with irregular perfect")
		end
	end

	make_pres_1st(data, typeinfo, typeinfo.pres_stem)
	make_perf_and_supine(data, typeinfo)
	make_sigm(data, typeinfo, typeinfo.sigm_stem)
	
	--Additional forms in specific cases
	if typeinfo.pres_stem == "dīlapid" then
		add_form(data, "3p_sigf_actv_indc", "", "dīlapidāssunt", 2 )
	elseif typeinfo.pres_stem == "invol" then
		add_form(data, "3s_sigf_actv_indc", "", "involāsit", 2 )
	elseif typeinfo.pres_stem == "viol" then
		local noteindex = #(data.footnotes) + 1
		add_form(data, "3p_futp_actv_indc", "", "violārint", 2 )
		add_form(data, "3p_perf_actv_subj", "", "violārint", 2 )
		add_form(data, "3s_sigf_actv_indc", "", "violāsit", 2 )
		data.form_footnote_indices["3p_futp_actv_indc"] = tostring(noteindex)
		data.form_footnote_indices["3p_perf_actv_subj"] = tostring(noteindex)
		data.footnotes[noteindex] = 'Archaic.'
	end
end

conjugations["2nd"] = function(args, data, typeinfo)
	get_regular_stems(args, typeinfo)

	table.insert(data.title, "[[Appendix:Latin second conjugation|second conjugation]]")
	table.insert(data.categories, "Latin second conjugation verb")

	for _, perf_stem in ipairs(typeinfo.perf_stem) do
		local pres_stem = typeinfo.pres_stem
		pres_stem = pres_stem:gsub("qu", "1")
		perf_stem = perf_stem:gsub("qu", "1")
		if perf_stem == pres_stem .. "ēv" then
			table.insert(data.categories, "Latin second conjugation verb with perfect in -ev-")
		elseif perf_stem == pres_stem .. "u" then
			table.insert(data.categories, "Latin second conjugation verb with perfect in -u-")
		elseif perf_stem == pres_stem then
			table.insert(data.categories, "Latin second conjugation verb with suffixless perfect")
		elseif has_perf_in_s_or_x(pres_stem, perf_stem) then
			table.insert(data.categories, "Latin second conjugation verb with perfect in -s- or -x-")
		else
			table.insert(data.categories, "Latin second conjugation verb with irregular perfect")
		end
	end

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

local function set_3rd_conj_categories(data, typeinfo)
	table.insert(data.categories, "Latin third conjugation verb")

	for _, perf_stem in ipairs(typeinfo.perf_stem) do
		local pres_stem = typeinfo.pres_stem
		pres_stem = pres_stem:gsub("qu", "1")
		perf_stem = perf_stem:gsub("qu", "1")
		if perf_stem == pres_stem .. "āv" then
			table.insert(data.categories, "Latin third conjugation verb with perfect in -av-")
		elseif perf_stem == pres_stem .. "ēv" then
			table.insert(data.categories, "Latin third conjugation verb with perfect in -ev-")
		elseif perf_stem == pres_stem .. "īv" then
			table.insert(data.categories, "Latin third conjugation verb with perfect in -iv-")
		elseif perf_stem == pres_stem .. "i" then
			table.insert(data.categories, "Latin third conjugation verb with perfect in -i-")
		elseif perf_stem == pres_stem .. "u" then
			table.insert(data.categories, "Latin third conjugation verb with perfect in -u-")
		elseif perf_stem == pres_stem then
			table.insert(data.categories, "Latin third conjugation verb with suffixless perfect")
		elseif has_perf_in_s_or_x(pres_stem, perf_stem) then
			table.insert(data.categories, "Latin third conjugation verb with perfect in -s- or -x-")
		else
			table.insert(data.categories, "Latin third conjugation verb with irregular perfect")
		end
	end
end

conjugations["3rd"] = function(args, data, typeinfo)
	get_regular_stems(args, typeinfo)

	table.insert(data.title, "[[Appendix:Latin third conjugation|third conjugation]]")
	set_3rd_conj_categories(data, typeinfo)

	if typeinfo.pres_stem and match(typeinfo.pres_stem,"[āēīōū]sc$") then
		table.insert(data.categories, "Latin inchoative verbs")
	end

	make_pres_3rd(data, typeinfo, typeinfo.pres_stem)
	make_perf_and_supine(data, typeinfo)
	make_sigm(data, typeinfo, typeinfo.sigm_stem)
	
	--Additional forms in specific cases
	--FIXME: needs to be cleared up
	if match(typeinfo.pres_stem, "nōsc") then
		local noteindex = #(data.footnotes) + 1
		add_form(data, "2s_perf_actv_indc", "", sub(typeinfo.perf_stem[1],1,-2) .. "stī", 2 )
		add_form(data, "1p_perf_actv_indc", "", sub(typeinfo.perf_stem[1],1,-2) .. "mus", 2 )
		add_form(data, "2p_perf_actv_indc", "", sub(typeinfo.perf_stem[1],1,-2) .. "stis", 2 )
		add_form(data, "3p_perf_actv_indc", "", sub(typeinfo.perf_stem[1],1,-2) .. "runt", 3 )
		add_form(data, "1s_plup_actv_indc", "", sub(typeinfo.perf_stem[1],1,-2) .. "ram", 2 )
		add_form(data, "2s_plup_actv_indc", "", sub(typeinfo.perf_stem[1],1,-2) .. "rās", 2 )
		add_form(data, "3s_plup_actv_indc", "", sub(typeinfo.perf_stem[1],1,-2) .. "rat", 2 )
		add_form(data, "1p_plup_actv_indc", "", sub(typeinfo.perf_stem[1],1,-2) .. "rāmus", 2 )
		add_form(data, "2p_plup_actv_indc", "", sub(typeinfo.perf_stem[1],1,-2) .. "rātis", 2 )
		add_form(data, "3p_plup_actv_indc", "", sub(typeinfo.perf_stem[1],1,-2) .. "rant", 2 )
		add_form(data, "1s_futp_actv_indc", "", sub(typeinfo.perf_stem[1],1,-2) .. "rō", 2 )
		add_form(data, "2s_futp_actv_indc", "", sub(typeinfo.perf_stem[1],1,-2) .. "ris", 2 )
		add_form(data, "3s_futp_actv_indc", "", sub(typeinfo.perf_stem[1],1,-2) .. "rit", 2 )
		add_form(data, "1p_futp_actv_indc", "", sub(typeinfo.perf_stem[1],1,-2) .. "rimus", 2 )
		add_form(data, "2p_futp_actv_indc", "", sub(typeinfo.perf_stem[1],1,-2) .. "ritis", 2 )
		add_form(data, "3p_futp_actv_indc", "", sub(typeinfo.perf_stem[1],1,-2) .. "rint", 2 )
		add_form(data, "1s_perf_actv_subj", "", sub(typeinfo.perf_stem[1],1,-2) .. "rim", 2 )
		add_form(data, "2s_perf_actv_subj", "", sub(typeinfo.perf_stem[1],1,-2) .. "rīs", 2 )
		add_form(data, "3s_perf_actv_subj", "", sub(typeinfo.perf_stem[1],1,-2) .. "rit", 2 )
		add_form(data, "1p_perf_actv_subj", "", sub(typeinfo.perf_stem[1],1,-2) .. "rīmus", 2 )
		add_form(data, "2p_perf_actv_subj", "", sub(typeinfo.perf_stem[1],1,-2) .. "rītis", 2 )
		add_form(data, "3p_perf_actv_subj", "", sub(typeinfo.perf_stem[1],1,-2) .. "rint", 2 )
		add_form(data, "1s_plup_actv_subj", "", sub(typeinfo.perf_stem[1],1,-2) .. "ssem", 2 )
		add_form(data, "2s_plup_actv_subj", "", sub(typeinfo.perf_stem[1],1,-2) .. "ssēs", 2 )
		add_form(data, "3s_plup_actv_subj", "", sub(typeinfo.perf_stem[1],1,-2) .. "sset", 2 )
		add_form(data, "1p_plup_actv_subj", "", sub(typeinfo.perf_stem[1],1,-2) .. "ssēmus", 2 )
		add_form(data, "2p_plup_actv_subj", "", sub(typeinfo.perf_stem[1],1,-2) .. "ssētis", 2 )
		add_form(data, "3p_plup_actv_subj", "", sub(typeinfo.perf_stem[1],1,-2) .. "ssent", 2 )
		add_form(data, "perf_actv_inf", "", sub(typeinfo.perf_stem[1],1,-2) .. "sse", 2 )
		data.form_footnote_indices["2s_perf_actv_indc"] = tostring(noteindex)
		data.form_footnote_indices["1p_perf_actv_indc"] = tostring(noteindex)
		data.form_footnote_indices["2p_perf_actv_indc"] = tostring(noteindex)
		data.form_footnote_indices["3p_perf_actv_indc"] = tostring(noteindex)
		data.form_footnote_indices["1s_plup_actv_indc"] = tostring(noteindex)
		data.form_footnote_indices["2s_plup_actv_indc"] = tostring(noteindex)
		data.form_footnote_indices["3s_plup_actv_indc"] = tostring(noteindex)
		data.form_footnote_indices["1p_plup_actv_indc"] = tostring(noteindex)
		data.form_footnote_indices["2p_plup_actv_indc"] = tostring(noteindex)
		data.form_footnote_indices["3p_plup_actv_indc"] = tostring(noteindex)
		data.form_footnote_indices["1s_futp_actv_indc"] = tostring(noteindex)
		data.form_footnote_indices["2s_futp_actv_indc"] = tostring(noteindex)
		data.form_footnote_indices["3s_futp_actv_indc"] = tostring(noteindex)
		data.form_footnote_indices["1p_futp_actv_indc"] = tostring(noteindex)
		data.form_footnote_indices["2p_futp_actv_indc"] = tostring(noteindex)
		data.form_footnote_indices["3p_futp_actv_indc"] = tostring(noteindex)
		data.form_footnote_indices["1s_perf_actv_subj"] = tostring(noteindex)
		data.form_footnote_indices["2s_perf_actv_subj"] = tostring(noteindex)
		data.form_footnote_indices["3s_perf_actv_subj"] = tostring(noteindex)
		data.form_footnote_indices["1p_perf_actv_subj"] = tostring(noteindex)
		data.form_footnote_indices["2p_perf_actv_subj"] = tostring(noteindex)
		data.form_footnote_indices["3p_perf_actv_subj"] = tostring(noteindex)
		data.form_footnote_indices["1s_plup_actv_subj"] = tostring(noteindex)
		data.form_footnote_indices["2s_plup_actv_subj"] = tostring(noteindex)
		data.form_footnote_indices["3s_plup_actv_subj"] = tostring(noteindex)
		data.form_footnote_indices["1p_plup_actv_subj"] = tostring(noteindex)
		data.form_footnote_indices["2p_plup_actv_subj"] = tostring(noteindex)
		data.form_footnote_indices["3p_plup_actv_subj"] = tostring(noteindex)
		data.form_footnote_indices["perf_actv_inf"] = tostring(noteindex)
		data.footnotes[noteindex] = 'The verb \"nōscō\" and its compounds frequently drop the syllables \"vi\" and \"ve\" from their perfect, pluperfect and future perfect conjugations.'
	end
	if typeinfo.pres_stem == "ulcīsc" then
		local noteindex = #(data.footnotes) + 1
		add_form(data, "1s_sigf_actv_indc", "", "ullō", 2 )
		data.form_footnote_indices["1s_sigf_actv_indc"] = tostring(noteindex)
		data.footnotes[noteindex] = 'The form \"ullō\" may have resulted from a later, erroneous misreading of \"ulsō\".'
	end
end

conjugations["3rd-io"] = function(args, data, typeinfo)
	get_regular_stems(args, typeinfo)

	table.insert(data.title, "[[Appendix:Latin third conjugation|third conjugation]] ''iō''-variant")
	set_3rd_conj_categories(data, typeinfo)

	make_pres_3rd_io(data, typeinfo, typeinfo.pres_stem)
	make_perf_and_supine(data, typeinfo)
	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

conjugations["4th"] = function(args, data, typeinfo)
	get_regular_stems(args, typeinfo)

	table.insert(data.title, "[[Appendix:Latin fourth conjugation|fourth conjugation]]")
	table.insert(data.categories, "Latin fourth conjugation verb")


	for _, perf_stem in ipairs(typeinfo.perf_stem) do
		local pres_stem = typeinfo.pres_stem
		pres_stem = pres_stem:gsub("qu", "1")
		perf_stem = perf_stem:gsub("qu", "1")
		if perf_stem == pres_stem .. "īv" then
			table.insert(data.categories, "Latin fourth conjugation verb with perfect in -iv-")
		elseif perf_stem == pres_stem .. "i" then
			table.insert(data.categories, "Latin fourth conjugation verb with perfect in -i-")
		elseif perf_stem == pres_stem .. "u" then
			table.insert(data.categories, "Latin fourth conjugation verb with perfect in -u-")
		elseif perf_stem == pres_stem then
			table.insert(data.categories, "Latin fourth conjugation verb with suffixless perfect")
		elseif has_perf_in_s_or_x(pres_stem, perf_stem) then
			table.insert(data.categories, "Latin fourth conjugation verb with perfect in -s- or -x-")
		else
			table.insert(data.categories, "Latin fourth conjugation verb with irregular perfect")
		end
	end

	make_pres_4th(data, typeinfo, typeinfo.pres_stem)
	make_perf_and_supine(data, typeinfo)
	make_sigm(data, typeinfo, typeinfo.sigm_stem)

	if form_contains(data.forms["1s_pres_actv_indc"], "serviō") or form_contains(data.forms["1s_pres_actv_indc"], "saeviō") then
		add_forms(data, "impf_actv_indc", typeinfo.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(data, "futr_actv_indc", typeinfo.pres_stem,
			{"iam", "ībō"},
			{"iēs", "ībis"},
			{"iet", "ībit"},
			{"iēmus", "ībimus"},
			{"iētis", "ībitis"},
			{"ient", "ībunt"}
		)
	end

	if typeinfo.subtypes.alwayssyncperf or typeinfo.subtypes.optsyncperf then
		for key, form in pairs(data.forms) do
			if cfind(key, "perf") or cfind(key, "plup") or cfind(key, "futp") then
				local forms = data.forms[key]
				if type(forms) ~= "table" then
					forms = {forms}
				end
				data.forms[key] = {}
				for _, f in ipairs(forms) do
					if typeinfo.subtypes.optsyncperf then
						m_table.insertIfNot(data.forms[key], f)
					end
					m_table.insertIfNot(data.forms[key], ivi_ive(f))
				end
			end
		end
	end
end

-- Irregular conjugations
local irreg_conjugations = {}

conjugations["irreg"] = function(args, data, typeinfo)
	local verb = ine(args[1])
	local prefix = ine(args[2])

	if not verb then
		if NAMESPACE == "Template" then
			verb = "sum"
		else
			error("The verb to be conjugated has not been specified.")
		end
	end

	if not irreg_conjugations[verb] then
		error("The verb '" .. verb .. "' is not recognised as an irregular verb.")
	end

	typeinfo.verb = verb
	typeinfo.prefix = prefix

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

irreg_conjugations["aio"] = function(args, data, typeinfo)
	table.insert(data.title, "[[Appendix:Latin third conjugation|third conjugation]] iō-variant")
	table.insert(data.title, "[[Appendix:Latin irregular verbs|irregular]]")
	table.insert(data.title, "active only")
	table.insert(data.title, "highly [[defective verb|defective]]")
	table.insert(data.categories, "Latin third conjugation verb")
	table.insert(data.categories, "Latin irregular verbs")
	table.insert(data.categories, "Latin active-only verbs")
	table.insert(data.categories, "Latin defective verbs")

	-- Signal to {{la-verb}} to display the verb as irregular and highly defective
	typeinfo.subtypes.irreg = true
	typeinfo.subtypes.highlydef = true

	local prefix = typeinfo.prefix or ""

	data.forms["1s_pres_actv_indc"] = prefix .. "aiō"
	data.forms["2s_pres_actv_indc"] = prefix .. "ais"
	data.forms["3s_pres_actv_indc"] = prefix .. "ait"
	data.forms["3p_pres_actv_indc"] = prefix .. "aiunt"

	data.forms["1s_impf_actv_indc"] = prefix .. "aiēbam"
	data.forms["2s_impf_actv_indc"] = prefix .. "aiēbās"
	data.forms["3s_impf_actv_indc"] = prefix .. "aiēbat"
	data.forms["1p_impf_actv_indc"] = prefix .. "aiēbāmus"
	data.forms["2p_impf_actv_indc"] = prefix .. "aiēbātis"
	data.forms["3p_impf_actv_indc"] = prefix .. "aiēbant"

	data.forms["2s_perf_actv_indc"] = prefix .. "aistī"
	data.forms["3s_perf_actv_indc"] = prefix .. "ait"

	data.forms["2s_pres_actv_subj"] = prefix .. "aiās"
	data.forms["3s_pres_actv_subj"] = prefix .. "aiat"
	data.forms["3p_pres_actv_subj"] = prefix .. "aiant"

	data.forms["2s_pres_actv_impr"] = prefix .. "ai"

	data.forms["pres_actv_inf"] = prefix .. "aiere"
	data.forms["pres_actv_ptc"] = prefix .. "aiēns"
end

irreg_conjugations["aiio"] = function(args, data, typeinfo)
	table.insert(data.title, "[[Appendix:Latin third conjugation|third conjugation]] iō-variant")
	table.insert(data.title, "[[Appendix:Latin irregular verbs|irregular]]")
	table.insert(data.title, "active only")
	table.insert(data.title, "highly [[defective verb|defective]]")
	table.insert(data.categories, "Latin third conjugation verb")
	table.insert(data.categories, "Latin irregular verbs")
	table.insert(data.categories, "Latin active-only verbs")
	table.insert(data.categories, "Latin defective verbs")

	-- Signal to {{la-verb}} to display the verb as irregular and highly defective
	typeinfo.subtypes.irreg = true
	typeinfo.subtypes.highlydef = true

	local prefix = typeinfo.prefix or ""

	data.forms["1s_pres_actv_indc"] = prefix .. "aiiō"
	data.forms["2s_pres_actv_indc"] = prefix .. "ais"
	data.forms["3s_pres_actv_indc"] = prefix .. "ait"
	data.forms["3p_pres_actv_indc"] = prefix .. "aiunt"

	data.forms["1s_impf_actv_indc"] = prefix .. "aiiēbam"
	data.forms["2s_impf_actv_indc"] = prefix .. "aiiēbās"
	data.forms["3s_impf_actv_indc"] = prefix .. "aiiēbat"
	data.forms["1p_impf_actv_indc"] = prefix .. "aiiēbāmus"
	data.forms["2p_impf_actv_indc"] = prefix .. "aiiēbātis"
	data.forms["3p_impf_actv_indc"] = prefix .. "aiiēbant"

	data.forms["2s_perf_actv_indc"] = prefix .. "aistī"
	data.forms["3s_perf_actv_indc"] = prefix .. "ait"

	data.forms["2s_pres_actv_subj"] = prefix .. "aiiās"
	data.forms["3s_pres_actv_subj"] = prefix .. "aiiat"
	data.forms["3p_pres_actv_subj"] = prefix .. "aiiant"

	data.forms["2s_pres_actv_impr"] = prefix .. "ai"

	data.forms["pres_actv_inf"] = prefix .. "aiiere"
	data.forms["pres_actv_ptc"] = prefix .. "aiiēns"
end

irreg_conjugations["ajo"] = function(args, data, typeinfo)
	table.insert(data.title, "[[Appendix:Latin third conjugation|third conjugation]] iō-variant")
	table.insert(data.title, "[[Appendix:Latin irregular verbs|irregular]]")
	table.insert(data.title, "active only")
	table.insert(data.title, "highly [[defective verb|defective]]")
	table.insert(data.categories, "Latin third conjugation verb")
	table.insert(data.categories, "Latin irregular verbs")
	table.insert(data.categories, "Latin active-only verbs")
	table.insert(data.categories, "Latin defective verbs")

	-- Signal to {{la-verb}} to display the verb as irregular and highly defective
	typeinfo.subtypes.irreg = true
	typeinfo.subtypes.highlydef = true

	local prefix = typeinfo.prefix or ""

	data.forms["1s_pres_actv_indc"] = prefix .. "ajō"
	data.forms["2s_pres_actv_indc"] = prefix .. "ais"
	data.forms["3s_pres_actv_indc"] = prefix .. "ait"
	data.forms["3p_pres_actv_indc"] = prefix .. "ajunt"

	data.forms["1s_impf_actv_indc"] = prefix .. "ajēbam"
	data.forms["2s_impf_actv_indc"] = prefix .. "ajēbās"
	data.forms["3s_impf_actv_indc"] = prefix .. "ajēbat"
	data.forms["1p_impf_actv_indc"] = prefix .. "ajēbāmus"
	data.forms["2p_impf_actv_indc"] = prefix .. "ajēbātis"
	data.forms["3p_impf_actv_indc"] = prefix .. "ajēbant"

	data.forms["2s_perf_actv_indc"] = prefix .. "aistī"
	data.forms["3s_perf_actv_indc"] = prefix .. "ait"

	data.forms["2s_pres_actv_subj"] = prefix .. "ajās"
	data.forms["3s_pres_actv_subj"] = prefix .. "ajat"
	data.forms["3p_pres_actv_subj"] = prefix .. "ajant"

	data.forms["2s_pres_actv_impr"] = prefix .. "ai"

	data.forms["pres_actv_inf"] = prefix .. "ajere"
	data.forms["pres_actv_ptc"] = prefix .. "ajēns"
end

irreg_conjugations["dico"] = function(args, data, typeinfo)
	table.insert(data.title, "[[Appendix:Latin third conjugation|third conjugation]]")
	table.insert(data.title, "[[Appendix:Latin irregular verbs|irregular]] short imperative")
	table.insert(data.categories, "Latin third conjugation verb")
	table.insert(data.categories, "Latin irregular verbs")

	local prefix = typeinfo.prefix or ""

	make_pres_3rd(data, typeinfo, prefix .. "dīc")
	make_perf(data, prefix .. "dīx")
	make_supine(data, typeinfo, prefix .. "dict")
	make_sigm(data, typeinfo, prefix .. "dīx")
	
	--Archaic regular imperative
	local noteindex = #(data.footnotes) + 1
	add_form(data, "2s_pres_actv_impr", prefix, "dīc", 1)
	data.form_footnote_indices["2s_pres_actv_impr"] = tostring(noteindex)
	
	--Archaic future forms
	if prefix == "" then
		add_form(data, "1s_futr_actv_indc", "", "dīcēbō", 2 )
		add_form(data, "3s_futr_actv_indc", "", "dīcēbit", 2 )
		data.form_footnote_indices["1s_futr_actv_indc"] = tostring(noteindex)
		data.form_footnote_indices["3s_futr_actv_indc"] = tostring(noteindex)
	end
	data.footnotes[noteindex] = 'Archaic.'
end

irreg_conjugations["do"] = function(args, data, typeinfo)
	table.insert(data.title, "[[Appendix:Latin first conjugation|first conjugation]]")
	table.insert(data.title, "[[Appendix:Latin irregular verbs|irregular]] short ''a'' in most forms except " .. make_link({lang = lang, alt = '', alt = "dās"}, "term") .. " and " .. make_link({lang = lang, alt = '', alt = "dā"}, "term"))
	table.insert(data.categories, "Latin first conjugation verb")
	table.insert(data.categories, "Latin irregular verbs")

	-- Signal to {{la-verb}} to display the verb as irregular
	typeinfo.subtypes.irreg = true

	local prefix = typeinfo.prefix or ""

	make_perf(data, prefix .. "ded")
	make_supine(data, typeinfo, prefix .. "dat")

	-- Active imperfective indicative
	add_forms(data, "pres_actv_indc", prefix, "dō", "dās", "dat", "damus", "datis", "dant")
	add_forms(data, "impf_actv_indc", prefix, "dabam", "dabās", "dabat", "dabāmus", "dabātis", "dabant")
	add_forms(data, "futr_actv_indc", prefix, "dabō", "dabis", "dabit", "dabimus", "dabitis", "dabunt")

	-- Passive imperfective indicative
	add_forms(data, "pres_pasv_indc", prefix, "dor", {"daris", "dare"}, "datur", "damur", "daminī", "dantur")
	add_forms(data, "impf_pasv_indc", prefix, "dabar", {"dabāris", "dabāre"}, "dabātur", "dabāmur", "dabāminī", "dabantur")
	add_forms(data, "futr_pasv_indc", prefix, "dabor", {"daberis", "dabere"}, "dabitur", "dabimur", "dabiminī", "dabuntur")

	-- Active imperfective subjunctive
	add_forms(data, "pres_actv_subj", prefix, "dem", "dēs", "det", "dēmus", "dētis", "dent")
	add_forms(data, "impf_actv_subj", prefix, "darem", "darēs", "daret", "darēmus", "darētis", "darent")

	-- Passive imperfective subjunctive
	add_forms(data, "pres_pasv_subj", prefix, "der", {"dēris", "dēre"}, "dētur", "dēmur", "dēminī", "dentur")
	add_forms(data, "impf_pasv_subj", prefix, "darer", {"darēris", "darēre"}, "darētur", "darēmur", "darēminī", "darentur")

	-- Imperative
	add_2_forms(data, "pres_actv_impr", prefix, "dā", "date")
	add_23_forms(data, "futr_actv_impr", prefix, "datō", "datō", "datōte", "dantō")

	add_2_forms(data, "pres_pasv_impr", prefix, "dare", "daminī")
	-- no 2p form
	add_23_forms(data, "futr_pasv_impr", prefix, "dator", "dator", {}, "dantor")

	-- Present infinitives
	data.forms["pres_actv_inf"] = prefix .. "dare"
	data.forms["pres_pasv_inf"] = prefix .. "darī"

	-- Imperfective participles
	data.forms["pres_actv_ptc"] = prefix .. "dāns"

	-- Gerund
	make_gerund(data, typeinfo, prefix .. "dand")
end

irreg_conjugations["duco"] = function(args, data, typeinfo)
	table.insert(data.title, "[[Appendix:Latin third conjugation|third conjugation]]")
	table.insert(data.title, "[[Appendix:Latin irregular verbs|irregular]] short imperative")
	table.insert(data.categories, "Latin third conjugation verb")
	table.insert(data.categories, "Latin irregular verbs")

	local prefix = typeinfo.prefix or ""

	make_pres_3rd(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(data, "2s_pres_actv_impr", prefix, "dūc", 1)
end

irreg_conjugations["edo"] = function(args, data, typeinfo)
	table.insert(data.title, "[[Appendix:Latin third conjugation|third conjugation]]")
	table.insert(data.title, "some [[Appendix:Latin irregular verbs|irregular]] alternative forms")
	table.insert(data.categories, "Latin third conjugation verb")
	table.insert(data.categories, "Latin irregular verbs")

	-- Signal to {{la-verb}} to display the verb as irregular
	typeinfo.subtypes.irreg = true

	local prefix = typeinfo.prefix or ""

	make_pres_3rd(data, typeinfo, prefix .. "ed")
	make_perf(data, prefix .. "ēd")
	make_supine(data, typeinfo, prefix .. "ēs")

	-- Active imperfective indicative
	add_forms(data, "pres_actv_indc", prefix, {}, "ēs", "ēst", {}, "ēstis", {})

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

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

	-- Active imperative
	add_2_forms(data, "pres_actv_impr", prefix, "ēs", "ēste")
	add_23_forms(data, "futr_actv_impr", prefix, "ēstō", "ēstō", "ēstōte", {})

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

irreg_conjugations["eo"] = function(args, data, typeinfo)
	table.insert(data.title, "[[Appendix:Latin irregular verbs|irregular]]")
	table.insert(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(data, "pres_actv_indc", prefix, "eō", "īs", "it", "īmus", "ītis",
		prefix == "prōd" and {"eunt", "īnunt"} or "eunt")
	add_forms(data, "impf_actv_indc", prefix, "ībam", "ībās", "ībat", "ībāmus", "ībātis", "ībant")
	add_forms(data, "futr_actv_indc", prefix, "ībō", "ībis", "ībit", "ībimus", "ībitis", "ībunt")

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

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

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

	-- Active perfective subjunctive
	data.forms["1s_plup_actv_subj"] = prefix .. "īssem"
	data.forms["2s_plup_actv_subj"] = prefix .. "īssēs"
	data.forms["3s_plup_actv_subj"] = prefix .. "īsset"
	data.forms["1p_plup_actv_subj"] = prefix .. "īssēmus"
	data.forms["2p_plup_actv_subj"] = prefix .. "īssētis"
	data.forms["3p_plup_actv_subj"] = prefix .. "īssent"

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

	-- Imperative
	add_2_forms(data, "pres_actv_impr", prefix, "ī", "īte")
	add_23_forms(data, "futr_actv_impr", prefix, "ītō", "ītō", "ītōte", "euntō")

	add_2_forms(data, "pres_pasv_impr", prefix, "īre", "īminī")
	add_23_forms(data, "futr_pasv_impr", prefix, "ītor", "ītor", {}, "euntor")

	-- Present infinitives
	data.forms["pres_actv_inf"] = prefix .. "īre"
	data.forms["pres_pasv_inf"] = prefix .. "īrī"

	-- Perfect/future infinitives
	data.forms["perf_actv_inf"] = prefix .. "īsse"

	-- Imperfective participles
	data.forms["pres_actv_ptc"] = prefix .. "iēns"

	-- Gerund
	make_gerund(data, typeinfo, prefix .. "eund")
end

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

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

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

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

irreg_conjugations["facio"] = function(args, data, typeinfo)
	table.insert(data.title, "[[Appendix:Latin third conjugation|third conjugation]] ''iō''-variant")
	table.insert(data.title, "[[Appendix:Latin irregular verbs|irregular]] and partially [[suppletive]] in the passive")
	table.insert(data.categories, "Latin third conjugation verb")
	table.insert(data.categories, "Latin irregular verbs")
	table.insert(data.categories, "Latin suppletive verbs")

	local prefix = typeinfo.prefix or ""

	make_pres_3rd_io(data, typeinfo, prefix .. "fac", "nopass")
	-- We said no passive, but we do want the future passive participle.
	make_gerund(data, typeinfo, prefix .. "faciend", "und-variant", "no-gerund")

	make_perf(data, prefix .. "fēc")
	make_supine(data, typeinfo, prefix .. "fact")
	make_sigm(data, typeinfo, prefix .. "fax")

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

	fio(data, prefix, "pasv")
end

irreg_conjugations["fio"] = function(args, data, typeinfo)
	table.insert(data.title, "[[Appendix:Latin third conjugation|third conjugation]] ''iō''-variant")
	table.insert(data.title, "[[Appendix:Latin irregular verbs|irregular]] long ''ī''")
	if not typeinfo.subtypes.nosup then
		table.insert(data.title, "[[suppletive]] in the supine stem")
	end
	table.insert(data.categories, "Latin third conjugation verb")
	table.insert(data.categories, "Latin irregular verbs")
	table.insert(data.categories, "Latin suppletive verbs")

	local prefix = typeinfo.prefix or ""

	typeinfo.subtypes.semidepon = true

	fio(data, prefix, "actv")

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

	-- Perfect/future infinitives
	data.forms["futr_actv_inf"] = data.forms["futr_pasv_inf"]

	-- Imperfective participles
	data.forms["pres_actv_ptc"] = nil
	data.forms["futr_actv_ptc"] = nil

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

irreg_conjugations["fero"] = function(args, data, typeinfo)
	table.insert(data.title, "[[Appendix:Latin third conjugation|third conjugation]]")
	table.insert(data.title, "[[Appendix:Latin irregular verbs|irregular]]")
	table.insert(data.title, "[[suppletive]]")
	table.insert(data.categories, "Latin third conjugation verb")
	table.insert(data.categories, "Latin irregular verbs")
	table.insert(data.categories, "Latin suppletive verbs")

	-- Signal to {{la-verb}} to display the verb as irregular
	typeinfo.subtypes.irreg = true

	local prefix_pres = typeinfo.prefix or ""
	local prefix_perf = ine(args[3])
	local prefix_supine = ine(args[4])

	prefix_perf = prefix_perf or prefix_pres
	prefix_supine = prefix_supine or prefix_pres

	make_pres_3rd(data, typeinfo, prefix_pres .. "fer")
	if prefix_perf == "" then
		make_perf(data, {"tul", "tetul"})
		local noteindex = #(data.footnotes) + 1
		for slot in iter_slots(false, false) do
			if cfind(slot, "perf") or cfind(slot, "plup") or cfind(slot, "futp") then
				data.form_footnote_indices[slot] = tostring(noteindex)
				data.footnotes[noteindex] = 'Archaic.'
			end
		end
	else
		make_perf(data, prefix_perf .. "tul")
	end
	make_supine(data, typeinfo, prefix_supine .. "lāt")

	-- Active imperfective indicative
	data.forms["2s_pres_actv_indc"] = prefix_pres .. "fers"
	data.forms["3s_pres_actv_indc"] = prefix_pres .. "fert"
	data.forms["2p_pres_actv_indc"] = prefix_pres .. "fertis"

	-- Passive imperfective indicative
	data.forms["3s_pres_pasv_indc"] = prefix_pres .. "fertur"

	-- Active imperfective subjunctive
	data.forms["1s_impf_actv_subj"] = prefix_pres .. "ferrem"
	data.forms["2s_impf_actv_subj"] = prefix_pres .. "ferrēs"
	data.forms["3s_impf_actv_subj"] = prefix_pres .. "ferret"
	data.forms["1p_impf_actv_subj"] = prefix_pres .. "ferrēmus"
	data.forms["2p_impf_actv_subj"] = prefix_pres .. "ferrētis"
	data.forms["3p_impf_actv_subj"] = prefix_pres .. "ferrent"

	-- Passive present indicative
	data.forms["2s_pres_pasv_indc"] = {prefix_pres .. "ferris", prefix_pres .. "ferre"}

	-- Passive imperfective subjunctive
	data.forms["1s_impf_pasv_subj"] = prefix_pres .. "ferrer"
	data.forms["2s_impf_pasv_subj"] = {prefix_pres .. "ferrēris", prefix_pres .. "ferrēre"}
	data.forms["3s_impf_pasv_subj"] = prefix_pres .. "ferrētur"
	data.forms["1p_impf_pasv_subj"] = prefix_pres .. "ferrēmur"
	data.forms["2p_impf_pasv_subj"] = prefix_pres .. "ferrēminī"
	data.forms["3p_impf_pasv_subj"] = prefix_pres .. "ferrentur"

	-- Imperative
	data.forms["2s_pres_actv_impr"] = prefix_pres .. "fer"
	data.forms["2p_pres_actv_impr"] = prefix_pres .. "ferte"

	data.forms["2s_futr_actv_impr"] = prefix_pres .. "fertō"
	data.forms["3s_futr_actv_impr"] = prefix_pres .. "fertō"
	data.forms["2p_futr_actv_impr"] = prefix_pres .. "fertōte"

	data.forms["2s_pres_pasv_impr"] = prefix_pres .. "ferre"

	data.forms["2s_futr_pasv_impr"] = prefix_pres .. "fertor"
	data.forms["3s_futr_pasv_impr"] = prefix_pres .. "fertor"

	-- Present infinitives
	data.forms["pres_actv_inf"] = prefix_pres .. "ferre"
	data.forms["pres_pasv_inf"] = prefix_pres .. "ferrī"
end

irreg_conjugations["inquam"] = function(args, data, typeinfo)
	table.insert(data.title, "[[Appendix:Latin irregular verbs|irregular]]")
	table.insert(data.title, "highly [[defective verb|defective]]")
	table.insert(data.categories, "Latin irregular verbs")
	table.insert(data.categories, "Latin defective verbs")

	-- Signal to {{la-verb}} to display the verb as highly defective
	-- (it already displays as irregular because conj == "irreg" and
	-- subconj == "irreg")
	typeinfo.subtypes.highlydef = true

	data.forms["1s_pres_actv_indc"] = "inquam"
	data.forms["2s_pres_actv_indc"] = "inquis"
	data.forms["3s_pres_actv_indc"] = "inquit"
	data.forms["1p_pres_actv_indc"] = "inquimus"
	data.forms["2p_pres_actv_indc"] = "inquitis"
	data.forms["3p_pres_actv_indc"] = "inquiunt"

	data.forms["2s_futr_actv_indc"] = "inquiēs"
	data.forms["3s_futr_actv_indc"] = "inquiet"

	data.forms["3s_impf_actv_indc"] = "inquiēbat"

	data.forms["1s_perf_actv_indc"] = "inquiī"
	data.forms["2s_perf_actv_indc"] = "inquistī"
	data.forms["3s_perf_actv_indc"] = "inquit"

	data.forms["3s_pres_actv_subj"] = "inquiat"

	data.forms["2s_pres_actv_impr"] = "inque"
	data.forms["2s_futr_actv_impr"] = "inquitō"
	data.forms["3s_futr_actv_impr"] = "inquitō"

	data.forms["pres_actv_ptc"] = "inquiēns"
end

local function libet_lubet(data, typeinfo, stem)
	table.insert(data.title, "[[Appendix:Latin second conjugation|second conjugation]]")
	table.insert(data.title, "mostly [[impersonal]]")
	table.insert(data.categories, "Latin second conjugation verb")
	table.insert(data.categories, "Latin impersonal verbs")

	typeinfo.subtypes.nopass = true
	local prefix = typeinfo.prefix or ""

	stem = prefix .. stem

	-- Active imperfective indicative
	data.forms["3s_pres_actv_indc"] = stem .. "et"

	data.forms["3s_impf_actv_indc"] = stem .. "ēbat"

	data.forms["3s_futr_actv_indc"] = stem .. "ēbit"

	-- Active perfective indicative
	data.forms["3s_perf_actv_indc"] = {stem .. "uit", "[[" .. stem .. "itum]] [[est]]"}

	data.forms["3s_plup_actv_indc"] = {stem .. "uerat", "[[" .. stem .. "itum]] [[erat]]"}

	data.forms["3s_futp_actv_indc"] = {stem .. "uerit", "[[" .. stem .. "itum]] [[erit]]"}

	-- Active imperfective subjunctive
	data.forms["3s_pres_actv_subj"] = stem .. "eat"

	data.forms["3s_impf_actv_subj"] = stem .. "ēret"

	-- Active perfective subjunctive
	data.forms["3s_perf_actv_subj"] = {stem .. "uerit", "[[" .. stem .. "itum]] [[sit]]"}

	data.forms["3s_plup_actv_subj"] = {stem .. "uisset", "[[" .. stem .. "itum]] [[esset]]"}
	data.forms["3p_plup_actv_subj"] = stem .. "uissent"

	-- Present infinitives
	data.forms["pres_actv_inf"] = stem .. "ēre"

	-- Perfect infinitive
	data.forms["perf_actv_inf"] = {stem .. "uisse", "[[" .. stem .. "itum]] [[esse]]"}

	-- Imperfective participles
	data.forms["pres_actv_ptc"] = stem .. "ēns"
	data.forms["perf_actv_ptc"] = stem .. "itum"
end

irreg_conjugations["libet"] = function(args, data, typeinfo)
	libet_lubet(data, typeinfo, "lib")
end

irreg_conjugations["lubet"] = function(args, data, typeinfo)
	libet_lubet(data, typeinfo, "lub")
end

irreg_conjugations["licet"] = function(args, data, typeinfo)
	table.insert(data.title, "[[Appendix:Latin second conjugation|second conjugation]]")
	table.insert(data.title, "mostly [[impersonal]]")
	table.insert(data.categories, "Latin second conjugation verb")
	table.insert(data.categories, "Latin impersonal verbs")

	typeinfo.subtypes.nopass = true

	-- Active imperfective indicative
	data.forms["3s_pres_actv_indc"] = "licet"
	data.forms["3p_pres_actv_indc"] = "licent"

	data.forms["3s_impf_actv_indc"] = "licēbat"
	data.forms["3p_impf_actv_indc"] = "licēbant"

	data.forms["3s_futr_actv_indc"] = "licēbit"

	-- Active perfective indicative
	data.forms["3s_perf_actv_indc"] = {"licuit", "[[licitum]] [[est]]"}

	data.forms["3s_plup_actv_indc"] = {"licuerat", "[[licitum]] [[erat]]"}

	data.forms["3s_futp_actv_indc"] = {"licuerit", "[[licitum]] [[erit]]"}

	-- Active imperfective subjunctive
	data.forms["3s_pres_actv_subj"] = "liceat"
	data.forms["3p_pres_actv_subj"] = "liceant"

	data.forms["3s_impf_actv_subj"] = "licēret"

	-- Perfective subjunctive
	data.forms["3s_perf_actv_subj"] = {"licuerit", "[[licitum]] [[sit]]"}

	data.forms["3s_plup_actv_subj"] = {"licuisset", "[[licitum]] [[esset]]"}

	-- Imperative
	data.forms["2s_futr_actv_impr"] = "licētō"
	data.forms["3s_futr_actv_impr"] = "licētō"

	-- Infinitives
	data.forms["pres_actv_inf"] = "licēre"
	data.forms["perf_actv_inf"] = {"licuisse", "[[licitum]] [[esse]]"}
	data.forms["futr_actv_inf"] = "[[licitūrum]] [[esse]]"

	-- Participles
	data.forms["pres_actv_ptc"] = "licēns"
	data.forms["perf_actv_ptc"] = "licitus"
	data.forms["futr_actv_ptc"] = "licitūrus"
end

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

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

	-- Present infinitives
	data.forms["pres_actv_inf"] = subj_stem .. "le"

	-- Imperfective participles
	data.forms["pres_actv_ptc"] = indc_stem .. "ēns"
end

irreg_conjugations["volo"] = function(args, data, typeinfo)
	table.insert(data.title, "[[Appendix:Latin irregular verbs|irregular]]")
	table.insert(data.title, "[[suppletive]] in the second-person singular indicative present")
	table.insert(data.categories, "Latin irregular verbs")
	table.insert(data.categories, "Latin suppletive verbs")

	local prefix = typeinfo.prefix or ""

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

	-- Active imperfective indicative
	add_forms(data, "pres_actv_indc", prefix,
		"volō", "vīs", prefix ~= "" and "vult" or {"vult", "volt"},
		"volumus", prefix ~= "" and "vultis" or {"vultis", "voltis"}, "volunt")
	volo_malo_nolo(data, prefix .. "vol", prefix .. "vel")
end

irreg_conjugations["malo"] = function(args, data, typeinfo)
	table.insert(data.title, "[[Appendix:Latin irregular verbs|irregular]]")
	table.insert(data.title, "[[suppletive]] in the second-person singular indicative present")
	table.insert(data.categories, "Latin irregular verbs")
	table.insert(data.categories, "Latin suppletive verbs")

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

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

irreg_conjugations["nolo"] = function(args, data, typeinfo)
	table.insert(data.title, "[[Appendix:Latin irregular verbs|irregular]]")
	table.insert(data.title, "[[suppletive]] in the second-person singular indicative present")
	table.insert(data.categories, "Latin irregular verbs")
	table.insert(data.categories, "Latin suppletive verbs")

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

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

	-- Imperative
	add_2_forms(data, "pres_actv_impr", "nōlī", "", "te")
	add_23_forms(data, "futr_actv_impr", "nōl", "itō", "itō", "itōte", "untō")
end

irreg_conjugations["possum"] = function(args, data, typeinfo)
	table.insert(data.title, "highly [[Appendix:Latin irregular verbs|irregular]]")
	table.insert(data.title, "[[suppletive]]")
	table.insert(data.categories, "Latin irregular verbs")
	table.insert(data.categories, "Latin suppletive verbs")

	typeinfo.subtypes.nopass = true
	make_perf(data, "potu")

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

	-- Active imperfective subjunctive
	add_forms(data, "pres_actv_subj", "poss", "im", "īs", "it", "īmus", "ītis", "int")
	add_forms(data, "impf_actv_subj", "poss", "em", "ēs", "et", "ēmus", "ētis", "ent")

	-- Present infinitives
	data.forms["pres_actv_inf"] = "posse"

	-- Imperfective participles
	data.forms["pres_actv_ptc"] = "potēns"
end

irreg_conjugations["piget"] = function(args, data, typeinfo)
	table.insert(data.title, "[[Appendix:Latin second conjugation|second conjugation]]")
	table.insert(data.title, "[[impersonal]]")
	table.insert(data.title, "[[semi-deponent]]")
	table.insert(data.categories, "Latin second conjugation verb")
	table.insert(data.categories, "Latin impersonal verbs")
	table.insert(data.categories, "Latin semi-deponent verbs")
	table.insert(data.categories, "Latin defective verbs")

	local prefix = typeinfo.prefix or ""

	--[[
	-- not used
	local ppplink = make_link({lang = lang, alt = '', term = prefix .. "ausus"}, "term")
	local sumlink = make_link({lang = lang, alt = '', term = "sum"}, "term")
	--]]

	data.forms["3s_pres_actv_indc"] = prefix .. "piget"

	data.forms["3s_impf_actv_indc"] = prefix .. "pigēbat"

	data.forms["3s_futr_actv_indc"] = prefix .. "pigēbit"

	data.forms["3s_perf_actv_indc"] = {prefix .. "piguit", "[[" .. prefix .. "pigitum]] [[est]]"}

	data.forms["3s_plup_actv_indc"] = {prefix .. "piguerat", "[[" .. prefix .. "pigitum]] [[erat]]"}

	data.forms["3s_futp_actv_indc"] = {prefix .. "piguerit", "[[" .. prefix .. "pigitum]] [[erit]]"}

	data.forms["3s_pres_actv_subj"] = prefix .. "pigeat"

	data.forms["3s_impf_actv_subj"] = prefix .. "pigēret"

	data.forms["3s_perf_actv_subj"] = {prefix .. "piguerit", "[[" .. prefix .. "pigitum]] [[sit]]"}

	data.forms["3s_plup_actv_subj"] = {prefix .. "piguisset", "[[" .. prefix .. "pigitum]] [[esset]]"}

	data.forms["pres_actv_inf"] = prefix .. "pigēre"
	data.forms["perf_actv_inf"] = "[[" .. prefix .. "pigitum]] [[esse]]"
	data.forms["pres_actv_ptc"] = prefix .. "pigēns"
	data.forms["perf_actv_ptc"] = prefix .. "pigitum"

	-- Gerund
	make_gerund(data, typeinfo, prefix .. "pigend")
end

irreg_conjugations["coepi"] = function(args, data, typeinfo)
	table.insert(data.title, "[[Appendix:Latin third conjugation|third conjugation]]")
	table.insert(data.title, "no [[present tense|present]] stem")
	table.insert(data.categories, "Latin third conjugation verb")
	table.insert(data.categories, "Latin verbs with missing present stem")
	table.insert(data.categories, "Latin defective verbs")

	local prefix = typeinfo.prefix or ""

	make_perf(data, prefix .. "coep")
	make_supine(data, typeinfo, prefix .. "coept")
	make_perfect_passive(data)
end

-- The vowel of the prefix is lengthened if it ends in -n and the next word begins with f- or s-.
local function lengthen_prefix(prefix)
	return prefix:gsub("([aeiou]n)$", {["an"] = "ān", ["en"] = "ēn", ["in"] = "īn", ["on"] = "ōn", ["un"] = "ūn"})
end

irreg_conjugations["sum"] = function(args, data, typeinfo)
	table.insert(data.title, "highly [[Appendix:Latin irregular verbs|irregular]]")
	table.insert(data.title, "[[suppletive]]")
	table.insert(data.categories, "Latin irregular verbs")
	table.insert(data.categories, "Latin suppletive verbs")

	local prefix = typeinfo.prefix or ""
	local prefix_e = ine(args[3]) or prefix
	local prefix_f = lengthen_prefix(ine(args[4]) or prefix)
	local prefix_s = lengthen_prefix(prefix)

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

	-- Active imperfective indicative
	data.forms["1s_pres_actv_indc"] = prefix_s .. "sum"
	data.forms["2s_pres_actv_indc"] = prefix_e .. "es"
	data.forms["3s_pres_actv_indc"] = prefix_e .. "est"
	data.forms["1p_pres_actv_indc"] = prefix_s .. "sumus"
	data.forms["2p_pres_actv_indc"] = prefix_e .. "estis"
	data.forms["3p_pres_actv_indc"] = prefix_s .. "sunt"

	data.forms["1s_impf_actv_indc"] = prefix_e .. "eram"
	data.forms["2s_impf_actv_indc"] = prefix_e .. "erās"
	data.forms["3s_impf_actv_indc"] = prefix_e .. "erat"
	data.forms["1p_impf_actv_indc"] = prefix_e .. "erāmus"
	data.forms["2p_impf_actv_indc"] = prefix_e .. "erātis"
	data.forms["3p_impf_actv_indc"] = prefix_e .. "erant"

	data.forms["1s_futr_actv_indc"] = prefix_e .. "erō"
	data.forms["2s_futr_actv_indc"] = {prefix_e .. "eris", prefix_e .. "ere"}
	data.forms["3s_futr_actv_indc"] = prefix_e .. "erit"
	data.forms["1p_futr_actv_indc"] = prefix_e .. "erimus"
	data.forms["2p_futr_actv_indc"] = prefix_e .. "eritis"
	data.forms["3p_futr_actv_indc"] = prefix_e .. "erunt"

	-- Active imperfective subjunctive
	data.forms["1s_pres_actv_subj"] = prefix_s .. "sim"
	data.forms["2s_pres_actv_subj"] = prefix_s .. "sīs"
	data.forms["3s_pres_actv_subj"] = prefix_s .. "sit"
	data.forms["1p_pres_actv_subj"] = prefix_s .. "sīmus"
	data.forms["2p_pres_actv_subj"] = prefix_s .. "sītis"
	data.forms["3p_pres_actv_subj"] = prefix_s .. "sint"
	if prefix_s == "ad" then
		local noteindex = #(data.footnotes) + 1
		add_form(data, "3p_pres_actv_subj", "", "adessint", 2 )
		data.form_footnote_indices["3p_pres_actv_subj"] = tostring(noteindex)
		data.footnotes[noteindex] = 'Archaic.'
	end

	data.forms["1s_impf_actv_subj"] = {prefix_e .. "essem", prefix_f .. "forem"}
	data.forms["2s_impf_actv_subj"] = {prefix_e .. "essēs", prefix_f .. "forēs"}
	data.forms["3s_impf_actv_subj"] = {prefix_e .. "esset", prefix_f .. "foret"}
	data.forms["1p_impf_actv_subj"] = {prefix_e .. "essēmus", prefix_f .. "forēmus"}
	data.forms["2p_impf_actv_subj"] = {prefix_e .. "essētis", prefix_f .. "forētis"}
	data.forms["3p_impf_actv_subj"] = {prefix_e .. "essent", prefix_f .. "forent"}

	-- Imperative
	data.forms["2s_pres_actv_impr"] = prefix_e .. "es"
	data.forms["2p_pres_actv_impr"] = prefix_e .. "este"

	data.forms["2s_futr_actv_impr"] = prefix_e .. "estō"
	data.forms["3s_futr_actv_impr"] = prefix_e .. "estō"
	data.forms["2p_futr_actv_impr"] = prefix_e .. "estōte"
	data.forms["3p_futr_actv_impr"] = prefix_s .. "suntō"

	-- Present infinitives
	data.forms["pres_actv_inf"] = prefix_e .. "esse"

	-- Future infinitives
	data.forms["futr_actv_inf"] = {"[[" .. prefix_f .. "futūrum]] [[esse]]", prefix_f .. "fore"}

	-- Imperfective participles
	if prefix == "ab" then
		data.forms["pres_actv_ptc"] = "absēns"
	elseif prefix == "prae" then
		data.forms["pres_actv_ptc"] = "praesēns"
	end

	-- Gerund
	data.forms["ger_gen"] = nil
	data.forms["ger_dat"] = nil
	data.forms["ger_acc"] = nil
	data.forms["ger_abl"] = nil

	-- Supine
	data.forms["sup_acc"] = nil
	data.forms["sup_abl"] = nil
end


-- Form-generating functions

make_pres_1st = function(data, typeinfo, pres_stem)
	if not pres_stem then
		return
	end

	-- Active imperfective indicative
	add_forms(data, "pres_actv_indc", pres_stem, "ō", "ās", "at", "āmus", "ātis", "ant")
	add_forms(data, "impf_actv_indc", pres_stem, "ābam", "ābās", "ābat", "ābāmus", "ābātis", "ābant")
	add_forms(data, "futr_actv_indc", pres_stem, "ābō", "ābis", "ābit", "ābimus", "ābitis", "ābunt")

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

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

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

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

	add_2_forms(data, "pres_pasv_impr", pres_stem, "āre", "āminī")
	add_23_forms(data, "futr_pasv_impr", pres_stem, "ātor", "ātor", {}, "antor")

	-- Present infinitives
	data.forms["pres_actv_inf"] = pres_stem .. "āre"
	data.forms["pres_pasv_inf"] = pres_stem .. "ārī"

	-- Imperfective participles
	data.forms["pres_actv_ptc"] = pres_stem .. "āns"

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

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

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

	-- Active imperative
	if not noimpr then
		add_2_forms(data, "pres_actv_impr", pres_stem, "ē", "ēte")
		add_23_forms(data, "futr_actv_impr", pres_stem, "ētō", "ētō", "ētōte", "entō")
	end

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

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

		-- Passive imperative
		if not noimpr then
			add_2_forms(data, "pres_pasv_impr", pres_stem, "ēre", "ēminī")
			add_23_forms(data, "futr_pasv_impr", pres_stem, "ētor", "ētor", {}, "entor")
		end
	end

	-- Present infinitives
	data.forms["pres_actv_inf"] = pres_stem .. "ēre"
	if not nopass then
		data.forms["pres_pasv_inf"] = pres_stem .. "ērī"
	end

	-- Imperfective participles
	data.forms["pres_actv_ptc"] = pres_stem .. "ēns"

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

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

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

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

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

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

	add_2_forms(data, "pres_pasv_impr", pres_stem, "ere", "iminī")
	add_23_forms(data, "futr_pasv_impr", pres_stem, "itor", "itor", {}, "untor")

	-- Present infinitives
	data.forms["pres_actv_inf"] = pres_stem .. "ere"
	data.forms["pres_pasv_inf"] = pres_stem .. "ī"

	-- Imperfective participles
	data.forms["pres_actv_ptc"] = pres_stem .. "ēns"

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

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

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

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

	-- Passive imperfective indicative
	if not nopass then
		add_forms(data, "pres_pasv_indc", pres_stem, "ior", {"eris", "ere"}, "itur", "imur", "iminī", "iuntur")
		add_forms(data, "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(data, "futr_pasv_indc", pres_stem, "iar", {"iēris", "iēre"}, "iētur", "iēmur", "iēminī", "ientur")

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

		-- Passive imperative
		add_2_forms(data, "pres_pasv_impr", pres_stem, "ere", "iminī")
		add_23_forms(data, "futr_pasv_impr", pres_stem, "itor", "itor", {}, "iuntor")
	end

	-- Present infinitives
	data.forms["pres_actv_inf"] = pres_stem .. "ere"
	if not nopass then
		data.forms["pres_pasv_inf"] = pres_stem .. "ī"
	end

	-- Imperfective participles
	data.forms["pres_actv_ptc"] = pres_stem .. "iēns"

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

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

	-- Passive imperfective indicative
	add_forms(data, "pres_pasv_indc", pres_stem, "ior", {"īris", "īre"}, "ītur", "īmur", "īminī", "iuntur")
	add_forms(data, "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(data, "futr_pasv_indc", pres_stem, "iar", {"iēris", "iēre"}, "iētur", "iēmur", "iēminī", "ientur")

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

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

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

	add_2_forms(data, "pres_pasv_impr", pres_stem, "īre", "īminī")
	add_23_forms(data, "futr_pasv_impr", pres_stem, "ītor", "ītor", {}, "iuntor")

	-- Present infinitives
	data.forms["pres_actv_inf"] = pres_stem .. "īre"
	data.forms["pres_pasv_inf"] = pres_stem .. "īrī"

	-- Imperfective participles
	data.forms["pres_actv_ptc"] = pres_stem .. "iēns"

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

make_perf_and_supine = function(data, typeinfo)
	if typeinfo.subtypes.optsemidepon then
		make_perf(data, typeinfo.perf_stem, "noinf")
		make_deponent_perf(data, typeinfo.supine_stem)
	else
		make_perf(data, typeinfo.perf_stem)
		make_supine(data, typeinfo, typeinfo.supine_stem)
	end
end

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

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

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

make_deponent_perf = function(data, supine_stem)
	if not supine_stem then
		return
	end
	if type(supine_stem) ~= "table" then
		supine_stem = {supine_stem}
	end

	-- Perfect/future infinitives
	for _, stem in ipairs(supine_stem) do
		local stems = "[[" .. stem .. "us]] "
		local stemp = "[[" .. stem .. "ī]] "

		add_forms(data, "perf_actv_indc", stems, "[[sum]]", "[[es]]", "[[est]]", {}, {}, {})
		add_forms(data, "perf_actv_indc", stemp, {}, {}, {}, "[[sumus]]", "[[estis]]", "[[sunt]]")

		add_forms(data, "plup_actv_indc", stems, "[[eram]]", "[[erās]]", "[[erat]]", {}, {}, {})
		add_forms(data, "plup_actv_indc", stemp, {}, {}, {}, "[[erāmus]]", "[[erātis]]", "[[erant]]")

		add_forms(data, "futp_actv_indc", stems, "[[erō]]", "[[eris]]", "[[erit]]", {}, {}, {})
		add_forms(data, "futp_actv_indc", stemp, {}, {}, {}, "[[erimus]]", "[[eritis]]", "[[erint]]")

		add_forms(data, "perf_actv_subj", stems, "[[sim]]", "[[sīs]]", "[[sit]]", {}, {}, {})
		add_forms(data, "perf_actv_subj", stemp, {}, {}, {}, "[[sīmus]]", "[[sītis]]", "[[sint]]")

		add_forms(data, "plup_actv_subj", stems, "[[essem]]", "[[essēs]]", "[[esset]]", {}, {}, {})
		add_forms(data, "plup_actv_subj", stemp, {}, {}, {}, "[[essēmus]]", "[[essētis]]", "[[essent]]")

		add_form(data, "perf_actv_inf", "", "[[" .. stem .. "um]] [[esse]]")
		add_form(data, "futr_actv_inf", "", "[[" .. stem .. "ūrum]] [[esse]]")
		add_form(data, "perf_actv_ptc", stem, "us")
		add_form(data, "futr_actv_ptc", stem, "ūrus")

		-- Supine
		add_form(data, "sup_acc", stem, "um")
		add_form(data, "sup_abl", stem, "ū")
	end
end

make_supine = function(data, typeinfo, supine_stem)
	if not supine_stem then
		return
	end
	if type(supine_stem) ~= "table" then
		supine_stem = {supine_stem}
	end

	-- Perfect/future infinitives
	for _, stem in ipairs(supine_stem) do
		local futr_actv_inf, perf_pasv_inf, futr_pasv_inf, futr_actv_ptc
		local perf_pasv_ptc_lemma, perf_actv_ptc, perf_actv_ptc_acc
		-- Perfect/future participles
		futr_actv_ptc = stem .. "ūrus"
		if typeinfo.subtypes.passimpers then
			perf_pasv_ptc_lemma = stem .. "um"
			perf_pasv_ptc = perf_pasv_ptc_lemma
			perf_pasv_ptc_acc = perf_pasv_ptc_lemma
		else
			perf_pasv_ptc_lemma = stem .. "us"
			if typeinfo.subtypes.mp then
				perf_pasv_ptc = stem .. "ī"
				perf_pasv_ptc_acc = stem .. "ōs"
			elseif typeinfo.subtypes.fp then
				perf_pasv_ptc = stem .. "ae"
				perf_pasv_ptc_acc = stem .. "ās"
			elseif typeinfo.subtypes.np then
				perf_pasv_ptc = stem .. "a"
				perf_pasv_ptc_acc = perf_pasv_ptc
			elseif typeinfo.subtypes.f then
				perf_pasv_ptc = stem .. "a"
				perf_pasv_ptc_acc = stem .. "am"
			elseif typeinfo.subtypes.n then
				perf_pasv_ptc = stem .. "um"
				perf_pasv_ptc_acc = perf_pasv_ptc
			else
				perf_pasv_ptc = perf_pasv_ptc_lemma
				perf_pasv_ptc_acc = stem .. "um"
			end
		end

		perf_pasv_inf = make_raw_link(perf_pasv_ptc_lemma,
			perf_pasv_ptc_acc ~= perf_pasv_ptc_lemma and perf_pasv_ptc_acc or nil) .. " [[esse]]"
		futr_pasv_inf = make_raw_link(stem .. "um") .. " [[īrī]]"

		-- Exceptions
		local mortu = {
			["conmortu"] = true,
			["commortu"] = true,
			["dēmortu"] = true,
			["ēmortu"] = true,
			["inmortu"] = true,
			["immortu"] = true,
			["inēmortu"] = true,
			["intermortu"] = true,
			["permortu"] = true,
			["praemortu"] = true,
			["superēmortu"] = true
		}
		local ort = {
			["ort"] = true,
			["abort"] = true,
			["adort"] = true,
			["coort"] = true,
			["exort"] = true,
			["hort"] = true,
			["obort"] = true
		}
		if mortu[stem] then
			futr_actv_ptc = stem:gsub("mortu$", "moritūrus")
		elseif ort[stem] then
			futr_actv_ptc = stem:gsub("ort$", "oritūrus")
		elseif stem == "mortu" then
			-- FIXME, are we sure about this?
			futr_actv_inf = {}
			futr_actv_ptc = "moritūrus"
		end

		if not futr_actv_inf then
			futr_actv_inf = make_raw_link(futr_actv_ptc, futr_actv_ptc:gsub("us$", "um")) .. " [[esse]]"
		end

		add_form(data, "futr_actv_inf", "", futr_actv_inf)
		add_form(data, "perf_pasv_inf", "", perf_pasv_inf)
		add_form(data, "futr_pasv_inf", "", futr_pasv_inf)
		add_form(data, "futr_actv_ptc", "", futr_actv_ptc)
		add_form(data, "perf_pasv_ptc", "", perf_pasv_ptc)

		-- Supine itself
		add_form(data, "sup_acc", stem, "um")
		add_form(data, "sup_abl", stem, "ū")
	end
end

make_sigm = function(data, typeinfo, sigm_stem)
	if not (typeinfo.subtypes.sigm or typeinfo.subtypes.sigmpasv) then
		return
	end
	if type(sigm_stem) ~= "table" then
		sigm_stem = {sigm_stem}
	end
	local noteindex = #(data.footnotes) + 1
	
	for _, stem in ipairs(sigm_stem) do
		-- Deponent verbs use the passive form
		if typeinfo.subtypes.depon then
			add_form(data, "1s_sigf_actv_indc", stem, "or")
			add_form(data, "2s_sigf_actv_indc", stem, "eris")
			add_form(data, "3s_sigf_actv_indc", stem, "itur")
		else
			for _, stem in ipairs(sigm_stem) do
				-- Sigmatic future active indicative
				add_forms(data, "sigf_actv_indc", stem, "ō", "is", "it", "imus", "itis", "int")
				-- Sigmatic future passive indicative (option)
				if typeinfo.subtypes.sigmpasv then
					add_form(data, "1s_sigf_pasv_indc", stem, "or")
					add_form(data, "2s_sigf_pasv_indc", stem, "eris")
					add_form(data, "3s_sigf_pasv_indc", stem, "itur")
				end
				-- Sigmatic future active subjunctive
				add_forms(data, "siga_actv_subj", stem, "im", "īs", "īt", "īmus", "ītis", "int")
				-- Perfect infinitive
				if not no_inf then
					add_form(data, "sigm_actv_inf", stem, "ere")
				end
			end
		end
	end
	data.form_footnote_indices["sigm"] = noteindex
	if (typeinfo.subtypes.sigm or typeinfo.subtypes.sigmpasv) and not (typeinfo.subtypes.depon) then
		data.footnotes[noteindex] = 'At least one use of the archaic \"sigmatic future\" and \"sigmatic aorist\" tenses is attested, which are used by [[Old Latin]] writers; most notably [[w:Plautus|Plautus]] and [[w:Terence|Terence]]. The sigmatic future is generally ascribed a future or future perfect meaning, while the sigmatic aorist expresses a possible desire (\"might want to\").'
		if typeinfo.subtypes.sigmpasv then
			data.footnotes[noteindex] = data.footnotes[noteindex] .. ' 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
	elseif typeinfo.subtypes.depon then
		data.footnotes[noteindex] = 'At least one use of the archaic \"sigmatic future\" tense is attested, which is used by [[Old Latin]] writers; most notably [[w:Plautus|Plautus]] and [[w:Terence|Terence]]. 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).'
	end
end

-- 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(form, accel)
	if not form then
		return "&mdash;"
	end

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

	for key, subform in ipairs(form) do
		if form_is_empty(subform) then
			form[key] = "&mdash;"
		elseif reconstructed and not subform:find(NAMESPACE .. ":Latin/") then
			form[key] = make_link({lang = lang, alt = '', term = NAMESPACE .. ":Latin/" .. subform, alt = subform})
		elseif subform:find("[%[%]]") 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[key] = make_link({ lang = lang, alt = '' .. subform })
		else
			form[key] = make_link({ lang = lang, alt = '' .. subform })
		end
	end

	return table.concat(form, ",<br> ")
end

parts_to_tags = {
  ['1s'] = {'1', 's'},
  ['2s'] = {'2', 's'},
  ['3s'] = {'3', 's'},
  ['1p'] = {'1', 'p'},
  ['2p'] = {'2', 'p'},
  ['3p'] = {'3', 'p'},
  ['actv'] = {'act'},
  ['pasv'] = {'pass'},
  ['pres'] = {'pres'},
  ['impf'] = {'impf'},
  ['futr'] = {'fut'},
  ['perf'] = {'perf'},
  ['plup'] = {'plup'},
  ['futp'] = {'futp'},
  ['sigm'] = {'sigm'},
  ['sigf'] = {'sigm', 'fut'},
  ['siga'] = {'sigm', 'aor'},
  ['indc'] = {'ind'},
  ['subj'] = {'sub'},
  ['impr'] = {'imp'},
  ['inf'] = {'inf'},
  ['ptc'] = {'part'},
  ['ger'] = {'ger'},
  ['sup'] = {'sup'},
  ['nom'] = {'nom'},
  ['gen'] = {'gen'},
  ['dat'] = {'dat'},
  ['acc'] = {'acc'},
  ['abl'] = {'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 = data.actual_lemma[1]
	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[part]) do
				table.insert(tags, tag)
			end
		end

		-- put the case first for verbal nouns
		local accel_slot
		if tags[1] == "sup" or tags[1] == "ger" then
			accel_slot = tags[2] .. "|" .. tags[1]
		else
			accel_slot = table.concat(tags, "|")
		end

		local accel = {form = accel_slot, lemma = accel_lemma}
		data.forms[slot] = show_form(data.forms[slot], 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
				table.insert(valid_forms, subform)
			end
		end
	end
	return valid_forms
end

function export.get_lemma_forms(data, do_linked)
	local linked_prefix = do_linked and "linked_" or ""
	for _, slot in ipairs(potential_lemma_slots) do
		local lemma_forms = export.get_valid_forms(data.forms[linked_prefix .. slot])
		if #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
		table.insert(lemma_links, make_link({lang = lang, alt = '', alt = subform}, "term"))
	end
	return table.concat(lemma_links, ", ")
end

-- Make the table
make_table = function(data)
	local pagename = PAGENAME
	if reconstructed then
		pagename = pagename:gsub("Latin/","")
	end
	data.actual_lemma = export.get_lemma_forms(data)
	convert_forms_into_links(data)

	return [=[
{| style="width: 100%; background: #EEE; border: 1px solid #AAA; font-size: 95%; text-align: center;" class="inflection-table vsSwitcher" data-toggle-category="inflection"
|-
! colspan="8" class="vsToggleElement" style="background: #CCC; text-align: left;" | &nbsp;&nbsp;&nbsp;Conjugation of ]=] .. get_displayable_lemma(data.actual_lemma) .. (#data.title > 0 and " (" .. table.concat(data.title, ", ") .. ")" or "") .. [=[

]=] .. make_indc_rows(data) .. make_subj_rows(data) .. make_impr_rows(data) .. make_nonfin_rows(data) .. make_vn_rows(data) .. [=[

|}]=].. make_footnotes(data)

end

local tenses = {
	["pres"] = "present",
	["impf"] = "imperfect",
	["futr"] = "future",
	["perf"] = "perfect",
	["plup"] = "pluperfect",
	["futp"] = "future&nbsp;perfect",
	["sigf"] = "sigmatic&nbsp;future",
	["siga"] = "sigmatic&nbsp;aorist"
}

local voices = {
	["actv"] = "active",
	["pasv"] = "passive",
}

--[[
local moods = {
	["indc"] = "indicative",
	["subj"] = "subjunctive",
	["impr"] = "imperative",
}
--]]

local nonfins = {
	["inf"] = "infinitives",
	["ptc"] = "participles",
}

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

--[[
local cases = {
	["nom"] = "nominative",
	["gen"] = "genitive",
	["dat"] = "dative",
	["acc"] = "accusative",
	["abl"] = "ablative",
}
--]]

make_indc_rows = function(data)
	local indc = {}

	for _, v in ipairs({"actv", "pasv"}) do
		local group = {}
		local nonempty = false
		
		for _, t in ipairs({"pres", "impf", "futr", "perf", "plup", "futp", "sigf"}) do
			local row = {}
			local notempty = false

			if data.forms[t .. "_" .. v .. "_indc"] then
				row = "\n! colspan=\"6\" style=\"background: #CCC\" |" .. data.forms[t .. "_" .. v .. "_indc"]
				nonempty = true
				notempty = true
			else
				for col, p in ipairs({"1s", "2s", "3s", "1p", "2p", "3p"}) do
					local slot = p .. "_" .. t .. "_" .. v .. "_indc"
					row[col] = "\n| " .. data.forms[slot] .. (
						data.form_footnote_indices[slot] == nil and "" or
						'<sup style="color: red">' .. data.form_footnote_indices[slot].."</sup>"
					)

					-- show_form() already called so can just check for "&mdash;"
					if data.forms[slot] ~= "&mdash;" then
						nonempty = true
						notempty = true
					end
				end

				row = table.concat(row)
			end
			
			local fn
			if t == "sigf" and data.form_footnote_indices["sigm"] then
				fn = '<sup style="color: red">' .. data.form_footnote_indices["sigm"].."</sup>"
			else
				fn = ""
			end
			
			if notempty then
				table.insert(group, "\n! style=\"background:#c0cfe4\" | " .. tenses[t] ..  fn .. row)
			end
		end

		if nonempty and #group > 0 then
			table.insert(indc, "\n|- class=\"vsHide\"\n! rowspan=\"" .. tostring(#group) .. "\" style=\"background:#c0cfe4\" | " .. voices[v] .. "\n" .. table.concat(group, "\n|- class=\"vsHide\""))
		end
	end

	return
[=[

|- class="vsHide"
! colspan="2" rowspan="2" style="background:#c0cfe4" | indicative
! colspan="3" style="background:#c0cfe4" | ''singular''
! colspan="3" style="background:#c0cfe4" | ''plural''
|- class="vsHide"
! style="background:#c0cfe4;width:12.5%" | [[first person|first]]
! style="background:#c0cfe4;width:12.5%" | [[second person|second]]
! style="background:#c0cfe4;width:12.5%" | [[third person|third]]
! style="background:#c0cfe4;width:12.5%" | [[first person|first]]
! style="background:#c0cfe4;width:12.5%" | [[second person|second]]
! style="background:#c0cfe4;width:12.5%" | [[third person|third]]
]=] .. table.concat(indc)

end

make_subj_rows = function(data)
	local subj = {}

	for _, v in ipairs({"actv", "pasv"}) do
		local group = {}
		local nonempty = false

		for _, t in ipairs({"pres", "impf", "perf", "plup", "siga"}) do
			local row = {}
			local notempty = false

			if data.forms[t .. "_" .. v .. "_subj"] then
				row = "\n! colspan=\"6\" style=\"background: #CCC\" |" .. data.forms[t .. "_" .. v .. "_subj"]
				nonempty = true
				notempty = true
			else
				for col, p in ipairs({"1s", "2s", "3s", "1p", "2p", "3p"}) do
					local slot = p .. "_" .. t .. "_" .. v .. "_subj"
					row[col] = "\n| " .. data.forms[slot] .. (
						data.form_footnote_indices[slot] == nil and "" or
						'<sup style="color: red">' .. data.form_footnote_indices[slot].."</sup>"
					)

					-- show_form() already called so can just check for "&mdash;"
					if data.forms[slot] ~= "&mdash;" then
						nonempty = true
						notempty = true
					end
				end

				row = table.concat(row)
			end
			
			local fn
			if t == "siga" and data.form_footnote_indices["sigm"] then
				fn = '<sup style="color: red">' .. data.form_footnote_indices["sigm"].."</sup>"
			else
				fn = ""
			end

			if notempty then
				table.insert(group, "\n! style=\"background:#c0e4c0\" | " .. tenses[t] .. fn .. row)
			end
		end

		if nonempty and #group > 0 then
			table.insert(subj, "\n|- class=\"vsHide\"\n! rowspan=\"" .. tostring(#group) .. "\" style=\"background:#c0e4c0\" | " .. voices[v] .. "\n" .. table.concat(group, "\n|- class=\"vsHide\""))
		end
	end

	return
[=[

|- class="vsHide"
! colspan="2" rowspan="2" style="background:#c0e4c0" | subjunctive
! colspan="3" style="background:#c0e4c0" | ''singular''
! colspan="3" style="background:#c0e4c0" | ''plural''
|- class="vsHide"
! style="background:#c0e4c0;width:12.5%" | [[first person|first]]
! style="background:#c0e4c0;width:12.5%" | [[second person|second]]
! style="background:#c0e4c0;width:12.5%" | [[third person|third]]
! style="background:#c0e4c0;width:12.5%" | [[first person|first]]
! style="background:#c0e4c0;width:12.5%" | [[second person|second]]
! style="background:#c0e4c0;width:12.5%" | [[third person|third]]
]=] .. table.concat(subj)

end

make_impr_rows = function(data)
	local impr = {}
	local has_impr = false

	for _, v in ipairs({"actv", "pasv"}) do
		local group = {}
		local nonempty = false

		for _, t in ipairs({"pres", "futr"}) do
			local row = {}

			if data.forms[t .. "_" .. v .. "_impr"] then
				row = "\n! colspan=\"6\" style=\"background: #CCC\" |" .. data.forms[t .. "_" .. v .. "_impr"]
				nonempty = true
			else
				for col, p in ipairs({"1s", "2s", "3s", "1p", "2p", "3p"}) do
					local slot = p .. "_" .. t .. "_" .. v .. "_impr"
					row[col] = "\n| " .. data.forms[slot] .. (
						data.form_footnote_indices[slot] == nil and "" or
						'<sup style="color: red">' .. data.form_footnote_indices[slot].."</sup>"
					)

					-- show_form() already called so can just check for "&mdash;"
					if data.forms[slot] ~= "&mdash;" then
						nonempty = true
					end
				end

				row = table.concat(row)
			end

			table.insert(group, "\n! style=\"background:#e4d4c0\" | " .. tenses[t] .. row)
		end

		if nonempty and #group > 0 then
			has_impr = true
			table.insert(impr, "\n|- class=\"vsHide\"\n! rowspan=\"" .. tostring(#group) .. "\" style=\"background:#e4d4c0\" | " .. voices[v] .. "\n" .. table.concat(group, "\n|- class=\"vsHide\""))
		end
	end

	if not has_impr then
		return ""
	end
	return
[=[

|- class="vsHide"
! colspan="2" rowspan="2" style="background:#e4d4c0" | imperative
! colspan="3" style="background:#e4d4c0" | ''singular''
! colspan="3" style="background:#e4d4c0" | ''plural''
|- class="vsHide"
! style="background:#e4d4c0;width:12.5%" | [[first person|first]]
! style="background:#e4d4c0;width:12.5%" | [[second person|second]]
! style="background:#e4d4c0;width:12.5%" | [[third person|third]]
! style="background:#e4d4c0;width:12.5%" | [[first person|first]]
! style="background:#e4d4c0;width:12.5%" | [[second person|second]]
! style="background:#e4d4c0;width:12.5%" | [[third person|third]]
]=] .. table.concat(impr)
end

make_nonfin_rows = function(data)
	local nonfin = {}

	for _, f in ipairs({"inf", "ptc"}) do
		local row = {}

		for col, t in ipairs({"pres_actv", "perf_actv", "futr_actv", "pres_pasv", "perf_pasv", "futr_pasv"}) do
			local slot = t .. "_" .. f
			--row[col] = "\n| " .. data.forms[slot]
			row[col] = "\n| " .. data.forms[slot] .. (
				data.form_footnote_indices[slot] == nil and "" or
				'<sup style="color: red">' .. data.form_footnote_indices[slot] .."</sup>"
			)

		end

		row = table.concat(row)
		table.insert(nonfin, "\n|- class=\"vsHide\"\n! style=\"background:#e2e4c0\" colspan=\"2\" | " .. nonfins[f] .. row)
	end

	return
[=[

|- class="vsHide"
! colspan="2" rowspan="2" style="background:#e2e4c0" | non-finite forms
! colspan="3" style="background:#e2e4c0" | active
! colspan="3" style="background:#e2e4c0" | passive
|- class="vsHide"
! style="background:#e2e4c0;width:12.5%" | present
! style="background:#e2e4c0;width:12.5%" | perfect
! style="background:#e2e4c0;width:12.5%" | future
! style="background:#e2e4c0;width:12.5%" | present
! style="background:#e2e4c0;width:12.5%" | perfect
! style="background:#e2e4c0;width:12.5%" | future
]=] .. table.concat(nonfin)

end

make_vn_rows = function(data)
	local vn = {}
	local has_vn = false

	local row = {}

	for col, slot in ipairs({"ger_gen", "ger_dat", "ger_acc", "ger_abl", "sup_acc", "sup_abl"}) do
		-- show_form() already called so can just check for "&mdash;"
		if data.forms[slot] ~= "&mdash;" then
			has_vn = true
		end
		row[col] = "\n| " .. data.forms[slot] .. (
			data.form_footnote_indices[slot] == nil and "" or
			'<sup style="color: red">' .. data.form_footnote_indices[slot] .. "</sup>"
		)
	end

	row = table.concat(row)

	if has_vn then
		table.insert(vn, "\n|- class=\"vsHide\"" .. row)
	end

	if not has_vn then
		return ""
	end
	return
[=[

|- class="vsHide"
! colspan="2" rowspan="3" style="background:#e0e0b0" | verbal nouns
! colspan="4" style="background:#e0e0b0" | gerund
! colspan="2" style="background:#e0e0b0" | supine
|- class="vsHide"
! style="background:#e0e0b0;width:12.5%" | genitive
! style="background:#e0e0b0;width:12.5%" | dative
! style="background:#e0e0b0;width:12.5%" | accusative
! style="background:#e0e0b0;width:12.5%" | ablative
! style="background:#e0e0b0;width:12.5%" | accusative
! style="background:#e0e0b0;width:12.5%" | ablative]=] .. table.concat(vn)

end

make_footnotes = function(data)
	local tbl = {}
	local i = 0
	for k,v in pairs(data.footnotes) do
		i = i + 1
		tbl[i] = '<sup style="color: red">'..tostring(k)..'</sup>'..v..'<br>' end
	return table.concat(tbl)
end

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

checkexist = function(data)
	if NAMESPACE ~= '' then return end
	local outerbreak = false
	for _, conjugation in pairs(data.forms) do
		if conjugation then
			if type(conjugation) == "string" 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
						table.insert(data.categories, "Latin verbs with red links in their inflection tables")
						outerbreak = true
						break
					end
				end
			end
		end
		if outerbreak then
			break
		end
	end
end

checkirregular = function(args,data)
	local apocopic = sub(args[1],1,-2)
	apocopic = gsub(apocopic,'[^aeiouyāēīōūȳ]+$','')
	if args[1] and args[2] and not find(args[2],'^'..apocopic) then
		table.insert(data.categories,'Latin stem-changing verbs')
	end
end







-- functions for creating external search hyperlinks

flatten_values = function(T)
	function noaccents(x)
		return gsub(toNFD(x),'[^%w]+',"")
	end
	function cleanup(x)
		return noaccents(gsub(gsub(gsub(x, '%[', ''), '%]', ''), ' ', '+'))
	end
		local tbl = {}
	for _, v in pairs(T) do
		if type(v) == "table" then
			local FT = flatten_values(v)
			for _, V in pairs(FT) do
				tbl[#tbl+1] = cleanup(V)
			end
		else
			if find(v, '<') == nil then
				tbl[#tbl+1] = cleanup(v)
			end
		end
	end
	return tbl
end

link_google_books = function(verb, forms, domain)
	function partition_XS_into_N(XS, N)
		local count = 0
		local mensae = {}
		for _, v in pairs(XS) do
			if count % N == 0 then mensae[#mensae+1] = {} end
			count = count + 1
			mensae[#mensae][#(mensae[#mensae])+1] = v end
		return mensae end
	function forms_N_to_link(fs, N, args, site)
		return '[https://www.google.com/search?'..args..'&q='..site..'+%22'.. table.concat(fs, "%22+OR+%22") ..'%22 '..N..']' end
	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[#links+1] = forms_N_to_link(v,k,args,site=="Books" and "" or site) end
		return table.concat(links, ' - ') end
	return "Google "..domain.." forms of "..verb.." : "..make_links_txt(forms, 30, domain)
end

return export