local p = {} local modwrap = require('Module:Wrapper').getWrapper('Module:Navseasoncats/translate') local l10n = { 	the = ,  -- ' the' at enwiki 	s = ,  -- 's' at enwiki 	decades = '-এর দশক',  --  at enwiki 	catnames = { 		[14] = 'বিষয়শ্রেণী পরিভ্রমণ পরিসীমা সমাপ্তি (খালি)', 		[15] = 'বিষয়শ্রেণী পরিভ্রমণ বিচ্ছিন্ন', 		[27] = 'বিষয়শ্রেণী পরিভ্রমণ বছর ও দশক', 		[28] = 'বিষয়শ্রেণী পরিভ্রমণ দশক ও শতাব্দী', 		failed = 'বিষয়শ্রেণী পরিভ্রমণ টেমপ্লেট পরিভ্রমণ বাক্স উৎপন্ন করতে ব্যর্থ হয়েছে', 	} }  --========================================================================== --                                Globals                                    --==========================================================================  local currtitle = mw.title.getCurrentTitle() currtitle = { 	subpageText = modwrap.text2en(currtitle.subpageText), 	text = modwrap.text2en(currtitle.text), 	nsText = modwrap.text2en(currtitle.nsText), } local nexistingcats = 0 local errors =  local testcasecolon = local testcases = string.match(currtitle.subpageText, '^testcases') if    testcases then testcasecolon = ':' end local navborder = true local followRs = true local skipgaps = false local listall = false local tlistall = {} local tlistallbwd = {} local tlistallfwd = {} local ttrackingcats = { --when reindexing, Ctrl+H 'trackcat(1,' & 'ttrackingcats[1]' 	, -- [1] placeholder for Category:Navseasoncats using cat parameter 	, -- [2] placeholder for Category:Navseasoncats using testcase parameter 	, -- [3] placeholder for Category:Navseasoncats using unknown parameter 	, -- [4] placeholder for Category:Navseasoncats range not using en dash 	, -- [5] placeholder for Category:Navseasoncats range abbreviated (MOS) 	, -- [6] placeholder for Category:Navseasoncats range redirected (base change) 	, -- [7] placeholder for Category:Navseasoncats range redirected (end) 	, -- [8] placeholder for Category:Navseasoncats range redirected (MOS) 	, -- [9] placeholder for Category:Navseasoncats range redirected (other) 	, --[10] placeholder for Category:Navseasoncats range gaps 	, --[11] placeholder for Category:Navseasoncats range irregular 	, --[12] placeholder for Category:Navseasoncats range irregular, 0-length 	, --[13] placeholder for Category:Navseasoncats range ends (present) 	, --[14] placeholder for Category:Navseasoncats range ends (blank, MOS) 	, --[15] placeholder for Category:Navseasoncats isolated 	, --[16] placeholder for Category:Navseasoncats default season gap size 	, --[17] placeholder for Category:Navseasoncats decade redirected 	, --[18] placeholder for Category:Navseasoncats year redirected (base change) 	, --[19] placeholder for Category:Navseasoncats year redirected (var change) --new 	, --[20] placeholder for Category:Navseasoncats year redirected (other) 	, --[21] placeholder for Category:Navseasoncats roman numeral redirected 	, --[22] placeholder for Category:Navseasoncats nordinal redirected 	, --[23] placeholder for Category:Navseasoncats wordinal redirected 	, --[24] placeholder for Category:Navseasoncats TV season redirected 	, --[25] placeholder for Category:Navseasoncats using skip-gaps parameter 	, --[26] placeholder for Category:Navseasoncats year and range 	, --[27] placeholder for Category:Navseasoncats year and decade 	, --[28] placeholder for Category:Navseasoncats decade and century 	, --[29] placeholder for Category:Navseasoncats in mainspace 	, --[30] placeholder for Category:Navseasoncats redirection error } local avoidself =  (not string.match(currtitle.text, 'Navseasoncats with') and 					not string.match(currtitle.text, 'Navseasoncats.*/doc') and 					not string.match(currtitle.text, 'Navseasoncats.*/sandbox') and 					currtitle.text ~= 'বিষয়শ্রেণী_পরিভ্রমণ' and 					currtitle.nsText ~= 'User_talk' and 					currtitle.nsText ~= 'Template_talk' and 					(currtitle.nsText ~= 'টেমপ্লেট' or testcases)) --avoid nested transclusion errors (i.e. )  --========================================================================== --                      Utility & category functions                         --==========================================================================  --Determine if a page exists (in a function for localization). local function pageexists( title, namespace, secondary, lastpart ) 	if secondary == 'year2decade' then 		for _, t in ipairs(modwrap.year2decade) do 			if title:find(t.find) then 				for _, v in ipairs(t) do 					if pageexists( title:gsub(t.find, v[1]), namespace ) then 						if t.replace then 							return true, (v[2] or ) 						end 						return true, lastpart:gsub( '◆YEAR◆', '◆DECADE◆'..(v[2] or ) ) 					end 				end 			end 		end 	elseif secondary == 'decade2century' then 		mw.log(title, namespace, secondary) 	end 	return mw.title.new( modwrap.text2local(title), namespace ).exists end  --Error message handling function p.errorclass( msg ) 	return mw.text.tag( 'span', {class='error mw-ext-cite-error'}, 'Error! '..string.gsub(msg, '&#', '&#') ) end  --Failure handling function p.failedcat( errors, sortkey ) 	if avoidself then 		return (errors or )..'***'..l10n.catnames.failed..'***'.. 			   ''..(sortkey or 'O')..'\n' 	end 	return end  --Tracking cat handling. --	key: 15 (when reindexing ttrackingcats{}, Ctrl+H 'trackcat(1,' & 'ttrackingcats[1]') --	cat: 'Navseasoncats isolated';  to remove --Used by main, all nav_*(), & several utility functions. local function trackcat( key, cat ) 	if avoidself and key and cat then 		if cat ~=  then 			local catname = l10n.catnames[key] 			if catname then 				ttrackingcats[key] = ''..testcasecolon..'Category:'..catname..'' 			end 		else 			ttrackingcats[key] =  		end 	end end  --Check for unknown parameters. --Used by main only. local function checkforunknownparams( tbl ) 	local knownparams = { 		['min'] = 'min', 		['max'] = 'max', 		['cat'] = 'cat', 		['testcase'] = 'testcase', 		['testcasegap'] = 'testcasegap', 		['skip-gaps'] = 'skip-gaps', 		['list-all-links'] = 'list-all-links', 		['follow-redirects'] = 'follow-redirects', 	} 	for k, _ in pairs (tbl) do 		if knownparams[k] == nil then 			trackcat(3, 'Navseasoncats using unknown parameter') 		end 	end end  --Check for nav_*() navigational isolation (not necessarily an error). --Used by all nav_*(). local function isolatedcat() 	if nexistingcats == 0 then 		trackcat(15, 'Navseasoncats isolated') 	end end  --Returns the target of , if it exists, else returns the original cat. --, etc., if found, are evaluated. --Used by catlinkfollowr(), and so indirectly by all nav_*(). local function rtarget( frame, cat ) 	local catcontent = mw.title.new( modwrap.text2local(cat or ), 'Category' ):getContent() 	if string.match( catcontent or , '{{ *[Cc]at' ) then --prelim test 		local regex = { 			--the following 11 pages (7 condensed) redirect to Template:Category redirect (as of 6/2019): 			'{{ *[Cc]ategory *[Rr]edirect', --most likely match 1st 			'{{ *[Cc]at *redirect',         --444+240 transclusions 			'{{ *[Cc]at *redir',            --8+3 			'{{ *[Cc]ategory *move',        --6 			'{{ *[Cc]at *red',              --6 			'{{ *[Cc]atr',                  --4 			'{{ *[Cc]at *move',             --0 		} 		for _, v in pairs (regex) do 			local rtarget = mw.ustring.match( catcontent, v..'%s*|%s*([^|}]+)' ) 			if rtarget then 				if string.match(rtarget, '{{') then --, etc., exists; evaluate 					local regex_ty = '%s*|%s*([^{}]*{{([^{|}]+)}}[^{}]-)%s*}}' --eval null-param templates only; expanded if/as needed 					local rtarget_orig, ty = mw.ustring.match( catcontent, v..regex_ty ) 					if rtarget_orig then 						local ty_eval = frame:expandTemplate{ title = ty, args = { page = cat } } --frame:newChild doesn't work, use 'page' param instead 						local rtarget_eval = mw.ustring.gsub(rtarget_orig, '', ty_eval ) 						return rtarget_eval 					else --sub-parameters present; track & return default 						trackcat(30, 'Navseasoncats redirection error') 					end 				end 				rtarget = mw.ustring.gsub(rtarget, '^1%s*=%s*', ) 				rtarget = string.gsub(rtarget, '^[Cc]ategory:', ) 				return rtarget 			end 		end --for 	end --if 	return cat end  --Similar to : make a piped link to a category, if it exists; --if it doesn't exist, just display the greyed link title without linking. --Follows s. --Returns { --			['cat'] = cat, --			['catexists'] = true, --			['rtarget'] = <#R target>, --			['navelement'] = <#R target navelement>, --			['displaytext'] = displaytext, --		  } if #R followed; --returns { --			['cat'] = cat, --			['catexists'] = <true|false>, --			['rtarget'] = nil, --			['navelement'] = <cat navelement>, --			['displaytext'] = displaytext, --		  } otherwise. --Used by all nav_*(). local function catlinkfollowr( frame, cat, displaytext, displayend, i ) 	cat         = mw.text.trim(cat or ) 	displaytext = mw.text.trim(displaytext or ) 	displayend  = displayend or false --bool flag to override displaytext IIF the cat/target is terminal (e.g. "2021–present" or "2021–")  	local grey = '#888' 	local disp = cat 	if displaytext ~=  then --use 'displaytext' parameter if present 		disp = mw.ustring.gsub(displaytext, '%s+%(.+$', ); --strip any trailing disambiguator 	end 	if i == 0 and cat:find('◆DECADE◆', 1, true) then 		disp = disp..l10n.decades 	end  	local link, nilorR 	local exists = pageexists( cat, 'Category' ) 	if exists then 		nexistingcats = nexistingcats + 1 		if followRs then 			local R = rtarget(frame, cat) --find & follow #R 			if R ~= cat then --#R followed 				nilorR = R 			end  			if displayend then 				local y, hyph, ending = mw.ustring.match(R, '^.-(%d+)([–-])(.*)$') 				if ending == 'present' then 					disp = y..hyph..ending 				elseif ending ==  then 					disp = y..hyph..''..y..'' --hidden y to match spacing 				end 			end  			link = ''..disp..'' 		else 			link = ''..disp..'' 		end 	else 		link = ''..disp..'' 	end  	if listall then 		if nilorR then --#R followed 			table.insert( tlistall, ':Category:'..cat..' → '..':Category:'..nilorR..' ('..link..')' ) 		else --no #R 			table.insert( tlistall, ':Category:'..cat..' ('..link..')' ) 		end 	end  	return { 		['cat'] = cat, 		['catexists'] = exists, 		['rtarget'] = nilorR, 		['navelement'] = link, 		['displaytext'] = disp, 	} end  --Returns a numbered list of all s followed by catlinkfollowr() -> rtarget(). --For a nav_hyphen() cat, also returns a formatted list of all cats searched for & found, & all loop indices. --Used by all nav_*(). local function listalllinks() 	local nl = '\n# ' 	local out =  	if currtitle.nsText == 'Category' then 		errors = p.errorclass('The |list-all-links=yes parameter/utility '.. 							'should not be saved in category space, only previewed.') 		out = p.failedcat(errors, 'Z') 	end  	local bwd, fwd = ,  	if tlistallbwd[1] then 		bwd = '\n\nbackward search:\n# '..table.concat(tlistallbwd, nl) 	end 	if tlistallfwd[1] then 		fwd = '\n\nforward search:\n# '..table.concat(tlistallfwd, nl) 	end  	if tlistall[1] then 		return out..nl..table.concat(tlistall, nl)..bwd..fwd 	else 		return out..nl..'No links found!?'..bwd..fwd 	end end  --Returns the difference b/w 2 ints separated by endash|hyphen, nil if error. --Used by nav_hyphen() only. local function find_duration( cat ) 	local from, to = mw.ustring.match(cat, '(%d+)[–-](%d+)') 	if from and to then 		if to == '00' then return nil end --doesn't follow MOS:DATERANGE 		if (#from == 4) and (#to == 2) then             --1900-01 			to = string.match(from, '(%d%d)%d%d')..to   --1900-1901 		elseif (#from == 2) and (#to == 4) then         --  01-1902 			from = string.match(to, '(%d%d)%d%d')..from --1901-1902 		end 		return (tonumber(to) - tonumber(from)) 	end 	return 0 end  --Returns the ending of a terminal cat, and sets the appropriate tracking cat, else nil. --Used by nav_hyphen() only. local function find_terminaltxt( cat ) 	local terminaltxt = nil 	if mw.ustring.match(cat, '%d+[–-]present$') then 		terminaltxt = 'present' 		trackcat(13, 'Navseasoncats range ends (present)') 	elseif mw.ustring.match(cat, '%d+[–-]$') then 		terminaltxt = 		trackcat(14, 'Navseasoncats range ends (blank, MOS)') 	end 	return terminaltxt end  --Returns an unsigned string of the 1-4 digit decade ending in "0", else nil. --Used by nav_decade() only. local function sterilizedec( decade ) 	if decade == nil or decade ==  then 		return nil 	end  	local dec = string.match(decade, '^[-%+]?(%d?%d?%d?0)$') or 				string.match(decade, '^[-%+]?(%d?%d?%d?0)%D') 	if dec then 		return dec 	else 		--fix 2-4 digit decade 		local decade_fixed234 = string.match(decade, '^[-%+]?(%d%d?%d?)%d$') or 								string.match(decade, '^[-%+]?(%d%d?%d?)%d%D') 		if decade_fixed234 then 			return decade_fixed234..'0' 		end  		--fix 1-digit decade 		local decade_fixed1   = string.match(decade, '^[-%+]?(%d)$') or 								string.match(decade, '^[-%+]?(%d)%D') 		if decade_fixed1 then 			return '0' 		end  		--unfixable 		return nil 	end end  --Check for nav_hyphen default gap size + isolatedcat() (not necessarily an error). --Used by nav_hyphen() only. local function defaultgapcat( bool ) 	if bool and nexistingcats == 0 then 		--using "nexistingcats > 0" isn't as useful, since the default gap size obviously worked 		trackcat(16, 'Navseasoncats default season gap size') 	end end  --12 -> 12th, etc. --Used by nav_nordinal() & nav_wordinal(). function p.addord(i)     if tonumber(i) then         local s = tostring(i)                  local tens = string.match(s, '%d%d$')         if tens then         	if tens == '10' then             return s .. 'ম'          else  return s .. 'শ'         end         end         local ones = string.match(s, '%d$')         if ones == '1' or ones == '5' or ones == '7' or ones == '8' or ones == '9' or ones == '10' then             return s .. 'ম'         elseif ones == '2' or ones == '3' then             return s .. 'য়'         elseif ones == '4' then             return s .. 'র্থ'         elseif ones == '6' then              return s .. 'ষ্ঠ'         else  return s .. 'শ'         	         	       end     end     return i end  --Returns the properly formatted central nav element. --Expects an integer i, and a catlinkfollowr() table. --Used by nav_decade() & nav_ordinal() only. local function navcenter( i, catlink ) 	if i == 0 then --center nav element 		if navborder == true then 			return '*'..catlink.displaytext..'\n' 		else 			return '*'..catlink.navelement..'\n' 		end 	else 		return '*'..catlink.navelement..'\n' 	end end  --Return conditionally aligned stacked navs. --Used by main only. local function nav1nav2( nav1, nav2 ) 	if avoidself then 		local forcealign = '' 		return forcealign..'\n'..nav1..'\n'..nav2..'\n' 	else 		return nav1..'\n'..nav2 	end end  --========================================================================== --                  Formerly separated templates/modules                     --==========================================================================  --========================================================  local function nav_hyphen( frame, start, hyph, finish, firstpart, lastpart, minseas, maxseas, testgap ) 	--Expects a PAGENAME of the form "Some sequential 2015–16 example cat", where 	--	start     = 2015 	--	hyph      = – 	--	finish    = 16 (sequential years can be abbreviated, but others should be full year, e.g. "2001–2005") 	--	firstpart = Some sequential 	--	lastpart  = example cat 	--	minseas   = 1800 ('min' starting season shown; optional; defaults to -9999) 	--	maxseas   = 2000 ('max' starting season shown; optional; defaults to 9999; 2000 will show 2000-01) 	--	testgap   = 0 (testcasegap parameter for easier testing; optional)  	--sterilize start 	if string.match(start or , '^%d%d?%d?%d?$') == nil then --1-4 digits, AD only 		local start_fixed = mw.ustring.match(start or , '^%s*(%d%d?%d?%d?)%D') 		if start_fixed then 			start = start_fixed 		else 			errors = p.errorclass('Function nav_hyphen can\'t recognize the number "'..(start or )..'" '.. 								  'in the first part of the "season" that was passed to it. '.. 								  'For e.g. "2015–16", "2015" is expected via "|2015|–|16|".') 			return p.failedcat(errors, 'H') 		end 	end 	local nstart = tonumber(start)  	--en dash check 	if hyph ~= '–' then 		trackcat(4, 'Navseasoncats range not using en dash') --nav still processable, but track 	end  	--sterilize finish & check for weird parents 	local tgaps   = {} --table of gap sizes found b/w terms    { [<gap size found>]    = 1 } 	local ttlens  = {} --table of term lengths found w/i terms { [<term length found>] = 1 } 	local tirregs = {} --table of ir/regular-term-length cats' "from"s & "to"s found 	local regularparent = true 	if (finish == -1) or --"Members of the Scottish Parliament 2021–present" 	   (finish == 0)	 --"Members of the Scottish Parliament 2021–" 	then 		regularparent = false 		if maxseas == nil or maxseas ==  then 			maxseas = start --hide subsequent ranges 		end 		if finish == -1 then trackcat(13, 'Navseasoncats range ends (present)') 		else				 trackcat(14, 'Navseasoncats range ends (blank, MOS)') end 	elseif (start == finish) and 		   (ttrackingcats[15] ~= ) --nav_year found isolated; check for surrounding hyphenated terms (e.g. UK MPs 1974) 	then 		trackcat(15, ) --reset for another check later 		trackcat(12, 'Navseasoncats range irregular, 0-length') 		ttlens[0] = 1 --calc ttlens for std cases below 		regularparent = 'isolated' 	end 	if (string.match(finish or , '^%d+$') == nil) and 	   (string.match(finish or , '^%-%d+$') == nil) 	then 		local finish_fixed = mw.ustring.match(finish or , '^%s*(%d%d?%d?%d?)%D') 		if finish_fixed then 			finish = finish_fixed 		else 			errors = p.errorclass('Function nav_hyphen can\'t recognize "'..(finish or )..'" '.. 								  'in the second part of the "season" that was passed to it. '.. 								  'For e.g. "2015–16", "16" is expected via "|2015|–|16|".') 			return p.failedcat(errors, 'I') 		end 	else 		if string.len(finish) >= 5 then 			errors = p.errorclass('The second part of the season passed to function nav_hyphen should only be four or fewer digits, not "'..(finish or )..'". '.. 								  'See MOS:DATERANGE for details.') 			return p.failedcat(errors, 'J') 		end 	end 	local nfinish = tonumber(finish)  	--save sterilized parent range for easier lookup later 	tirregs['from0'] = nstart 	tirregs['to0']   = nfinish  	--sterilize min/max 	local nminseas_default = -9999 	local nmaxseas_default =  9999 	local nminseas = tonumber(minseas) or nminseas_default --same behavior as nav_year 	local nmaxseas = tonumber(maxseas) or nmaxseas_default --same behavior as nav_year 	if nminseas > nstart then nminseas = nstart end 	if nmaxseas < nstart then nmaxseas = nstart end  	local lspace = ' ' --assume a leading space (most common) 	local tspace = ' ' --assume a trailing space (most common) 	if string.match(firstpart, '%($') then lspace =  end --DNE for "Madrid city councillors (2007–2011)"-type cats 	if string.match(lastpart,  '^%)') then tspace =  end --DNE for "Madrid city councillors (2007–2011)"-type cats  	--calculate term length/intRAseason size & finishing year 	local term_limit = 10 	local t = 1 	while t <= term_limit and regularparent == true do 		local nish = nstart + t --use switchADBC to flip this sign to work for years BC, if/when the time comes 		if (nish == nfinish) or (string.match(nish, '%d?%d$') == finish) then 			ttlens[t] = 1 			break 		end 		if t == term_limit then 			errors = p.errorclass('Function nav_hyphen can\'t determine a reasonable term length for "'..start..hyph..finish..'".') 			return p.failedcat(errors, 'K') 		end 		t = t + 1 	end  	--apply MOS:DATERANGE to parent 	local lenstart = string.len(start) 	local lenfinish = string.len(finish) 	if lenstart == 4 and regularparent == true then --"2001–..." 		if t == 1 then --"2001–02" & "2001–2002" both allowed 			if lenfinish ~= 2 and lenfinish ~= 4 then 				errors = p.errorclass('The second part of the season passed to function nav_hyphen should be two or four digits, not "'..finish..'".') 				return p.failedcat(errors, 'L') 			end 		else --"2001–2005" is required for t > 1; track "2001–05"; anything else = error 			if lenfinish == 2 then 				trackcat(5, 'Navseasoncats range abbreviated (MOS)') 			elseif lenfinish ~= 4 then 				errors = p.errorclass('The second part of the season passed to function nav_hyphen should be four digits, not "'..finish..'".') 				return p.failedcat(errors, 'M') 			end 		end 		if finish == '00' then --full year required regardless of term length 			trackcat(5, 'Navseasoncats range abbreviated (MOS)') 		end 	end  	--calculate intERseason gap size 	local hgap_default     = 0 --assume & start at the most common case: 2001–02 -> 2002–03, etc. 	local hgap_limit_reg   = 6 --less expensive per-increment (inc x 4) 	local hgap_limit_irreg = 6 --more expensive per-increment (inc x 23: inc x (k_bwd + k_fwd) = inc x (12 + 11)) 	local hgap_success = false 	local hgap = hgap_default 	while hgap <= hgap_limit_reg and regularparent == true do --verify 		local prevseason2 = firstpart..lspace..(nstart-t-hgap)..hyph..string.match(nstart-hgap, '%d?%d$')    ..tspace..lastpart 		local nextseason2 = firstpart..lspace..(nstart+t+hgap)..hyph..string.match(nstart+2*t+hgap, '%d?%d$')..tspace..lastpart 		local prevseason4 = firstpart..lspace..(nstart-t-hgap)..hyph..(nstart-hgap)    ..tspace..lastpart 		local nextseason4 = firstpart..lspace..(nstart+t+hgap)..hyph..(nstart+2*t+hgap)..tspace..lastpart 		if t == 1 then --test abbreviated range first, then full range, to be frugal with expensive functions 			if pageexists(prevseason2, 'Category') or --use 'or', in case we're at the edge of the cat structure, 			   pageexists(nextseason2, 'Category') or --or we hit a "–00"/"–2000" situation on one side 			   pageexists(prevseason4, 'Category') or 			   pageexists(nextseason4, 'Category') 			then 				hgap_success = true 				break 			end 		elseif t > 1 then --test full range first, then abbreviated range, to be frugal with expensive functions 			if pageexists(prevseason4, 'Category') or --use 'or', in case we're at the edge of the cat structure, 			   pageexists(nextseason4, 'Category') or --or we hit a "–00"/"–2000" situation on one side 			   pageexists(prevseason2, 'Category') or 			   pageexists(nextseason2, 'Category') 			then 				hgap_success = true 				break 			end 		end 		hgap = hgap + 1 	end 	if hgap_success == false then 		hgap = tonumber(testgap) or hgap_default --tracked via defaultgapcat() 	end  	--preliminary scan to determine ir/regular spacing of nearby cats; 	--to limit expensive function calls, MOS:DATERANGE-violating cats are ignored; 	--an irregular-term-length series should follow "YYYY..hyph..YYYY" throughout 	if hgap <= hgap_limit_reg then --also to isolate temp vars 		--find # of nav-visible ir/regular-term-length cats 		local bwanchor = nstart       --backward anchor/common year 		local fwanchor = bwanchor + t --forward anchor/common year 		if regularparent == 'isolated' then 			fwanchor = bwanchor 		end 		local spangreen = '[<span style="color:green">j, g, k = ' --used for/when debugging via list-all-links=yes 		local spanblue = '<span style="color:blue">' 		local spanred = ' (' 		local span = '' 		local lastg = nil --to check for run-on searches 		local lastk = nil --to check for run-on searches 		local endfound = false --switch used to stop searching forward 		local iirregs = 0 --index of tirregs[] for j < 0, since search starts from parent 		local j = -3 --index of tirregs[] for j > 0 & pseudo nav position 		while j <= 3 do  			if j < 0 then --search backward from parent 				local gbreak = false --switch used to break out of g-loop 				local g = 0 --gap size 				while g <= hgap_limit_irreg do 					local k = 0 --term length: 0 = "0-length", 1+ = normal 					while k <= term_limit do 						local from = bwanchor - k - g 						local to   = bwanchor - g 						local full = mw.text.trim( firstpart..lspace..from..hyph..to..tspace..lastpart ) 						if k == 0 then 							if regularparent ~= 'isolated' then --+restrict to g == 0 if repeating year problems arise 								to = '0-length' 								full = mw.text.trim( firstpart..lspace..from..tspace..lastpart ) 								if catlinkfollowr( frame, full ).rtarget ~= nil then --#R followed 									table.insert( tlistallbwd, spangreen..j..', '..g..', '..k..span..'] '..full..spanred..'#R ignored'..span..')' ) 									full, to = ,  --don't use/follow 0-length cat #Rs from nav_hyphen(); otherwise gets messy 								end 							end 						end 						if (k >= 1) or		  --the normal case; only continue k = 0 if 0-length found 						   (to == '0-length') --ghetto "continue" (thx Lua) to avoid expensive searches for "UK MPs 1974-1974", etc. 						then 							table.insert( tlistallbwd, spangreen..j..', '..g..', '..k..span..'] '..full ) 							if (k == 1) and 							   (g == 0 or g == 1) and 							   (pageexists( full, 'Category' ) == false) 							then --allow bare-bones MOS:DATERANGE alternation, in case we're on a 0|1-gap, 1-year term series 								local to2 = string.match(to, '%d%d$') 								if to2 and to2 ~= '00' then --and not at a century transition (i.e. 1999–2000) 									to = to2 									full = mw.text.trim( firstpart..lspace..from..hyph..to..tspace..lastpart ) 									table.insert( tlistallbwd, spangreen..j..', '..g..', '..k..span..'] '..full ) 								end 							end 							if pageexists( full, 'Category' ) then 								if to == '0-length' then 									trackcat(12, 'Navseasoncats range irregular, 0-length') 								end 								tlistallbwd[#tlistallbwd] = spanblue..tlistallbwd[#tlistallbwd]..span..' (found)' 								ttlens[ find_duration(full) ] = 1 								tgaps[g] = 1 								iirregs = iirregs + 1 								tirregs['from-'..iirregs] = from 								tirregs['to-'..iirregs] = to 								bwanchor = from --ratchet down 								if to ~= '0-length' then 									gbreak = true 									break 								else 									g = 0 --soft-reset g, to keep stepping thru k 									j = j + 1 --save, but keep searching thru k 									if j > 3 then --lest we keep searching & finding 0-length cats ("MEPs for the Republic of Ireland 1973" & down) 										gbreak = true 										break 									end 								end 							end 						end --ghetto "continue" 						k = k + 1 						lastk = k 					end --while k 					if gbreak == true then break end 					g = g + 1 					lastg = g 				end --while g 			end --if j < 0  			if j > 0 and endfound == false then --search forward from parent 				local gbreak = false --switch used to break out of g-loop 				local g = 0 --gap size 				while g <= hgap_limit_irreg do 					local k = -2 --term length: -2 = "0-length", -1 = "2020–present", 0 = "2020–", 1+ = normal 					while k <= term_limit do 						local from = fwanchor + g 						local to4  = fwanchor + k + g	--override carefully 						local to2  = nil				--last 2 digits of to4, IIF exists 						if k == -1 then to4 = 'present'	--see if end-cat exists (present) 						elseif k == 0 then to4 =  end	--see if end-cat exists (blank) 						local full = mw.text.trim( firstpart..lspace..from..hyph..to4..tspace..lastpart ) 						if k == -2 then 							if regularparent ~= 'isolated' then --+restrict to g == 0 if repeating year problems arise 								to4 = '0-length' --see if 0-length cat exists 								full = mw.text.trim( firstpart..lspace..from..tspace..lastpart ) 								if catlinkfollowr( frame, full ).rtarget ~= nil then --#R followed 									table.insert( tlistallfwd, spangreen..j..', '..g..', '..k..span..'] '..full..spanred..'#R ignored'..span..')' ) 									full, to4 = ,  --don't use/follow 0-length cat #Rs from nav_hyphen(); otherwise gets messy 								end 							end 						end 						if (k >= -1) or		   --only continue k = -2 if 0-length found 						   (to4 == '0-length') --ghetto "continue" (thx Lua) to avoid expensive searches for "UK MPs 1974-1974", etc. 						then 							table.insert( tlistallfwd, spangreen..j..', '..g..', '..k..span..'] '..full ) 							if (k == 1) and 							   (g == 0 or g == 1) and 							   (pageexists( full, 'Category' ) == false) 							then --allow bare-bones MOS:DATERANGE alternation, in case we're on a 0|1-gap, 1-year term series 								to2 = string.match(to4, '%d%d$') 								if to2 and to2 ~= '00' then --and not at a century transition (i.e. 1999–2000) 									full = mw.text.trim( firstpart..lspace..from..hyph..to2..tspace..lastpart ) 									table.insert( tlistallfwd, spangreen..j..', '..g..', '..k..span..'] '..full ) 								end 							end 							if pageexists( full, 'Category' ) then 								if to4 == '0-length' then 									if rtarget(frame, full) == full then --only use 0-length cats that don't #R 										trackcat(12, 'Navseasoncats range irregular, 0-length') 									end 								end 								tirregs['from'..j] = from 								tirregs['to'..j] = (to2 or to4) 								if (k == -1) or (k == 0) then 									endfound = true --tentative 								else --k == { -2, > 0 } 									tlistallfwd[#tlistallfwd] = spanblue..tlistallfwd[#tlistallfwd]..span..' (found)' 									ttlens[ find_duration(full) ] = 1 									tgaps[g] = 1 									endfound = false 									if to4 ~= '0-length' then --k > 0 										fwanchor = to4 --ratchet up 										gbreak = true 										break --only break on k > 0 b/c old end-cat #Rs still exist like "Members of the Scottish Parliament 2011–" 									else --k == -2 										j = j + 1 --save, but keep searching k's, in case "1974" → "1974-1979" 										if j > 3 then --lest we keep searching & finding 0-length cats ("2018 CONCACAF Champions League" & up) 											gbreak = true 											break 										end 									end 								end 							end 						end --ghetto "continue" 						k = k + 1 						lastk = k 					end --while k 					if gbreak == true then break end 					g = g + 1 					lastg = g 				end --while g 			end --if j > 0  			if (lastg == (hgap_limit_irreg + 1)) and 			   (lastk == (term_limit + 1)) 			then --search exhausted 				if j < 0 then j = 0 --bwd search exhausted; continue fwd 				elseif j > 0 then break end --fwd search exhausted 			end  			j = j + 1 		end --while j <= 3 	end --if hgap <= hgap_limit_reg  	--begin navhyphen 	local navh = '{| class="toccolours hlist" style="text-align: center; margin: auto;"\n'..'|\n'  	local terminalcat = false --switch used to hide future cats 	local terminaltxt = nil 	local i = -3 --nav position 	while i <= 3 do 		local from = nstart + i*(t+hgap) --the logical, but not necessarily correct, 'from' 		if tirregs['from'..i] then from = tonumber(tirregs['from'..i]) end --prefer irregular term table 		local from2 = string.match(from, '%d?%d$')  		local to = tostring(from+t)	--the logical, naive range, but 		if tirregs['to'..i] then	--prefer irregular term table 			to = tirregs['to'..i] 		elseif regularparent == false and tirregs and i > 0 then 			to = tirregs['to-1']	--special treatment for parent terminal cats, since they have no natural 'to' 		end 		local to2 = string.match(to, '%d?%d$') 		local tofinal = (to2 or )    --assume t=1 and abbreviated 'to' (the most common case) 		if t > 1 or                    --per MOS:DATERANGE (e.g. 1999-2004) 		  (from2 - (to2 or from2)) > 0 --century transition exception (e.g. 1999–2000) 		then 			tofinal = (to or )       --default to the MOS-correct format, in case no fallbacks found 		end 		if to == '0-length' then 			tofinal = to 		end  		--check existance of 4-digit, MOS-correct range, with abbreviation fallback 		if tofinal ~= '0-length' then 			if t > 1 and string.len(from) == 4 then --e.g. 1999-2004 				--determine which link exists (full or abbr) 				local full = firstpart..lspace..from..hyph..tofinal..tspace..lastpart 				if not pageexists( full, 'Category' ) then 					local abbr = firstpart..lspace..from..hyph..to2..tspace..lastpart 					if pageexists( abbr, 'Category' ) then 						tofinal = (to2 or ) --rv to MOS-incorrect format; if full AND abbr DNE, then tofinal is still in its MOS-correct format 					end 				end 			elseif t == 1 then --full-year consecutive ranges are also allowed 				local abbr = firstpart..lspace..from..hyph..tofinal..tspace..lastpart --assume tofinal is in abbr format 				if not pageexists( abbr, 'Category' ) and tofinal ~= to then 					local full = firstpart..lspace..from..hyph..to..tspace..lastpart 					if pageexists( full, 'Category' ) then 						tofinal = (to or ) --if abbr AND full DNE, then tofinal is still in its abbr format (unless it's a century transition) 		end	end	end	end  		--populate navh 		if i ~= 0 then --left/right navh 			local orig = firstpart..lspace..from..hyph..tofinal..tspace..lastpart 			local disp = from..hyph..tofinal 			if tofinal == '0-length' then 				orig = firstpart..lspace..from..tspace..lastpart 				disp = from 			end 			local catlink = catlinkfollowr(frame, orig, disp, true) --force terminal cat display  			if terminalcat == false then 				terminaltxt = find_terminaltxt( disp ) --also sets tracking cats 				terminalcat = (terminaltxt ~= nil) 			end 			if catlink.rtarget and avoidself then --a  was followed, figure out why 				--determine new term length & gap size 				ttlens[ find_duration( catlink.rtarget ) ] = 1 				if i > -3 then 					local lastto = tirregs['to'..(i-1)] 					if lastto == nil then 						local lastfrom = nstart + (i-1)*(t+hgap) 						lastto = lastfrom+t --use last logical 'from' to calc lastto 					end 					if lastto then 						local gapcat = lastto..'-'..from --dummy cat to calc with 						local gap = find_duration(gapcat) or -1	--in case of nil, 						tgaps[ gap ] = 1						--tgaps[-1] is ignored 					end 				end  				--display/tracking handling 				local base_regex = '%d+[–-]%d+' 				local origbase = mw.ustring.gsub(orig, base_regex, ) 				local rtarbase = mw.ustring.gsub(catlink.rtarget, base_regex, ) 				local terminal_regex = '%d+[–-]'..(terminaltxt or )..'$' --more manual ORs bc Lua regex sux 				if mw.ustring.match(orig, terminal_regex) then 					origbase = mw.ustring.gsub(orig, terminal_regex, ) 				end 				if mw.ustring.match(catlink.rtarget, terminal_regex) then 					--finagle/overload terminalcat type to set nmaxseas on 1st occurence only 					if terminalcat == false then terminalcat = 1 end 					local dummy = find_terminaltxt( catlink.rtarget ) --also sets tracking cats 					rtarbase = mw.ustring.gsub(catlink.rtarget, terminal_regex, ) 				end 				origbase = mw.text.trim(origbase) 				rtarbase = mw.text.trim(rtarbase) 				if origbase ~= rtarbase then 					trackcat(6, 'Navseasoncats range redirected (base change)') 				elseif terminalcat == 1 then 					trackcat(7, 'Navseasoncats range redirected (end)') 				else 					local all4s_regex = '%d%d%d%d[–-]%d%d%d%d' 					local all4s = (mw.ustring.match(orig, all4s_regex) and 								   mw.ustring.match(catlink.rtarget, all4s_regex)) 					if all4s then 						trackcat(9, 'Navseasoncats range redirected (other)') 					else 						trackcat(8, 'Navseasoncats range redirected (MOS)') 					end 				end 			end  			if terminalcat then --true or 1 				if type(terminalcat) ~= 'boolean' then nmaxseas = from end --only want to do this once 				terminalcat = true --done finagling/overloading 			end 			if (from >= 0) and (nminseas <= from) and (from <= nmaxseas) then 				navh = navh..'*'..catlink.navelement..'\n' 				if terminalcat then nmaxseas = nminseas_default end --prevent display of future ranges 			else 				local hidden = ''..disp..'' 				navh = navh..'*'..hidden..'\n' 				if listall then 					tlistall[#tlistall] = tlistall[#tlistall]..' ('..hidden..')' 				end 			end 		else --center navh 			if finish == -1 then finish = 'present' 			elseif finish == 0 then finish = ''..start..'' end 			local disp = start..hyph..finish 			if regularparent == 'isolated' then disp = start end 			navh = navh..'*'..disp..'\n' 		end  		i = i + 1 	end  	--tracking cats & finalize 	if avoidself then 		local igaps  = 0 --# of diff gap sizes > 0 found 		local itlens = 0 --# of diff term lengths found 		for s = 1, hgap_limit_reg do --must loop; #tgaps, #ttlens unreliable 			igaps = igaps + (tgaps[s] or 0) 		end 		for s = 0, term_limit do 			itlens = itlens + (ttlens[s] or 0) 		end 		if igaps  > 0 then trackcat(10, 'Navseasoncats range gaps') end 		if itlens > 1 and ttrackingcats[12] ==  then --avoid duplication in "Navseasoncats range irregular, 0-length" 			trackcat(11, 'Navseasoncats range irregular') 		end 	end 	isolatedcat() 	defaultgapcat(not hgap_success) 	if listall then 		return listalllinks() 	else 		return navh..'|}' 	end end  --======================================================  local function nav_tvseason( frame, firstpart, tv, lastpart, maximumtv ) 	--Expects a PAGENAME of the form "Futurama (season 1) episodes", where 	--	firstpart = Futurama (season 	--	tv        = 1 	--	maximumtv = 7 ('max' tv season parameter; optional; defaults to 9999) 	tv = tonumber(tv) 	if tv == nil then 		errors = p.errorclass('Function nav_tvseason can\'t recognize the TV season number sent to its 2nd parameter.') 		return p.failedcat(errors, 'T') 	end  	local maxtv = tonumber(maximumtv) or 9999 --allow +/- qualifier 	if maxtv < tv then maxtv = tv end --input error; maxtv should be >= parent  	--begin navtvseason 	local navt = '{| class="toccolours hlist" style="text-align: center; margin: auto;"\n'..'|\n'  	local i = -5 --nav position 	while i <= 5 do 		local t = tv + i 		if i ~= 0 then --left/right navt 			local catlink = catlinkfollowr( frame, firstpart..' '..t..lastpart, t ) 			if (t >= 1 and t <= maxtv) then --hardcode mintv 				if catlink.rtarget then --a  was followed 					trackcat(24, 'Navseasoncats TV season redirected') 				end 				navt = navt..'*'..catlink.navelement..'\n' 			else 				local hidden = ''..'0'..'' --'0' to maintain dot spacing 				navt = navt..'*'..hidden..'\n' 				if listall then 					tlistall[#tlistall] = tlistall[#tlistall]..' ('..hidden..')' 				end 			end 		else --center navt 			navt = navt..'*'..tv..'\n' 		end  		i = i + 1 	end  	isolatedcat() 	if listall then 		return listalllinks() 	else 		return navt..'|}' 	end end  --========================================================  local function nav_decade( frame, firstpart, decade, lastpart, mindecade, maxdecade ) 	--Expects a PAGENAME of the form "Some sequential 2000 example cat", where 	--	firstpart = Some sequential 	--	decade    = 2000 	--	lastpart  = example cat 	--	mindecade = 1800 ('min' decade parameter; optional; defaults to -9999) 	--	maxdecade = 2020 ('max' decade parameter; optional; defaults to 9999)  	--sterilize dec 	local dec = sterilizedec(decade) 	if dec == nil then 		errors = p.errorclass('Function nav_decade was sent "'..(decade or )..'" as its 2nd parameter, '.. 							'but expects a 1 to 4-digit year ending in "0".') 		return p.failedcat(errors, 'D') 	end 	local ndec = tonumber(dec)  	--sterilize mindecade & determine AD/BC 	local mindefault = '-9999' 	local mindec = sterilizedec(mindecade) --returns a tostring(unsigned int), or nil 	if mindec then 		if string.match(mindecade, '-%d') or 		   string.match(mindecade, 'BC') 		then 			mindec = '-'..mindec --better +/-0 behavior with strings (0-initialized int == "-0" string...) 		end 	elseif mindec == nil and mindecade and mindecade ~=  then 		errors = p.errorclass('Function nav_decade was sent "'..(mindecade or )..'" as its 4th parameter, '.. 							'but expects a 1 to 4-digit year ending in "0", the earliest decade to be shown.') 		return p.failedcat(errors, 'E') 	else --mindec == nil 		mindec = mindefault --tonumber() later, after error checks 	end  	--sterilize maxdecade & determine AD/BC 	local maxdefault = '9999' 	local maxdec = sterilizedec(maxdecade) --returns a tostring(unsigned int), or nil + error 	if maxdec then 		if string.match(maxdecade, '-%d') or 		   string.match(maxdecade, 'BC') 		then                     --better +/-0 behavior with strings (0-initialized int == "-0" string...), 			maxdec = '-'..maxdec --but a "-0" string -> tonumber() -> tostring() = "-0", 		end                      --and a  "0" string -> tonumber() -> tostring() =  "0" 	elseif maxdec == nil and maxdecade and maxdecade ~=  then 		errors = p.errorclass('Function nav_decade was sent "'..(maxdecade or )..'" as its 5th parameter, '.. 							'but expects a 1 to 4-digit year ending in "0", the highest decade to be shown.') 		return p.failedcat(errors, 'F') 	else --maxdec == nil 		maxdec = maxdefault 	end  	local tspace = ' ' --assume trailing space for "1950s in X"-type cats 	if string.match(lastpart, '^-') then tspace =  end --DNE for "1970s-related"-type cats  	--AD/BC switches & vars 	local parentBC = mw.ustring.match(firstpart, 'খ্রিস্টপূর্ব%s?$')  	firstpart = mw.ustring.gsub(firstpart, 'খ্রিস্টপূর্ব%s?$', ) --handle BC separately; AD never used 	--TODO?: handle BCE, but only if it exists in the wild  	local dec0to40AD = (ndec >= 0 and ndec <= 40 and not parentBC) --special behavior in this range 	local switchADBC = 1                 --  1=AD parent 	if parentBC then switchADBC = -1 end -- -1=BC parent; possibly adjusted later 	local BCdisp =  	local D = -math.huge --secondary switch & iterator for AD/BC transition  	--check non-default min/max more carefully 	if mindec ~= mindefault then 		if tonumber(mindec) > ndec*switchADBC then 			mindec = tostring(ndec*switchADBC) --input error; mindec should be <= parent 		end 	end 	if maxdec ~= maxdefault then 		if tonumber(maxdec) < ndec*switchADBC then 			maxdec = tostring(ndec*switchADBC) --input error; maxdec should be >= parent 		end 	end 	local nmindec = tonumber(mindec) --similar behavior to nav_year & nav_nordinal 	local nmaxdec = tonumber(maxdec) --similar behavior to nav_nordinal  	--begin navdecade 	local bnb =  --border/no border 	if navborder == false then --for Navseasoncats year and decade 		bnb = ' border-style: none; background-color: transparent;' 	end 	local navd = '{| class="toccolours hlist" style="text-align: center; margin: auto;'..bnb..'"\n'..'|\n'  	local i = -50 --nav position x 10 	while i <= 50 do 		local d = ndec + i*switchADBC  		local BC =  		BCdisp = 		if dec0to40AD then 			if D < -10 then 				d = math.abs(d + 10) --b/c 2 "0s" decades exist: "0s BC" & "0s" (AD) 				BC = 'খ্রিস্টপূর্ব ' 				if d == 0 then 					D = -10 --track 1st d = 0 use (BC) 				end 			elseif D >= -10 then 				D = D + 10 --now iterate from 0s AD 				d = D      --2nd d = 0 use 			end 		elseif parentBC then 			if switchADBC == -1 then --parentBC looking at the BC side (the common case) 				BC = 'খ্রিস্টপূর্ব ' 				if d == 0 then     --prepare to switch to the AD side on the next iteration 					switchADBC = 1 --1st d = 0 use (BC) 					D = -10        --prep 				end 			elseif switchADBC == 1 then --switched to the AD side 				D = D + 10 --now iterate from 0s AD 				d = D      --2nd d = 0 use (on first use) 			end 		end 		if BC ~=  and ndec <= 50 then 			BCdisp = ' খ্রিস্টপূর্ব' --show BC for all BC decades whenever a "0s" is displayed on the nav 		end  		--determine target cat 		local disp = BCdisp..tspace..d..l10n.s 		local catlink = catlinkfollowr( frame, BC..firstpart..' '..d..'s'..tspace..lastpart, disp, nil, i ) 		if catlink.rtarget then --a  was followed 			trackcat(17, 'Navseasoncats decade redirected') 		end  		--populate left/right navd 		local shown = navcenter(i, catlink) 		local hidden = ''..disp..'' 		local dsign = d --use d for display & dsign for logic 		if BC ~=  then dsign = -dsign end 		if (nmindec <= dsign) and (dsign <= nmaxdec) then 			if dsign == 0 and (nmindec == 0 or nmaxdec == 0) then --distinguish b/w -0 (BC) & 0 (AD) 				--"zoom in" on +/- 0 and turn dsign/min/max temporarily into +/- 1 for easier processing 				local zsign, zmin, zmax = 1, nmindec, nmaxdec 				if BC ~=  then zsign = -1 end 				if     mindec == '-0' then zmin = -1 				elseif mindec ==  '0' then zmin =  1 end 				if     maxdec == '-0' then zmax = -1 				elseif maxdec ==  '0' then zmax =  1 end  				if (zmin <= zsign) and (zsign <= zmax) then 					navd = navd..shown 					hidden = nil 				else 					navd = navd..'*'..hidden..'\n' 				end 			else 				navd = navd..shown --the common case 				hidden = nil 			end 		else 			navd = navd..'*'..hidden..'\n' 		end 		if listall and hidden then 			tlistall[#tlistall] = tlistall[#tlistall]..' ('..hidden..')' 		end  		i = i + 10 	end  	isolatedcat() 	if listall then 		return listalllinks() 	else 		return navd..'|}' 	end end  --==========================================================  local function nav_year( frame, firstpart, year, lastpart, minimumyear, maximumyear ) 	--Expects a PAGENAME of the form "Some sequential 1760 example cat", where 	--	firstpart   = Some sequential 	--	year        = 1760 	--	lastpart    = example cat 	--	minimumyear = 1758 ('min' year parameter; optional) 	--	maximumyear = 1800 ('max' year parameter; optional) 	local minyear_default = -9999 	local maxyear_default =  9999 	year = tonumber(year) or tonumber(mw.ustring.match(year or , '^%s*(%d*)')) 	local minyear = tonumber(string.match(minimumyear or , '-?%d+')) or minyear_default --allow +/- qualifier 	local maxyear = tonumber(string.match(maximumyear or , '-?%d+')) or maxyear_default --allow +/- qualifier 	if string.match(minimumyear or , 'BC') then minyear = -math.abs(minyear) end --allow BC qualifier (AD otherwise assumed) 	if string.match(maximumyear or , 'BC') then maxyear = -math.abs(maxyear) end --allow BC qualifier (AD otherwise assumed)  	if year == nil then 		errors = p.errorclass('Function nav_year can\'t recognize the year sent to its 2nd parameter.') 		return p.failedcat(errors, 'Y') 	end  	--AD/BC switches & vars  	local yearBCElastparts = { --needed for parent = AD 1-5, when the BC/E format is unknown 		--"BCE" removed to match both AD & BCE cats; easier & faster than multiple string.match()s 		['example_Hebrew people_example'] = 'BCE', --example entry format; add to & adjust as needed 	} 	local parentAD = string.match(firstpart, 'AD$')  --following the "AD 1" convention from AD 1 to AD 10 	local parentBC = string.match(lastpart, '^BCE?') --following the "1 BC" convention for all years BC 	firstpart = mw.ustring.gsub(firstpart, '%s*AD$', ) --handle AD/BC separately for easier & faster accounting 	lastpart  = mw.ustring.gsub(lastpart,  '^BCE?%s*', ) 	local BCe = parentBC or yearBCElastparts[lastpart] or 'BC' --"BC" default  	local year1to10 = (year >= 1 and year <= 10) 	local year1to10ADBC = year1to10 and (parentBC or parentAD) --special behavior 1-10 for low-# non-year series 	local year1to15AD = (year >= 1 and year <= 15 and not parentBC) --special behavior 1-15 for AD/BC display 	local switchADBC = 1                 --  1=AD parent 	if parentBC then switchADBC = -1 end -- -1=BC parent; possibly adjusted later 	local Y = 0 --secondary iterator for AD-on-a-BC-parent  	if minyear > year*switchADBC then minyear = year*switchADBC end --input error; minyear should be <= parent 	if maxyear < year*switchADBC then maxyear = year*switchADBC end --input error; maxyear should be >= parent  	local lspace = ' ' --leading space before year, after firstpart 	if string.match(firstpart, '[%-VW]$') then 		lspace =  --e.g. "Straight-8 engines" 	end  	local tspace = ' ' --trailing space after year, before lastpart 	if string.match(lastpart, '^-') then 		tspace =  --e.g. "2018-related timelines" 	end  	--determine interyear gap size to condense special category types, if possible 	local ygapdefault = 1 --assume/start at the most common case: 2001, 2002, etc. 	local ygap = ygapdefault 	if string.match(lastpart, 'presidential') then 		local ygap1, ygap2 = ygapdefault, ygapdefault --need to determine previous & next year gaps indepedently 		local ygap1_success, ygap2_success = false, false  		local prevseason = nil 		while ygap1 <= 5 do --Czech Republic, Poland, Sri Lanka, etc. have 5-year terms 			prevseason = firstpart..lspace..(year-ygap1)..tspace..lastpart 			if pageexists(prevseason, 'Category') then 				ygap1_success = true 				break 			end 			ygap1 = ygap1 + 1 		end  		local nextseason = nil 		while ygap2 <= 5 do --Czech Republic, Poland, Sri Lanka, etc. have 5-year terms 			nextseason = firstpart..lspace..(year+ygap2)..tspace..lastpart 			if pageexists(nextseason, 'Category') then 				ygap2_success = true 				break 			end 			ygap2 = ygap2 + 1 		end  		if ygap1_success and ygap2_success then 			if ygap1 == ygap2 then ygap = ygap1 end 		elseif ygap1_success then  ygap = ygap1 		elseif ygap2_success then  ygap = ygap2 		end 	end  	--skip non-existing years, if requested 	local ynogaps = {} --populate with existing years in the range, at most, [year - (skipgaps_limit * 5), year + (skipgaps_limit * 5)] 	local skipgaps_limit = 25 	if skipgaps then 		if minyear == minyear_default then 			minyear = 0 --automatically set minyear to 0, as AD/BC not supported anyway 		end 		if (year > 70) or --add support for AD/BC (<= AD 10) if/when needed 		   (minyear >= 0 and --must be a non-year series like "AC with 0 elements" 		   	not parentAD and not parentBC) 		then 			local yskipped = {} --track skipped y's to avoid double-checking 			local cat, found, Yeary  			 --populate nav element queue outwards positively from the parent 			local Year = year --to save/ratchet progression 			local i = 1 			while i <= 5 do 				local y = 1 				while y <= skipgaps_limit do 					found = false 					Yeary = Year + y 					if yskipped[Yeary] == nil then 						yskipped[Yeary] = Yeary 						cat = firstpart..lspace..Yeary..tspace..lastpart 						found = pageexists( cat, 'Category' ) 						if found then break end 					end 					y = y + 1 				end 				if found then Year = Yeary 				else          Year = Year + 1 end 				ynogaps[i] =  Year 				i = i + 1 			end  			ynogaps[0] = year --the parent  			--populate nav element queue outwards negatively from the parent 			Year = year --reset ratchet 			i = -1 			while i >= -5 do 				local y = -1 				while y >= -skipgaps_limit do 					found = false 					Yeary = Year + y 					if yskipped[Yeary] == nil then 						yskipped[Yeary] = Yeary 						cat = firstpart..lspace..Yeary..tspace..lastpart 						found = pageexists( cat, 'Category' ) 						if found then break end 					end 					y = y - 1 				end 				if found then Year = Yeary 				else          Year = Year - 1 end 				ynogaps[i] =  Year 				i = i - 1 			end 		else 			skipgaps = false --AD/BC not supported 		end 	end  	--begin navyears 	local navy = '{| class="toccolours hlist" style="text-align: center; margin: auto;"\n'..'|\n'  	local y 	local i = -5 --nav position 	while i <= 5 do 		if skipgaps then 			y = ynogaps[i] 		else 			y = year + i*ygap*switchADBC 		end 		local BCdisp =  		if i ~= 0 then --left/right navy  			local AD = 			local BC =  			if year1to15AD and not 			   (year1to10 and not year1to10ADBC) --don't AD/BC 1-10's if parents don't contain AD/BC 			then 				if year >= 11 then --parent = AD 11-15 					if y <= 10 then --prepend AD on y = 1-10 cats only, per existing cats 						AD = 'AD ' 					end  				elseif year >= 1 then --parent = AD 1-10 					if y <= 0 then 						BC = BCe..' ' 						y = math.abs(y - 1) --skip y = 0 (DNE) 					elseif y >= 1 and y <= 10 then --prepend AD on y = 1-10 cats only, per existing cats 						AD = 'AD ' 					end 				end  			elseif parentBC then 				if switchADBC == -1 then --displayed y is in the BC regime 					if y >= 1 then     --the common case 						BC = BCe..' ' 					elseif y == 0 then --switch from BC to AD regime 						switchADBC = 1 					end 				end 				if switchADBC == 1 then --displayed y is now in the AD regime 					Y = Y + 1 --skip y = 0 (DNE) 					y = Y     --easiest solution: start another iterator for these AD y's displayed on a BC year parent 					AD = 'AD ' 				end 			end 			if BC ~=  and year <= 5 then --only show 'BC' for parent years <= 5: saves room, easier to read, 				BCdisp = ' '..BCe          --and 6 is the first/last nav year that doesn't need a disambiguator; 			end                            --the center/parent year will always show BC, so no need to show it another 10x  			--populate left/right navy 			local ysign = y --use y for display & ysign for logic 			local disp = y..BCdisp 			if BC ~=  then ysign = -ysign end 			local firsttry = firstpart..lspace..AD..y..tspace..BC..lastpart 			if (minyear <= ysign) and (ysign <= maxyear) then 				local catlinkAD = catlinkfollowr( frame, firsttry, disp ) --try AD 				local catlink = catlinkAD --tentative winner 				if AD ~=  then --for "ACArt with 5 suppressed elements"-type cats 					local catlinkNoAD = catlinkfollowr( frame, firstpart..lspace..y..tspace..BC..lastpart, disp ) --try !AD 					if catlinkNoAD.catexists == true then 						catlink = catlinkNoAD --usurp 					elseif listall then 						tlistall[#tlistall] = tlistall[#tlistall]..' (tried; not displayed)' 					end 				end 				if (AD..BC == ) and (catlink.catexists == false) and (y >= 1000) then --!ADBC & DNE; 4-digit only, to be frugal 					--try basic hyphenated cats: 1-year, endash, MOS-correct only, no #Rs 					local yHyph_4 = y..'–'..(y+1) --try 2010–2011 type cats 					local catlinkHyph_4 = catlinkfollowr( frame, firstpart..lspace..yHyph_4..tspace..BC..lastpart, yHyph_4 ) 					if catlinkHyph_4.catexists and catlinkHyph_4.rtarget == nil then --exists & no #Rs 						catlink = catlinkHyph_4 --usurp 						trackcat(26, 'Navseasoncats year and range') 					else 						if listall then 							tlistall[#tlistall] = tlistall[#tlistall]..' (tried; not displayed)' 						end 						local yHyph_2 = y..'–'..string.match(y+1, '%d%d$') --try 2010–11 type cats 						local catlinkHyph_2 = catlinkfollowr( frame, firstpart..lspace..yHyph_2..tspace..BC..lastpart, yHyph_2 ) 						if catlinkHyph_2.catexists and catlinkHyph_2.rtarget == nil then --exists & no #Rs 							catlink = catlinkHyph_2 --usurp 							trackcat(26, 'Navseasoncats year and range') 						elseif listall then 							tlistall[#tlistall] = tlistall[#tlistall]..' (tried; not displayed)' 						end 					end 				end 				if catlink.rtarget then --#R followed; determine why 					local r = catlink.rtarget 					local c = catlink.cat 					local regex_year  = '%d%d%d%d[–-]?%d?%d?%d?%d?' --prioritize year/range stripping, e.g. for "2006 Super 14 season" 					local regex_num   = '%d+' --strip any number otherwise 					local regex_final = nil   --best choice goes here 					if string.match(r, regex_year) and string.match(c, regex_year) then 						regex_final = regex_year 					elseif string.match(r, regex_num) and string.match(c, regex_num) then 						regex_final = regex_num 					end 					if regex_final then 						local r_base = mw.ustring.gsub(r, regex_final, ) 						local c_base = mw.ustring.gsub(c, regex_final, ) 						if r_base ~= c_base then 							trackcat(18, 'Navseasoncats year redirected (base change)') --acceptable #Rs go here 						else 							trackcat(20, 'Navseasoncats year redirected (other)')  --exceptions go here 						end 					else 						trackcat(19, 'Navseasoncats year redirected (var change)') --e.g. "V2 engines" to "V-twin engines" 					end 				end 				navy = navy..'*'..catlink.navelement..'\n' 			else --OOB vs min/max 				local hidden = ''..disp..'' 				navy = navy..'*'..hidden..'\n' 				if listall then 					local dummy = catlinkfollowr( frame, firsttry, disp ) 					tlistall[#tlistall] = tlistall[#tlistall]..' ('..hidden..')' 				end 			end 		else --center navy 			if parentBC then BCdisp = ' '..BCe end 			navy = navy..'*'..year..BCdisp..'\n' 		end  		i = i + 1 	end  	isolatedcat() 	if listall then 		return listalllinks() 	else 		return navy..'|}' 	end end  --=========================================================  local function nav_roman( frame, firstpart, roman, lastpart, minimumrom, maximumrom ) 	local toarabic = require('Module:ConvertNumeric/খেলাঘর').roman_to_numeral 	local toroman  = require('Module:Roman').main  	--sterilize/convert rom/num 	local num = tonumber(toarabic(roman)) 	local rom = toroman({ [1] = num }) 	if num == nil or rom == nil then --out of range or some other error 		errors = p.errorclass('Function nav_roman can\'t recognize one or more of "'..(num or 'nil')..'" & "'.. 							(rom or 'nil')..'" in category "'..firstpart..' '..roman..' '..lastpart..'".') 		return p.failedcat(errors, 'R') 	end  	--sterilize min/max 	local minrom = tonumber(minimumrom or ) or tonumber(toarabic(minimumrom or )) 	local maxrom = tonumber(maximumrom or ) or tonumber(toarabic(maximumrom or )) 	if minrom < 1 then minrom = 1 end    --toarabic() returns -1 on error 	if maxrom < 1 then maxrom = 9999 end --toarabic() returns -1 on error 	if minrom > num then minrom = num end 	if maxrom < num then maxrom = num end  	--begin navroman 	local navr = '{| class="toccolours hlist" style="text-align: center; margin: auto;"\n'..'|\n'  	local i = -5 --nav position 	while i <= 5 do 		local n = num + i  		if n >= 1 then 			local r = toroman({ [1] = n }) 			if i ~= 0 then --left/right navr 				local catlink = catlinkfollowr( frame, firstpart..' '..r..' '..lastpart, r ) 				if minrom <= n and n <= maxrom then 					if catlink.rtarget then --a  was followed 						trackcat(21, 'Navseasoncats roman numeral redirected') 					end 					navr = navr..'*'..catlink.navelement..'\n' 				else 					local hidden = ''..r..'' 					navr = navr..'*'..hidden..'\n' 					if listall then 						tlistall[#tlistall] = tlistall[#tlistall]..' ('..hidden..')' 					end 				end 			else --center navr 				navr = navr..'*'..r..'\n' 			end 		else 			navr = navr..'*'..'I'..'\n' 		end  		i = i + 1 	end  	isolatedcat() 	if listall then 		return listalllinks() 	else 		return navr..'|}' 	end end  --====================================================== local function nav_nordinal(frame, firstpart, ord, lastpart, minimumord, maximumord)     local patterns_ = {         '(-?%d+ম?)',   -- Matches Ilike 1ম or 1ম         '(-?%d+য়?)',   -- Matches Ilike 1য় or 1য়         '(-?%d+র্থ?)', -- Matches Ilike 1র্থ or 1র্থ         '(-?%d+ষ্ঠ?)', -- Matches Ilike 1ষ্ঠ or 1ষ্ঠ         '(-?%d+শ?)'    -- Matches Ilike 1শ or 1শ     }      local nord = tonumber(ord)     local minord, maxord      -- Correctly match the Ito get minord and maxord values     for _, pattern_ in ipairs(patterns_) do         minord = tonumber(string.match(minimumord or , pattern_)) or -9999         maxord = tonumber(string.match(maximumord or , pattern_)) or 9999                  -- If both minord and maxord are valid numbers, break out of the loop         if minord and maxord then break end     end       if string.match(minimumord or , 'BC') then minord = -math.abs(minord) end --allow BC qualifier (AD otherwise assumed) 	if string.match(maximumord or , 'BC') then maxord = -math.abs(maxord) end --allow BC qualifier (AD otherwise assumed)  	local temporal = string.match(lastpart, 'century') or 					 string.match(lastpart, 'সহস্রাব্দ')  	local tspace = ' ' --assume a trailing space after ordinal 	if string.match(lastpart, '^-') then tspace =  end --DNE for "19th-century"-type cats  	--AD/BC switches & vars  	local ordBCElastparts = { --needed for parent = AD 1-5, when the BC/E format is unknown 		--lists the lastpart of valid BCE cats 		--"BCE" removed to match both AD & BCE cats; easier & faster than multiple string.match()s 		['-century Hebrew people'] = 'BCE', --WP:CFD/Log/2016 June 21#Category:11th-century BC Hebrew people 		['-century Jews']          = 'BCE', --co-nominated 		['-century Judaism']       = 'BCE', --co-nominated 		['-century rabbis']        = 'BCE', --co-nominated 		['-century High Priests of Israel'] = 'BCE', 	} 	local parentBC = mw.ustring.match(firstpart, '(খ্রিস্টপূর্ব)%s?$') 	local firstpartNoBC = mw.ustring.gsub(firstpart, '(খ্রিস্টপূর্ব)%s?$', ) 	local BCe = parentBC or ordBCElastparts[firstpartNoBC] or 'খ্রিস্টপূর্ব' --"BC" default  	local switchADBC = 1                 --  1=AD parent 	if parentBC then switchADBC = -1 end -- -1=BC parent; possibly adjusted later 	local O = 0 --secondary iterator for AD-on-a-BC-parent  	if not temporal and minord < 1 then minord = 1 end --nothing before "1st parliament", etc. 	if minord > nord*switchADBC then minord = nord*switchADBC end --input error; minord should be <= parent 	if maxord < nord*switchADBC then maxord = nord*switchADBC end --input error; maxord should be >= parent  	--begin navnordinal 	local bnb =  --border/no border 	if navborder == false then --for Navseasoncats decade and century 		bnb = ' border-style: none; background-color: transparent;' 	end 	local navo = '{| class="toccolours hlist" style="text-align: center; margin: auto;'..bnb..'"\n'..'|\n'  	local i = -5 --nav position 	while i <= 5 do 		local o = nord + i*switchADBC 		local BC =  		local BCdisp = 		if parentBC then 			if switchADBC == -1 then --parentBC looking at the BC side 				if o >= 1 then     --the common case 					BC = ' '..BCe 				elseif o == 0 then --switch to the AD side 					BC =  					switchADBC = 1 				end 			end 			if switchADBC == 1 then --displayed o is now in the AD regime 				O = O + 1 --skip o = 0 (DNE) 				o = O     --easiest solution: start another iterator for these AD o's displayed on a BC year parent 			end 		elseif o <= 0 then --parentAD looking at BC side 			BC = ' '..BCe 			o = math.abs(o - 1) --skip o = 0 (DNE) 		end 		if BC ~=  and nord <= 5 then --only show 'BC' for parent ords <= 5: saves room, easier to read, 			BCdisp = ' '..BCe          --and 6 is the first/last nav ord that doesn't need a disambiguator; 		end                            --the center/parent ord will always show BC, so no need to show it another 10x  		--populate left/right navo 		local oth = p.addord(o) 		local osign = o --use o for display & osign for logic 		if BC ~=  then osign = -osign end 		local hidden = ''..oth..'' 		if temporal then --e.g. "3rd-century BC" 			local firstpart = BC--lest we recursively add multiple "BC"s 			if BC ~=  then 				lastpart = string.gsub(lastpart, temporal, temporal) --replace BC if needed 			end 			local catlink = catlinkfollowr( frame, firstpart..tspace..oth..tspace..' '..lastpart,tspace..BC..tspace..oth..tspace) 			if (minord <= osign) and (osign <= maxord) then 				if catlink.rtarget then --a  was followed 					trackcat(22, 'Navseasoncats nordinal redirected') 				end 				navo = navo..navcenter(i, catlink) 			else 				navo = navo..'*'..hidden..'\n' 				if listall then 					tlistall[#tlistall] = tlistall[#tlistall]..' ('..hidden..')' 				end 			end 		elseif BC ==  and minord <= osign and osign <= maxord then --e.g. >= "1st parliament" 			local catlink = catlinkfollowr( frame, firstpart..' '..oth..tspace..lastpart, oth ) 			if catlink.rtarget then --a  was followed 				trackcat(22, 'Navseasoncats nordinal redirected') 			end 			navo = navo..navcenter(i, catlink) 		else --either out-of-range (hide), or non-temporal + BC = something might be wrong (2nd X parliament BC?); handle exceptions if/as they arise 			navo = navo..'*'..hidden..'\n' 		end  		i = i + 1 	end  	isolatedcat() 	if listall then 		return listalllinks() 	else 		return navo..'|}' 	end end  --======================================================  local function nav_wordinal( frame, firstpart, word, lastpart, minimumword, maximumword, ordinal, frame ) 	--Module:ConvertNumeric.numeral_to_english() flags: 	--   ordinal == 'on': 'second' is output instead of 'two' 	--   ordinal ~= 'on': 'two' is output instead of 'second' 	local ord2eng = require('Module:ConvertNumeric/খেলাঘর').numeral_to_english 	local eng2ord = require('Module:ConvertNumeric/খেলাঘর').english_to_ordinal 	local th = 'শ' 	if ordinal ~= 'on' then 		th = 		eng2ord = require('Module:ConvertNumeric/খেলাঘর').english_to_numeral 	end 	local sc = string.match(word, '^%u') --sentence-case check 	local lc = string.lower(word) --operate on/with lc, and restore any sc later 	local nord = eng2ord(lc) 	local case = nil 	if sc then case = 'U' end  	local lspace = ' ' --assume a leading space (most common) 	local tspace = ' ' --assume a trailing space (most common) 	if string.match(firstpart, '[%-%(]$') then lspace =  end --DNE for "Straight-eight engines"-type cats 	if string.match(lastpart, '^[%-%)]' ) then tspace =  end --DNE for "Nine-cylinder engines"-type cats  	--sterilize min/max 	local minword = 1 	local maxword = 99 	if minimumword then 		local num = tonumber(minimumword) 		if num and 0 < num and num < maxword then 			minword = num 		else 			local ord = eng2ord(minimumword) 			if 0 < ord and ord < maxword then 				minword = ord 			end 		end 	end 	if maximumword then 		local num = tonumber(maximumword) 		if num and 0 < num and num < maxword then 			maxword = num 		else 			local ord = eng2ord(maximumword) 			if 0 < ord and ord < maxword then 				maxword = ord 			end 		end 	end 	if minword > nord then minword = nord end 	if maxword < nord then maxword = nord end  	--begin navwordinal 	local navw = '{| class="toccolours hlist" style="text-align: center; margin: auto;"\n'..'|\n'  	local i = -5 --nav position 	while i <= 5 do 		local n = nord + i  		if n >= 1 then 			local nth = p.addord(n) 			if ordinal ~= 'on' then nth = n end 			if i ~= 0 then --left/right navw 				local frame_args = frame:newChild{ args = { n, ord = ordinal, case = case } } --easier to do this than modify Module:ConvertNumeric 				local w = ord2eng( frame_args ) 				local catlink = catlinkfollowr( frame, firstpart..lspace..w..tspace..lastpart, nth ) 				if minword <= n and n <= maxword then 					if catlink.rtarget then --a  was followed 						trackcat(23, 'Navseasoncats wordinal redirected') 					end 					navw = navw..'*'..catlink.navelement..'\n' 				else 					local hidden = ''..nth..'' 					navw = navw..'*'..hidden..'\n' 					if listall then 						tlistall[#tlistall] = tlistall[#tlistall]..' ('..hidden..')' 					end 				end 			else --center navw 				navw = navw..'*'..nth..'\n' 			end 		else 			navw = navw..'*'..'0'..th..'\n' 		end  		i = i + 1 	end  	isolatedcat() 	if listall then 		return listalllinks() 	else 		return navw..'|}' 	end end  --==========================================================  local function find_var( pn ) 	--Extracts the variable text (e.g. 2015, 2015–16, 2000s, 3rd, III, etc.) from a string, 	--and returns { ['vtype'] = <'year'|'season'|etc.>, <v> = <2015|2015–16|etc.> } 	local pagename = currtitle.text 	if pn and pn ~=  then 		pagename = pn 	end  	local cpagename = 'Category:'..pagename --limited-Lua-regex workaround  	local d_season = mw.ustring.match(cpagename, ':(%d+s).+%(%d+[–-]%d+%)') --i.e. "1760s in the Province of Quebec (1763–1791)"  	local y_season = mw.ustring.match(cpagename, ':(%d+) .+%(%d+[–-]%d+%)') --i.e. "1763 establishments in the Province of Quebec (1763–1791)"  	local e_season = mw.ustring.match(cpagename, '%s(%d+[–-])$') or --irreg; ending unknown, e.g. "Members of the Scottish Parliament 2021–" 					 mw.ustring.match(cpagename, '%s(%d+[–-]present)$') --e.g. "UK MPs 2019–present"  	local season   = mw.ustring.match(cpagename, '[:%s%(](%d+[–-]%d+)[%)%s]') or --split in 2 b/c you can't frontier '$'/eos? 					 mw.ustring.match(cpagename, '[:%s](%d+[–-]%d+)$')  	local tvseason = mw.ustring.match(cpagename, 'season (%d+)') or 					 mw.ustring.match(cpagename, 'series (%d+)')     local patterns = {     '%s*(%d+ম)[%s-]', -- Matches Ilike : 1ম or :1ম     '%s*(%d+য়)[%s-]', -- Matches Ilike : 1য় or :1য়     '%s*(%d+র্থ)[%s-]', -- Matches Ilike : 1র্থ or :1র্থ     '%s*(%d+ষ্ঠ)[%s-]', -- Matches Ilike : 1ষ্ঠ or :1ষ্ঠ     '%s*(%d+শ)[%s-]', -- Matches Ilike : 1শ or :1শ     '%s*(%d+ম)$', -- Matches Ilike : 1ম at the end of the string     '%s*(%d+য়)$', -- Matches Ilike : 1য় at the end of the string     '%s*(%d+র্থ)$', -- Matches Ilike : 1র্থ at the end of the string     '%s*(%d+ষ্ঠ)$', -- Matches Ilike : 1ষ্ঠ at the end of the string     '%s*(%d+শ)$' -- Matches Ilike : 1শ at the end of the string     }  local nordinal for _, pattern in ipairs(patterns) do     nordinal = mw.ustring.match(cpagename, pattern)     if nordinal then break end end 	 	local decade   = mw.ustring.match(cpagename, '[:%s](%d+s)[%s-]') or 					 mw.ustring.match(cpagename, '[:%s](%d+s)$')  	local year     = mw.ustring.match(cpagename, '[:%s](%d%d%d%d)%s') or --prioritize 4-digit years 					 mw.ustring.match(cpagename, '[:%s](%d%d%d%d)$') or 					 mw.ustring.match(cpagename, '[:%s](%d+)%s') or 					 mw.ustring.match(cpagename, '[:%s](%d+)$') or 					 --expand/combine exceptions below as needed 					 mw.ustring.match(cpagename, '[:%s](%d+)-related') or 					 mw.ustring.match(cpagename, '[:%s](%d+)-cylinder') or 					 mw.ustring.match(cpagename, '[:%-VW](%d+)%s') --e.g. "Straight-8 engines"  	local roman    = string.match(cpagename, '%s([IVXLCDM]+)%s')  	local found    = d_season or y_season or e_season or season or tvseason or 					 nordinal or decade or year or roman  	if found then 		if string.match(found, '%d%d%d%d%d') == nil then 			--return in order of decreasing complexity/least chance for duplication 			if nordinal and season --i.e. "18th-century establishments in the Province of Quebec (1763–1791)" 						then return { ['vtype'] = 'nordinal', ['v'] = nordinal } end 			if d_season then return { ['vtype'] = 'decade',   ['v'] = d_season } end 			if y_season then return { ['vtype'] = 'year',     ['v'] = y_season } end 			if e_season then return { ['vtype'] = 'ending',   ['v'] = e_season } end 			if season   then return { ['vtype'] = 'season',   ['v'] = season   } end 			if tvseason then return { ['vtype'] = 'tvseason', ['v'] = tvseason } end 			if nordinal then return { ['vtype'] = 'nordinal', ['v'] = nordinal } end 			if decade   then return { ['vtype'] = 'decade',   ['v'] = decade   } end 			if year     then return { ['vtype'] = 'year',     ['v'] = year     } end 			if roman    then return { ['vtype'] = 'roman',    ['v'] = roman    } end 		end 	else 		--try wordinals ('zeroth' to 'ninety-ninth' only) 		local eng2ord = require('Module:ConvertNumeric/খেলাঘর').english_to_ordinal 		local split = mw.text.split(pagename, ' ') 		for i=1, #split do 			if eng2ord(split[i]) > -1 then 				return { ['vtype'] = 'wordinal', ['v'] = split[i] } 			end 		end  		--try English numerics ('one'/'single' to 'ninety-nine' only) 		local eng2num = require('Module:ConvertNumeric/খেলাঘর').english_to_numeral 		local split = mw.text.split(pagename, '[%s%-]') --e.g. "Nine-cylinder engines" 		for i=1, #split do 			if eng2num(split[i]) > -1 then 				return { ['vtype'] = 'enumeric', ['v'] = split[i] } 			end 		end 	end  	errors = p.errorclass('Function find_var can\'t find the variable text in category "'..pagename..'".') 	return { ['vtype'] = 'error', ['v'] = p.failedcat(errors, 'V') } end  --========================================================================== --                                  Main                                     --==========================================================================  function p.navseasoncats( frame ) 	--arg checks & handling 	local args = frame:getParent().args 	checkforunknownparams(args)       --for template args 	checkforunknownparams(frame.args) --for #invoke'd args 	local cat  = args['cat']                --'testcase' alias for catspace 	local list = args['list-all-links']     --debugging utility to output all links & followed #Rs 	local follow = args['follow-redirects'] --default 'yes' 	local testcase    = args['testcase'] 	local testcasegap = args['testcasegap'] 	local minimum = args['min'] 	local maximum = args['max'] 	local skip_gaps = args['skip-gaps']  	--apply args 	local pagename = testcase or cat or currtitle.text 	local testcaseindent = 	if testcasecolon == ':' then testcaseindent = '\n::' end 	if follow and follow == 'no' then followRs = false end 	if list and list == 'yes' then listall = true end 	if skip_gaps and skip_gaps == 'yes' then 		skipgaps = true 		trackcat(25, 'Navseasoncats using skip-gaps parameter') 	end  	--ns checks 	if currtitle.nsText == 'Category' then 		if cat and cat ~=  then 			trackcat(1, 'Navseasoncats using cat parameter') 		end 		if testcase and testcase ~=  then 			trackcat(2, 'Navseasoncats using testcase parameter') 		end 	elseif currtitle.nsText ==  then 		trackcat(29, 'Navseasoncats in mainspace') 	end  	--find the variable parts of pagename 	local findvar = find_var(pagename) 	if findvar.vtype == 'error' then --basic format error checking in find_var() 		return findvar.v..table.concat(ttrackingcats) 	end 	local start = string.match(findvar.v, '^%d+')  	--the rest is static 	local findvar_escaped = string.gsub( findvar.v, '%-', '%%%-') 	local firstpart, lastpart = string.match(pagename, '^(.-)'..findvar_escaped..'(.*)$') 	if findvar.vtype == 'tvseason' then --double check for cases like '30 Rock (season 3) episodes' 		firstpart, lastpart = string.match(pagename, '^(.-season )'..findvar_escaped..'(.*)$') 		if firstpart == nil then 			firstpart, lastpart = string.match(pagename, '^(.-series )'..findvar_escaped..'(.*)$') 		end 	end 	firstpart = mw.text.trim(firstpart or ) 	lastpart  = mw.text.trim(lastpart or )  	--call the appropriate nav function, in order of decreasing popularity 	if findvar.vtype == 'year' then     --e.g. "500", "2001"; nav_year..nav_decade; ~75% of cats 		local nav1 = nav_year( frame, firstpart, start, lastpart, minimum, maximum )..testcaseindent..table.concat(ttrackingcats)  		local dec = math.floor(findvar.v/10) 		local decadecat = nil 		local firstpart_dec = firstpart 		if firstpart_dec ~=  then 			firstpart_dec = firstpart_dec..l10n.the 		elseif firstpart_dec == 'AD' and dec <= 1 then 			firstpart_dec =  			if dec == 0 then dec =  end 		end 		local decade = dec..'0s ' 		decadecat = mw.text.trim( firstpart_dec..' '..decade..lastpart ) 		local exists, newlast = pageexists( decadecat, 'Category', 'year2decade', lastpart ) 		lastpart = newlast or lastpart 		if exists then 			navborder = false 			trackcat(27, 'Navseasoncats year and decade') 			local nav2 = nav_decade( frame, firstpart_dec, decade, lastpart, minimum, maximum )..testcaseindent..table.concat(ttrackingcats) 			return nav1nav2( nav1, nav2 ) 		elseif ttrackingcats[15] ~=  then --nav_year isolated; check nav_hyphen (e.g. UK MPs 1974, Moldovan MPs 2009, etc.) 			local hyphen = '–' 			local finish = start 			local nav2 = nav_hyphen( frame, start, hyphen, finish, firstpart, lastpart, minimum, maximum, testcasegap )..testcaseindent..table.concat(ttrackingcats) 			if ttrackingcats[15] ~= '' then return nav1 --still isolated; rv to nav_year 			else return nav2 end 		else --regular nav_year 			return nav1 		end  	elseif findvar.vtype == 'decade' then   --e.g. "0s", "2010s"; nav_decade..nav_nordinal; ~12% of cats 		local nav1 = nav_decade( frame, firstpart, start, lastpart, minimum, maximum )..testcaseindent..table.concat(ttrackingcats)  		local decade = tonumber(string.match(findvar.v, '^(%d+)s')) 		local century = math.floor( ((decade-1)/100) + 1 ) --from  		if century == 0 then century = 1 end --no 0th century 		if string.match(decade, '00$') then 			century = century + 1 --'2000' is in the 20th, but the rest of the 2000s is in the 21st 		end 		local clastpart = ' century '..lastpart 		local centurycat = mw.text.trim( firstpart..' '..p.addord(century)..clastpart ) 		local exists = pageexists( centurycat, 'Category', 'decade2century' ) 		if not exists then --check for hyphenated century 			clastpart = '-century '..lastpart 			centurycat = mw.text.trim( firstpart..' '..p.addord(century)..clastpart ) 			exists = pageexists( centurycat, 'Category' ) 		end 		if exists then 			navborder = false 			trackcat(28, 'Navseasoncats decade and century') 			local nav2 = nav_nordinal( frame, firstpart, century, clastpart, minimum, maximum )..testcaseindent..table.concat(ttrackingcats) 			return nav1nav2( nav1, nav2 ) 		else 			return nav1 		end  	elseif findvar.vtype == 'nordinal' then --e.g. "1st", "99th"; ~7.5% of cats 		return nav_nordinal( frame, firstpart, start, lastpart, minimum, maximum )..testcaseindent..table.concat(ttrackingcats)  	elseif findvar.vtype == 'season' then   --e.g. "1–4", "1999–2000", "2001–02", "2001–2002", "2005–2010", etc.; ~5.25% 		local hyphen, finish = mw.ustring.match(findvar.v, '%d([–-])(%d+)') --ascii 150 & 45 (ndash & keyboard hyphen); mw req'd 		return nav_hyphen( frame, start, hyphen, finish, firstpart, lastpart, minimum, maximum, testcasegap )..testcaseindent..table.concat(ttrackingcats)  	elseif findvar.vtype == 'tvseason' then --e.g. "1", "15" but preceded with "season" or "series"; <1% of cats 		return nav_tvseason( frame, firstpart, start, lastpart, maximum )..testcaseindent..table.concat(ttrackingcats) --"minimum" defaults to 1  	elseif findvar.vtype == 'wordinal' then --e.g. "first", "ninety-ninth"; <<1% of cats 		local ordinal = 'on' 		return nav_wordinal( frame, firstpart, findvar.v, lastpart, minimum, maximum, ordinal, frame )..testcaseindent..table.concat(ttrackingcats)  	elseif findvar.vtype == 'enumeric' then --e.g. "one", "ninety-nine"; <<1% of cats 		local ordinal = 'off' 		return nav_wordinal( frame, firstpart, findvar.v, lastpart, minimum, maximum, ordinal, frame )..testcaseindent..table.concat(ttrackingcats)  	elseif findvar.vtype == 'roman' then    --e.g. "I", "XXVIII"; <<1% of cats 		return nav_roman( frame, firstpart, findvar.v, lastpart, minimum, maximum )..testcaseindent..table.concat(ttrackingcats)  	elseif findvar.vtype == 'ending' then   --e.g. "2021–" (irregular; ending unknown); <<<1% of cats 		local hyphen, finish = mw.ustring.match(findvar.v, '%d([–-])present$'), -1 --ascii 150 & 45 (ndash & keyboard hyphen); mw req'd 		if hyphen == nil then 			hyphen, finish = mw.ustring.match(findvar.v, '%d([–-])$'), 0 --0/-1 are hardcoded switches for nav_hyphen() 		end 		return nav_hyphen( frame, start, hyphen, finish, firstpart, lastpart, minimum, maximum, testcasegap )..testcaseindent..table.concat(ttrackingcats)  	else                                 --malformed 		errors = p.errorclass('Failed to determine the appropriate nav function from malformed season "'..findvar.v..'". ') 		return p.failedcat(errors, 'N')..table.concat(ttrackingcats) 	end end  return modwrap.wrapper(p)