From Wikipedia, the free encyclopedia

require('strict')



local p = {}

local title = mw.title.getCurrentTitle()

local namespace = title.namespace

local testcases = (string.sub(title.subpageText,1,9) == 'testcases')



--[[==========================================================================]]

--[[                            Category functions                            ]]

--[[==========================================================================]]



function p.getCatForId( id )

	local catName = ''

	if namespace == 0 then

		catName = 'Wikipedia articles with '..id..' identifiers'

	elseif namespace == 2 and not title.isSubpage then

		catName = 'User pages with '..id..' identifiers'

	else

		catName = 'Miscellaneous pages with '..id..' identifiers'

	end

	return '[[Category:'..catName..']]'..p.redCatLink(catName)

end



function p.redCatLink( catName ) --catName == 'Blah' (not 'Category:Blah', not '[[Category:Blah]]')

	if catName and catName ~= '' and

	   testcases == false and

	   mw.title.new(catName, 14).exists == false

	then

		return '[[Category:Pages with red-linked authority control categories]]'

	end

	return ''

end



function p.createRow( id, rawValues, link, links, withUid, specialCat, prefix)

	local catName = 'Wikipedia articles with faulty '..(specialCat or id)..' identifiers'

	if links then -- all links[] use withUid = false; no check needed

		local row = ''

		if prefix then 

			row = row .. '*' .. prefix

		end

		for i, l in ipairs( links ) do

			if i == 1 and not prefix then row = row..'*'

			else           row = row..'\n**' end

			if l then

				row = row..'<span class="uid">'..l..'</span>'

			else

				row = row..'<span class="error">The '..id..' id '..rawValuesi..' is not valid.</span>[[Category:'..catName..']]'..p.redCatLink(catName)

			end

		end

		return row..'\n'

	elseif link then -- All IDs that have a prefix support multiple identifiers, so prefix is not needed

		if withUid then

			return '*<span class="nowrap"><span class="uid">'..link..'</span></span>\n'

		end

		return '*<span class="nowrap">'..link..'</span>\n'

	end

	

	return '* <span class="error">The '..id..' id '..rawValues..' is not valid.</span>[[Category:'..catName..']]'..p.redCatLink(catName)..'\n'

end



--[[==========================================================================]]

--[[                      Property formatting functions                       ]]

--[[==========================================================================]]



-- If a link has a suitable entry in the global inter-wiki prefix table at [[:m:Interwiki_map]], please consider routing through this prefix rather than as external link URL. This will ease future maintenance as necessary updates to the link can be centrally carried out there rather than by updating this module. The "external link" icon would disappear for such entries.



function p.aagLink( id, label)

	--P3372's format regex: \d+ (e.g. 1)

	if not id:match( '^%d+$' ) then

		return false

	end

	return '[https://www.aucklandartgallery.com/explore-art-and-ideas/artist/'..id..'/ '..(label or 'Auckland')..']'..p.getCatForId( 'AAG' )

end



function p.acmLink( id, label )

	--P864's format regex: \d{11} (e.g. 12345678901)

	if not id:match( '^%d%d%d%d%d%d%d%d%d%d%d$' ) then

		return false

	end

	return '[https://dl.acm.org/profile/'..id..' '..(label or 'Association for Computing Machinery')..']'..p.getCatForId( 'ACM-DL' )

end



function p.adbLink( id, label )

	--P1907's format regex: [a-z][-a-z]+-([1-2]\d|[1-9])\d{0,3} (e.g. barton-sir-edmund-toby-71)

	if not id:match( '^[a-z][-a-z]+-[1-2]%d%d?%d?%d?$' ) and

	   not id:match( '^[a-z][-a-z]+-[1-9]%d?%d?%d?$' ) then

		return false

	end

	return '[http://adb.anu.edu.au/biography/'..id..' '..(label or 'Australia')..']'..p.getCatForId( 'ADB' )

end



function p.agsaLink( id, label )

	--P6804's format regex: [1-9]\d* (e.g. 3625)

	if not id:match( '^[1-9]%d*$' ) then

		return false

	end

	return '[https://www.agsa.sa.gov.au/collection-publications/collection/creators/_/'..id..'/ '..(label or 'South Australia')..']'..p.getCatForId( 'AGSA' )

end



function p.autoresuyLink( id, label )

	--P2558's format regex: [1-9]\d{0,4} (e.g. 12345)

	if not id:match( '^[1-9]%d?%d?%d?%d?$' ) then

		return false

	end

	return '[https://autores.uy/autor/'..id..' '..(label or 'Uruguay')..']'..p.getCatForId( 'autores.uy' )

end



function p.awrLink( id, label )

	--P4186's format regex: (([A-Z]{3}\d{4})|([A-Z]{2}\d{5}))[a-z] (e.g. PR00768b)

	if not id:match( '^[A-Z][A-Z][A-Z]%d%d%d%d[a-z]$' ) and

	   not id:match( '^[A-Z][A-Z]%d%d%d%d%d[a-z]$' ) then

		return false

	end

	return '[http://www.womenaustralia.info/biogs/'..id..'.htm '..(label or 'Australian Women\'s Register')..']'..p.getCatForId( 'AWR' )

end



function p.balatLink( id, label )

	--P3293's format regex: \d+ (e.g. 1)

	if not id:match( '^%d+$' ) then

		return false

	end

	return '[http://balat.kikirpa.be/object/104257'..id..' '..(label or 'BALaT (Belgium)')..']'..p.getCatForId( 'BALaT' ) --no https as of 9/2019

end



