Module:pi-conj/verb


local export = {}

local links = require("Module:links")
local lang = require("Module:languages").getByCode("pi")
local m_parameters = require("Module:parameters")
local m_noun =       require("Module:pi-decl/noun")
local m_scripts =    require("Module:scripts")
local to_script =    require("Module:pi-Latn-translit").tr
local debug_outer = ''
local gsub = mw.ustring.gsub
local match = mw.ustring.match
local sub = mw.ustring.sub
local asub = string.sub   -- quicker for ASCII, but works on bytes.
local ti = table.insert
-- require "Module:log globals" -- Check for abuse of globals.  Only require for testing! 

local function dc(x) -- Use this to make marks legible.  The name 'dc' means 'drop carrier'.
	return gsub(x, "[𑀓कকකကกᨠកກ𑄇]", "") -- These are the letter ka in the 10 supported Indic scripts.
end

local endings = {
-- pa endings
	["ati"] = "ati",		["ติ"] = "ati",			["ति"] = "ati",			["তি"] = "ati",
	["တိ"] = "ati",									["ᨲᩥ"] = "ati",
	["តិ"] = "ati",			["ති"] = "ati",			["𑀢𑀺"] = "ati",  	    ["ຕິ"] = "ati",
	["𑄖𑄨"] = "ati",

	["ayati"] = "ayati",	["ยติ"] = "ayati",		["यति"] = "ayati",			["যতি"] = "ayati",
	["ယတိ"] = "ayati",		["ะยะติ"] = "ayati",		["ᨿᨲᩥ"] = "ayati",	["ຢຕິ"] = "ayati",
	["យតិ"] = "ayati",		["යති"] = "ayati",		["𑀬𑀢𑀺"] = "ayati",	["ະຢະຕິ"] = "ayati",
	["ຍຕິ"] = "ayati",		["ະຍະຕິ"] = "ayati",		["𑄠𑄖𑄨"] = "ayati",

	["āti"] = "āti",		["าติ"] = "āti",			[dc("काति")] = "āti",	[dc("কাতি")] = "āti",
	["ာတိ"] = "āti",		["ါတိ"] = "āti",		["ᩣᨲᩥ"] = "āti",		["ᩤᨲᩥ"] = "āti",
	[dc("កាតិ")] = "āti",	[dc("කාති")] = "āti",	[dc("𑀓𑀸𑀢𑀺")] = "āti",		["າຕິ"] = "āti",
	[dc("𑄇𑄂𑄖𑄨")] = "āti",

	["eti"] = "eti",								[dc("केति")] = "eti",	[dc("কেতি")] = "eti",
	["ေတိ"] = "eti",								[dc("ᨠᩮᨲᩥ")] = "eti",
	[dc("កេតិ")] = "eti",	[dc("කෙති")] = "eti",	[dc("𑀓𑁂𑀢𑀺")] = "eti",		[dc("𑄇𑄬𑄖𑄨")] = "eti",

	["oti"] = "oti",								[dc("कोति")] = "oti",	[dc("কোতি")] = "oti",
	["ောတိ"] = "oti",		["ေါတိ"] = "oti",		["ᩮᩣᨲᩥ"] = "oti",		["ᩮᩤᨲᩥ"] = "oti",
	[dc("កោតិ")] = "oti",	[dc("කොති")] = "oti",	[dc("𑀓𑁄𑀢𑀺")] = "oti",		[dc("𑄇𑄮𑄖𑄨")] = "oti",
-- pm endings
	["ate"] = "ate",		["เต"] = "ate",			["ते"] = "ate",			["তে"] = "ate",
	["တေ"] = "ate",									["ᨲᩮ"] = "ate",
	["តេ"] = "ate",			["තෙ"] = "ate",			["𑀢𑁂"] = "ate",   ["ເຕ"] = "ate",
	["𑄖𑄬"] = "ate",

	["ayate"] = "ayate",	["ยเต"] = "ayate",		["यते"] = "ayate",			["যতে"] = "ayate",
	["ယတေ"] = "ayate",		["ะยะเต"] = "ayate",		["ᨿᨲᩮ"] = "ayate",	["ຢເຕ"] = "ayate",
	["យតេ"] = "ayate",		["යතෙ"] = "ayate",		["𑀬𑀢𑁂"] = "ayate",	["ະຢະເຕ"] = "ayate",
	["ຍເຕ"] = "ayate",		["ະຍະເຕ"] = "ayate",	["𑄠𑄖𑄬"] = "ayate",

	["āte"] = "āte",		["าเต"] = "āte",		[dc("काते")] = "āte",	[dc("কাতে")] = "āte",
	["ာတေ"] = "āte",		["ါတေ"] = "āte",		["ᩣᨲᩮ"] = "āte",		["ᩤᨲᩮ"] = "āte",
	[dc("កាតេ")] = "āte",	[dc("කාතෙ")] = "āte",	[dc("𑀓𑀸𑀢𑁂")] = "āte",  ["າເຕ"] = "āte",
	[dc("𑄇𑄂𑄖𑄬")] = "āte",

	["ete"] = "ete",								[dc("केते")] = "ete",	[dc("কেতে")] = "ete",
	["ေတေ"] = "ete",								[dc("ᨠᩮᨲᩮ")] = "ete",
	[dc("កេតេ")] = "ete",	[dc("කෙතෙ")] = "ete",	[dc("𑀓𑁂𑀢𑁂")] = "ete",	[dc("𑄇𑄬𑄖𑄬")] = "ete",

	["ute"] = "ute",		[dc("กุเต")] = "ute",		[dc("कुते")] = "ute",	[dc("কুতে")] = "ute",
	[dc("ကုတေ")] = "ute",							[dc("ᨠᩩᨲᩮ")] = "ute",	
	[dc("កុតេ")] = "ute",	[dc("කුතෙ")] = "ute",	[dc("𑀓𑀼𑀢𑁂")] = "ute",  [dc("ກຸເຕ")] = "ute",
	[dc("𑄇𑄪𑄖𑄬")] = "ute",
-- optative endings
	["eyya"] = "eyya",		["ยฺย"] = "eyya",		[dc("केय्य")] = "eyya",	[dc("কেয্য")] = "eyya",
	["ေယျ"] = "eyya",		["ยยะ"] = "eyya",		[dc("ᨠᩮᨿ᩠ᨿ")] = "eyya",	["ຢຢະ"] = "eyya",
	[dc("កេយ្យ")] = "eyya",	[dc("කෙය්‍ය")] = "eyya",	[dc("𑀓𑁂𑀬𑁆𑀬")] = "eyya",	["ຢ຺ຢ"] = "eyya",
	["ຍ຺ຍ"] = "eyya",		["ຍຍະ"] = "eyya",		[dc("𑄇𑄬𑄠𑄳𑄠")] = "eyya",

	["ā"] = "ā",			["า"] = "ā",			[dc("का")] = "ā",	[dc("কা")] = "ā",
	["ာ"] = "ā",			["ါ"] = "ā",			["ᩣ"] = "ā",		["ᩤ"] = "ā",
	[dc("កា")] = "ā",		[dc("කා")] = "ā",		[dc("𑀓𑀸")] = "ā",    ["າ"] = "ā",
	[dc("𑄇𑄂")] = "ā",

	["etha"] = "etha",		["ถ"] = "etha",			[dc("केथ")] = "etha",	[dc("কেথ")] = "etha",
	["ေထ"] = "etha",		["ถะ"] = "etha",		[dc("ᨠᩮᨳ")] = "etha",	["ຖ"] = "etha",
	[dc("កេថ")] = "etha",	[dc("කෙථ")] = "etha",	[dc("𑀓𑁂𑀣")] = "etha",	["ຖະ"] = "etha",
	[dc("𑄇𑄬𑄗")] = "etha",
-- imperfect endings
	["attha"] = "attha",	["ตฺถ"] = "attha",		["त्थ"] = "attha",	["ত্থ"] = "attha",
	["တ္ထ"] = "attha",		["ᨲ᩠ᨳ"] = "attha",		["ตถะ"] = "attha",	["ຕຖະ"] = "attha",
	["ត្ថ"] = "attha",		["ත්‍ථ"] = "attha",		["𑀢𑁆𑀣"] = "attha",  ["ຕ຺ຖ"] = "attha",
	["𑄖𑄴𑄗"] = "attha",
	["a"] = "a",          ["ะ"] = "a",             ["ະ"] = "a", -- Only good for alphabets!
}

local aorist_endings = {
-- aorist endings.  Only active seems predictable enough.
	["i"] = "i",		[dc("กิ")] = "i",		 [dc("कि")] = "i",		 [dc("কি")] = "i",
	[dc("ကိ")] = "i",	[dc("ᨠᩥ")] = "i",
	[dc("កិ")] = "i",	[dc("කි")] = "i",		 [dc("𑀓𑀺")] = "i",	 [dc("ກິ")] = "i",
	[dc("𑄇𑄨")] = "i",

	["esi"] = "esi",		["สิ"] = "esi",		[dc("केसि")] = "esi",	[dc("কেসি")] = "esi",
	[dc("ကေသိ")] = "esi",	 [dc("ᨠᩮᩈᩥ")] = "esi",
	[dc("កេសិ")] = "esi",	[dc("කෙසි")] = "esi",	[dc("𑀓𑁂𑀲𑀺")] = "esi",   ["ສິ"] = "esi",
	[dc("𑄇𑄬𑄥𑄨")] = "esi",

	["āsi"] = "āsi",		["าสิ"] = "āsi",	[dc("कासि")] = "āsi",	 [dc("কাসি")] = "āsi",
	[dc("ကာသိ")] = "āsi", [dc("ကါသိ")] = "āsi", [dc("ᨠᩣᩈᩥ")] = "āsi", [dc("ᨠᩤᩈᩥ")] = "āsi",
	[dc("កាសិ")] = "āsi",	[dc("කාසි")] = "āsi",	[dc("𑀓𑀸𑀲𑀺")] = "āsi",	[dc("ກາສິ")] = "āsi",
	[dc("𑄇𑄂𑄖𑄨")] = "āsi",

-- Purpose of "osi" is to bleed "āsi" when the vowel is written as two characters in from NFC
	[dc("ကောသိ")] = "osi", [dc("ကေါသိ")] = "osi", [dc("ᨠᩮᩣᩈᩥ")] = "osi", [dc("ᨠᩮᩤᩈᩥ")] = "osi",


	["ayi"] = "ayi",		["ยิ"] = "ayi",	["यि"] = "ayi",	 ["যি"] = "ayi",
	["ယိ"] = "ayi", ["ᨿᩥ"] = "ayi",	["ะยิ"] = "ayi",	["ະຍິ"] = "ayi",	["ະຢິ"] = "ayi",
	["យិ"] = "ayi",	["යි"] = "ayi",	["𑀬𑀺"] = "ayi",	["ຍິ"] = "ayi",	["ຢິ"] = "ayi",
	["𑄠𑄨"] = "ayi",
}