function p.bibsysLink( id, label )

	--P1015's format regex: [1-9]\d* or [1-9](\d{0,8}|\d{12}) (e.g. 1234567890123)

	--TODO: follow up @ [[d:Property talk:P1015#Discrepancy between the 2 regex constraints]] or escalate/investigate

	if not id:match( '^[1-9]%d?%d?%d?%d?%d?%d?%d?%d?$' ) and

	   not id:match( '^[1-9]%d%d%d%d%d%d%d%d%d%d%d%d$' ) then

		return false

	end

	return '[https://authority.bibsys.no/authority/rest/authorities/html/'..id..' '..(label or 'Norway')..']'..p.getCatForId( 'BIBSYS' )

end



function p.bildLink( id, label )

	--P2092's format regex: \d+ (e.g. 1)

	if not id:match( '^%d+$' ) then

		return false

	end

	return '[https://www.bildindex.de/document/obj'..id..' '..(label or 'Bildindex (Germany)')..']'..p.getCatForId( 'Bildindex' )

end



function p.bncLink( id, label )

	--P1890's format regex: \d{9} (e.g. 123456789)

	if not id:match( '^%d%d%d%d%d%d%d%d%d$' ) then

		return false

	end

	return '[http://www.bncatalogo.cl/F?func=direct&local_base=red10&doc_number='..id..' '..(label or 'Chile')..']'..p.getCatForId( 'BNC' )

end



function p.bneLink( id, label )

	--P950's format regex: (XX|FF|a)\d{4,7}|(bima|bimo|bica|bis[eo]|bivi|Mise|Mimo|Mima)\d{10} (e.g. XX1234567)

	if not id:match( '^[XF][XF]%d%d%d%d%d?%d?%d?$' ) and

	   not id:match( '^a%d%d%d%d%d?%d?%d?$' ) and

	   not id:match( '^bi[mcsv][aoei]%d%d%d%d%d%d%d%d%d%d$' ) and

	   not id:match( '^Mi[sm][eoa]%d%d%d%d%d%d%d%d%d%d$' ) then

		return false

	end

	return '[http://catalogo.bne.es/uhtbin/authoritybrowse.cgi?action=display&authority_id='..id..' '..(label or 'Spain')..']'..p.getCatForId( 'BNE' ) --no https as of 9/2019

end



function p.bnfLink( id, label )

	--P268's format regex: \d{8}[0-9bcdfghjkmnpqrstvwxz] (e.g. 123456789)

	if not id:match( '^c?b?%d%d%d%d%d%d%d%d[0-9bcdfghjkmnpqrstvwxz]$' ) then

		return false

	end

	--Add cb prefix if it has been removed

	if not id:match( '^cb.+$' ) then

		id = 'cb'..id

	end

	return '[https://catalogue.bnf.fr/ark:/12148/'..id..' ' .. (label or 'France')..'] [https://data.bnf.fr/ark:/12148/'..id..' (data)]'..p.getCatForId( 'BNF' )

end



function p.botanistLink( id, label )

	--P428's format regex: ('t )?(d')?(de )?(la )?(van (der )?)?(Ma?c)?(De)?(Di)?\p{Lu}?C?['\p{Ll}]*([-'. ]*(van )?(y )?(d[ae][nr]?[- ])?(Ma?c)?[\p{Lu}bht]?C?['\p{Ll}]*)*\.? ?f?\.? (e.g. L.)

	--not easily/meaningfully implementable in Lua's regex since "(this)?" is not allowed...

	if not mw.ustring.match( id, "^[%u%l%d%. '-]+$" ) then --better than nothing

		return false

	end

	id = id:gsub(' +', '%%20')

	return '[https://www.ipni.org/ipni/advAuthorSearch.do?find_abbreviation='..id..' '..(label or 'International Plant Names Index')..']'..p.getCatForId( 'Botanist' )

end



function p.bpnLink( id, label )

	--P651's format regex: \d{6,8} (e.g. 00123456)

	if not id:match( '^%d%d%d%d%d%d%d%d$' ) and --original format regex, changed 8/2019 to

	   not id:match( '^0?%d%d%d%d%d%d%d$' ) and --allow 1-2 leading 0s, allowed by the website

	   not id:match( '^0?0?%d%d%d%d%d%d$' ) then

		return false

	end

	return '[http://www.biografischportaal.nl/en/persoon/'..id..' '..(label or 'Netherlands')..']'..p.getCatForId( 'BPN' ) --no https as of 9/2019

end



function p.canticLink( id, label )

	--P1273's format regex: a\d{7}[0-9x] (e.g. a10640745)

	if not id:match( '^a%d%d%d%d%d%d%d[%dx]$' ) then

		return false

	end

	return '[http://cantic.bnc.cat/registres/CUCId/'..id..' '..(label or 'Catalonia')..']'..p.getCatForId( 'CANTIC' ) --no https as of 10/2019

end



function p.ciniiLink( id, label )

	--P271's format regex: DA\d{7}[\dX] (e.g. DA12345678)

	if not id:match( '^DA%d%d%d%d%d%d%d[%dX]$' ) then

		return false

	end

	return '[https://ci.nii.ac.jp/author/'..id..'?l=en '..(label or 'CiNii (Japan)')..']'..p.getCatForId( 'CINII' )

end



function p.cwgcLink( id, label )

	--P1908's format regex: [1-9]\d* (e.g. 75228351)

	if not id:match( '^[1-9]%d*$' ) then

		return false

	end

	return '[https://www.cwgc.org/find-war-dead/casualty/'..id..'/ '..(label or 'Commonwealth War Graves Commission')..']'..p.getCatForId( 'CWGC' )

end



function p.daaoLink( id, label )

	--P1707's format regex: [a-z\-]+\d* (e.g. rolf-harris)

	if not id:match( '^[a-z%-]+%d*$' ) then

		return false

	end

	return '[https://www.daao.org.au/bio/'..id..' '..(label or 'Australian Artists')..']'..p.getCatForId( 'DAAO' )

end



function p.dblpLink( id, label )

	--P2456's format regex: \d{2,3} /\d+(-\d+)?|[a-z] /[a-zA-Z][0-9A-Za-z]*(-\d+)? (e.g. 123/123)

	if not id:match( '^%d%d%d?/%d+$' ) and

	   not id:match( '^%d%d%d?/%d+%-%d+$' ) and

	   not id:match( '^[a-z]/[a-zA-Z][0-9A-Za-z]*$' ) and

	   not id:match( '^[a-z]/[a-zA-Z][0-9A-Za-z]*%-%d+$' ) then

		return false

	end

	return '[https://dblp.org/pid/'..id..' '..(label or 'DBLP (computer science)')..']'..p.getCatForId( 'DBLP' )

end



function p.dibLink( id, label )

	--P6829's format regex: a\d{4}\d?(-[A-D])? (e.g. a1953)

	if not id:match( '^a%d%d%d%d%d?%-?[A-D]?$' ) then

		return false

	end

	return '[https://dib.cambridge.org/viewReadPage.do?articleId='..id..' '..(label or 'Ireland')..']'..p.getCatForId( 'DIB' )

end



function p.dsiLink( id, label )

	--P2349's format regex: [1-9]\d* (e.g. 1538)

	if not id:match( '^[1-9]%d*$' ) then

		return false

	end

	return '[http://www.uni-stuttgart.de/hi/gnt/dsi2/index.php?table_name=dsi&function=details&where_field=id&where_value='..id..' '..(label or 'Scientific illustrators')..']'..p.getCatForId( 'DSI' )

end



function p.fnzaLink( id, label )

	--P6792's format regex: [1-9]\d* (e.g. 9785)

	if not id:match( '^[1-9]%d*$' ) then

		return false

	end

	return '[https://findnzartists.org.nz/artist/'..id..'/ '..(label or 'New Zealand Artists')..']'..p.getCatForId( 'FNZA' )

end



function p.gndLink( id, label )

	--P227's format regex: 1[012]?\d{7}[0-9X]|[47]\d{6}-\d|[1-9]\d{0,7}-[0-9X]|3\d{7}[0-9X] (e.g. 4079154-3)

	if not id:match( '^1[012]?%d%d%d%d%d%d%d[0-9X]$' ) and

	   not id:match( '^[47]%d%d%d%d%d%d%-%d$' ) and

	   not id:match( '^[1-9]%d?%d?%d?%d?%d?%d?%d?%-[0-9X]$' ) and

	   not id:match( '^3%d%d%d%d%d%d%d[0-9X]$' ) then

		return false

	end

	return '[https://d-nb.info/gnd/'..id..' '..(label or 'Germany')..']'..p.getCatForId( 'GND' )

end



function p.hdsLink( id, label )

	--P902's format regex: \d{6} (e.g. 050123)

	if not id:match( '^%d%d%d%d%d%d$' ) then

		return false

	end

	return '[https://hls-dhs-dss.ch/fr/articles/'..id..' '..(label or 'Historical Dictionary of Switzerland')..']'..p.getCatForId( 'HDS' )

end



function p.iaafLink( id, label )

	--P1146's format regex: [0-9][0-9]* (e.g. 012)

	if not id:match( '^%d+$' ) then

		return false

	end

	return '[https://www.iaaf.org/athletes/_/'..id..' '..(label or 'World Athletics')..']'..p.getCatForId( 'IAAF' )

end





function p.iccuLink( id, label )

	--P396's format regex: IT\\ICCU\\(\d{10}|\D\D[\D\d]\D\\\d{6}) (e.g. IT\ICCU\CFIV\000163)

	if not id:match( '^IT\\ICCU\\%d%d%d%d%d%d%d%d%d%d$' ) and

	   not id:match( '^IT\\ICCU\\%u%u[%u%d]%u\\%d%d%d%d%d%d$' ) then --legacy: %u used here instead of %D (but the faulty ID cat is empty, out of ~12k uses)

		return false

	end

	return '[https://opac.sbn.it/opacsbn/opac/iccu/scheda_authority.jsp?bid='..id..' '..(label or 'Italy')..']'..p.getCatForId( 'ICCU' )

end

function p.iciaLink( id, label )

	--P1736's format regex: \d+ (e.g. 1)

	if not id:match( '^%d+$' ) then

		return false

	end

	return '[https://www.imj.org.il/artcenter/newsite/en/?artist='..id..' '..(label or 'ICIA (Israel)')..']'..p.getCatForId( 'ICIA' )

end



function p.ieuLink( id, label )

	--P9070's format regex: [A-Z]\\[A-Z]\\[A-Za-z0-9]+ (e.g. K\Y\Kyiv)

	if not id:match( '^[A-Z]\\[A-Z]\\%w+$' ) then

		return false

	end

	return '[http://www.encyclopediaofukraine.com/display.asp?linkpath=pages\\'..id..' '..(label or 'Internet Encyclopedia of Ukraine')..']'..p.getCatForId( 'IEU' )

end



function p.isniLink( id, label )

	id = p.validateIsni( id ) --e.g. 0000-0000-6653-4145

	if not id then

		return false

	end

	return '[https://isni.org/isni/'..id..' '..(label or 'ISNI')..']'..p.getCatForId( 'ISNI' )

end



function p.jocondeLink( id, label )

	--P347's format regex: [\-0-9A-Za-z]{11} (e.g. 12345678901)

	local regex = '^'..string.rep('[%-0-9A-Za-z]', 11)..'$'

	if not id:match( regex ) then

		return false

	end

	return '[https://www.pop.culture.gouv.fr/notice/joconde/'..id..' '..(label or 'Joconde (France)')..']'..p.getCatForId( 'Joconde' )

end



function p.kulturnavLink( id, label )

	--P1248's format regex: [0-9a-f]{8}\-[0-9a-f]{4}\-[0-9a-f]{4}\-[0-9a-f]{4}\-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)

	if not id:match( '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then

		return false

	end

	return '[http://kulturnav.org/'..id..' '..(label or 'KulturNav (Norway)')..']'..p.getCatForId( 'KULTURNAV' ) --no https as of 9/2019

end



function p.lccnLink( id, label )

	local parts = p.splitLccn( id ) --e.g. n78039510

	if not parts then

		return false

	end

	local lccnType = parts1 ~= 'sh' and 'names' or 'subjects'

	id = parts1 .. parts2 .. p.append( parts3], '0', 6 )

	return '[https://id.loc.gov/authorities/'..lccnType..'/'..id..' '..(label or 'United States')..']'..p.getCatForId( 'LCCN' )

end



function p.lirLink( id, label )

	--P886's format regex: \d+ (e.g. 1)

	if not id:match( '^%d+$' ) then

		return false

	end

	return '[http://www.e-lir.ch/e-LIR___Lexicon.'..id..'.450.0.html '..(label or 'Lexicon Istoric Retic (Switzerland)')..']'..p.getCatForId( 'LIR' ) --no https as of 9/2019

end



function p.lnbLink( id, label )

	--P1368's format regex: \d{9} (e.g. 123456789)

	if not id:match( '^%d%d%d%d%d%d%d%d%d$' ) then

		return false

	end

	return '[https://kopkatalogs.lv/F?func=direct&local_base=lnc10&doc_number='..id..'&P_CON_LNG=ENG '..(label or 'Latvia')..']'..p.getCatForId( 'LNB' )

end



function p.leonoreLink( id, label )

	--P640's format regex: LH/\d{1,4}/\d{1,3}|19800035/\d{1,4}/\d{1,5}(Bis)?|C/0/\d{1,2} (e.g. LH/2064/18)

	if not id:match( '^LH/%d%d?%d?%d?/%d%d?%d?$' ) and             --IDs from       LH/1/1 to         LH/2794/54 (legionaries)

	   not id:match( '^19800035/%d%d?%d?%d?/%d%d?%d?%d?%d?$' ) and --IDs from 19800035/1/1 to 19800035/385/51670 (legionnaires who died 1954-1977 & some who died < 1954)

	   not id:match( '^C/0/%d%d?$' ) then                          --IDs from        C/0/1 to             C/0/84 (84 famous legionaries)

		return false

	end

	return '[http://www.culture.gouv.fr/public/mistral/leonore_fr?ACTION=CHERCHER&FIELD_1=COTE&VALUE_1='..id..' '..(label or 'Léonore (France)')..']'..p.getCatForId( 'Léonore' ) --no https as of 9/2019

end



function p.maLink( id, label )

	--P6366's format regex: [1-9]\d{4,9} (e.g. 1498221862)

	if not id:match( '^[1-9]%d%d%d%d%d?%d?%d?%d?%d?$' ) then

		return false

	end

	return '[https://academic.microsoft.com/v2/detail/'..id..' '..(label or 'Microsoft Academic')..']'..p.getCatForId( 'MA' )

end



function p.mbaLink( id, label )

	--P434's format regex: [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)

	if not id:match( '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then

		return false

	end

	local url = 'https://musicbrainz.org/artist/'..id

	local cat = p.getCatForId( 'MusicBrainz' )--special category name

	if label then

		return '['..url..' '..label..']'..cat	

	else

		return '[[MBA (identifier)|MusikBrainz]] [' .. url .. ' artist]' .. cat

	end

end



function p.mbareaLink( id, label )

	--P982's format regex: [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)

	if not id:match( '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then

		return false

	end

	local url = 'https://musicbrainz.org/area/'..id

	local cat = p.getCatForId( 'MusicBrainz area' )--special category name

	if label then

		return '['..url..' '..label..']'..cat	

	else

		return '[[MBAREA (identifier)|MusikBrainz]] [' .. url .. ' area]' .. cat

	end

end



function p.mbiLink( id, label )

	--P1330's format regex: [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)

	if not id:match( '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then

		return false

	end

	local url = 'https://musicbrainz.org/instrument/'..id

	local cat = p.getCatForId( 'MusicBrainz instrument' )--special category name

	if label then

		return '['..url..' '..label..']'..cat	

	else

		return '[[MBI (identifier)|MusikBrainz]] [' .. url .. ' instrument]' .. cat

	end

end



function p.mblLink( id, label )

	--P966's format regex: [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)

	if not id:match( '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then

		return false

	end

	local url = 'https://musicbrainz.org/label/'..id

	local cat = p.getCatForId( 'MusicBrainz label' )--special category name

	if label then

		return '['..url..' '..label..']'..cat	

	else

		return '[[MBL (identifier)|MusikBrainz]] [' .. url .. ' label]' .. cat

	end

end



function p.mbpLink( id, label )

	--P1004's format regex: [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)

	if not id:match( '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then

		return false

	end

	local url = 'https://musicbrainz.org/place/'..id

	local cat = p.getCatForId( 'MusicBrainz place' )--special category name

	if label then

		return '['..url..' '..label..']'..cat	

	else

		return '[[MBP (identifier)|MusikBrainz]] [' .. url .. ' place]' .. cat

	end

end



function p.mbrgLink( id, label )

	--P436's format regex: [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)

	if not id:match( '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then

		return false

	end

	local url = 'https://musicbrainz.org/release-group/'..id

	local cat = p.getCatForId( 'MusicBrainz release group' )--special category name

	if label then

		return '['..url..' '..label..']'..cat	

	else

		return '[[MBRG (identifier)|MusikBrainz]] [' .. url .. ' release group]' .. cat

	end

end



function p.mbsLink( id, label )

	--P1407's format regex: [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)

	if not id:match( '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then

		return false

	end

	local url = 'https://musicbrainz.org/series/'..id

	local cat = p.getCatForId( 'MusicBrainz series' )--special category name

	if label then

		return '['..url..' '..label..']'..cat	

	else

		return '[[MBS (identifier)|MusikBrainz]] [' .. url .. ' series]' .. cat

	end

end



function p.mbwLink( id, label )

	--P435's format regex: [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)

	if not id:match( '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then

		return false

	end

	

	local url = 'https://musicbrainz.org/work/'..id

	local cat = p.getCatForId( 'MusicBrainz work' )--special category name

	if label then

		return '['..url..' '..label..']'..cat	

	else

		return '[[MBW (identifier)|MusikBrainz]] [' .. url .. ' work]' .. cat

	end

end



function p.mgpLink( id, label )

	--P549's format regex: \d{1,6} (e.g. 123456)

	if not id:match( '^%d%d?%d?%d?%d?%d?$' ) then

		return false

	end

	return '[https://genealogy.math.ndsu.nodak.edu/id.php?id='..id..' '..(label or 'Mathematics Genealogy Project')..']'..p.getCatForId( 'MGP' )

end



function p.naraLink( id, label )

	--P1225's format regex: ^([1-9]\d{0,8})$ (e.g. 123456789)

	if not id:match( '^[1-9]%d?%d?%d?%d?%d?%d?%d?%d?$' ) then

		return false

	end

	return '[https://catalog.archives.gov/id/'..id..' '..(label or 'National Archives (US)')..']'..p.getCatForId( 'NARA' )

end



function p.nclLink( id, label )

	--P1048's format regex: \d+ (e.g. 1081436)

	if not id:match( '^%d+$' ) then

		return false

	end

	return '[http://aleweb.ncl.edu.tw/F/?func=accref&acc_sequence='..id..'&CON_LNG=ENG '..(label or 'Taiwan')..']'..p.getCatForId( 'NCL' ) --no https as of 9/2019

end



function p.ndlLink( id, label )

	--P349's format regex: 0?\d{8} (e.g. 012345678)

	if not id:match( '^0?%d%d%d%d%d%d%d%d$' ) then

		return false

	end

	return '[https://id.ndl.go.jp/auth/ndlna/'..id..' '..(label or 'Japan')..']'..p.getCatForId( 'NDL' )

end



function p.ngvLink( id, label )

	--P2041's format regex: \d+ (e.g. 12354)

	if not id:match( '^%d+$' ) then

		return false

	end

	return '[https://www.ngv.vic.gov.au/explore/collection/artist/'..id..'/ '..(label or 'Victoria')..']'..p.getCatForId( 'NGV' )

end



function p.nkcLink( id, label )

	--P691's format regex: [a-z]{2,4}[0-9]{2,14} (e.g. abcd12345678901234)

	if not id:match( '^[a-z][a-z][a-z]?[a-z]?%d%d%d?%d?%d?%d?%d?%d?%d?%d?%d?%d?%d?%d?$' ) then

		return false

	end

	return '[https://aleph.nkp.cz/F/?func=find-c&local_base=aut&ccl_term=ica='..id..'&CON_LNG=ENG '..(label or 'Czech Republic')..']'..p.getCatForId( 'NKC' )

end



function p.nlaLink( id, label )

	--P409's format regex: [1-9][0-9]{0,11} (e.g. 123456789012)

	if not id:match( '^[1-9]%d?%d?%d?%d?%d?%d?%d?%d?%d?%d?%d?$' ) then

		return false

	end

	return '[https://nla.gov.au/anbd.aut-an'..id..' '..(label or 'Australia')..']'..p.getCatForId( 'NLA' )

end



function p.nlgLink( id, label )

	--P3348's format regex: [1-9]\d* (e.g. 1)

	if not id:match( '^[1-9]%d*$' ) then

		return false

	end

	return '[https://data.nlg.gr/resource/authority/record'..id..' '..(label or 'Greece')..']'..p.getCatForId( 'NLG' )

end



function p.nliLink( id, label )

	--P949's format regex: \d{9} (e.g. 123456789)

	if not id:match( '^%d%d%d%d%d%d%d%d%d$' ) then

		return false

	end

	return '[http://uli.nli.org.il/F/?func=direct&doc_number='..id..'&local_base=nlx10'..' '..(label or 'Israel')..']'..p.getCatForId( 'NLI' )

end



function p.nlkLink( id, label )

	--P5034's format regex: KA.(19|20).{7} (e.g. KAC201501465)

	if not id:match( '^KA.19.......$' ) and

	   not id:match( '^KA.20.......$' ) then

		return false

	end

	return '[https://nl.go.kr/authorities/resource/'..id..' '..(label or 'Korea')..']'..p.getCatForId( 'NLK' )

end



function p.nlpLink( id, label )

	--P1695's format regex: 9810[0-9]\d* or A[0-9]{7}[0-9X] (e.g. 9810123456789012345 or A10414836)

	if not id:match( '^9810%d+$' ) and

	   not id:match( '^A%d%d%d%d%d%d%d[%dX]$' ) then

		return false

	end

	return '[https://tools.wmflabs.org/wikidata-externalid-url?p=1695&id='..id..' '..(label or 'Poland')..']'..p.getCatForId( 'NLP' )

end



function p.nlrLink( id, label )

	--P1003's format regex: \d{9} (e.g. 123456789)

	if not id:match( '^%d%d%d%d%d%d%d%d%d$' ) then

		return false

	end

	return '[http://aleph.bibnat.ro:8991/F/?func=direct&local_base=NLR10&doc_number='..id..' '..(label or 'Romania')..']'..p.getCatForId( 'NLR' )

end



function p.nskLink( id, label )

	--P1375's format regex: \d{9} (e.g. 123456789)

	if not id:match( '^%d%d%d%d%d%d%d%d%d$' ) then

		return false

	end

	return '[http://katalog.nsk.hr/F/?func=direct&doc_number='..id..'&local_base=nsk10 '..(label or 'Croatia')..']'..p.getCatForId( 'NSK' ) --no https as of 9/2019

end



function p.ntaLink( id, label )

	--P1006's format regex: \d{8}[\dX] (e.g. 12345678X)

	if not id:match( '^%d%d%d%d%d%d%d%d[%dX]$' ) then

		return false

	end

	return '[http://data.bibliotheken.nl/id/thes/p'..id..' '..(label or 'Netherlands')..']'..p.getCatForId( 'NTA' )

end



function p.orcidLink( id, label )

	id = p.validateIsni( id ) --e.g. 0000-0002-7398-5483

	if not id then

		return false

	end

	id = id:sub( 1, 4 )..'-'..id:sub( 5, 8 )..'-'..id:sub( 9, 12 )..'-'..id:sub( 13, 16 )

	return '[https://orcid.org/'..id..' '..(label or 'ORCID')..']'..p.getCatForId( 'ORCID' )

end



function p.picLink( id, label )

	--P2750's format regex: [1-9]\d* (e.g. 1)

	if not id:match( '^[1-9]%d*$' ) then

		return false

	end

	return '[https://pic.nypl.org/constituents/'..id..' '..(label or 'Photographers\' Identities')..']'..p.getCatForId( 'PIC' )

end



function p.plwabnLink( id, label )

	--P7293's format regex: 981[0-9]{8}05606 (e.g. 9810696457305606)

	if not id:match( '^981%d%d%d%d%d%d%d%d05606*$' ) then

		return false

	end

	return '[http://mak.bn.org.pl/cgi-bin/KHW/makwww.exe?BM=1&NU=1&IM=4&WI='..id..' '..(label or 'Poland')..']'..p.getCatForId( 'PLWABN' )

end



function p.publonsLink( id, label )

	--P3829's format regex: \d+ (e.g. 654601)

	if not id:match( '^%d+$' ) then

		return false

	end

	return '[https://publons.com/author/'..id..'/ '..(label or 'Publons (researchers)')..']'..p.getCatForId( 'Publons' )

end



function p.ridLink( id, label )

	--P1053's format regex: [A-Z]{1,3}-\d{4}-(19|20)\d\d (e.g. AAS-5150-2020)

	if not id:match( '^[A-Z][A-Z]?[A-Z]?%-%d%d%d%d%-19%d%d$' ) and

	   not id:match( '^[A-Z][A-Z]?[A-Z]?%-%d%d%d%d%-20%d%d$' ) then

		return false

	end

	return '[https://www.researcherid.com/rid/'..id..' '..(label or 'ResearcherID')..']'..p.getCatForId( 'RID' )

end



function p.reroLink( id, label )

	--P3065's format regex: 0[1-2]-[A-Z0-9]{1,10} (e.g. 02-A012345678)

	if not id:match( '^0[1-2]%-[A-Z%d][A-Z%d]?[A-Z%d]?[A-Z%d]?[A-Z%d]?[A-Z%d]?[A-Z%d]?[A-Z%d]?[A-Z%d]?[A-Z%d]?$' ) then

		return false

	end

	return '[http://data.rero.ch/'..id..' '..(label or 'RERO (Switzerland)')..']'..p.getCatForId( 'RERO' )

end



function p.rkdartistsLink( id, label )

	--P650's format regex: [1-9]\d{0,5} (e.g. 123456)

	if not id:match( '^[1-9]%d?%d?%d?%d?%d?$' ) then

		return false

	end

	return '[https://rkd.nl/en/explore/artists/'..id..' '..(label or 'RKD Artists (Netherlands)')..']'..p.getCatForId( 'RKDartists' )

end



function p.rkdidLink( id, label )

	--P350's format regex: [1-9]\d{0,5} (e.g. 123456)

	if not id:match( '^[1-9]%d?%d?%d?%d?%d?$' ) then

		return false

	end

	return '[https://rkd.nl/nl/explore/images/'..id..' '..(label or 'RKD ID (Netherlands)')..']'..p.getCatForId( 'RKDID' )

end



function p.rslLink( id, label )

	--P947's format regex: \d{1,9} (e.g. 123456789)

	if not id:match( '^%d%d?%d?%d?%d?%d?%d?%d?%d?$' ) then

		return false

	end

	return '[http://aleph.rsl.ru/F?func=find-b&find_code=SYS&adjacent=Y&local_base=RSL11&request='..id..'&CON_LNG=ENG '..(label or 'Russia')..']'..p.getCatForId( 'RSL' ) --no https as of 9/2019

end



function p.selibrLink( id, label )

	--P906's format regex: [1-9]\d{4,5} (e.g. 123456)

	if not id:match( '^[1-9]%d%d%d%d%d?$' ) then

		return false

	end

	return '[https://libris.kb.se/auth/'..id..' '..(label or 'Sweden')..']'..p.getCatForId( 'SELIBR' )

end



function p.sikartLink( id, label )

	--P781's format regex: \d{7,9} (e.g. 123456789)

	if not id:match( '^%d%d%d%d%d%d%d%d?%d?$' ) then

		return false

	end

	return '[http://www.sikart.ch/KuenstlerInnen.aspx?id='..id..'&lng=en '..(label or 'SIKART (Switzerland)')..']'..p.getCatForId( 'SIKART' ) --no https as of 9/2019

end



function p.snacLink( id, label )

	--P3430's format regex: \d*[A-Za-z][0-9A-Za-z]* (e.g. A)

	if not id:match( '^%d*[A-Za-z][0-9A-Za-z]*$' ) then

		return false

	end

	return '[https://snaccooperative.org/ark:/99166/'..id..' '..(label or 'Social Networks and Archival Context')..']'..p.getCatForId( 'SNAC-ID' )

end



function p.sudocLink( id, label )

	--P269's format regex: (\d{8}[\dX]|) (e.g. 026927608)

	if not id:match( '^%d%d%d%d%d%d%d%d[%dxX]$' ) then --legacy: allow lowercase 'x'

		return false

	end

	return '[https://www.idref.fr/'..id..' '..(label or 'SUDOC (France)')..']'..p.getCatForId( 'SUDOC' )

end



function p.s2authoridLink( id, label )

	--P4012's format regex: [1-9]\d* (e.g. 1796130)

	if not id:match( '^[1-9]%d*$' ) then

		return false

	end

	return '[https://www.semanticscholar.org/author/'..id..' '..(label or 'Semantic Scholar')..']'..p.getCatForId( 'Semantic Scholar author' ) --special category name

end



function p.ta98Link( id, label )

	--P1323's format regex: A\d{2}\.\d\.\d{2}\.\d{3}[FM]? (e.g. A12.3.45.678)

	if not id:match( '^A%d%d%.%d%.%d%d%.%d%d%d[FM]?$' ) then

		return false

	end

	return '[http://tools.wmflabs.org/wikidata-externalid-url/?p=1323&url_prefix=https:%2F%2Fwww.unifr.ch%2Fifaa%2FPublic%2FEntryPage%2FTA98%20Tree%2FEntity%20TA98%20EN%2F&url_suffix=%20Entity%20TA98%20EN.htm&id='..id..' '..(label or 'Terminologia Anatomica')..']'..p.getCatForId( 'TA98' )

end



function p.tdviaLink( id, label )

	--P7314's format regex: [a-z/-]+] (e.g. barkan-omer-lutfi)

	if not id:match( '^[a-z/-]+$' ) then

		return false

	end

	return '[https://islamansiklopedisi.org.tr/'..id..' '..(label or 'Encyclopedia of Islam')..']'..p.getCatForId( 'TDVİA' )

end



function p.teLink( id, label )

	--P1693's format regex: E[1-8]\.\d{1,2}\.\d{1,2}\.\d{1,2}\.\d{1}\.\d{1}\.\d{1,3} (e.g. E1.23.45.67.8.9.0)

	local e1, e2 = id:match( '^E([1-8])%.(%d%d?)%.%d%d?%.%d%d?%.%d%.%d%.%d%d?%d?$' )

	if not e1 then

		return false

	end

	local TEnum = 'TEe0'..e1 --no formatter URL in WD, probably due to this complexity

	if e1 == '5' or e1 == '7' then

		if #e2 == 1 then e2 = '0'..e2 end

		TEnum = TEnum..e2

	end

	return '[http://www.unifr.ch/ifaa/Public/EntryPage/ViewTE/'..TEnum..'.html '..(label or 'Terminologia Embryologica')..']'..p.getCatForId( 'TE' )

end



function p.tepapaLink( id, label )

	--P3544's format regex: \d+ (e.g. 1)

	if not id:match( '^%d+$' ) then

		return false

	end

	return '[https://collections.tepapa.govt.nz/agent/'..id..' '..(label or 'Te Papa (New Zealand)')..']'..p.getCatForId( 'TePapa' )

end



function p.thLink( id, label )

	--P1694's format regex: H\d\.\d{2}\.\d{2}\.\d\.\d{5} (e.g. H1.23.45.6.78901)

	local h1, h2 = id:match( '^H(%d)%.(%d%d)%.%d%d%.%d%.%d%d%d%d%d$' )

	if not h1 then

		return false

	end

	local THnum = 'THh'..h1..h2 --no formatter URL in WD, probably due to this complexity

	return '[http://www.unifr.ch/ifaa/Public/EntryPage/ViewTH/'..THnum..'.html '..(label or 'Terminologia Histologica')..']'..p.getCatForId( 'TH' )

end



function p.tlsLink( id, label )

	id = id:gsub(' +', '_')

	--P1362's format regex: \p{Lu}[\p{L}\d_',\.\-\(\)\*/–]{3,59} (e.g. Abcd)

	local class = "[%a%d_',%.%-%(%)%*/–]"

	local regex = "^%u"..string.rep(class, 3)..string.rep(class.."?", 56).."$"

	if not mw.ustring.match( id, regex ) then

		return false

	end

	return '[http://tls.theaterwissenschaft.ch/wiki/'..id..' '..(label or 'Theaterlexikon (Switzerland)')..']'..p.getCatForId( 'TLS' ) --no https as of 9/2019

end



function p.troveLink( id, label )

	--P1315's format regex: [1-9]\d{5,7} (e.g. 12345678)

	if not id:match( '^[1-9]%d%d%d%d%d%d?%d?$' ) then

		return false

	end

	return '[https://trove.nla.gov.au/people/'..id..' '..(label or 'Trove (Australia)')..']'..p.getCatForId( 'Trove' )

end



function p.ukparlLink( id, label )

	--P6213's format regex: [a-zA-Z\d]{8} (e.g. AQUupyiR)

	if not id:match( '^[a-zA-Z%d][a-zA-Z%d][a-zA-Z%d][a-zA-Z%d][a-zA-Z%d][a-zA-Z%d][a-zA-Z%d][a-zA-Z%d]$' ) then

		return false

	end

	return '[https://id.parliament.uk/'..id..' '..(label or 'UK Parliament')..']'..p.getCatForId( 'UKPARL' )

end



function p.ulanLink( id, label )

	--P245's format regex: 500\d{6} (e.g. 500123456)

	if not id:match( '^500%d%d%d%d%d%d$' ) then

		return false

	end

	return '[https://www.getty.edu/vow/ULANFullDisplay?find=&role=&nation=&subjectid='..id..' '..(label or 'Artist Names (Getty)')..']'..p.getCatForId( 'ULAN' )

end



function p.uscongressLink( id, label )

	--P1157's format regex: [A-Z]00[01]\d{3} (e.g. A000123)

	if not id:match( '^[A-Z]00[01]%d%d%d$' ) then

		return false

	end

	return '[http://bioguide.congress.gov/scripts/biodisplay.pl?index='..id..' '..(label or 'US Congress')..']'..p.getCatForId( 'USCongress' ) --no https as of 9/2019

end



function p.vcbaLink( id, label )

	--P8034's format regex: \d{3}\/[1-9]\d{0,5} (e.g. 494/9793)

	if not id:match( '^%d%d%d\/[1-9]%d?%d?%d?%d?%d?$' ) then

		return false

	end

	id = id:gsub('\/', '_')

	return '[https://opac.vatlib.it/auth/detail/'..id..' '..(label or 'Vatican')..']'..p.getCatForId( 'VcBA' )

end



function p.viafLink( id, label )

	--P214's format regex: [1-9]\d(\d{0,7}|\d{17,20}) (e.g. 123456789, 1234567890123456789012)

	if not id:match( '^[1-9]%d%d?%d?%d?%d?%d?%d?%d?$' ) and

	   not id:match( '^[1-9]%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d?%d?%d?$' ) then

		return false

	end

	-- If the "VIAF" entry at [[:m:Interwiki map]] would resolve to "https://viaf.org/viaf/$1" (rather than "http://viaf.org/viaf/$1", as it currently still does), the code below could change from '[https://viaf.org/viaf/'..id..' '..id..']' to '[[:VIAF:'..id..'|'..id..']]'.

	return '[https://viaf.org/viaf/'..id..' '..(label or 'VIAF')..']'..p.getCatForId( 'VIAF' )

end



--[[=========================== Helper functions =============================]]



function p.append(str, c, length)

	while str:len() < length do

		str = c .. str

	end

	return str

end



--Returns the ISNI check digit isni must be a string where the 15 first elements are digits, e.g. 0000000066534145

function p.getIsniCheckDigit( isni )

	local total = 0

	for i = 1, 15 do

		local digit = isni:byte( i ) - 48 --Get integer value

		total = (total + digit) * 2

	end

	local remainder = total % 11

	local result = (12 - remainder) % 11

	if result == 10 then

		return "X"

	end

	return tostring( result )

end



--Validate ISNI (and ORCID) and retuns it as a 16 characters string or returns false if it's invalid

--See http://support.orcid.org/knowledgebase/articles/116780-structure-of-the-orcid-identifier

function p.validateIsni( id )

	--P213 (ISNI) format regex: [0-9]{4} [0-9]{4} [0-9]{4} [0-9]{3}[0-9X] (e.g. 0000-0000-6653-4145)

	--P496 (ORCID) format regex: 0000-000(1-[5-9]|2-[0-9]|3-[0-4])\d{3}-\d{3}[\dX] (e.g. 0000-0002-7398-5483)

	id = id:gsub( '[ %-]', '' ):upper()

	if not id:match( '^%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d[%dX]$' ) then

		return false

	end

	if p.getIsniCheckDigit( id ) ~= string.char( id:byte( 16 ) ) then

		return false

	end

	return id

end



function p.splitLccn( id )

	--P244's format regex: (n|nb|nr|no|ns|sh)([4-9][0-9]|00|20[0-1][0-9])[0-9]{6} (e.g. n78039510)

	if id:match( '^%l%l?%l?%d%d%d%d%d%d%d%d%d?%d?$' ) then

		id = id:gsub( '^(%l+)(%d+)(%d%d%d%d%d%d)$', '%1/%2/%3' )

	end

	if id:match( '^%l%l?%l?/%d%d%d?%d?/%d+$' ) then

		return mw.text.split( id, '/' )

	end

	return false

end



--[[==========================================================================]]

--[[                    Wikidata & documentation functions                    ]]

--[[==========================================================================]]



function p.getIdsFromWikidata( itemId, property )

	local ids = {}

	local statements = mw.wikibase.getBestStatements( itemId, property )

	if statements then

		for _, statement in ipairs( statements ) do

			if statement.mainsnak.datavalue then

				table.insert( ids, statement.mainsnak.datavalue.value )

			end

		end

	end

	return ids

end



function p.matchesWikidataRequirements( itemId, reqs )

	for _, group in ipairs( reqs ) do

		local property = 'P'..group1

		local qid = group2

		local statements = mw.wikibase.getBestStatements( itemId, property )

		if statements then

			for _, statement in ipairs( statements ) do

				if statement.mainsnak.datavalue then

					if statement.mainsnak.datavalue.value'numeric-id' == qid then

						return true

	end	end	end	end	end

	return false

end



-- Creates a human-readable standalone wikitable version of p.conf, and tracking categories with page counts, for use in the documentation

function p.docConfTable( frame )

	local wikiTable = '{| class="wikitable sortable"\n'..

					  '! rowspan=2 | Parameter\n'..

					  '! rowspan=2 | Section\n'..

					  '! rowspan=2 | Appears as\n'..

					  '! rowspan=2; data-sort-type=number | Wikidata property\n'..

					  '! colspan=4 | Tracking categories and page counts\n'..

					  '|-\n'..

					  '! [[:Category:Wikipedia articles with authority control information|'..       'Articles]]\n'..

					  '! [[:Category:User pages with authority control information|'..               'User pages]]\n'..

					  '! [[:Category:Miscellaneous pages with authority control information|'..      'Misc. pages]]\n'..

					  '! [[:Category:Wikipedia articles with faulty authority control information|'..'Faulty IDs]]\n'..

					  '|-\n'

	local lang = mw.getContentLanguage()

	for _, conf in pairs( p.conf ) do

		local param, pid, section = conf1], conf2], conf4

		local appearsAs, link

		if param == "WORLDCATID" then

			-- WorldCat is special

			appearsAs = "[https://www.worldcat.org/identities/lccn-n78039510 WorldCat]"

			link = "WorldCat Identities (identifier)"

		elseif conf.prefix then

			appearsAs = conf.prefix

			link = conf.link or param .. ' (identifier)'

		else

			appearsAs = conf3](conf5])

			link = conf.link or param .. ' (identifier)'

		end

		local category = conf.category or param

		local args = { id = 'f', pid }

		local wpl = frame:expandTemplate{ title = 'Wikidata property link', args = args }

		--cats

		local articleCat = 'Wikipedia articles with '..category..' identifiers'

		local userCat =    'User pages with '..category..' identifiers'

		local miscCat =    'Miscellaneous pages with '..category..' identifiers'

		local faultyCat =  'Wikipedia articles with faulty '..category..' identifiers'

		--counts

		local articleCount = lang:formatNum( mw.site.stats.pagesInCategory(articleCat, 'pages') )

		local userCount =    lang:formatNum( mw.site.stats.pagesInCategory(userCat, 'pages') )

		local miscCount =    lang:formatNum( mw.site.stats.pagesInCategory(miscCat, 'pages') )

		local faultyCount =  lang:formatNum( mw.site.stats.pagesInCategory(faultyCat, 'pages') )

		

		--concat

		wikiTable = wikiTable..'\n'..

					'|-\n'..

					'||[['..link..'|'..param..']]'..

					'||'..section..

					'||'..appearsAs..

					'||data-sort-value='..pid..'|'..wpl..

					'||style="text-align: right;"|[[:Category:'..articleCat..'|'..articleCount..']]'..

					'||style="text-align: right;"|[[:Category:'..   userCat..'|'..   userCount..']]'..

					'||style="text-align: right;"|[[:Category:'..   miscCat..'|'..   miscCount..']]'..

					'||style="text-align: right;"|[[:Category:'.. faultyCat..'|'.. faultyCount..']]'

	end

	

	--append derivative WorldCat cats

	local wcd = { 'WorldCat-LCCN', 'WorldCat-VIAF' }

	for _, w in pairs(wcd) do

		local articleCat = 'Wikipedia articles with '..w..' identifiers'

		local articleCount = lang:formatNum( mw.site.stats.pagesInCategory(articleCat, 'pages') )

		local appearsAs 

		if w == "WorldCat-LCCN" then

			appearsAs = "[https://www.worldcat.org/identities/lccn-n79-113947 WorldCat (via Library of Congress)]"

		else

			appearsAs = "[https://www.worldcat.org/identities/containsVIAFID/12345789 WorldCat (via VIAF)]"

		end

		wikiTable = wikiTable..'\n'..

					'|-\n'..

					'||'..'—'..

					'||General'..

					'||'..appearsAs..

					'||data-sort-value='..w..'|'..'—'..

					'||style="text-align: right;"|[[:Category:'..articleCat..'|'..articleCount..']]'..

					'||style="text-align: right;"|—'..

					'||style="text-align: right;"|—'..

					'||style="text-align: right;"|—'

	end

	

	return require("Module:Suppress categories").main(wikiTable)..'\n|}'

end



--[[==========================================================================]]

--[[                              Configuration                               ]]

--[[==========================================================================]]



-- If a specific "(identifier) redirect" exists for an identifier, please route through this particular redirect rather than linking directly to the target page. This reduces clutter in "What links here" and improves reverse lookup of articles where a manifestation of this particular identifier is used.



-- Check that the Wikidata item has this property-->value before adding it

local reqs = {}



-- Parameter format: { 'parameter name', propertyId # in Wikidata, formatting/validation function, section, example ID for documentation }

-- Optional named parameters: `link` to override the link in the documentation (defaults to parameter + (identifer)),

-- category to override the ID in category names (defaults to parameter),

-- prefix to include a prefix (usually a wikilink explaining what the identifier is) before the external link itself

p.conf = {

	{ 'AAG', 3372, p.aagLink, "Art galleries and museums", "1"},

	{ 'ACM-DL', 864, p.acmLink, "Scientific databases", "12345678901", link="ACM DL (identifier)"},

	{ 'ADB', 1907, p.adbLink,"Biographical dictionaries", "barton-sir-edmund-toby-71"},

	{ 'AGSA', 6804, p.agsaLink, "Art galleries and museums", "3625"},

	{ 'autores.uy', 2558, p.autoresuyLink, "Biographical dictionaries", "12345"},

	{ 'AWR', 4182, p.awrLink, "Biographical dictionaries", "PR00768b"},

	{ 'BALaT', 3293, p.balatLink, "Art research institutes", "1"},

	{ 'BIBSYS', 1015, p.bibsysLink, "National libraries", "1234567890123"},

	{ 'Bildindex', 2092, p.bildLink, "Art research institutes", "1"},

	{ 'BNC', 1890, p.bncLink, "National libraries", "123456789"},

	{ 'BNE', 950, p.bneLink, "National libraries", "XX1234567"},

	{ 'BNF', 268, p.bnfLink, "National libraries", "123456789"},

	{ 'Botanist', 428, p.botanistLink , "Scientific databases", "L."},

	{ 'BPN', 651, p.bpnLink , "Biographical dictionaries", "12345678"},

	{ 'CANTIC', 1273, p.canticLink, "National libraries", "a12345678"},

	{ 'CINII', 271, p.ciniiLink, "Scientific databases", "DA12345678", link = "CiNii (identifier)"},

	{ 'CWGC', 1908, p.cwgcLink, "Other", "1234567"},

	{ 'DAAO', 1707, p.daaoLink, "Art research institutes", "rolf-harris"},

	{ 'DBLP', 2456, p.dblpLink, "Scientific databases", "123/123"},

	{ 'DIB',  6829, p.dibLink, "Biographical dictionaries", "a1234"},

	{ 'DSI', 2349, p.dsiLink, "Art research institutes", "1538"},

	{ 'FNZA', 6792, p.fnzaLink, "Art research institutes", "12"},

	{ 'GND', 227, p.gndLink, "National libraries", "4079154-3"},

	{ 'HDS', 902, p.hdsLink, "Other", "050123"},

	{ 'IAAF', 1146, p.iaafLink, "Other", "123"},

	{ 'ICCU', 396, p.iccuLink, "National libraries", "IT\\ICCU\\CFIV\\000163"}, --formerly SBN

	{ 'ICIA', 1736, p.iciaLink, "Art research institutes", "1"},

	{ 'IEU', 9070, p.ieuLink, "Other", "N\\A\\NationalAcademyofArtandArchitecture"},

	{ 'ISNI', 213, p.isniLink, "General", "0000-0000-6653-4145", prefix = '[[ISNI (identifier)|ISNI]]'},

	{ 'Joconde', 347, p.jocondeLink, "Art research institutes", "12345678901"},

	{ 'KULTURNAV', 1248, p.kulturnavLink, "Art research institutes", "12345678-1234-1234-1234-1234567890AB", link="KulturNav (identifier)"},

	{ 'LCCN', 244, p.lccnLink, "National libraries", "n78039510"},

	{ 'LIR', 886, p.lirLink, "Other", "1"},

	{ 'LNB', 1368, p.lnbLink, "National libraries", "123456789"},

	{ 'Léonore', 640, p.leonoreLink, "Other", "LH/1/1", prefix = "[[Léonore (identifier)|Léonore (France)]]"},

	{ 'MA', 6366, p.maLink, "Other", "123456789"},

	{ 'MBA', 434, p.mbaLink, "Other", "12345678-1234-1234-1234-1234567890AB", category = 'MusicBrainz'}, --special category name

	{ 'MBAREA', 982, p.mbareaLink, "Other", "12345678-1234-1234-1234-1234567890AB", category = 'MusicBrainz area' }, --special category name

	{ 'MBI', 1330, p.mbiLink, "Other", "12345678-1234-1234-1234-1234567890AB", category = 'MusicBrainz instrument' }, --special category name

	{ 'MBL', 966, p.mblLink, "Other", "12345678-1234-1234-1234-1234567890AB", category = 'MusicBrainz label' }, --special category name

	{ 'MBP', 1004, p.mbpLink, "Other", "12345678-1234-1234-1234-1234567890AB", category = 'MusicBrainz place' }, --special category name

	{ 'MBRG', 436, p.mbrgLink, "Other", "12345678-1234-1234-1234-1234567890AB", category = 'MusicBrainz release group' }, --special category name

	{ 'MBS', 1407, p.mbsLink, "Other", "12345678-1234-1234-1234-1234567890AB", category = 'MusicBrainz series' }, --special category name

	{ 'MBW',  435, p.mbwLink, "Other", "12345678-1234-1234-1234-1234567890AB", category = 'MusicBrainz work' }, --special category name

	{ 'MGP', 549, p.mgpLink, "Scientific databases", "123456"},

	{ 'NARA', 1225, p.naraLink, "Other", "12345678"},

	{ 'NCL', 1048, p.nclLink, "National libraries", "1081436"},

	{ 'NDL', 349, p.ndlLink, "National libraries", "012345678"},

	{ 'NGV', 2041, p.ngvLink, "Art galleries and museums", "12354"},

	{ 'NKC', 691, p.nkcLink, "National libraries", "abcd12345678901234"},

	{ 'NLA', 409, p.nlaLink, "National libraries", "123456789012"},

	{ 'NLG', 3348, p.nlgLink, "National libraries", "12345678"},

	{ 'NLI', 949, p.nliLink, "National libraries", "123456789"},

	{ 'NLK', 5034, p.nlkLink, "National libraries", "KAB197000000"},

	{ 'NLP', 1695, p.nlpLink, "National libraries", "9810123456789012345"},

	{ 'NLR', 1003, p.nlrLink, "National libraries", "123456789"},

	{ 'NSK', 1375, p.nskLink, "National libraries", "123456789"},

	{ 'NTA', 1006, p.ntaLink, "National libraries", "12345678X"},

	{ 'ORCID', 496, p.orcidLink, "General", "0000-0002-7398-5483", prefix = '[[ORCID (identifier)|ORCID]]'},

	{ 'PIC', 2750, p.picLink, "Art research institutes", "1"},

	{ 'PLWABN',  7293, p.plwabnLink, "National libraries", "9812345678905606"},

	{ 'Publons', 3829, p.publonsLink, "Scientific databases", "2776255"},

	{ 'RID', 1053, p.ridLink, "Scientific databases", "A-1234-1934"},

	{ 'RERO', 3065, p.reroLink, "Other", "02-A012345678", prefix = '[[RERO (identifier)|RERO (Switzerland)]]'},

	{ 'RKDartists', 650, p.rkdartistsLink, "Art research institutes", "123456"},

	{ 'RKDID', 350, p.rkdidLink, "Art research institutes", "123456"},

	{ 'RSL', 947, p.rslLink, "National libraries", "123456789"},

	{ 'SELIBR', 906, p.selibrLink, "National libraries", "123456"},

	{ 'SIKART', 781, p.sikartLink, "Art research institutes", '123456789'},

	{ 'SNAC-ID', 3430, p.snacLink, "Other", "A"},

	{ 'SUDOC', 269, p.sudocLink, "Other", "026927608", prefix = '[[SUDOC (identifier)|SUDOC]]'},

	{ 'S2AuthorId', 4012, p.s2authoridLink, "Scientific databases", "1796130", category = 'Semantic Scholar author' }, --special category name

	{ 'TA98', 1323, p.ta98Link, "Scientific databases", "A12.3.45.678"},

	{ 'TDVİA', 7314, p.tdviaLink, "Other", "asim-b-behdele"},

	{ 'TE', 1693, p.teLink, "Scientific databases", "E1.23.45.67.8.9.0"},

	{ 'TePapa', 3544, p.tepapaLink, "Art galleries and museums", "1"},

	{ 'TH', 1694, p.thLink, "Scientific databases", "H1.23.45.6.78901"},

	{ 'TLS',  1362, p.tlsLink, "Other", "Abcd"},

	{ 'Trove', 1315, p.troveLink, "Other", "12345678", prefix = '[[Trove (identifier)|Trove (Australia)]]'}, --formerly NLA-person

	{ 'UKPARL', 6213, p.ukparlLink, "Other", "AQUupyiR"},

	{ 'ULAN', 245, p.ulanLink, "Art research institutes", "500123456"},

	{ 'USCongress', 1157, p.uscongressLink, "Other", "A000123", link = "US Congress (identifier)"},

	{ 'VcBA', 8034, p.vcbaLink, "National libraries", "494/9793"},

	{ 'VIAF', 214, p.viafLink, "General", "123456789", prefix = "[[VIAF (identifier)|VIAF]]"},

	{ 'WORLDCATID', 7859, nil, "General", nil},

}



-- Legitimate aliases to p.conf, for convenience

-- Format: { 'alias', 'parameter name in p.conf' }

p.aliases = {

	{ 'DNB', 'GND' }, --Deutsche Nationalbibliothek -> Gemeinsame Normdatei

	{ 'Leonore', 'Léonore' }, --alias name without diacritics

	{ 'leonore', 'Léonore' }, --lowercase variant without diacritics

	{ 'MusicBrainz', 'MBA' },

	{ 'MusicBrainz artist', 'MBA' },

	{ 'MusicBrainz label', 'MBL' },

	{ 'MusicBrainz release group', 'MBRG' },

	{ 'MusicBrainz work', 'MBW' },

	{ 'SBN', 'ICCU' }, --SBN alias to be deprecated at a later stage

	{ 'TDVIA', 'TDVİA' }, --alias name without diacritics

	{ 'tdvia', 'TDVİA' }, --lowercase variant without diacritics

}



-- Deprecated aliases to p.conf; tracked in [[Category:Wikipedia articles with deprecated authority control identifiers]]

-- Format: { 'deprecated parameter name', 'replacement parameter name in p.conf' }

p.deprecated = {

	{ 'GKD', 'GND' },

	{ 'PND', 'GND' },

	{ 'RLS', 'RSL' },	

	{ 'SWD', 'GND' },

	{ 'NARA-organization', 'NARA' },

	{ 'NARA-person', 'NARA' },

}



--[[==========================================================================]]

--[[                                   Main                                   ]]

--[[==========================================================================]]



function p.authorityControl( frame )

	local resolveEntity = require( "Module:ResolveEntityId" )

	local parentArgs = frame:getParent().args --WD IDs added here later

	local iParentArgs = 0 --count original/manual parent args only later

	local worldcatCat = ''

	local multipleIdCat = ''

	local suppressedIdCat = ''

	local deprecatedIdCat = ''

	local differentOnWDCat = ''

	local sameOnWDCat = ''

	

	--Redirect aliases to proper parameter names

	for _, a in pairs( p.aliases ) do

		local alias, param = a1], a2

		if (parentArgsparam == nil or parentArgsparam == '') and parentArgsalias then

			parentArgsparam = parentArgsalias

		end

	end

	

	--Redirect deprecated parameters to proper parameter names, and assign tracking cat

	for _, d in pairs( p.deprecated ) do

		local dep, param = d1], d2

		if (parentArgsparam == nil or parentArgsparam == '') and parentArgsdep then

			parentArgsparam = parentArgsdep

			if namespace == 0 then

				deprecatedIdCat = '[[Category:Wikipedia articles with deprecated authority control identifiers|'..dep..']]'

			end

		end

	end

	

	--Use QID= parameter for testing/example purposes only

	local itemId = nil

	if namespace ~= 0 then

		local qid = parentArgs'qid' or parentArgs'QID'

		if qid then

			itemId = 'Q'..mw.ustring.gsub(qid, '^[Qq]', '')

			itemId = resolveEntity._id(itemId) --nil if unresolvable

		end

	else

		itemId = mw.wikibase.getEntityIdForCurrentPage()

	end

	

	--Wikidata fallback if available

	if itemId then

		local iMatches = 0

		for _, params in ipairs( p.conf ) do

			if params2 > 0 then

				local val = parentArgsmw.ustring.lower(params1])] or parentArgsparams1]]

				if val == nil or val == '' then

					local canUseWikidata = nil

					if reqsparams1]] then

						--since reqs{} has been nil afaicr, p.matchesWikidataRequirements will never be called

						--TODO: reqs{} doesn't seem useful/necessary; appendage of early WD incorporation?

						canUseWikidata = p.matchesWikidataRequirements( itemId, reqsparams1]] )

					else

						canUseWikidata = true

					end

					if canUseWikidata then

						local wikidataIds = p.getIdsFromWikidata( itemId, 'P'..params2 )

						if wikidataIds1 then

							if val == '' and (namespace == 0 or testcases) then

								suppressedIdCat = '[[Category:Wikipedia articles with suppressed authority control identifiers|'..params1..']]'

							else

								parentArgsparams1]] = wikidataIds1 --add ID from WD

							end

						end

					end

				else

					iParentArgs = iParentArgs + 1

					local wikidataIds = p.getIdsFromWikidata( itemId, 'P'..params3 )

					if wikidataIds1 and differentOnWDCat == '' then

						local bMatch = false

						for _, wd in pairs( wikidataIds ) do

							if val == wd then

								iMatches = iMatches + 1

								bMatch = true

							end

						end

						if bMatch == false then

							differentOnWDCat = '[[Category:Pages using authority control with parameters different on Wikidata|'..params1..']]'

		end	end	end	end	end

		if iMatches > 0 and iMatches == iParentArgs then

			sameOnWDCat = '[[Category:Pages using authority control with parameters all matching Wikidata]]'

		end

	end

	--Configured rows

	local rct = 0

	local sectionOrder = {"General","National libraries","Art galleries and museums",

						  "Art research institutes","Biographical dictionaries","Scientific databases",

						  "Other"}

	local sections = {

		"General" = {},

		"National libraries" = {},

		"Art galleries and museums" = {},

		"Art research institutes" = {},

		"Biographical dictionaries" = {},

		"Scientific databases" = {},

		"Other" = {}

	}

	-- Don't show NLP is PLWABN is present, since they both go to the National Library of Poland

	-- and the library has deprecated NLP IDs in favor of PLWABN IDs

	if parentArgs.PLWABN or parentArgs.plwabn then

		parentArgs.NLP = nil

		parentArgs.nlp = nil

	end

	for _, params in ipairs( p.conf ) do

		local val = parentArgsmw.ustring.lower(params1])] or parentArgsparams1]]

		local tval, tlinks = {}, {} --init tables

		if val and val ~= '' and type(params3]) == 'function' then

			table.insert( tval, val )

			if params.prefix then 

				table.insert( tlinks, params3]( val, "1" ) )

			else

				table.insert( tlinks, params3]( val ) )

			end

		end

		--collect other unique vals (IDs) from WD, if present

		if itemId and tval1 then

			local nextIdVal = 2

			local wikidataIds = p.getIdsFromWikidata( itemId, 'P'..params2 )

			for _, v in pairs( wikidataIds ) do

				local bnew = true

				for _, w in pairs( tval ) do

					if v == w then bnew = false end

				end

				if bnew then

					table.insert( tval, v )

					table.insert( tlinks, params3]( v, tostring(nextIdVal) ) )

					nextIdVal = nextIdVal + 1

				end

			end

		end

		--assemble

		if tval1 then

			table.insert( sectionsparams4]], p.createRow( params1], tval, nil, tlinks, true, params.category, params.prefix) )

			rct = rct + 1

			if tval2 then

				multipleIdCat = p.getCatForId( 'multiple' )

			end

		end



	end

	

	--WorldCat

	local worldcatId = parentArgs'worldcatid' or parentArgs'WORLDCATID'

	if worldcatId and worldcatId ~= '' then --if WORLDCATID present & unsuppressed

		table.insert( sections"General"], p.createRow( 'WORLDCATID', worldcatId, '[https://www.worldcat.org/identities/'..mw.uri.encode(worldcatId, 'PATH')..' WorldCat]', nil, false ) ) --Validation?

		worldcatCat = p.getCatForId( 'WORLDCATID' )

	elseif worldcatId == nil then --if WORLDCATID absent but unsuppressed

		local viafId = parentArgs'viaf' or parentArgs'VIAF'

		local lccnId = parentArgs'lccn' or parentArgs'LCCN'

		if viafId and viafId ~= '' and p.viafLink( viafId ) then --VIAF must be present, unsuppressed, & validated

			table.insert( sections"General"], p.createRow( 'VIAF', viafId, '[https://www.worldcat.org/identities/containsVIAFID/'..viafId..' WorldCat (via VIAF)]', nil, false ) )

			if namespace == 0 then 

				worldcatCat = '[[Category:Wikipedia articles with WorldCat-VIAF identifiers]]'

			end

		elseif lccnId and lccnId ~= '' and p.lccnLink( lccnId ) then --LCCN must be present, unsuppressed, & validated

			local lccnParts = p.splitLccn( lccnId )

			if lccnParts and lccnParts1 ~= 'sh' then

				local lccnIdFmtd = lccnParts1..lccnParts2..'-'..lccnParts3

				table.insert( sections"General"	], p.createRow( 'LCCN', lccnId, '[https://www.worldcat.org/identities/lccn-'..lccnIdFmtd..' WorldCat (via Library of Congress)]', nil, false ) )

				if namespace == 0 then

					worldcatCat = '[[Category:Wikipedia articles with WorldCat-LCCN identifiers]]'

				end

			end

		end

	elseif worldcatId == '' then --if WORLDCATID suppressed

		suppressedIdCat = '[[Category:Wikipedia articles with suppressed authority control identifiers|WORLDCATID]]'

	end

	

	local Navbox = require('Module:Navbox')

	local elementsCat = ''

	if rct == 0 or rct >= 25 then

		local eCat = 'AC with '..rct..' elements'

		elementsCat  = '[[Category:'..eCat..']]'..p.redCatLink(eCat)

	end

	

	local outString = ''

	if rct > 0 then

		local sectionID = 1

		local args = { pid = 'identifiers' } -- #target the list of identifiers

		if testcases and itemId then args = { pid = 'identifiers', qid = itemId } end --expensive

		local pencil = frame:expandTemplate{ title = 'EditAtWikidata', args = args}

		local navboxArgs = {

			name  = 'Authority control',

			navboxclass = 'authority-control',

			bodyclass = 'hlist',

			state = 'off',

			navbar = 'off'

		}

		for _, sectName in ipairs(sectionOrder) do

			if #sectionssectName ~= 0 then

				navboxArgs"group" .. sectionID = sectName

				navboxArgs"list" .. sectionID = table.concat(sectionssectName])

				sectionID = sectionID + 1

			end

		end

		if navboxArgs.group2 then

			if rct >= 4 then 

				navboxArgs.title = "[[Help:Authority control|Authority control]]" .. pencil

			else

				navboxArgs.border = "child"

				navboxArgs = {

					group1 = "[[Help:Authority control|Authority control]]" .. pencil,

					list1 = Navbox._navbox(navboxArgs)

				}

			end

		else

			local sect = navboxArgs.group1

			if sect == "General" or sect == "Other" then

				-- Just say "Authority control" with no label if only general or only other IDs are present

				-- since "general" is redundant and "other" is silly when there's nothing to contrast it with

				navboxArgs.group1 = "[[Help:Authority control|Authority control]]" .. pencil

			else 

				navboxArgs.group1 = "[[Help:Authority control|Authority control: " .. sect .. "]] " .. pencil

			end

		end

		outString = Navbox._navbox(navboxArgs)

	end

	

	local auxCats = worldcatCat .. elementsCat .. multipleIdCat .. suppressedIdCat .. 

					deprecatedIdCat .. differentOnWDCat .. sameOnWDCat

	if testcases then

		auxCats = mw.ustring.gsub(auxCats, '(%[%[)(Category)', '%1:%2') --for easier checking

	end

	outString = outString .. auxCats

	if namespace ~= 0 then

		outString = mw.ustring.gsub(outString, '(%[%[)(Category:Wikipedia articles)', '%1:%2') --by definition

	end

	

	return outString

end



return p