local future_endings = {
-- fa endings
	["ati"] = "ati",		["ติ"] = "ati",			["ति"] = "ati",			["তি"] = "ati",
	["တိ"] = "ati",									["ᨲᩥ"] = "ati",
	["តិ"] = "ati",			["ති"] = "ati",			["𑀢𑀺"] = "ati",  	["ຕິ"] = "ati",
	["𑄖𑄨"] = "ati",

	["iti"] = "iti",		[dc("กิติ")]="iti",			[dc("किति")]="iti",		[dc("কিতি")]="iti",
	[dc("ကိတိ")]="iti",									[dc("ᨠᩥᨲᩥ")]="iti",
	[dc("កិតិ")]="iti",		[dc("කිති")]="iti",			[dc("𑀓𑀺𑀢𑀺")]="iti",		[dc("ກິຕິ")]="iti",
	[dc("𑄇𑄨𑄖𑄨")]="iti",

	["heti"] = "heti",		["เหติ"] = "heti",			["हेति"] = "heti",		["হেতি"] = "heti",
	["ဟေတိ"] = "heti",									["ᩉᩮᨲᩥ"] = "heti",
	["ហេតិ"] = "heti",		["හෙති"] = "heti",			["𑀳𑁂𑀢𑀺"] = "heti",		["ເຫຕິ"] = "heti",
	["𑄦𑄬𑄖𑄨"] = "heti",
	
	["kāhiti"] = "kāhiti",	["กาหิติ"] = "kāhiti",		["काहिति"] = "kāhiti",	["কাহিতি"] = "kāhiti",
	["ကာဟိတိ"] = "kāhiti",							["ᨠᩣᩉᩥᨲᩥ"] = "kāhiti",
	["កាហិតិ"] = "kāhiti",	["කාහිති"] = "kāhiti",		["𑀓𑀸𑀳𑀺𑀢𑀺"] = "kāhiti",	["ກາຫິຕິ"] = "kāhiti",
	["𑄇𑄂𑄦𑄨𑄖𑄨"] = "kāhiti",

-- fm endings
	["ate"] = "ate",		["เต"] = "ate",			["ते"] = "ate",			["তে"] = "ate",
	["တေ"] = "ate",									["ᨲᩮ"] = "ate",
	["តេ"] = "ate",			["තෙ"] = "ate",			["𑀢𑁂"] = "ate",   ["ເຕ"] = "ate",
	["𑄖𑄬"] = "ate",

-- ca ending.
	["ā"] = "ā",			["า"] = "ā",			[dc("का")] = "ā",	[dc("কা")] = "ā",
	["ာ"] = "ā",			["ါ"] = "ā",			["ᩣ"] = "ā",		["ᩤ"] = "ā",
	[dc("កា")] = "ā",		[dc("කා")] = "ā",		[dc("𑀓𑀸")] = "ā",    ["າ"] = "ā",
	[dc("𑄇𑄂")] = "ā",

	["a"] = "a",
-- cm ending
	["atha"] = "atha",		["ถ"] = "atha",			["थ"] = "atha",	["থ"] = "atha",
	["ထ"] = "atha",		["ถะ"] = "atha",		["ᨳ"] = "atha",	["ຖ"] = "atha",
	["ថ"] = "atha",	["ථ"] = "atha",	["𑀣"] = "atha",	["ຖະ"] = "atha",
	["𑄗"] = "atha",
}

local cons = "^[ก-ฮअक-हঅক-হက-ဢᨠ-ᩌᩔ-ᩖᩚ-ᩞក-អඅක-ෆ𑀅𑀓-𑀷ກ-ຮໜ-ໟ𑄇-𑄦𑅇]"

local detectEndingFromList = function(cite3s, endings, latinise, flags)
	if latinise then
		local cfin = cite3s		                     -- In general, this logic would need the
		cite3s = (lang:transliterate(cfin, latinise))  -- trwo() function from the transliterator.
	end
	local len
	local ending
	local wordEnd
	for len = 5, 1, -1 do
		wordEnd = sub(cite3s, -len)
		ending = endings[wordEnd]
		if not ending then
			-- Try again
		elseif wordEnd == "ติ" then
			if match(cite3s, 'เ.ติ$') then
				return "eti"
			elseif match(cite3s, 'โ.ติ$') then
				return "oti"
			elseif match(cite3s, dc('เ.กฺ.ติ$')) then
				return "eti"
			elseif match(cite3s, dc('โ.กฺ.ติ$')) then
				return "oti"
			else
				return ending
			end
		elseif wordEnd == "เต" then
			if match(cite3s, 'เ.เต$') then
				return "ete"
			elseif match(cite3s, dc('เ.กฺ.เต$')) then
				return "ete"
			else
				return ending
			end
		elseif wordEnd == "ຕິ" then
			if match(cite3s, 'ເ.ຕິ$') then
				return "eti"
			elseif match(cite3s, 'ໂ.ຕິ$') then
				return "oti"
			elseif match(cite3s, dc('ເ.ກ຺.ຕິ$')) then
				return "eti"
			elseif match(cite3s, dc('ໂ.ກ຺.ຕິ$')) then
				return "oti"
			else
				return ending
			end
		elseif wordEnd == "ເຕ" then
			if match(cite3s, 'ເ.ເຕ$') then
				return "ete"
			elseif match(cite3s, dc('ເ.ກ຺.ເຕ$')) then
				return "ete"
			else
				return ending
			end
		elseif wordEnd == "สิ" then
			if match(cite3s, 'เ.สิ$') then
				return "esi"
			elseif match(cite3s, dc('เ.กฺ.สิ$')) then
				return "esi"
			else
				-- Try again
			end
		elseif wordEnd == "ສິ" then
			if match(cite3s, 'ເ.ສິ$') then
				return "esi"
			elseif match(cite3s, dc('ເ.ກ຺.ສິ$')) then
				return "esi"
			else
				-- Try again
			end
		elseif ending == 'ayati' and len == 3 then
			if match(sub(cite3s,-4,-4), cons) then
				return ending
			else
				return "ati"
			end
		elseif ending == 'ayate' and len == 3 then
			if match(sub(cite3s,-4,-4), cons) then
				return ending
			else
				return "ate"
			end
		elseif ending == 'ayi' and len == 2 then
			if match(sub(cite3s,-3,-3), cons) then
				return ending
			else
				return "i"
			end
		elseif ending == 'āsi' and flags then
			if flags.aasi == '1' then
				return ending
			else
				-- Try again
			end
		elseif ending == 'osi' then
			return "i" -- Avoid misrecognising as 'āsi' in Burmese and similar.
		else
--			if ending == "i" and not allowi then
--				ending = "ati" -- For brūti and similar
--			end
-- debug_outer = debug_outer..'\n'..cite3s..'='..ending..'\n'
			return ending
		end
	end
	if match(sub(cite3s, -1, -1), cons) then
-- debug_outer = debug_outer..'\n'..cite3s..'='.."a"..'\n'
		return "a"
	else
-- debug_outer = debug_outer..'\n'..cite3s..'='.."ati"..'\n'
		return "ati"
	end
end

export.detectEnding = function(cite3s, latinise)
	return detectEndingFromList(cite3s, endings, latinise)
end

export.detectAoristEnding = function(cite3s, latinise, flags)
	return detectEndingFromList(cite3s, aorist_endings, latinise, flags)
end


local active_optative = {
	{"⌫⌫⌫eyyāmi", "⌫⌫⌫eyyaṃ", "⌫⌫⌫e"}, {"⌫⌫⌫eyyāma"},
	{"⌫⌫⌫eyyāsi", "⌫⌫⌫e"},             {"⌫⌫⌫eyyātha"},
	{"⌫⌫⌫eyya",   "⌫⌫⌫e"},             {"⌫⌫⌫eyyuṃ"}
}
local active_optative_ati  = mw.clone(active_optative)
local active_optative_aati = mw.clone(active_optative)
active_optative_aati[1] = {"⌫⌫⌫eyyāmi", "⌫⌫⌫eyyaṃ"}
active_optative_aati[3] = {"⌫⌫⌫eyyāsi"}
local active_optative_eti  = mw.clone(active_optative_aati)
active_optative_eti[5] = {"⌫⌫⌫eyya"}
local active_optative_oti  = mw.clone(active_optative_eti)
local active_optative_ute  = mw.clone(active_optative_eti)
local middle_optative = {
	{"⌫⌫⌫eyyaṃ"}, {"⌫⌫⌫eyyāmhe"},
	{"⌫⌫⌫etho"},  {"⌫⌫⌫eyyavho"},
	{"⌫⌫⌫etha"},  {"⌫⌫⌫eraṃ"}
}
local middle_optative_ati  = mw.clone(middle_optative)
local middle_optative_aati = mw.clone(middle_optative)
local middle_optative_eti  = mw.clone(middle_optative)
local middle_optative_oti  = mw.clone(middle_optative)
local middle_optative_ute  = mw.clone(middle_optative)

local empty = {{}, {}, {}, {}, {}, {}}

local pattern = {
	["ati"] = {
		{"⌫⌫⌫āmi"}, {"⌫⌫⌫āma"},   {"⌫⌫si"}, {"⌫ha"},    {""}, {"⌫⌫nti"},
		mid = "ate",
		imperfect = {
			{"⌫⌫", "⌫⌫ṃ"},	{"⌫⌫mhā"},
			{"⌫⌫⌫o"},		{"⌫tha"},
			{"⌫⌫", "⌫⌫⌫ā"},	{"⌫⌫⌫u", "⌫⌫⌫ū"},
		},
		imperative = {
			{"⌫⌫⌫āmi"}, {"⌫⌫⌫āma"}, {"⌫⌫", "⌫⌫⌫āhi"}, {"⌫ha"}, {"⌫u"}, {"⌫⌫ntu"}
		},
		optative_active = active_optative_ati,
		optative_middle = middle_optative_ati,
		participle = {{"⌫⌫nt"}},
	},
	["āti"] = { 
		{"⌫⌫mi"}, {"⌫⌫ma"},   {"⌫⌫si"}, {"⌫ha"},    {""}, {"⌫⌫⌫anti"},
		mid = "āte",
		imperfect = {
			{"⌫⌫⌫a", "⌫⌫⌫aṃ"},	{"⌫⌫⌫amhā"},
			{"⌫⌫⌫o"},			{"⌫⌫⌫attha"},
			{"⌫⌫⌫a", "⌫⌫"},		{"⌫⌫⌫u", "⌫⌫⌫ū"},
		},
		imperative = {
			{"⌫⌫mi"}, {"⌫⌫ma"},  {"⌫⌫", "⌫⌫hi"}, {"⌫ha"},  {"⌫u"}, {"⌫⌫⌫antu"},
		},
		optative_active = active_optative_aati,
		optative_middle = middle_optative_aati,
		participle = {{"⌫⌫⌫ant"}},
	},
	["eti"] = {
		{"⌫⌫mi"}, {"⌫⌫ma"},   {"⌫⌫si"}, {"⌫ha"},    {""}, {"⌫⌫nti"},
		mid = nil, -- "ete",
		imperfect = empty,
		imperative = {
			{"⌫⌫mi"}, {"⌫⌫ma"},   {"⌫⌫hi"}, {"⌫ha"},    {"⌫u"}, {"⌫⌫ntu"},
		},
		optative_active = active_optative_eti,
		optative_middle = middle_optative_eti,
		participle = {{"⌫⌫nt"}},
	},
	["oti"] = {
		{"⌫⌫mi"}, {"⌫⌫ma"},   {"⌫⌫si"}, {"⌫ha"},    {""}, {"⌫⌫nti"},
		mid = "oti_ute",
		imperfect = empty,
		imperative = {
			{"⌫⌫mi"}, {"⌫⌫ma"},   {"⌫⌫hi"}, {"⌫ha"},    {"⌫u"}, {"⌫⌫ntu"},
		},
		optative_active = active_optative_oti,
		optative_middle = middle_optative_oti,
		participle = {{"⌫⌫nt"}},
	},
	["ate"] = { -- Also services "ati"
		{"⌫⌫⌫e"}, {"⌫⌫⌫āmhe"},  {"⌫⌫se"}, {"⌫⌫vhe"},  {"⌫e"}, {"⌫⌫nte", "⌫⌫re"},
		mid = "ate",
		imperfect = {
			{"⌫⌫⌫iṃ"},	{"⌫⌫⌫āmhase", "⌫⌫mhase"},
			{"⌫⌫se"},	{"⌫⌫vhaṃ"},
			{"⌫tha"},	{"⌫thuṃ"},
		},
		imperative = {
			{"⌫⌫⌫e"}, {"⌫⌫⌫āmase"},  {"⌫⌫ssu"}, {"⌫⌫vho"},  {"⌫aṃ"}, {"⌫⌫ntaṃ"},
		},
		optative_active = active_optative_ati,
		optative_middle = middle_optative_ati,
		participle = {{"⌫⌫māna"}},
	},
	["āte"] = { -- Also services "āti"
		{"⌫⌫⌫e"},	{"⌫⌫mhe"},
		{"⌫⌫se"},	{"⌫⌫⌫avhe"},
		{"⌫e"},		{"⌫⌫⌫ante", "⌫⌫re"},
		mid = "āte",
		imperfect = empty,
		optative_active = active_optative_aati,
		optative_middle = middle_optative_aati,
		participle = {{"⌫⌫⌫amāna"}},
	},
	["ete"] = { -- Also services "eti"
		{"⌫⌫⌫aye"},		{"⌫⌫⌫ayāmhe"},
		{"⌫⌫⌫ayase"},	{"⌫⌫⌫ayavhe"},
		{"⌫⌫⌫ayate"},	{"⌫⌫⌫ayante", "⌫⌫⌫ayare"},
		mid = "ete",
		imperfect = empty,
		imperative = {
			{"⌫⌫⌫aye"},		{"⌫⌫⌫ayāmase"},
			{"⌫⌫⌫ayassu"},	{"⌫⌫⌫ayavho"},
			{}, -- {"⌫⌫⌫ayataṃ", "⌫aṃ"}, -- 3s needs review
			{"⌫⌫⌫ayantaṃ"}
		},
		optative_active = active_optative_eti,
		optative_middle = middle_optative_eti,
		participle = {{"⌫⌫⌫ayamāna"}},
	},
	["ute"] = { -- Actual ending is "oti" when copied to "oti_ute".
		{"⌫⌫⌫ve"},	{"⌫⌫⌫umhe"},
		{"⌫⌫⌫use"},	{"⌫⌫⌫uvhe"},
		{"⌫⌫⌫ute"},	{"⌫⌫⌫unte", "⌫⌫⌫ure"},
		mid = "ute",
		imperfect = empty,
		imperative = {
			{"⌫⌫⌫ve"},		{"⌫⌫⌫vāmase"},
			{"⌫⌫⌫ussu"},	{"⌫⌫⌫uvho"},
			{"⌫⌫⌫utaṃ"},	{"⌫⌫⌫untaṃ"}
			-- 1s, 1pl and 3pl will often need manual repair.
		},
		optative_active = active_optative_ute,
		optative_middle = middle_optative_ute,
	},
	["eyya"] = {
		optative_active = { -- '-e' is not included!
			{"⌫āmi", "⌫aṃ"}, {"⌫āma"},
			{"⌫āsi"},        {"⌫ātha"},
			{""},           {"⌫uṃ"}
		},
		optative_middle = {
			{"ṃ"},       {"⌫āmhe"},
			{"⌫⌫⌫tho"},  {"vho"},
			{"⌫⌫⌫tha"},  {"⌫⌫⌫raṃ"}
		},
	},
	["ā"] = {
		optative_active = {
			{"mi"}, {"ma"},  {"si"}, {"tha"},  {""}, {"⌫uṃ"}
		}
	},
	["etha"] = {
		mid = "etha",
		optative_middle = {
			{"⌫⌫⌫yyaṃ"}, {"⌫⌫⌫yyāmhe"},
			{"⌫o"},      {"⌫⌫⌫yyavho"},
			{""},        {"⌫⌫⌫raṃ"}
		},
	},
	["a"] = {
		mid="a_attha",
		imperfect = {
			{"", "ṃ"},	{"mhā"},
			{"⌫o"},		{"ttha"},
			{"", "⌫ā"},	{"⌫u", "⌫ū"},
		}
	},
	["ā2"] = { -- imperfect form
		mid="ā_attha",
		imperfect = {
			{"⌫a", "⌫aṃ"},	{"⌫amhā"},
			{"⌫o"},			{"⌫attha"},
			{"⌫a", ""},		{"⌫u", "⌫ū"},
		}
	},
	["attha"] = {
		mid = "attha",
		imperfect = {
			{"⌫⌫⌫⌫⌫iṃ"}, {"⌫⌫⌫⌫⌫āmhase", "⌫⌫⌫⌫mhase"},
            {"⌫⌫⌫⌫se"}, {"⌫⌫⌫⌫vhaṃ"}, {""}, {"⌫uṃ"}
		}
	},
	["a_attha"] = {
		mid = "a_attha",
		imperfect = {
			{"⌫iṃ"}, {"⌫āmhase", "mhase"},
            {"se"}, {"vhaṃ"}, {"ttha"}, {"tthuṃ"}
		}
	},
	["ā_attha"] = {
		mid = "ā_attha",
		imperfect = {
			{"⌫iṃ"}, {"mhase", "⌫amhase"},
            {"⌫ase"}, {"⌫avhaṃ"}, {"⌫attha"}, {"⌫atthuṃ"}
		}
	},
	["i"] = {
		mid = nil,
		aorist = {{"ṃ"}, {"mha"}, {""}, {"ttha"}, {""}, {"ṃsu"}} -- 3p might be wrong too often
		-- However, -ayi seems to consistently yield -ayiṃsu rather than ayuṃ.
	},
	["esi"] = {
		mid = nil,
		aorist = {{"ṃ"}, {}, {""}, {}, {""}, {"⌫uṃ"}} -- 3p might be wrong too often
	},
	["āsi"] = { -- A common mix of sigmatic and thematic aorists!
		mid = nil,
		aorist = {{"ṃ"}, {"⌫⌫⌫amha", "⌫⌫⌫amhā"}, {""}, {"⌫⌫⌫attha"}, {""}, {"⌫⌫⌫aṃsu"}}
	},
}

-- -oti is too different from -ute in some scripts.
pattern.oti_ute = mw.clone(pattern.ute)
pattern.oti_ute.mid = "oti_ute"

pattern.ayati = mw.clone(pattern.ati)
pattern.ayati.mid = "ayate"
-- Lose some of the shorter forms.
pattern.ayati.imperative[3] = {"⌫⌫⌫āhi"}
pattern.ayati.optative_active[1] = {"⌫⌫⌫eyyāmi", "⌫⌫⌫eyyaṃ"}
pattern.ayati.optative_active[3] = {"⌫⌫⌫eyyāsi"}
pattern.ayate = mw.clone(pattern.ate)
pattern.ayate.mid = "ayate"
pattern.ayate.imperative[5] = {} -- {"⌫aṃ", "⌫⌫⌫⌫⌫etaṃ"} -- 3s needs review
pattern.ayate.optative_active = pattern.ayati.optative_active

pattern.ayi = mw.clone(pattern.i)
pattern.ayi.aorist = mw.clone(pattern.i.aorist)
pattern.ayi.aorist[6] = {"ṃsu"}

-- convert Latin script inflections to another script
-- Ⓓ deletes last preposed vowel (for Thai and Lao)
-- Ⓣ toggles last preposed vowel between 'e' and 'o' (for Thai and Lao)
local convert_suffixes = function(stem, nstrip, suffixes, sc, impl, yopt)
	local form, pre, stem_pre
	local yung, yaa
	local xlitend = {}
	local strip = string.rep("⌫", nstrip)
	local altstem, as1
	local option = {impl = impl}
	if sc == 'Thai' or sc == 'Laoo' then
		altstem = to_script(stem, sc)
		stem_pre = match(altstem, "^[เโເໂ]")
		as1 = sub(altstem, 1, 1)
		if yopt and sc == 'Laoo' then
			if yopt == 'yung' or yopt == 'ຍ' then
				yung = 1
			elseif yopt == 'yaa' or yopt == 'ຢ' then
				yaa = 1
			elseif yopt == 'both' then
				yung = 1 yaa = 1
			else
				error('Parameter yaa has invalid value "'..yopt..'"')
			end
		end
	end
	for k = 1, #suffixes do
		xlitend[k] = {}
		form = export.joinSuffix('Latn', stem, suffixes[k])
		local altforms = {}
		for ia, va in pairs(form) do
			altform = to_script(va, sc, option)
			if yung or yaa then
				local y1, y2
				if yung then y1 = gsub(altform, 'ຢ', 'ຍ') end
				if yaa  then y2 = gsub(altform, 'ຍ', 'ຢ') end
				if y1 then
					table.insert(altforms, y1)
					if y2 then
						if y1 ~= y2 then table.insert(altforms, y2) end
					end
				else
					table.insert(altforms, y2)
				end
			else
				table.insert(altforms, altform)
			end
		end
		local ib = 1 -- Conversion is not one-to-one!
		for ia, altform in pairs(altforms) do
			local ibin = ib
-- Special handling is needed for a preposed vowel.
			pre = match(altform, "^[เโເໂ]")
			if pre then
				if stem_pre then
					if sub(altform, 1, 1) == as1 then
						xlitend[k][ib] = sub(strip, 2) .. sub(altform, 3)
						ib = ib + 1
					else
--						mw.addWarning("Cannot abstract derivation "..altstem.." > "..altform)
						xlitend[k].voi = true
						xlitend[k][ib] = sub(strip, 2) .. 'Ⓣ' .. sub(altform, 3)
						ib = ib + 1
					end -- Don't generate gibberish
				else
					xlitend[k][ib] = strip .. "↶" .. pre .. sub(altform, 3)
					ib = ib + 1
				end
			else
				if stem_pre then
					xlitend[k].voi = true
					xlitend[k][ib] = sub(strip, 2) .. 'Ⓓ' .. sub(altform, 2)
					ib = ib + 1
				else
					xlitend[k][ib] = strip .. sub(altform, 2)
					ib = ib + 1
				end
			end
-- Add alternative Thai/Lao endings
-- A lot of data is needed to decide whether the disabled logic is needed.
-- Any dead code here must be kept around in case facts later show that it is
-- needed for some spelling variant.
			if (sc == 'Thai' or sc == 'Laoo') and impl == 'yes' and ib > ibin then
				local altend
				if impl=='yes' then
					altend = gsub(xlitend[k][ibin], dc("([มวມວ][กฺກ຺])([เโເໂ])([หຫ])"), "%2%1%3")
				else
					xlitend[k][ibin] = gsub(xlitend[k][ibin], "ัว", "ะว") -- TBC!
					altend = gsub(xlitend[k][ibin], dc("([มวມວ])([เโເໂ])([หຫ])"), "%2%1%3")
				end
				if altend ~= xlitend[k][ibin] then
					xlitend[k][ib] = xlitend[k][ibin]
					xlitend[k][ibin] = altend
					ib = ib + 1
				end
			end
		end
	end
	return xlitend
end

function export.getSuffixes(sc, ending, tense, option)
	local impl = option and option.impl or 'yes'
	local yopt = option and option.y
	local pat
	if not pattern[ending] then
		error('No verb pattern for ending "'..ending..'".')
	end
	if tense then
		pat = pattern[ending][tense]
	else
		pat = pattern[ending]
	end
	if 'Latn' == sc then
		return pat
	elseif not pat then
		return nil
	elseif pat[sc] then
		return pat[sc]
	else
		local olen = {Mymr = 2, Lana = 2} -- Assume NFC
		local attha_len = {Latn = 5, Sinh = 4}
		eyya_len = {Mymr = 3, Sinh = 5}
		local alen
		if impl == 'no' and (sc == 'Thai' or sc == 'Laoo') then
			alen = 1
		else
			alen = 0
		end
		local way = {
			["ati"]  = {pseudostem = "kati", ndel = 2 + alen},
			["ayati"]  = {pseudostem = "kayati", ndel = 3 + 2 * alen},
			["āti"]  = {pseudostem = "kāti", ndel = 3},
			["eti"]  = {pseudostem = "keti", ndel = 3},
			["oti"]  = {pseudostem = "soti", ndel = 2 + (olen[sc] or 1)},
			["oti_ute"]  = {pseudostem = "soti", ndel = 2 + (olen[sc] or 1)},
			["ate"]  = {pseudostem = "kate", ndel = 2 + alen},
			["ayate"]  = {pseudostem = "kayate", ndel = 3 + 2 * alen},
			["āte"]  = {pseudostem = "kāte", ndel = 3},
			["ete"]  = {pseudostem = "kete", ndel = 3},
			["ute"]  = {pseudostem = "sute", ndel = 3},
			["eyya"] = {pseudostem = "keyya", ndel = eyya_len[sc] or 4},
			["ā"]    = {pseudostem = "kā", ndel = 1},
			["ā2"]   = {pseudostem = "kā", ndel = 1},
			["a"]    = {pseudostem = "ka", ndel = alen},
			["a_attha"] = {pseudostem = "ka", ndel = alen},
			["ā_attha"] = {pseudostem = "kā", ndel = 1},
			["etha"] = {pseudostem = "ketha", ndel = 2 + alen},
			["attha"] = {pseudostem = "kattha", ndel = (attha_len[sc] or 3) + alen},
			["i"]     = {pseudostem = "ki", ndel = 1},
			["esi"]   = {pseudostem = "kesi", ndel = 3},
			["āsi"]   = {pseudostem = "kāsi", ndel = 3},
			["ayi"]   = {pseudostem = "kayi", ndel = 2 + alen},
		}
		way = way[ending]
		if way then
			pat[sc] = convert_suffixes(way.pseudostem, way.ndel, pat, sc,
									   impl, yopt)
			if ending == 'etha' then
				debug_outer=debug_outer..'tr:'..pat[sc][1][1]
			end
		else
			error('Cannot convert derivations from suffix '..ending..' for '..sc)
		end
		return pat[sc]
	end
end

local function dump_table(name, futargs) -- For debugging only!
	for k, v in pairs(futargs) do
		local t = type(v)
		if t == 'string' then
			mw.addWarning(name..'.'..k..'='..v)
		elseif t == 'table' then
			mw.addWarning(name..'.'..k..' is a table of '..#v)
		else
			mw.addWarning(name..'.'..k..' is a '..type(v))
		end
	end
end

local function define_overrides(params, prefix, mod_default)
	for _, pn in pairs({"1s", "2s", "3s", "1p", "2p", "3p"}) do
		params[prefix.."a"..pn] = {list = true}
		params[prefix.."m"..pn] = {list = true}
		params[prefix.."a"..pn.."_mod"] = {default = mod_default}
		params[prefix.."m"..pn.."_mod"] = {default = mod_default}
	end
end

local function addFutureParams(params, mod_default) -- i.e. future specific
	params.cond = {list = true} -- Only forms including augment
	params.cond_midl = {list = true} -- Only forms including augment
	params.cond_voice = {} -- defaults to (corrected) value of voice
	params.fap_mod = {default = mod_default}
	params.fap = {list = true}
	params.fmp_mod = {default = mod_default}
	params.fmp = {list = true}
	define_overrides(params, 'cond_', mod_default)
end

function export.parse(args)
	local mod_default = 'after'
	if args.impf_midl and not args.midl then
		error("Argument impf_midl may only be used if argument midl is.")
	end
	local params = {
		[1] = {}, -- Just ignore
		cite = {list = true},
		midl = {list = true},
		label = {},
		impl = {},
		y = {},
		aa = {},
		showtr = {},
		subst = {},
		sc = {},
   		voice = {default = 'both'}, -- Overridden by cite and midl
		impf_voice = {default = 'none'},
		augment = {default = 'both_made'},
		impf = {list = true}, -- Only forms including augment
		impf_midl = {list = true}, -- Only forms including augment
		impr_voice = {}, -- defaults to (corrected) value of voice
		opta_voice = {}, -- defaults to (corrected) value of voice
		opta_mod = {default = mod_default},
		opta = {list = true},
		opta_midl = {list = true},
		aori = {list = true},
		aor2 = {list = true},
		aori_voice = {default = 'act'}, -- paradigms only generate active forms!
		aori_augment = {}, -- No default!
		aasi = {}, -- Set to 1 to recognise "āsi" as a special ending.
		perf = {list = true},
		perf_voice = {default = 'both'},
		part_voice = {}, -- defaults to (corrected) value of voice
		pap_mod = {default = mod_default},
		pap = {list = true},
		pmp_mod = {default = mod_default},
		pmp = {list = true},
-- Options for future system:
		futu = {list = true},
		futu_midl = {list = true},
		futu_voice = {},
		cond_augment = {},
		futu_part_voice = {},
	}
	addFutureParams(params, mod_default)
	define_overrides(params, '',      mod_default)
	define_overrides(params, 'impf_', mod_default)
	define_overrides(params, 'impr_', mod_default)
	define_overrides(params, 'opta_', mod_default)
	define_overrides(params, 'aori_', mod_default)
	define_overrides(params, 'perf_', mod_default)
-- Options for future system:
	define_overrides(params, 'futu_', mod_default)
	define_overrides(params, 'cond_', mod_default)
	return m_parameters.process(args, params)
end

function export.futureParse(args, subset)
	local mod_default = 'after'
	if args.cond_midl and not args.midl then
		error("Argument cond_midl may only be used if argument midl is.")
	end
	local params = {
		[1] = {}, -- Just ignore
		cite = {list = true},
		midl = {list = true},
		label = {},
		impl = {},
		y = {},
		aa = {},
		showtr = {},
		subst = {},
		sc = {},
   		voice = {default = 'both'}, -- Overridden by cite and midl
		augment = {default = 'with_made'},
		part_voice = {} -- defaults to (corrected) value of voice
	}
	addFutureParams(params, mod_default)
	define_overrides(params, '',      mod_default)
	if subset then -- Lists have already been formed.
		define_overrides(params, 'futu_', mod_default)
		local futargs = {}
		local replaced = {
			[1] = 1, cite = 1, midl = 1, voice = 1, augment = 1, part_voice = 1
		}
		for k, v in pairs(args) do
			if params[k] and not replaced[k] then
				if type(k) ~= 'string' then
					-- Weird!  Just skip.
				elseif match(k,  '^futu_[am][123]') then
					futargs[sub(k, 6)] = args[k]
				elseif match(k, '^[am][123][sp]') then
					-- Don't copy
				else
					futargs[k] = args[k]
				end
			end
		end
		futargs.cite		= args.futu
		futargs.midl		= args.futu_midl
		futargs.voice		= args.futu_voice or 'both'
		futargs.augment		= args.cond_augment or
			match(args.augment, '^with') and args.augment or 'with_made' 
		futargs.part_voice	= args.futu_part_voice or args.part_voice
--		dump_table('futargs', futargs)
		return futargs
	else
		return m_parameters.process(args, params)
	end
end

local arrcat_nodup = m_noun.arrcat_nodup

local function kill_final_vole(stem)
	return gsub(stem, "([เโເໂ])([^เโເໂ]*)$", "%2")
end

local function toggle_vo(stem)
	local toggle = {["เ"]="โ", ["โ"]="เ", ["ເ"]="ໂ", ["ໂ"]="ເ"}
	local function f_toggle(m1, m2) return toggle[m1]..m2 end
	return gsub(stem, "([เโເໂ])([^เโເໂ]*)$", f_toggle)
end

function export.joinSuffix(sc, base, pattern, option)
	if pattern.voi then
		local res = {}
		for _,v in ipairs(pattern) do
			local forms, rest
			local base2 = nil
			if match(v, '⌫⌫⌫⌫⌫Ⓓ') then
				base2 = kill_final_vole(sub(base, 1, -6))
				rest = sub(v,7)
			elseif match(v, '⌫⌫⌫⌫Ⓓ') then
				base2 = kill_final_vole(sub(base, 1, -5))
				rest = sub(v,6)
			elseif match(v, '⌫⌫⌫Ⓓ') then
				base2 = kill_final_vole(sub(base, 1, -4))
				rest = sub(v,5)
			elseif match(v, '⌫⌫Ⓓ') then
				base2 = kill_final_vole(sub(base, 1, -3))
				rest = sub(v,4)
			elseif match(v, '⌫Ⓓ') then
				base2 = kill_final_vole(sub(base, 1, -2))
				rest = sub(v,3)
			elseif match(v, 'Ⓓ') then
				base2 = kill_final_vole(sub(base, 1, -1))
				rest = sub(v,2)
			elseif match(v, '⌫⌫⌫⌫⌫Ⓣ') then
				base2 = toggle_vo(sub(base, 1, -6))
				rest = sub(v,7)
			elseif match(v, '⌫⌫⌫⌫Ⓣ') then
				base2 = toggle_vo(sub(base, 1, -5))
				rest = sub(v,6)
			elseif match(v, '⌫⌫⌫Ⓣ') then
				base2 = toggle_vo(sub(base, 1, -4))
				rest = sub(v,5)
			elseif match(v, '⌫⌫Ⓣ') then
				base2 = toggle_vo(sub(base, 1, -3))
				rest = sub(v,4)
			elseif match(v, '⌫Ⓣ') then
				base2 = toggle_vo(sub(base, 1, -2))
				rest = sub(v,3)
			elseif match(v, 'Ⓣ') then
				base2 = toggle_vo(sub(base, 1, -1))
				rest = sub(v,2)
			end
			if base2 then
				forms = m_noun.joinSuffix(sc, base2, {rest})
			else
				forms = m_noun.joinSuffix(sc, base, {v})
			end
			res = arrcat_nodup(res, forms)
		end
		return res
	elseif pattern.kill_final_preposed then
		return m_noun.joinSuffix(sc, kill_final_vole(base), pattern, option)
	else
		return m_noun.joinSuffix(sc, base, pattern, option)
	end
end

function export.joinSuffixes(scriptCode, stem, pattern, option)
	local forms = {}
	for i = 1, 6 do
		forms[i] = export.joinSuffix(scriptCode, stem, pattern[i], option)
	end
	return forms
end

function export.modify_voice(forms, prefix, args)
	-- Only null action is implemented.
		for i, pn in pairs({"1s", "1p", "2s", "2p", "3s", "3p"}) do
			local flag = pn..'_mod'
			if #args[pn] > 0 then
				if args[flag] == 'after' then
					forms[i] = arrcat_nodup(forms[i], args[pn])
					forms.isnil = false
				elseif args[flag] == 'replace' then
					forms[i] = args[pn]
					forms.isnil = false
				elseif args[flag] == 'before' then
					forms[i] = arrcat_nodup(args[pn], forms[i])
					forms.isnil = false
				elseif args[flag] == 'blank' then
					forms[i] = {}
				else
					error('Argument '..prefix..pn..' has invalid value '..args[flag])
				end
			elseif args[flag] == 'blank' then
				forms[i] = {}
			end
		end
end

function export.modify_voice(forms, prefix, args)
		for i, pn in pairs({"1s", "1p", "2s", "2p", "3s", "3p"}) do
			local flag = pn..'_mod'
			local added = nil;
			if args[pn] and #args[pn] > 0 then
				added = true;
				if args[flag] == 'after' then
					forms[i] = arrcat_nodup(forms[i], args[pn])
					forms.isnil = false
				elseif args[flag] == 'replace' then
					forms[i] = args[pn]
					forms.isnil = false
				elseif args[flag] == 'before' then
					forms[i] = arrcat_nodup(args[pn], forms[i])
					forms.isnil = false
				elseif args[flag] == 'blank' then
					forms[i] = {}
					added = nil; 
				else
					error('Argument '..prefix..pn..' has invalid value '..args[flag])
				end
				if added and (i == 2) then forms.add1p = true end
			elseif args[flag] == 'blank' then
				forms[i] = {}
			end
		end
end

function export.present(cite, tense, formsa, formsm)
	local rows = {"1st", "2nd", "3rd"}
	local forms1
	local output = {}
	ti(output, '<div class="NavFrame" style="min-width:30%">'..
				'<div class="NavHead" style="background:#d9ebff">'..tense..
				' forms of "' .. cite .. '"' ..'</div><div class="NavContent">')
	ti(output,
	'<table class="inflection-table" style="background:#F9F9F9;'..
		'text-align:center;width:100%">')

	if formsa and formsm then
		ti(output, '<tr style="background:#eff7ff"><th></th><th colspan=2>Active</th><th colspan=2>Middle</th></tr>')
		ti(output, '<tr style="background:#eff7ff"><th></th><th>Singular</th><th>Plural</th><th>Singular</th><th>Plural</th></tr>')
		for i,v in ipairs(rows) do
			if #formsa[2*i-1] > 0 or #formsa[2*i] > 0 or #formsm[2*i-1] > 0 or #formsm[2*i] > 0 then
				ti(output, "<tr><th style=\"background-color:#eff7ff;\">" .. v .. "</th>")
				ti(output, "<td>" .. m_noun.orJoin(currentScript, formsa[2 * i - 1]) .. "</td>")
				ti(output, "<td>" .. m_noun.orJoin(currentScript, formsa[2 * i]) .. "</td>")
				ti(output, "<td>" .. m_noun.orJoin(currentScript, formsm[2 * i - 1]) .. "</td>")
				ti(output, "<td>" .. m_noun.orJoin(currentScript, formsm[2 * i]) .. "</td>")
				ti(output, "</tr>")
			end
		end
	else
		if formsa then
			forms1 = formsa
		else
			forms1 = formsm
		end
		ti(output, '<tr style="background:#eff7ff"><th></th><th>Singular</th><th>Plural</th></tr>')
		for i,v in ipairs(rows) do
			if #forms1[2*i-1] > 0 or #forms1[2*i] > 0 then
				ti(output, "<tr><td style=\"background-color:#eff7ff;\">" .. v .. "</td>")
				ti(output, "<td>" .. m_noun.orJoin(currentScript, forms1[2 * i - 1]) .. "</td>")
				ti(output, "<td>" .. m_noun.orJoin(currentScript, forms1[2 * i]) .. "</td>")
				ti(output, "</tr>")
			end
		end
	end
	ti(output, "</table></div></div>")
	return table.concat(output)
end

-- Basic form of augment:
local augment = {
	Latn="a",
	Thai="อ",
	Deva="अ",
	Beng="অ",
	Mymr="အ",
	Lana="ᩋ",
	Khmr="អ",
	Sinh="අ",
	Brah="𑀅",
	Laoo="ອ",
	Cakm="𑄃",
}
local augment_alph= {
	Thai = "อะ",
	Laoo = "ອະ"
}

local enda3s = {
	Latn="ti",
	Thai="ติ",
	Deva="ति",
	Beng="তি",
	Mymr="တိ",
	Lana="ᨲᩥ",
	Khmr="តិ",
	Sinh="ති",
	Brah="𑀢𑀺",
	Laoo="ຕິ",
	Cakm="𑄖𑄨",
}

local function select_args(old, prefix) -- prefix must be in ASCII
	local new = {}
	local pattern = '^'..prefix
	local follow = string.len(pattern)
	for i, v in pairs(old) do
		if string.match(i,pattern) then
			new[asub(i, follow)] = v
		end
	end
	return new
end

function export.modify(formsa, formsm, prefix, args)
	local subprefix
	if not formsa then formsa = {{}, {}, {}, {}, {}, {}, isnil=true} end
	subprefix = prefix..'a'
	export.modify_voice(formsa, subprefix, select_args(args,subprefix))
	if formsa.isnil then formsa = nil end
	if not formsm then formsm = {{}, {}, {}, {}, {}, {}, isnil=true} end
	subprefix = prefix..'m'
	export.modify_voice(formsm, subprefix, select_args(args,subprefix))
	if formsm.isnil then formsm = nil end
	return formsa, formsm
end

local hike = true -- Whether heading for voices and numbers should be in common for tables. 
local function rows2v(currentScript, tense, formsa, formsm, x0)
	local rows = {"1st", "2nd", "3rd"}
	local output = {}

    formsa = formsa or empty
    formsm = formsm or empty
	ti(output, '<tr style="background:#d9ebff"><th colspan=5>'..tense..'</th></tr>')
	if not hike then
		ti(output, '<tr style="background:#eff7ff"><th></th><th colspan=2>Active</th><th colspan=2>Middle</th></tr>')
		ti(output, '<tr style="background:#eff7ff"><th></th><th>Singular</th><th>Plural</th><th>Singular</th><th>Plural</th></tr>')
	end	
	for i,v in ipairs(rows) do
		if #formsa[2*i-1] > 0 or #formsa[2*i] > 0 or #formsm[2*i-1] > 0 or #formsm[2*i] > 0 then
			ti(output, "<tr><th style=\"background-color:#eff7ff;\">" .. v .. "</th>")
			ti(output, "<td>")
			ti(output, m_noun.orJoin(currentScript, formsa[2 * i - 1], x0))
			ti(output, "</td>")
			ti(output, "<td>")
			ti(output, m_noun.orJoin(currentScript, formsa[2 * i], x0))
			ti(output, "</td>")
			ti(output, "<td>")
			ti(output, m_noun.orJoin(currentScript, formsm[2 * i - 1], x0))
			ti(output, "</td>")
			ti(output, "<td>")
			ti(output, m_noun.orJoin(currentScript, formsm[2 * i], x0))
			ti(output, "</td>")
			ti(output, "</tr>")
		end
	end
	return table.concat(output)
end

local function rows1v(currentScript, tense, formsa, formsm, xo)
	local rows = {"1st", "2nd", "3rd"}
	local output = {}

	local forms1 = formsa or formsm or empty
	ti(output, '<tr style="background:#d9ebff"><th colspan=3>'..tense..'</th></tr>')
	if not hike then
		ti(output, '<tr style="background:#eff7ff"><th></th><th>Singular</th><th>Plural</th></tr>')
	end
	for i,v in ipairs(rows) do
		if #forms1[2*i-1] > 0 or #forms1[2*i] > 0 then
			ti(output, "<tr><td style=\"background-color:#eff7ff;\">")
			ti(output, v)
			ti(output, "</td>")
			ti(output, "<td>")
			ti(output, m_noun.orJoin(currentScript, forms1[2 * i - 1], xo))
			ti(output, "</td>")
			ti(output, "<td>")
			ti(output, m_noun.orJoin(currentScript, forms1[2 * i], xo))
			ti(output, "</td>")
			ti(output, "</tr>")
		end
	end
	return table.concat(output)
end

function export.present_conj(label, currentScript, parts, xo)
	local output = {}
	parts.cat = parts.cat or {}
	local havea = parts.presa or parts.impra or parts.impfa or parts.optaa
	local havea = havea or parts.aoria or parts.futua or parts.conda or parts.perfa
	local havem = parts.presm or parts.imprm or parts.impfm or parts.optam
	havem = havem or parts.aorim or parts.futum or parts.condm or parts.perfa
	local have2 = parts.presa and parts.presm  or  parts.impra and parts.imprm or
				parts.impfa and parts.impfm  or  parts.optaa and parts.optam or
				parts.aoria and parts.aorim  or  parts.futua and parts.futum or
				parts.conda and parts.condm  or  parts.perfa and parts.perfm
	ti(output, '<div class="NavFrame" style="min-width:30%">'..
				'<div class="NavHead" style="background:#d9ebff">'..
				'Conjugation of "' .. label .. '"</div><div class="NavContent">')
	ti(output, '<table class="inflection-table" style="background:#F9F9F9;'..
		'text-align:center;width:100%">')
	if have2 then
		if hike then
			ti(output, '<tr style="background:#eff7ff"><th></th><th colspan=2>Active</th><th colspan=2>Middle</th></tr>')
			ti(output, '<tr style="background:#eff7ff"><th></th><th>Singular</th><th>Plural</th><th>Singular</th><th>Plural</th></tr>')
		end	
		if parts.presa or parts.presm then
			ti(output, rows2v(currentScript, 'Present', parts.presa, parts.presm, xo))
		end
		if parts.impfa or parts.impfm then
			ti(output, rows2v(currentScript, 'Imperfect', parts.impfa, parts.impfm, xo))
		end
		if parts.impra or parts.imprm then
			ti(output, rows2v(currentScript, 'Imperative', parts.impra, parts.imprm, xo))
		end
		if parts.optaa or parts.optam then
			ti(output, rows2v(currentScript, 'Optative', parts.optaa, parts.optam, xo))
		end
		if parts.aoria or parts.aorim then
			ti(output, rows2v(currentScript, 'Aorist', parts.aoria, parts.aorim, xo))
		end
		if parts.futua or parts.futum then
			ti(output, rows2v(currentScript, 'Future', parts.futua, parts.futum, xo))
		end
		if parts.conda or parts.condm then
			ti(output, rows2v(currentScript, 'Conditional', parts.conda, parts.condm, xo))
		end
		if parts.perfa or parts.perfm then
			ti(output, rows2v(currentScript, 'Perfect', parts.perfa, parts.perfm, xo))
		end
	else
		if hike then
			ti(output, '<tr style="background:#eff7ff"><th></th><th>Singular</th><th>Plural</th></tr>')
		end
		if parts.presa or parts.presm then
			ti(output, rows1v(currentScript, 'Present', parts.presa, parts.presm, xo))
		end
		if parts.impfa or parts.impfm then
			ti(output, rows1v(currentScript, 'Imperfect', parts.impfa, parts.impfm, xo))
		end
		if parts.impra or parts.imprm then
			ti(output, rows1v(currentScript, 'Imperative', parts.impra, parts.imprm, xo))
		end
		if parts.optaa or parts.optam then
			ti(output, rows1v(currentScript, 'Optative', parts.optaa, parts.optam, xo))
		end
		if parts.aoria or parts.aorim then
			ti(output, rows1v(currentScript, 'Aorist', parts.aoria, parts.aorim, xo))
		end
		if parts.futua or parts.futum then
			ti(output, rows1v(currentScript, 'Future', parts.futua, parts.futum, xo))
		end
		if parts.conda or parts.condm then
			ti(output, rows1v(currentScript, 'Conditional', parts.conda, parts.condm, xo))
		end
		if parts.perfa or parts.perfm then
			ti(output, rows1v(currentScript, 'Perfect', parts.perfa, parts.perfm, xo))
		end
	end
	if parts.conda or parts.condm then
		ti(parts.cat, "[[category:Pali verbs exhibiting conditional mood]]")
		if parts.conda and parts.conda.add1p then
			ti(parts.cat, "[[category:Pali verbs with manual 1p conditional]]")
		end
	end
	ti(output, "</table></div></div>")
	if parts.pap and #parts.pap > 0 then
		table.insert(output, '\n* Present active participle: ')
		table.insert(output, m_noun.orJoin(currentScript, parts.pap, xo))
		table.insert(output, ', which see for forms and usage')
	end
	if parts.pmp and #parts.pmp > 0 then
		table.insert(output, '\n* Present middle participle: ')
		table.insert(output, m_noun.orJoin(currentScript, parts.pmp, xo))
		table.insert(output, ', which see for forms and usage')
	end
	if parts.fap and #parts.fap > 0 then
		table.insert(output, '\n* Future active participle: ')
		table.insert(output, m_noun.orJoin(currentScript, parts.fap, xo))
		table.insert(output, ', which see for forms and usage')
	end
	if parts.fmp and #parts.fmp > 0 then
		table.insert(output, '\n* Future middle participle: ')
		table.insert(output, m_noun.orJoin(currentScript, parts.fmp, xo))
		table.insert(output, ', which see for forms and usage')
	end
	if parts.debug then table.insert(output, parts.debug) end
	if parts.cat then ti(output, table.concat(parts.cat)) end
	return table.concat(output)
end

function export.present_special_oldway(label, currentScript, parts, xo)
	output = {}
	parts.cat = parts.cat or {}
	if parts.presa or parts.presm then
		table.insert(output, export.present(label, 'Present', parts.presa, parts.presm, xo))
	end
	if parts.impfa or parts.impfm then
		table.insert(output, export.present(label, 'Imperfect', parts.impfa, parts.impfm, xo))
	end
	if parts.impra or parts.imprm then
		table.insert(output, export.present(label, 'Imperative', parts.impra, parts.imprm, xo))
	end
	if parts.optaa or parts.optam then
		table.insert(output, export.present(label, 'Optative', parts.optaa, parts.optam, xo))
	end
	if parts.futua or parts.futum then
		ti(output, export.present(label, 'Future', parts.futua, parts.futum, xo))
	end
	if parts.conda or parts.condm then
		ti(output, export.present(label, 'Conditional', parts.conda, parts.condm, xo))
		ti(parts.cat, "[[category:Pali verbs exhibiting conditional mood]]")
		if parts.conda and parts.conda.add1p then
			ti(parts.cat, "[[category:Pali verbs with manual 1p conditional]]")
		end
	end
	if parts.pap and #parts.pap > 0 then
		table.insert(output, '\n* Present active participle: ')
		table.insert(output, m_noun.orJoin(currentScript, parts.pap, xo))
		table.insert(output, ', which see for forms and usage')
	end
	if parts.pmp and #parts.pmp > 0 then
		table.insert(output, '\n* Present middle participle: ')
		table.insert(output, m_noun.orJoin(currentScript, parts.pmp, xo))
		table.insert(output, ', which see for forms and usage')
	end
	if parts.fap and #parts.fap > 0 then
		table.insert(output, '\n* Future active participle: ')
		table.insert(output, m_noun.orJoin(currentScript, parts.fap, xo))
		table.insert(output, ', which see for forms and usage')
	end
	if parts.fmp and #parts.fmp > 0 then
		table.insert(output, '\n* Future middle participle: ')
		table.insert(output, m_noun.orJoin(currentScript, parts.fmp, xo))
		table.insert(output, ', which see for forms and usage')
	end
	if parts.debug then table.insert(output, parts.debug) end
	if parts.cat then ti(output, table.concat(parts.cat)) end
	return table.concat(output)
end

function export.present_future(label, currentScript, parts, xo)
	if true then -- Newer table format
	return export.present_conj(label, currentScript, parts, xo)
	else
	return export.present_special_oldway(label, currentScript, parts, xo)
	end
end

function export.present_special(label, currentScript, parts, xo)
	if true then -- Newer table format
	return export.present_conj(label, currentScript, parts, xo)
	else
	return export.present_special_oldway(label, currentScript, parts, xo)
	end
end

function export.special_forms(cite, args)
	local currentScript = lang:findBestScript(cite)
	local scriptCode = currentScript:getCode()
	local latinise = nil
	if scriptCode == "None" and args.sc then -- Resort to sc
		scriptCode = args["sc"]
		currentScript = m_scripts.getByCode(scriptCode, "No such script as ".. scriptCode .. ".")
		latinise = currentScript
		augment[scriptCode] = to_script("a", scriptCode)
	end
	args = export.parse(args)
	args.cite = arrcat_nodup({cite}, args.cite)
	local debug = '\n\nAt (1), args.cite = '..table.concat(args.cite, ' ')
	local patpa, patpm, basepa, basepm
	local parts = {}
	local option = {}
	option.aa = args.aa

-- Handle present tense.  This forms a base for others.
	local ending = export.detectEnding(cite, latinise)
--	mw.addWarning('ending='..ending)
	patpa = export.getSuffixes(scriptCode, ending, nil, args)
	basepa = cite
	local mset = nil
	local forBoth = false
	local aset = args.cite
	local voice = "both"
	if 'e' == asub(ending, -1) then -- No active inflection
		patpm = patpa
		patpa = nil
		basepm = cite
		aset = nil
		mset = args.cite
		voice = "midl"
	elseif args.midl and args.midl[1] then
		basepm = args.midl[1]
		mset = args.midl
		local midend = export.detectEnding(basepm, latinise)
		patpm = pattern[midend].mid and
		        export.getSuffixes(scriptCode, pattern[midend].mid, nil, args)
	elseif args.voice == 'act' then
		patpm = nil
		voice = "act"
	else
		patpm = pattern[ending].mid and
				export.getSuffixes(scriptCode, pattern[ending].mid, nil, args)
		basepm = cite
		forBoth = true
		mset = args.cite
	end
	local formsa = nil
	local formsm = nil
	if patpa then
		formsa = export.joinSuffixes(scriptCode, basepa, patpa, option)
		for k = 2, #aset do
			basepa = aset[k]
			patpa = export.getSuffixes(
				scriptCode, export.detectEnding(basepa, latinise), nil, args)
			local extras = export.joinSuffixes(scriptCode, basepa, patpa, option)
			for ip = 1, 6 do
				formsa[ip] = arrcat_nodup(formsa[ip], extras[ip])
			end
		end
	end
	if patpm or forBoth then
		if patpm then
			formsm = export.joinSuffixes(scriptCode, basepm, patpm, option)
		else
			formsm = {{}, {}, {}, {}, {}, {}}
		end
		for k = 2, #mset do
			basepm = mset[k]
			local kend = export.detectEnding(basepm, latinise)
			kend = pattern[kend].mid
			if kend then
				patpm = export.getSuffixes(scriptCode, kend, nil, args)
				local extras = export.joinSuffixes(scriptCode, basepm, patpm, option)
				for ip = 1, 6 do
					formsm[ip] = arrcat_nodup(formsm[ip], extras[ip])
				end
			end
		end
	end
	if args.voice == "act" or args.voice == "both" then
		-- handled already
	elseif args.voice == "none" then
		formsa = nil
		formsm = nil
		voice = "none"
	elseif args.voice then
		error('Argument voice has bad value "'..args.voice..'.')
	end
	formsa, formsm = export.modify(formsa, formsm, '', args)

	parts.presa = formsa
	parts.presm = formsm

-- Form imperative from present
    formsa = nil
    formsm = nil
    local imperativeVoice = args.impr_voice or voice
    if imperativeVoice == 'act' or imperativeVoice == 'both' then
    	local patma, kend
    	formsa = {{}, {}, {}, {}, {}, {}}
    	for k = 1, #aset do
    		kend = export.detectEnding(aset[k], latinise)
    		patma = export.getSuffixes(scriptCode, kend, 'imperative', args)
    		if patma then 
	   			local extras = export.joinSuffixes(scriptCode, aset[k], patma, option)
    			for ip = 1, 6 do
    				formsa[ip] = arrcat_nodup(formsa[ip], extras[ip])
    			end
    		end
    	end
    end
    if imperativeVoice == 'midl' or imperativeVoice == 'both' then
    	local patmm
    	formsm = {{}, {}, {}, {}, {}, {}}
    	local nstem, form3s
    	if forBoth then
    		nstem = #aset
    	else
    		nstem = #mset
    	end
    	for k = 1, nstem do
   			form3s = mset[k]
    		local kend = export.detectEnding(form3s, latinise)
    		if pattern[kend].mid then
    			kend = pattern[kend].mid
    		elseif kend == "eti" then
    			kend = nil
    		else
    			error('No middle endings for ending "'..kend..'".')
    			kend = nil
    		end
    		patmm = kend and export.getSuffixes(scriptCode, kend, 'imperative', args)
    		if patmm then 
	   			local extras = export.joinSuffixes(scriptCode, form3s, patmm, option)
    			for ip = 1, 6 do
    				formsm[ip] = arrcat_nodup(formsm[ip], extras[ip])
    			end
    		end
    	end
    end
    debug = debug..'\n\nAt (8), formsa='..tostring(formsa)
	formsa, formsm = export.modify(formsa, formsm, 'impr_', args)
	if formsa then
		debug = debug..'\n\nAt (9), formsa='..tostring(formsa)..
				' formsa.isnil='..tostring(formsa.isnil)
	else
		debug = debug..'\n\nAt (9), formsa='..tostring(formsa)
	end
	parts.impra = formsa
	parts.imprm = formsm

-- Form imperfect from present.  Existence defaults to false for both voices.
	local aug
	if args["impl"] == 'no' then
		aug = augment_alph[scriptCode] or augment[scriptCode]
	else
		aug = augment[scriptCode]
	end
--	error("impl="..tostring(args["impl"]).." aug="..aug)
	local impf = {}
	if (args.impf_voice ~= 'none') then
		if args.augment == 'without' then
			impf = aset or {}
		elseif args.augment == 'with_made' then
			if aset then
				for k = 1, #aset do
					impf[k] = aug .. aset[k]
				end
			end
		elseif args.augment == 'with_given' then
			impf = args.impf
		elseif args.augment == 'both_made' then
			if aset then
				for k = 1, #aset do
					impf[k] = aug .. aset[k]
				end
				for k = 1, #aset do
					impf[#aset+k] = aset[k]
				end
			end
		elseif args.augment == 'both_given' then
			impf = arrcat_nodup(args.impf or {}, aset or {})
		else
			error('Argument augment has bad value "'..args.augment..'".')
		end
	end
	if (args.impf_voice == 'both' or args.impf_voice == 'act')
--	and (voice == 'both' or voice == 'act')
	then
		formsa = {{}, {}, {}, {}, {}, {}}
		for k = 1, #impf do
			kend = export.detectEnding(impf[k], latinise)
			if kend == "ā" then kend = "ā2" end
			patma = export.getSuffixes(scriptCode, kend, 'imperfect', args)
			if true then -- Make false for debugging.
			elseif patma == nil or patma[5] == nil or patma[5][1] == nil then
				mw.addWarning('impf['..k..']='..impf[k]..' kend='..kend..' patma[5][1]=nil')
			else
				mw.addWarning('impf['..k..']='..impf[k]..' kend='..kend..' patma[5][1]='..patma[5][1])
			end
			if patma then
				local extras = export.joinSuffixes(scriptCode, impf[k], patma, option)
				for ip = 1, 6 do
					formsa[ip] = arrcat_nodup(formsa[ip], extras[ip])
				end
			end
		end
	elseif (args.impf_voice == 'none' or args.impf_voice == 'midl') then
		formsa = nil
	else
		error('Argument impf_voice has bad value "'..args.impf_voice..'".')
	end
	if (args.impf_voice == 'both' or args.impf_voice == 'midl')
--	and (voice == 'both' or voice == 'midl')
	then
		formsm = {{}, {}, {}, {}, {}, {}}
		if not forBoth then
			impf = {}
			if args.augment == 'without' then
				impf = mset
			elseif args.augment == 'with_made' then
				for k = 1, #mset do
					impf[k] = aug .. mset[k]
				end
			elseif args.augment == 'with_given' then
				impf = args.midl and args.midl[1] and args.impf_midl or args.midl
			elseif args.augment == 'both_made' then
				for k = 1, #mset do
					impf[k] = aug .. mset[k]
				end
				for k = 1, #mset do
					impf[#mset+k] = mset[k]
				end
			elseif args.augment == 'both_given' then
				impf = arrcat_nodup(
					args.midl and args.midl[1] and (args.impf_midl or {}) or
					args.impf or {},
					mset or {})
			else
				error('Argument augment has bad value "'..args.augment..'".')
			end
		end
		for k = 1, #impf do
			kend = export.detectEnding(impf[k], latinise)
			debug=debug..'\n\nAt (7) impf[k]='..impf[k]
			if kend == "ā" then kend = "ā2" end
			if forBoth then
	    		if pattern[kend].mid then
	    			kend = pattern[kend].mid
	    		elseif kend == "eti" then
	    			kend = nil
				else
	    			error('No middle endings for ending "'..kend..'".')
	    			kend = nil
	    		end
			end
			local pat = kend and export.getSuffixes(scriptCode, kend, 'imperfect', args)
			if not kend then
				-- nothng to do
			elseif pat then
				local extras = export.joinSuffixes(scriptCode, impf[k], pat, option)
				for ip = 1, 6 do
					formsm[ip] = arrcat_nodup(formsm[ip], extras[ip])
				end
			else
				mw.addWarning('No imperfect middle suffixes for '..impf[k]..
					' ('..kend..')')
			end
		end
	else
		formsm = nil
	end
	formsa, formsm = export.modify(formsa, formsm, 'impf_', args)
	parts.impfa = formsa
	parts.impfm = formsm
-- Form optative from present citation forms.
	args.opta_voice = args.opta_voice or voice
	debug = debug .. '\n\nAt (2), args.cite = '..table.concat(args.cite, ' ')
	local opta = args.cite
	debug = debug .. '\n\nAt (3), opta = '..table.concat(opta, ' ')
	if args.opta then
		debug = debug..'\n\nAt (4), args.opta='..table.concat(args.opta)..', args.opta_mod='..args.opta_mod
		if args.opta_mod == 'replace' then
			opta = args.opta
		elseif args.opta_mod == 'after' then
		opta = arrcat_nodup(opta, args.opta)
		elseif args.opta_mod == 'before' then
			opta = arrcat_nodup(args.opta, opta)
		elseif args.opta_mod == 'blank' then
			args.opta_voice = 'none'
		else
			error('Argument opta_mod has bad value "'..args.opta_mode..'".')
		end
	end
	debug = debug .. '\n\nAt (5), opta = '..table.concat(opta, ' ')..
			', args.opta_voice='..args.opta_voice
	if args.opta_voice == 'both' or args.opta_voice == 'act' then
		formsa = {{}, {}, {}, {}, {}, {}}
		local kend, patma
		for k = 1, #opta do
			kend = export.detectEnding(opta[k], latinise)
			patma = export.getSuffixes(scriptCode, kend, 'optative_active', args)
			if true then -- Make false for debugging.
			elseif patma == nil or patma[5] == nil or patma[5][1] == nil then
				mw.addWarning('opta['..k..']='..opta[k]..' kend='..kend..' patma[5][1]=nil')
			else
				mw.addWarning('opta['..k..']='..opta[k]..' kend='..kend..' patma[5][1]='..patma[5][1])
			end
			if patma then
				local extras = export.joinSuffixes(scriptCode, opta[k], patma, option)
				for ip = 1, 6 do
					formsa[ip] = arrcat_nodup(formsa[ip], extras[ip])
				end
			end
		end
	elseif args.opta_voice == 'midl' or args.opta_voice == 'none' then
		formsa = nil
	else
		error('Argument opta_voice has bad value "'..args.opta_voice..'".')
	end
	if #args.opta_midl > 0 then opta = args.opta_midl end
	formsm = nil

	if args.opta_voice == 'both' or args.opta_voice == 'midl' then
		formsm = {{}, {}, {}, {}, {}, {}}
		local kend, patma
		debug=debug..'\n\nAt (6), opta='..table.concat(opta)
		for k = 1, #opta do
			kend = export.detectEnding(opta[k], latinise)
			patma = export.getSuffixes(scriptCode, kend, 'optative_middle', args)
			if patma then
				local extras = export.joinSuffixes(scriptCode, opta[k], patma, option)
				for ip = 1, 6 do
					formsm[ip] = arrcat_nodup(formsm[ip], extras[ip])
				end
			else
				debug = debug.."\n\nNo "..kend..".optative_middle"
			end
		end
	else
		formsm = nil
	end

	formsa, formsm = export.modify(formsa, formsm, 'opta_', args)
		parts.optaa = formsa
	parts.optam = formsm

-- Generate aorist forms - currently only from aorist specific parameters.
	formsa = nil
	formsm = nil
	if args.aori and #args.aori > 0 or args.aor2 and #args.aor2 > 0 then
		local flags = {}
		flags.aasi = args.aasi
		formsa = { {}, {}, {}, {}, {}, {} }
		local aori = {} -- List of *aorist* 3s.  Cannnot tell -ati from -i!
		for k = 1, #(args.aor2 or {}) do
			aori[k] = aug .. args.aor2[k]
		end
		aori = arrcat_nodup(aori, args.aor2 or {})
		aori = arrcat_nodup(aori, args.aori)
		for k = 1, #aori do
			local kend = export.detectAoristEnding(aori[k], latinise, flags)
			if kend == "āsi" then
				parts.cat = parts.cat or {}
				ti(parts.cat, "[[category:Pali verbs with aorist in -āsi/-a]]")
			end
			local patma = export.getSuffixes(scriptCode, kend, 'aorist', args)
			if patma then
				local extras = export.joinSuffixes(scriptCode, aori[k], patma, option)
				for ip = 1, 6 do
					formsa[ip] = arrcat_nodup(formsa[ip], extras[ip])
				end
			else
				debug = debug.."\n\nNo "..kend..".aorist"
			end
		end
		if #formsa[1] == 0 and #formsa[2] == 0 and #formsa[3] == 0 and
		   #formsa[4] == 0 and #formsa[5] == 0 and #formsa[6] == 0 then 	
			formsa = nil
		end
	end
	parts.aoria, parts.aorim = export.modify(formsa, formsm, 'aori_', args)

-- Generate perfect tense by write in
	formsa = nil
	formsm = nil
	parts.perfa, parts.perfm = export.modify(formsa, formsm, 'perf_', args)
	
--	List present participles.
	local pap, pmp = {}, {}
	args.part_voice = args.part_voice or voice
	if args.part_voice == 'both' or args.part_voice == 'act' then
		if voice == 'both' or voice == 'act' then
			for k = 1, aset and #aset or 0 do
				local kend = export.detectEnding(aset[k], latinise)
				local pat = kend and export.getSuffixes(scriptCode, kend, "participle", args)
				if pat then
					local extra = export.joinSuffix(scriptCode, aset[k], pat[1])
					pap = arrcat_nodup(pap, extra)
				end
			end
		end
		if args.pap_mod == 'blank' then
			pap = {}
		elseif args.pap_mod == 'after' then
			pap = arrcat_nodup(pap, args.pap)
		elseif args.pap_mod == 'before' then
			pap = arrcat_nodup(args.pap, pap)
		elseif args.pap_mod == 'replace' then
			pap = args.pap
		else
			error('Argument pap_mod has invalid value '..args.pap_mod)
		end
		parts.pap = pap
	elseif args.part_voice == 'midl' or args.part_voice == 'none' then
		-- permissible
	else
		error('Argument part_voice has invalid value '..args.part_voice)
	end
	if args.part_voice == 'both' or args.part_voice == 'midl' then
		local list
		local from_active = forBoth or voice == 'act'
		if from_active then
			list = aset
		else
			list = mset
		end
		for k = 1, list and #list or 0 do
			local kend = export.detectEnding(list[k], latinise)
			if from_active then kend = pattern[kend].mid end
			local pat = kend and export.getSuffixes(scriptCode, kend, "participle", args)
			if pat then
				local extra = export.joinSuffix(scriptCode, list[k], pat[1])
				pmp = arrcat_nodup(pmp, extra)
			end
		end
		if args.pmp_mod == 'blank' then
			pmp = {}
		elseif args.pmp_mod == 'after' then
			pmp = arrcat_nodup(pmp, args.pmp)
		elseif args.pmp_mod == 'before' then
			pmp = arrcat_nodup(args.pmp, pmp)
		elseif args.pmp_mod == 'replace' then
			pmp = args.pmp
		else
			error('Argument pmp_mod has invalid value '..args.pmp_mod)
		end
		parts.pmp = pmp
	end
	
	-- parts.debug = debug..'\n\n'..debug_outer
	return parts
end

function export.show(frame)
	local args = mw.clone(frame:getParent().args)
	local cite = args[1] or args["cite"] or mw.title.getCurrentTitle().text
	local currentScript = lang:findBestScript(cite)
	local xo = {impl = args.impl, y = args.y, showtr = args.showtr, subst = args.subst}
	local parts = export.special_forms(cite, args)
	parts.cat = parts.cat or {}
	if args.futu then
		local fuparts = export.future_forms(args.futu, args, true)
		fuparts.cat = fuparts.cat or {}
		for k, v in pairs(fuparts) do
			if k == 'cat' then
				parts.cat = arrcat_nodup(parts.cat, fuparts.cat)
			else
				parts[k] = fuparts[k]
			end
		end
--		dump_table('parts', parts)
	end
	local label = args.label or cite
	return export.present_special(label, currentScript, parts, xo)
end

function export.detectFutureEnding(cite3s, latinise)
	if latinise then
		local cfin = cite3s
		cite3s = (lang:transliterate(cfin, latinise))
	end
	local len, wordEnd
	local ending
	for len = 6, 1, -1 do
		wordEnd = sub(cite3s, -len)
		ending = future_endings[wordEnd]
		if not ending then
			-- Try again
		else
-- debug_outer = debug_outer..'\n'..cite3s..'='..ending..'\n'
			return ending
		end
	end
-- debug_outer = debug_outer..'\n'..cite3s..'='.."a"..'\n'
	return "a"
end

local future_pattern = {
	["ati"] = {
		{"⌫⌫⌫āmi"}, {"⌫⌫⌫āma"},   {"⌫⌫si"}, {"⌫ha"},    {""}, {"⌫⌫nti"},
		mid = "ate",
		conditional = {
			{"⌫⌫ṃ"},  -- Collins also has 1s {"⌫⌫⌫āmi"}
							{},					 -- Kaccayano, Aggavamsa and Duroiselle have -amhā
												 -- while Warder and Collins have -āma.
--	                        {"⌫⌫mhā", "⌫⌫⌫āma"}, -- We need to gather evidence and then decide.  
			{"⌫⌫", "⌫⌫⌫i"},	{"⌫ha"},             -- Duroiselle also has 2s "⌫⌫⌫e", which Collins says is middle.
			{"⌫⌫⌫ā", "⌫⌫"},	{"⌫⌫ṃsu"},           -- Duroiselle also has 3s as in future.
		},
		participle = {{"⌫⌫nt"}},
	},
	["ate"] = { -- Also services "ati"
		{"⌫⌫ṃ"}, {"⌫⌫⌫āmhe"},  {"⌫⌫se"}, {"⌫⌫vhe"},  {"⌫e"}, {"⌫⌫nte"},
		conditional = {
			{"⌫⌫ṃ"},	        {"⌫⌫⌫āmhase"},
			{"⌫⌫⌫e", "⌫⌫se"},	{"⌫⌫vhe"}, -- Duroiselle has first 2s as active.
			{"⌫ha"},	        {"⌫⌫⌫iṃsu"},
		},
		participle = {{"⌫⌫māna", "⌫⌫⌫āna"}},
		mid = "ate"
	},
	["ā"] = {
		conditional = {
			{"⌫aṃ"},  -- Collins also has 1s {"mi"}
	        {}, --          {"⌫amhā", "ma"},  -- See above for controversy.  
			{"⌫a", "⌫i"},	{"⌫atha"},        -- Duroiselle also has 2s "⌫⌫⌫e", which Collins says is middle.
			{"", "⌫a"},  	{"⌫aṃsu"},        -- Duroiselle also has 3s as in future.
		},
        mid = "atha_for_aa",
	},
	["a"] = {
		conditional = {
			{"ṃ"},  -- Collins also has 1s {"āmi"}
	        {}, --         {"mhā", "⌫āma"}, -- See above for controversy.  
			{"", "⌫i"},	    {"tha"},        -- Duroiselle also has 2s "⌫⌫⌫e", which Collins says is middle.
			{"⌫ā", ""},  	{"ṃsu"},        -- Duroiselle also has 3s as in future.
		},
        mid = "atha_for_a",
	},
	["atha"] = {
		conditional = {
			{"⌫⌫⌫ṃ"},	        {"⌫⌫⌫⌫āmhase"},
			{"⌫⌫⌫⌫e", "⌫⌫⌫se"},	{"⌫⌫⌫vhe"}, -- Duroiselle has first 2s as active.
			{""},	            {"⌫⌫⌫⌫iṃsu"},
		},
		mid = "atha"
	},
    ["atha_for_a"] = {
       	conditional = {
			{"ṃ"},	        {"⌫āmhase"},
			{"⌫e", "se"},	{"vhe"}, -- Duroiselle has first 2s as active.
			{"tha"},	    {"⌫iṃsu"},
		},
		mid = "atha_for_a"
	},
	atha_for_aa = {
		conditional = {
			{"⌫aṃ"},	        {"mhase"},
			{"⌫e", "⌫ase"},	{"⌫avhe"}, -- Duroiselle has first 2s as active.
			{"⌫atha"},	    {"⌫iṃsu"},
		},
		mid = "atha_for_aa"
	},
	iti = {
		{"⌫⌫⌫āmi"}, {"⌫⌫⌫āma"}, {"⌫⌫si"}, {"⌫ha"}, {""}, {"⌫⌫nti"},
		mid = "void"
	},
	heti = {
		{"⌫⌫mi"}, {"⌫⌫ma"}, {"⌫⌫si"}, {"⌫ha"}, {""}, {"⌫⌫nti"},
		mid = "void"
	},
	["kāhiti"] = {
		{"⌫⌫⌫āmi"}, {"⌫⌫⌫āma"}, {"⌫⌫si"}, {}, {""}, {"⌫⌫nti"},
		mid = "void"
	},
	void = { {}, {}, {}, {}, {}, {}, mid = "void"}
}

-- convert Latin script inflections to another script
-- ⒹⓉ

function export.getFutureSuffixes(sc, ending, tense, option)
	local impl = option and option.impl or 'yes'
	local yopt = option and option.y
	local pat
	if not future_pattern[ending] then
		error('No verb pattern for ending "'..ending..'".')
	end
	if tense then
		pat = future_pattern[ending][tense]
	else
		pat = future_pattern[ending]
	end
	if 'Latn' == sc then
		return pat
	elseif not pat then
		return nil
	elseif pat[sc] then
		return pat[sc]
	else
		local olen = {Mymr = 2, Lana = 2} -- Assume NFC
		local attha_len = {Latn = 5, Sinh = 4}
		eyya_len = {Mymr = 3, Sinh = 5}
		local alen
		if impl == 'no' and (sc == 'Thai' or sc == 'Laoo') then
			alen = 1
		else
			alen = 0
		end
		local way = {
			["ati"]  = {pseudostem = "kati", ndel = 2 + alen},
			["heti"]  = {pseudostem = "heti", ndel = 3}, -- Tricky code
			["iti"]  = {pseudostem = "kiti", ndel = 3},
			["ate"]  = {pseudostem = "kate", ndel = 2 + alen},
			["ā"]    = {pseudostem = "kā", ndel = 1},
			["a"]    = {pseudostem = "ka", ndel = alen},
			["atha"] = {pseudostem = "katha", ndel = 1 + 2 * alen},
			atha_for_a = {pseudostem = "ka", ndel = alen},
			atha_for_aa = {pseudostem = "kā", ndel = 1},
			["kāhiti"]  = {pseudostem = "kāhiti", ndel = 5}, --Tricky code
			void = {pseudostem = "ku", ndel = 1}, -- rhubarb
		}
		way = way[ending]
		if way then
			pat[sc] = convert_suffixes(way.pseudostem, way.ndel, pat, sc,
									   impl, yopt)
		else
			error('Cannot convert derivations from suffix '..ending..' for '..sc)
		end
		return pat[sc]
	end
end

function export.future_forms(cite, args, subset)
	local currentScript = lang:findBestScript(cite)
	local scriptCode = currentScript:getCode()
	local latinise = nil
	if scriptCode == "None" and args.sc then -- Resort to sc
		scriptCode = args["sc"]
		currentScript = m_scripts.getByCode(scriptCode, "No such script as ".. scriptCode .. ".")
		latinise = currentScript
		augment[scriptCode] = to_script("a", scriptCode)
	end
	if subset then args = export.parse(args) end
	args = export.futureParse(args, subset)
	args.cite = arrcat_nodup({cite}, args.cite)
	local debug = '\n\nAt (1), args.cite = '..table.concat(args.cite, ' ')
	local patpa, patpm, basepa, basepm
	local parts = {}
	local option = {}
	option.aa = args.aa

-- Handle future tense.  This forms a base for others.
	local ending = export.detectFutureEnding(cite, latinise)
	patpa = export.getFutureSuffixes(scriptCode, ending, nil, args)
	basepa = cite
	local mset = nil
	local forBoth = false
	local aset = args.cite
	local voice = "both"
	if 'e' == asub(ending, -1) then -- No active inflection
		patpm = patpa
		patpa = nil
		basepm = cite
		aset = nil
		mset = args.cite
		voice = "midl"
	elseif args.midl and args.midl[1] then
		basepm = args.midl[1]
		mset = args.midl
		local midend = export.detectFutureEnding(basepm, latinise)
		patpm = future_pattern[midend].mid and
				export.getFutureSuffixes(scriptCode, future_pattern[midend].mid, nil, args)
	elseif args.voice == 'act' then
		patpm = nil
		voice = "act"
	else
		patpm = future_pattern[ending].mid and
				export.getFutureSuffixes(scriptCode, future_pattern[ending].mid, nil, args)
		basepm = cite
		forBoth = true
		mset = args.cite
	end
	local formsa = nil
	local formsm = nil
	if patpa then
		formsa = export.joinSuffixes(scriptCode, basepa, patpa, option)
		for k = 2, #aset do
			basepa = aset[k]
			patpa = export.getFutureSuffixes(
				scriptCode, export.detectFutureEnding(basepa, latinise), nil, args)
			local extras = export.joinSuffixes(scriptCode, basepa, patpa, option)
			for ip = 1, 6 do
				formsa[ip] = arrcat_nodup(formsa[ip], extras[ip])
			end
		end
	end
	if patpm or forBoth then
		if patpm then
			formsm = export.joinSuffixes(scriptCode, basepm, patpm, option)
		else
			formsm = {{}, {}, {}, {}, {}, {}}
		end
		for k = 2, #mset do
			basepm = mset[k]
			local kend = export.detectFutureEnding(basepm, latinise)
			kend = future_pattern[kend].mid
			if kend then
				patpm = export.getFutureSuffixes(scriptCode, kend, nil, args)
				local extras = export.joinSuffixes(scriptCode, basepm, patpm, option)
				for ip = 1, 6 do
					formsm[ip] = arrcat_nodup(formsm[ip], extras[ip])
				end
			end
		end
	end
	if args.voice == "act" or args.voice == "both" then
		-- handled already
	elseif args.voice == "none" then
		formsa = nil
		formsm = nil
		voice = "none"
	elseif args.voice then
		error('Argument voice has bad value "'..args.voice..'.')
	end
	formsa, formsm = export.modify(formsa, formsm, '', args)

	parts.futua = formsa
	parts.futum = formsm

-- Form conditional from future.
	local aug
	if args["impl"] == 'no' then
		aug = augment_alph[scriptCode] or augment[scriptCode]
	else
		aug = augment[scriptCode]
	end
--	error("impl="..tostring(args["impl"]).." aug="..aug)
--	error("impl="..tostring(args["impl"]).." aug="..aug.." aset="..tostring(aset[1])..
--		" args.augment="..args.augment)
	args.cond_voice = args.cond_voice or voice
--	error("args.cond_voice="..tostring(args.cond_voice))
	local cond = {}
	if (args.cond_voice ~= 'none') then
		if args.augment == 'without' then
			if aset then cond = aset end
		elseif args.augment == 'with_made' then
			if aset then
				for k = 1, #aset do
					cond[k] = aug .. aset[k]
				end
			end
		elseif args.augment == 'with_given' then
			cond = args.cond
		elseif args.augment == 'both_made' then
			if aset then
				for k = 1, #aset do
					cond[k] = aug .. aset[k]
				end
				for k = 1, #aset do
					cond[#aset+k] = aset[k]
				end
			end
		elseif args.augment == 'both_given' then
			cond = arrcat_nodup(args.cond or {}, aset or {})
		else
			error('Argument augment has bad value "'..args.augment..'".')
		end
	end
--	mw.addWarning('args.cite[1]='..args.cite[1]..', args.cond_voice='..args.cond_voice)
	if (args.cond_voice == 'both' or args.cond_voice == 'act')
	then
		formsa = {{}, {}, {}, {}, {}, {}}
		for k = 1, #cond do
			local kend = export.detectFutureEnding(cond[k], latinise)
			local patma = export.getFutureSuffixes(scriptCode, kend, 'conditional', args)
			if patma then
				local extras = export.joinSuffixes(scriptCode, cond[k], patma, option)
				for ip = 1, 6 do
					formsa[ip] = arrcat_nodup(formsa[ip], extras[ip])
				end
			end
		end
	elseif (args.cond_voice == 'none' or args.cond_voice == 'midl') then
		formsa = nil
	else
		error('Argument cond_voice has bad value "'..args.cond_voice..'".')
	end
	if (args.cond_voice == 'both' or args.cond_voice == 'midl')
	then
		formsm = {{}, {}, {}, {}, {}, {}}
		if not forBoth then
			cond = {}
			if args.augment == 'without' then
				cond = mset
			elseif args.augment == 'with_made' then
				for k = 1, #mset do
					cond[k] = aug .. mset[k]
				end
			elseif args.augment == 'with_given' then
				cond = args.midl and args.midl[1] and args.cond_midl or args.cond
			elseif args.augment == 'both_made' then
				for k = 1, #mset do
					cond[k] = aug .. mset[k]
				end
				for k = 1, #mset do
					cond[#mset+k] = mset[k]
				end
			elseif args.augment == 'both_given' then
				cond = arrcat_nodup(
					args.midl and args.midl[1] and (args.cond_midl or {}) or
					args.cond or {},
					mset or {})
			else
				error('Argument augment has bad value "'..args.augment..'".')
			end
		end
		for k = 1, #cond do
			local kend = export.detectFutureEnding(cond[k], latinise)
			debug=debug..'\n\nAt (7) cond[k]='..cond[k]
			if forBoth then
	    		if future_pattern[kend].mid then
	    			kend = future_pattern[kend].mid
	    		elseif kend == "eti" then
	    			kend = nil
				else
					mw.addWarning('No middle endings for ending "'..kend..'".')
	    			kend = nil
	    		end
			end
			local pat = kend and export.getFutureSuffixes(scriptCode, kend, 'conditional', args)
			if not kend then
				-- nothing to do
			elseif pat then
				local extras = export.joinSuffixes(scriptCode, cond[k], pat, option)
				for ip = 1, 6 do
					formsm[ip] = arrcat_nodup(formsm[ip], extras[ip])
				end
			else
				mw.addWarning('No conditional middle suffixes for '..cond[k]..
					' ('..kend..')')
			end
		end
	else
		formsm = nil
	end
	formsa, formsm = export.modify(formsa, formsm, 'cond_', args)
	parts.conda = formsa
	parts.condm = formsm

--	List present participles.
	local pap, pmp = {}, {}
	args.part_voice = args.part_voice or voice
	if args.part_voice == 'both' or args.part_voice == 'act' then
		if voice == 'both' or voice == 'act' then
			for k = 1, aset and #aset or 0 do
				local kend = export.detectFutureEnding(aset[k], latinise)
				local pat = kend and export.getFutureSuffixes(scriptCode, kend, "participle", args)
				if pat then
					local extra = export.joinSuffix(scriptCode, aset[k], pat[1])
					pap = arrcat_nodup(pap, extra)
				end
			end
		end
		if args.fap_mod == 'blank' then
			pap = {}
		elseif args.fap_mod == 'after' then
			pap = arrcat_nodup(pap, args.fap)
		elseif args.fap_mod == 'before' then
			pap = arrcat_nodup(args.fap, pap)
		elseif args.fap_mod == 'replace' then
			pap = args.fap
		else
			error('Argument fap_mod has invalid value '..args.fap_mod)
		end
		parts.fap = pap
	elseif args.part_voice == 'midl' or args.part_voice == 'none' then
		-- permissible
	else
		error('Argument part_voice has invalid value '..tostring(args.part_voice))
	end
	if args.part_voice == 'both' or args.part_voice == 'midl' then
		local list
		local from_active = forBoth or voice == 'act'
		if from_active then
			list = aset
		else
			list = mset
		end
		for k = 1, list and #list or 0 do
			local kend = export.detectFutureEnding(list[k], latinise)
			if from_active then kend = future_pattern[kend].mid end
			local pat = kend and export.getFutureSuffixes(scriptCode, kend, "participle", args)
			if pat then
--				if 2 == k then
--					error("k="..k.."list[k]="..list[k])
--				end
				local extra = export.joinSuffix(scriptCode, list[k], pat[1])
				pmp = arrcat_nodup(pmp, extra)
			end
		end
		if args.fmp_mod == 'blank' then
			pmp = {}
		elseif args.fmp_mod == 'after' then
			pmp = arrcat_nodup(pmp, args.fmp)
		elseif args.fmp_mod == 'before' then
			pmp = arrcat_nodup(args.fmp, pmp)
		elseif args.fmp_mod == 'replace' then
			pmp = args.fmp
		else
			error('Argument fmp_mod has invalid value '..args.fmp_mod)
		end
		parts.fmp = pmp
	end

	-- parts.debug = debug..'\n\n'..debug_outer
	return parts
end

function export.show_future(frame)
	local args = mw.clone(frame:getParent().args)
	local cite = args[1] or args["cite"] -- or mw.title.getCurrentTitle().text
	local xo = {impl = args.impl, y = args.y, showtr = args.showtr, subst = args.subst}
	local parts = export.future_forms(cite, args, false)
	local label = args.label or cite
	local currentScript = lang:findBestScript(cite)
	return export.present_future(label, currentScript, parts, xo)
end

return export