# Module:Build bracket

local p = {}  
local entries = {}  
local pathEntries = {}  
local pathcolor  
local shift = {}  
local hascross = {}  
local teams_per_match = {}  
local rlegs = {}  
local maxlegs = {}  
local autolegs  
local byes = {}  
local hide = {}  
local matchgroup = {}  
local autocol  
local seeds  
local forceseeds  
local nowrap  
local boldwinner  
local aggregate  
local paramstyle  
local masterindex  

local function isempty(s)  
return s==nil or s==''  
end  

local function notempty(s)  
return s~=nil and s~=''  
end  

local function bargs(s)  
return pargs[s] or fargs[s]  
end  

local function toChar(num)  
return string.char(string.byte("a")+num-1)  
end  

local function split(str,delim,tonum)  
result = {};  
local a = "[^"..table.concat(delim).."]+"  
for w in str:gmatch(a) do  
if tonum==true then  
table.insert(result, tonumber(w));  
else  
table.insert(result, w);  
end  
end  
return result;  
end  

local function getWidth(ctype, default)  
local result = bargs(ctype..'-width')  
if isempty(result) then return default end  
if tonumber(result)~=nil then return result..'px' end  
return result  
end  

local function matchGroups  
for j=minc,c do  
matchgroup[j]={}  
for i=1,r do  
if entries[j][i]~= nil and entries[j][i]['ctype']=='team' then  
matchgroup[j][i]=math.ceil(entries[j][i]['index']/teams_per_match[j])  
entries[j][i]['group'] = math.ceil(entries[j][i]['index']/teams_per_match[j])  
end  
end  
end  
end  

local function teamLegs(j,i)  
local legs = rlegs[j]  
if notempty(entries[j][i]['legs']) then  
legs = tonumber(entries[j][i]['legs'])  
end  
if autolegs then  
local l=1  
repeat l=l+1  
until isempty(entries[j][i]['score'][l])  
legs = l-1  
end  
return legs  
end  

local function boldWinner  
local function boldScore(j,i,l)  
if entries[j][i]~= nil and entries[j][i]['ctype']=='team' then  
local myscore = entries[j][i]['score'][l]:gsub('%W','')  
if myscore == "" or myscore:find("%D") then return 'normal'  
else myscore=tonumber(myscore) end  
local compscore = {}  
for k,v in pairs(matchgroup[j]) do  
if matchgroup[j][i]==v and k~=i then  
local theirscore = entries[j][k]['score'][l] or ''  
theirscore = theirscore:gsub('%W','')  
if theirscore== "" or theirscore:find("%D") then return 'normal'  
else table.insert(compscore,tonumber(theirscore)) end  
end  
end  
for k,v in pairs(compscore) do  
if myscore<=v then return 'normal' end  
end  
if l~='agg' then  
entries[j][i]['wins'] = entries[j][i]['wins']+1  
else  
entries[j][i]['aggwins'] = 1  
end  
return 'bold'  
end  
end  
local function boldTeam(j,i,agg)  
local wins  
local legs = teamLegs(j,i)  
if agg~=true then  
wins = 'wins'  
if entries[j][i][wins]>legs/2 then  
return 'bold'  
end  
if autolegs then  
for l=1,legs do  
if notempty(entries[j][i]['score'][l]) and string.find(entries[j][i]['score'][l],"nbsp") then  
return 'normal'  
end  
end  
else  
for l=1,legs do  
if isempty(entries[j][i]['score'][l]) or string.find(entries[j][i]['score'][l],"nbsp") then  
return 'normal'  
end  
end  
end  
else  
wins = 'aggwins'  
end  

local compteam = {}  
for k,v in pairs(matchgroup[j]) do  
if matchgroup[j][i]==v and k~=i then  
table.insert(compteam,tonumber(entries[j][k][wins]))  
end  
end  
for k,v in pairs(compteam) do  
if entries[j][i][wins]<=v then  
return 'normal'  
end  
end  
return 'bold'  
end  
for j=minc,c do  
for i=1,r do  
if entries[j][i]~= nil and entries[j][i]['ctype']=='team' then  
entries[j][i]['wins'] = 0  
entries[j][i]['aggwins'] = 0  
end  
end  
for i=1,r do  
if entries[j][i]~= nil and entries[j][i]['ctype']=='team' then  
local legs = teamLegs(j,i)  
for l=1,legs do  
entries[j][i]['score']['weight'][l] = boldScore(j,i,l)  
end  
if aggregate and legs>1 then  
entries[j][i]['score']['weight']['agg'] = boldScore(j,i,'agg')  
end  
end  
end  
for i=1,r do  
if entries[j][i]~= nil and entries[j][i]['ctype']=='team' then  
local agg  
local legs = teamLegs(j,i)  
if aggregate and legs>1 then agg=true end  
entries[j][i]['weight'] = boldTeam(j,i,agg)  
end  
end  
end  
end  

local function isBlankEntry(col,row,ctype)  
if isempty(entries[col][row]) then return true end  
if isempty(entries[col][row]['team']) and isempty(entries[col][row]['text']) then return true end  
return false  
end  

local function showSeeds(j,i)  
local showseed=false  
if forceseeds or notempty(entries[j][i]['seed']) then  
showseed=true  
else  
for k=1,teams_per_match[j]-1 do  
if notempty(entries[j][i+2*k]) and entries[j][i]['group']==entries[j][i+2*k]['group'] and notempty(entries[j][i+2*k]['seed']) then  
showseed=true  
end  
if notempty(entries[j][i-2*k]) and entries[j][i]['group']==entries[j][i-2*k]['group'] and notempty(entries[j][i-2*k]['seed']) then  
showseed=true  
end  
end  
end  
return showseed  
end  

local function blankCell(tbl,j,i,rowspan,colspan,text,align)  
local cell = tbl:tag('td')  
if colspan~=1 then  
cell:attr('colspan',colspan)  
end  
if rowspan~=1 then  
cell:attr('rowspan',rowspan)  
end  
if notempty(align) then  
cell:css('text-align',align)  
end  
if notempty(text) then  
if nowrap then  
cell:css('white-space', 'nowrap')  
end  
cell:wikitext(text)  
end  
return cell  
end  

local function headerCell(tbl,j,i,colspan)  
local cell = tbl:tag('td')  
:attr('rowspan',2)  
:css('text-align','')  
:css('',' solid #aaa')  
:css('background-color',entries[j][i]['shade'])  
:wikitext(entries[j][i]['header'])  
if colspan~=1 then  
cell:attr('colspan',colspan)  
end  
if nowrap then  
cell:css('white-space', 'nowrap')  
end  
if entries[j][i]['position']=='top' and entries[j][i-2]~=nil and entries[j][i-2]['ctype']~='blank' and entries[j][i-2]['ctype']~='text' and entries[j][i-2]['ctype']~='group' and (not byes[j][entries[j][i]['headerindex']] or not isBlankEntry(j,i-2)) then  
cell:css('-top',' solid #aaa')  
end  
return cell  
end  

local function teamCell(tbl,k,j,i,l,colspan)  
local bg = '#F2F2F2'  
local cell = tbl:tag('td')  
:attr('rowspan',2)  
:css('',' solid #aaa')  
if colspan~=1 and colspan~=nil then  
cell:attr('colspan',colspan)  
end  
if k=='seed' or k=='score' then  
cell:css('text-align','')  
end  
if k=='seed' or k=='score' or nowrap then  
cell:css('white-space', 'nowrap')  
end  
if k=='seed' then  
cell:css('-',' solid #aaa')  
else  
bg='#F9F9F9'  
end  
if k=='team' then  
cell:css('padding-','0.3em')  
end  
if l=='agg' then  
cell:css('-',' solid #aaa')  
end  
if (l==nil and entries[j][i]['weight']=='bold') or entries[j][i]['score']['weight'][l]=='bold' then  
cell:css('font-weight','bold')  
end  
if entries[j][i]['position']=='top' and entries[j][i-2]~=nil and entries[j][i-2]['ctype']~='blank' and entries[j][i-2]['ctype']~='text' and entries[j][i-2]['ctype']~='group' and (not byes[j][entries[j][i]['headerindex']] or not isBlankEntry(j,i-2)) then  
cell:css('-top',' solid #aaa')  
end  
cell:css('background-color',bg)  
if l==nil then  
cell:wikitext(entries[j][i][k])  
else  
cell:wikitext(tostring(entries[j][i][k][l]))  
end  
return cell  
end  

local function insertEntry(tbl,j,i)  
local entry_colspan=maxlegs[j]+2  
if not seeds then entry_colspan=entry_colspan-1 end  
if (aggregate and maxlegs[j]>1) or maxlegs[j]==0 then  
entry_colspan=entry_colspan+1  
end  

if entries[j][i]~=nil and entries[j][i]['ctype']=='blank' then  
return  
end  

if entries[j][i]==nil then  
if entries[j][i-1]~=nil or i==1 then  
local rowspan = 0  
local row = i  
repeat  
rowspan=rowspan+1  
row=row+1  
until entries[j][row]~=nil or row>r  
return blankCell(tbl,j,i,rowspan,entry_colspan)  
else  
return  
end  
end  

if entries[j][i]['ctype']=='header' then  
if byes[j][entries[j][i]['headerindex']] then  
local emptyround = true  
local row = i+1  
repeat  
if not isBlankEntry(j,row) then  
emptyround = false  
end  
row = row+1  
until (entries[j][row]~=nil and entries[j][row]['ctype']=='header') or row>r  
if emptyround == true then  
return blankCell(tbl,j,i,2,entry_colspan)  
end  
end  

if hide[j][entries[j][i]['headerindex']] then  
return blankCell(tbl,j,i,2,entry_colspan)  
end  

if isempty(entries[j][i]['header']) then  
if entries[j][i]['headerindex']==1 then  
if j==c then entries[j][i]['header'] = 'Final'  
elseif j==c-1 then entries[j][i]['header'] = 'Semifinals'  
elseif j==c-2 then entries[j][i]['header'] = 'Quarterfinals'  
else entries[j][i]['header'] = 'Round '..j  
end  
else  
entries[j][i]['header'] = 'Lower round '..j  
end  
end  

return headerCell(tbl,j,i,entry_colspan)  
end  

if entries[j][i]['ctype']=='team' then  
if byes[j][entries[j][i]['headerindex']] and isBlankEntry(j,i) then  
return blankCell(tbl,j,i,2,entry_colspan)  
end  
if hide[j][entries[j][i]['headerindex']] then  
return blankCell(tbl,j,i,2,entry_colspan)  
end  

local legs = teamLegs(j,i)  
local team_colspan  

team_colspan = maxlegs[j]-legs+1  
if aggregate and legs==1 and maxlegs[j]>1 then  
team_colspan=team_colspan+1  
end  
if maxlegs[j]==0 then  
team_colspan=team_colspan+1  
end  

if seeds then  
if showSeeds(j,i)==true then  
teamCell(tbl,'seed',j,i)  
else  
team_colspan=team_colspan+1  
end  
end  
teamCell(tbl,'team',j,i,nil,team_colspan)  
for l=1,legs do  
teamCell(tbl,'score',j,i,l)  
end  
if aggregate and legs>1 then  
teamCell(tbl,'score',j,i,'agg')  
end  
end  

if entries[j][i]['ctype']=='text' then  
blankCell(tbl,j,i,2,entry_colspan,entries[j][i]['text'])  
end  

if entries[j][i]['ctype']=='group' then  
local colspan=0  
for m=j,entries[j][i]['colspan']+j-1 do  
colspan=colspan+maxlegs[m]+2  
if not seeds then colspan=colspan-1 end  
if (aggregate and maxlegs[m]>1) or maxlegs[m]==0 then  
colspan=colspan+1  
end  
end  
colspan = colspan+2*(entries[j][i]['colspan']-1)  
blankCell(tbl,j,i,2,colspan,entries[j][i]['group'],'')  
end  

if entries[j][i]['ctype']=='line' then  
tbl:tag('td')  
:attr('rowspan',2)  
:attr('colspan',entry_colspan)  
:css('-bottom',' solid black')  
:wikitext(entries[j][i]['text'])  
end  
end  

local function isRoundHidden(j,i,headerindex)  
if notempty(entries[j][i]['pheader']) then  
hide[j][entries[j][i]['headerindex']] = false  
end  
local row = i+1  
repeat  
if not isBlankEntry(j,row) then  
hide[j][entries[j][i]['headerindex']] = false  
end  
row = row+1  
until (entries[j][row]~=nil and entries[j][row]['ctype']=='header') or row>r  
end  

local function paramNames(cname,j,i,l)  
local rname = {  
{'RD'..j, bargs('RD'..j..'-altname') or 'RD'..j},  
{'RD'..j..toChar(entries[j][i]['headerindex']),bargs('RD'..j..toChar(entries[j][i]['headerindex'])..'-altname') or 'RD'..j..toChar(entries[j][i]['headerindex'])}  
}  
local name = {cname, bargs(cname..'-altname') or cname}  
local index = {entries[j][i]['index'], entries[j][i]['altindex']}  
local result = {}  
if cname=='header' then  
if entries[j][i]['headerindex']==1 then  
for k=1,2 do  
table.insert(result,bargs(rname[1][3-k]) or '')  
table.insert(result,bargs(rname[2][3-k]) or '')  
end  
else  
for k=1,2 do  
table.insert(result,bargs(rname[2][3-k]) or '')  
end  
end  
elseif cname=='pheader' then  
if entries[j][i]['headerindex']==1 then  
for k=1,2 do  
table.insert(result,pargs[rname[1][3-k]] or '')  
table.insert(result,pargs[rname[2][3-k]] or '')  
end  
else  
for k=1,2 do  
table.insert(result,pargs[rname[2][3-k]] or '')  
end  
end  
elseif cname=='score' then  
for m=1,2 do for k=1,2 do  
if l==1 then  
table.insert(result,bargs(rname[3-m][3-k]..'-'..name[1]..index[3-m]) or bargs(rname[3-m][3-k]..'-'..name[1]..'0'..index[3-m]) or '')  
end  
table.insert(result,bargs(rname[3-m][3-k]..'-'..name[1]..index[3-m]..'-'..l) or bargs(rname[3-m][3-k]..'-'..name[1]..'0'..index[3-m]..'-'..l) or '')  
end end  
elseif cname=='shade' then  
for k=1,2 do  
if entries[j][i]['headerindex']==1 then  
table.insert(result,bargs(rname[1][3-k]..'-'..name[1]) or '')  
else  
table.insert(result,bargs(rname[2][3-k]..'-'..name[1]) or '')  
end  
end  
table.insert(result,bargs('RD-shade'))  
table.insert(result,'#F2F2F2')  
elseif cname=='text' then  
for n=1,2 do for m=1,2 do for k=1,2 do  
table.insert(result,bargs(rname[3-m][3-k]..'-'..name[3-n]..index[3-m]) or bargs(rname[3-m][3-k]..'-'..name[3-n]..'0'..index[3-m]) or '')  
end end end  
else  
for m=1,2 do for k=1,2 do  
table.insert(result,bargs(rname[3-m][3-k]..'-'..name[1]..index[3-m]) or bargs(rname[3-m][3-k]..'-'..name[1]..'0'..index[3-m]) or '')  
end end  
end  
for k=1,#result do  
if notempty(result[k]) then  
return result[k]  
end  
end  
return ''  
end  

local function indexedParams(j)  
for i=1,r do  
if entries[j][i]~=nil then  
if entries[j][i]['ctype']=='team' then  
local legs = rlegs[j]  
if forceseeds then  
entries[j][i]['seed'] = bargs(masterindex) or ''  
masterindex = masterindex+1  
end  
entries[j][i]['team'] = bargs(tostring(masterindex)) or ''  
masterindex = masterindex+1  
entries[j][i]['legs'] = paramNames('legs',j,i)  
entries[j][i]['score'] = {}  
entries[j][i]['weight'] = 'normal'  
entries[j][i]['score']['weight'] = {}  
if notempty(entries[j][i]['legs']) then  
legs = tonumber(entries[j][i]['legs'])  
end  
for l=1,legs do  
entries[j][i]['score'][l] = bargs(tostring(masterindex)) or ''  
masterindex = masterindex+1  
entries[j][i]['score']['weight'][l] = 'normal'  
end  
if aggregate and legs>1 then  
entries[j][i]['score']['agg'] = bargs(masterindex) or ''  
masterindex = masterindex+1  
entries[j][i]['score']['weight']['agg'] = 'normal'  
end  
end  
if entries[j][i]['ctype']=='header' then  
entries[j][i]['header'] = paramNames('header',j,i)  
entries[j][i]['pheader'] = paramNames('pheader',j,i)  
entries[j][i]['shade'] = paramNames('shade',j,i)  
end  
if entries[j][i]['ctype']=='text' then  
entries[j][i]['text'] = bargs(tostring(masterindex)) or ''  
masterindex = masterindex+1  
end  
if entries[j][i]['ctype']=='group' then  
entries[j][i]['group'] = bargs(tostring(masterindex)) or ''  
masterindex = masterindex+1  
end  
if entries[j][i]['ctype'] == 'line' and entries[j][i]['hastext']==true then  
entries[j][i]['text'] = bargs(masterindex) or ''  
masterindex = masterindex+1  
end  
end  
end  
end  

local function assignParams  
masterindex = 1  
local maxcol = 1  
local byerows = 1  
local hiderows = 1  
for j=minc,c do  
rlegs[j] = tonumber(bargs('RD'..j..'-legs')) or tonumber(bargs('legs')) or 1  
if notempty(bargs('RD'..j..'-legs')) or bargs('legs') then autolegs = false end  
if paramstyle == 'numbered' then  
indexedParams(j)  
else  
for i=1,r do  
if entries[j][i]~=nil then  
if entries[j][i]['ctype']=='team' then  
local legs = rlegs[j]  
entries[j][i]['seed'] = paramNames('seed',j,i)  
entries[j][i]['team'] = paramNames('team',j,i)  
entries[j][i]['legs'] = paramNames('legs',j,i)  
entries[j][i]['score'] = {}  
entries[j][i]['weight'] = 'normal'  
entries[j][i]['score']['weight'] = {}  
if notempty(entries[j][i]['legs']) then  
legs = tonumber(entries[j][i]['legs'])  
end  
if autolegs then  
local l=1  
repeat  
entries[j][i]['score'][l] = paramNames('score',j,i,l)  
entries[j][i]['score']['weight'][l] = 'normal'  
l=l+1  
until isempty(paramNames('score',j,i,l))  
legs = l-1  
else  
for l=1,legs do  
entries[j][i]['score'][l] = paramNames('score',j,i,l)  
entries[j][i]['score']['weight'][l] = 'normal'  
end  
end  
if aggregate and legs>1 then  
entries[j][i]['score']['agg'] = paramNames('score',j,i,'agg')  
entries[j][i]['score']['weight']['agg'] = 'normal'  
end  
end  
if entries[j][i]['ctype']=='header' then  
entries[j][i]['header'] = paramNames('header',j,i)  
entries[j][i]['pheader'] = paramNames('pheader',j,i)  
entries[j][i]['shade'] = paramNames('shade',j,i)  
end  
if entries[j][i]['ctype']=='text' then  
entries[j][i]['text'] = paramNames('text',j,i)  
end  
if entries[j][i]['ctype']=='group' then  
entries[j][i]['group'] = paramNames('group',j,i)  
end  
if entries[j][i]['ctype'] == 'line' and entries[j][i]['hastext']==true then  
entries[j][i]['text'] = paramNames('text',j,i)  
end  
end  
if autocol and not isBlankEntry(j,i) then  
maxcol = math.max(maxcol,j)  
end  
end  
end  
for i=1,r do  
if entries[j][i]~=nil and entries[j][i]['ctype']=='header' then  
isRoundHidden(j,i)  
end  
if entries[j][i]~=nil and not hide[j][entries[j][i]['headerindex']] then  
if not byes[j][entries[j][i]['headerindex']] or (byes[j][entries[j][i]['headerindex']] and not isBlankEntry(j,i)) then  
byerows = math.max(byerows,i)  
end  
end  
end  
end  
for j=minc,c do  
for k=1,headerindex[j] do  
if byes[j][k] or hide[j][k] then  
r=byerows+1  
end  
end  
end  
if autocol then  
c = maxcol  
end  
end  

local function getHide(j,headerindex)  
hide[j] = {}  
for k=1,headerindex[j] do  
if bargs('RD'..j..toChar(k)..'-hide')=='yes' or bargs('RD'..j..toChar(k)..'-hide')=='y' then  
hide[j][k]=true  
end  
end  
end  

local function getByes(j,headerindex)  
byes[j] = {}  
for k=1,headerindex[j] do  
if bargs('byes')=='yes' or bargs('byes')=='y' then  
byes[j][k]=true else byes[j][k]=false  
end  
if bargs('RD'..j..'-byes')=='yes' or bargs('RD'..j..'-byes')=='y' then  
byes[j][k]=true  
elseif bargs('RD'..j..'-byes')=='no' or bargs('RD'..j..'-byes')=='n' then  
byes[j][k]=false  
end  
if bargs('RD'..j..toChar(k)..'-byes')=='yes' or bargs('RD'..j..toChar(k)..'-byes')=='y' then  
byes[j][k]=true  
elseif bargs('RD'..j..'-byes')=='no' or bargs('RD'..j..'-byes')=='n' then  
byes[j][k]=false  
end  
end  
end  

local function getAltIndices  
local teamindex=1  
local textindex=1  
local groupindex=1  
for j=minc,c do  
headerindex[j]=0  
for i=1,r do  
if entries[j][i]==nil and i==1 then  
headerindex[j]=headerindex[j]+1  
end  
if entries[j][i]~=nil then  
if entries[j][i]['ctype'] == 'header' then  
entries[j][i]['altindex'] = headerindex[j]  
teamindex=1  
textindex=1  
headerindex[j]=headerindex[j]+1  
elseif entries[j][i]['ctype'] == 'team' then  
entries[j][i]['altindex'] = teamindex  
teamindex=teamindex+1  
elseif entries[j][i]['ctype'] == 'text' then  
entries[j][i]['altindex'] = textindex  
textindex=textindex+1  
elseif entries[j][i]['ctype'] == 'group' then  
entries[j][i]['altindex'] = groupindex  
groupindex=groupindex+1  
elseif entries[j][i]['ctype'] == 'line' and entries[j][i]['hastext']==true then  
entries[j][i]['altindex'] = textindex  
textindex=textindex+1  
end  
entries[j][i]['headerindex'] = headerindex[j]  
end  
end  
getByes(j,headerindex)  
getHide(j,headerindex)  
end  
end  

local function pathCell(tbl,j,i,k,a,b,c,bg,rowspan)  
if not hascross[j] and k==2 then return end  
if pathcolor==nil then pathcolor='black' end  
local cell=tbl:tag('td')  
if rowspan~=1 then  
cell:attr('rowspan',rowspan)  
end  
if notempty(bg[k]) then  
cell:css('background',bg[k])  
end  
if a[k][1]~=0 or a[k][2]~=0 or a[k][3]~=0 or a[k][4]~=0 then  
cell:css('','solid black')  
:css('-width',a[k][1]..'px '..a[k][2]..'px '..a[k][3]..'px '..a[k][4]..'px')  
end  
if c[k][2]~=0 then  
cell:css('-',c[k][2]..'px solid '..pathcolor)  
end  
if b[k][2]~=0 then  
cell:css('box-shadow', ' '..b[k][2]..'px '..pathcolor)  
end  
if c[k][4]~=0 then  
cell:css('-',c[k][4]..'px solid '..pathcolor)  
end  
return cell  
end  

local function insertPath(tbl,j,i)  
local function createCross(angle,color,percent)  
local result = 'linear-gradient('..angle..', transparent calc('..percent..'% - ),'..color..' calc('..percent..'% - ),'..color..' calc('..percent..'% + ), transparent calc('..percent..'% + ))'  
return result  
end  
local colspan = 2  
if hascross[j] then colspan = 3 end  

if pathEntries['black'][j][i]=='blank' then return end  

if isempty(pathEntries['black'][j][i]) and isempty(pathEntries['color'][j][i]) then  
if (pathEntries['black'][j][i-1]~='blank' and notempty(pathEntries['black'][j][i-1]) and (pathEntries['black'][j][i-1]['cross'][1] == 1 or pathEntries['black'][j][i-1]['cross'][2] == 1)) or (notempty(pathEntries['color'][j][i-1]) and (pathEntries['color'][j][i-1]['cross'][1] == 1 or pathEntries['color'][j][i-1]['cross'][2] == 1)) then  
return  
else  
local cell = tbl:tag('td')  
for k=1,colspan-1 do  
cell:tag('td')  
end  
return cell  
end  
end  

for key,_ in pairs(pathEntries) do  
if notempty(pathEntries[key][j]) and notempty(pathEntries[key][j][i-1]) and pathEntries[key][j][i-1]~='blank' then  
if pathEntries[key][j][i-1]['cross'][1]==1 or pathEntries[key][j][i-1]['cross'][2]==1 then return end  
end  
end  

local a = {}  
local b = {}  
local c = {}  
local bg = {}  
local cross = {}  
local rowspan = 1  

for k=1,3 do  
a[k]={0,0,0,0}  
b[k]={0,0}  
c[k]={0,0,0,0}  
bg[k] = ''  
cross[k] = {}  
for n=1,2 do  
cross[k][n] = ''  
end  
end  

for key,_ in pairs(pathEntries) do  
local color  
if key=='black' then color='black' else color=pathcolor end  
if notempty(pathEntries[key][j]) and notempty(pathEntries[key][j][i]) then  
if key=='black' then  
a[1][3] = 3*pathEntries['black'][j][i]['out'][1]  
a[2][3] = 3*pathEntries['black'][j][i]['mid'][1]  
a[3][3] = 3*pathEntries['black'][j][i]['in'][1]  
a[1][2] = 2*pathEntries['black'][j][i]['out'][2]  
a[3][4] = 2*pathEntries['black'][j][i]['in'][2]  
else  
if pathEntries['color'][j][i]['out'][3]=='below' then  
b[1][2] = 4*pathEntries['color'][j][i]['out'][1]  
end  
if pathEntries['color'][j][i]['mid'][3]=='below' then  
b[2][2] = 4*pathEntries['color'][j][i]['mid'][1]  
end  
if pathEntries['color'][j][i]['in'][3]=='below' then  
b[3][2] = 4*pathEntries['color'][j][i]['in'][1]  
end  
c[1][2] = 2*pathEntries['color'][j][i]['out'][2]  
c[3][4] = 2*pathEntries['color'][j][i]['in'][2]  
end  
if pathEntries[key][j][i]['cross'][1]==1 or pathEntries[key][j][i]['cross'][2]==1 then  
rowspan = 2  
if pathEntries[key][j][i]['cross'][1]==1 then  
cross[1][1] = createCross('65.5deg',color,100)  
cross[2][1] = createCross('to top ',color,50)  
cross[3][1] = createCross('245.5deg',color,100)  
end  
if pathEntries[key][j][i]['cross'][2]==1 then  
cross[1][2] = createCross('114.4deg',color,100)  
cross[2][2] = createCross('to bottom ',color,50)  
cross[3][2] = createCross('294.4deg',color,100)  
end  
end  
end  
end  
if notempty(pathEntries['color'][j]) and notempty(pathEntries['color'][j][i-1]) then  
if pathEntries['color'][j][i]['out'][3]=='above' then  
b[1][2] = -4  
end  
if pathEntries['color'][j][i]['mid'][3]=='above' then  
b[2][2] = -4  
end  
if pathEntries['color'][j][i]['in'][3]=='above' then  
b[3][2] = -4  
end  
end  
if notempty(cross[2][1]) and notempty(cross[2][2]) then  
for k=1,3 do cross[k][1] = cross[k][1]..',' end  
end  
for k=1,3 do  
bg[k] = cross[k][1]..cross[k][2]  
end  

for k=1,3 do  
pathCell(tbl,j,i,k,a,b,c,bg,rowspan)  
end  
end  

local function getPaths  
local paths = {}  
pathEntries['black'] = {}  
pathEntries['color'] = {}  
for j=minc,c-1 do  
pathEntries['black'][j] = {}  
pathEntries['color'][j] = {}  
for i=1,r do  
pathEntries['black'][j][i] = {['out']={0,0,nil},['mid']={0,0,nil},['in']={0,0,nil},['cross']={0,0,nil}}  
pathEntries['color'][j][i] = {['out']={0,0,nil},['mid']={0,0,nil},['in']={0,0,nil},['cross']={0,0,nil}}  
end  
paths[j] = {}  
hascross[j] = false  
local crossloc = split((fargs['col'..j..'-col'..(j+1)..'-cross'] or ''):gsub("%s+", ""),{","},true)  
local str = fargs['col'..j..'-col'..(j+1)..'-paths'] or ''  
for val in str:gsub("%s+","")  
:gsub(",",", ")  
:gsub("%S+","\0%0\0")  
:gsub("%b", function(s) return s:gsub("%z","") end)  
:gmatch("%z(.-)%z") do  
local result = split(val:gsub("%s+",""):gsub("%)",""):gsub("%(",""),{"-"})  
for k,_ in pairs(result) do  
result[k] = split(result[k],{","})  
end  
if notempty(result[2]) then  
for m=1,#result[2] do  
result[3] = {}  
result[2][m] = split(result[2][m],{":"})  
result[3][m] = result[2][m][2]  
result[2][m] = result[2][m][1]  
end  
for n=1,#result[1] do  
for m=1,#result[2] do  
table.insert(paths[j],{tonumber(result[1][n]),tonumber(result[2][m]),['color']=result[3][m]})  
end  
end  
end  
end  
if shift[j]~=0 and notempty(crossloc[1]) then  
for n=1,#crossloc do  
crossloc[n] = crossloc[n]+shift[j]  
end  
end  
for k,v in ipairs(paths[j]) do  
local color  
local hidepath = false  
if shift[j]~=0 then  
paths[j][k][1] = paths[j][k][1]+shift[j]  
end  
if shift[j+1]~=0 then  
paths[j][k][2] = paths[j][k][2]+shift[j+1]  
end  
local start = 2*paths[j][k][1]+(teams_per_match[j]-2)  
local mid = {}  
if notempty(crossloc[1]) then  
for n=1,#crossloc do  
mid[n] = 2*crossloc[n]+(teams_per_match[j]-2)  
end  
else  
mid[1]=0  
end  
local stop = 2*paths[j][k][2]+(teams_per_match[j+1]-2)  
if paths[j][k]['color']~=nil then  
pathcolor = paths[j][k]['color']  
color = 'color'  
else  
color = 'black'  
end  
if notempty(entries[j][start-1]) and (byes[j][entries[j][start-1]['headerindex']] and isBlankEntry(j,start-1) and isBlankEntry(j,start+1) or hide[j][entries[j][start-1]['headerindex']]) then  
if bargs('show-bye-paths')~='y' and bargs('show-bye-paths')~='yes' then  
hidepath=true  
end  
end  
if notempty(entries[j+1][stop-1]) and (byes[j+1][entries[j+1][stop-1]['headerindex']] and isBlankEntry(j+1,stop-1) and isBlankEntry(j+1,stop+1) or hide[j+1][entries[j+1][stop-1]['headerindex']])then  
if bargs('show-bye-paths')~='y' and bargs('show-bye-paths')~='yes' then  
hidepath=true  
end  
end  
if bargs('RD'..j..'-RD'..(j+1)..'-path')=='n' or bargs('RD'..j..'-RD'..(j+1)..'-path')=='no' or bargs('RD'..j..'-RD'..(j+1)..'-path')=='0' then  
if notempty(entries[j][start-1]) and entries[j][start-1]['headerindex']==1 then  
hidepath=true  
end  
end  

local cross = 0  
for n=1,#mid do  
if (start<stop and mid[n]<stop and mid[n]>start) or (start>stop and mid[n]>stop and mid[n]<start) then  
hascross[j]=true  
cross = mid[n]  
end  
end  
if not hidepath then  
if start==stop then  
if start>r then break end  
start = math.min(start,r)  
pathEntries[color][j][start]['out'][1] = 1  
pathEntries[color][j][start]['in'][1] = 1  
if cross==0 then  
pathEntries[color][j][start]['mid'][1] = 1  
end  
elseif start<stop then  
if stop>r then break end  
pathEntries[color][j][start]['out'][1] = 1  
pathEntries[color][j][stop]['in'][1] = 1  
pathEntries[color][j][stop]['in'][2] = 1  
if color~='black' then  
pathEntries[color][j][start]['out'][3] = 'below'  
pathEntries[color][j][stop+1]['in'][3] = 'above'  
end  
if cross==0 then  
pathEntries[color][j][start]['mid'][1] = 1  
pathEntries[color][j][start]['mid'][3] = 'below'  
for i=start+1,stop-1 do pathEntries[color][j][i]['in'][2] = 1 end  
else  
pathEntries[color][j][cross]['cross'][1] = 1  
for i=start+1,cross-1 do pathEntries[color][j][i]['out'][2] = 1 end  
for i=cross+2,stop-1 do pathEntries[color][j][i]['in'][2] = 1 end  
end  
elseif start>stop then  
if start>r then break end  
pathEntries[color][j][stop]['in'][1] = 1  
if cross==0 then  
for i=stop+1,start-1 do pathEntries[color][j][i]['in'][2] = 1 end  
pathEntries[color][j][start]['out'][1] = 1  
pathEntries[color][j][start]['mid'][1] = 1  
pathEntries[color][j][start]['in'][2] = 1  
if color~='black' then  
pathEntries[color][j][start+1]['out'][3] = 'above'  
pathEntries[color][j][start+1]['mid'][3] = 'above'  
pathEntries[color][j][stop]['in'][3] = 'below'  
end  
else  
pathEntries[color][j][cross]['cross'][2] = 1  
for i=stop+1,cross-1 do pathEntries[color][j][i]['in'][2] = 1 end  
for i=cross+2,start-1 do pathEntries[color][j][i]['out'][2] = 1 end  
pathEntries[color][j][start]['out'][1] = 1  
pathEntries[color][j][start]['out'][2] = 1  
if color~='black' then  
pathEntries[color][j][start+1]['out'][3] = 'above'  
pathEntries[color][j][stop]['in'][3] = 'below'  
end  
end  
end  
end  
end  
end  
for j=minc,c-1 do  
for i=1,r do  
local empty = true  
for key,_ in pairs(pathEntries) do  
for k,v in pairs(pathEntries[key][j][i]) do  
if v[1]~=0 or v[2]~=0 or v[3]~=nil then  
empty = false  
end  
end  
if empty then pathEntries[key][j][i] = nil end  
end  
end  
end  
for j=minc,c-1 do  
if notempty(pathEntries['black'][j]) and notempty(pathEntries['black'][j+1]) then  
for i=1,r do  
if (entries[j+1][i-1]==nil or (byes[j+1][entries[j+1][i-1]['headerindex']]) and isBlankEntry(j+1,i-1)) and notempty(pathEntries['black'][j][i]) and notempty(pathEntries['black'][j+1][i]) then  
if pathEntries['black'][j][i]['in'][1]==1 and pathEntries['black'][j+1][i]['out'][1]==1 then  
entries[j+1][i-1]={['ctype']='line'}  
entries[j+1][i]={['ctype']='blank'}  
end  
end  
end  
end  
end  
end  

local function getGroups  
local function check(j,i)  
local result=false  
if entries[j][i] == nil then  
if entries[j][i+1] == nil then  
result=true  
elseif entries[j][i+1]['ctype']=='text' and isBlankEntry(j,i+1) then  
result=true  
end  
elseif entries[j][i]['ctype']=='text' and isBlankEntry(j,i) then  
result=true  
end  
return result  
end  
for j=minc,c-1 do  
if teams_per_match[j]==2 then  
local n=0  
for i=1,r do  
if (notempty(pathEntries['black'][j][i]) and pathEntries['black'][j][i]['in'][1] == 1) or (notempty(pathEntries['color'][j][i]) and pathEntries['color'][j][i]['in'][1] == 1) then  
n=n+1  
if check(j,i) then  
local k=minc-1  
repeat  
if entries[j-k][i+1]~=nil and entries[j-k][i+1]['ctype']=='text' and isBlankEntry(j-k,i+1) then  
entries[j-k][i+2]=nil  
end  
entries[j-k][i]={['ctype']='blank'}  
entries[j-k][i+1]={['ctype']='blank'}  
if k>0 and isempty(pathEntries['black'][j-k][i]) and isempty(pathEntries['color'][j-k][i]) then  
pathEntries['black'][j-k][i] = 'blank'  
pathEntries['black'][j-k][i+1] = 'blank'  
end  
k=k+1  
until k>j-1 or not check(j-k,i) or notempty(pathEntries['black'][j-k][i]) or notempty(pathEntries['color'][j-k][i])  
k=k-1  
entries[j-k][i]={['ctype']='group',['index']=n,['colspan']=k+1}  
entries[j-k][i+1]={['ctype']='blank'}  
entries[j-k][i]['group'] = bargs('RD'..j..'-group'..n)  
end  
end  
end  
end  
end  
end  

local function getCells  
local maxrow = 1  
local colentry = {}  
local bool = true  
for j=minc,c do  
if notempty(fargs['col'..j..'-headers']) then bool=false end  
teams_per_match[j] = tonumber(fargs['RD'..j..'-teams-per-match']) or tonumber(fargs['col'..j..'-teams-per-match']) or tonumber(fargs['teams-per-match']) or 2  
maxtpm = math.max(maxtpm,teams_per_match[j])  
end  
for j=minc,c do  
entries[j] = {}  
shift[j] = tonumber(bargs('RD'..j..'-shift')) or tonumber(bargs('shift')) or 0  
colentry[j] = {  
split((fargs['col'..j..'-headers'] or ''):gsub("%s+", ""),{","},true),  
split((fargs['col'..j..'-matches'] or ''):gsub("%s+", ""),{","},true),  
split((fargs['col'..j..'-lines'] or ''):gsub("%s+", ""),{","},true),  
split((fargs['col'..j..'-text'] or ''):gsub("%s+", ""),{","},true),  
}  
if bool==true and fargs['noheaders']~='y' and fargs['noheaders']~='yes' then  
table.insert(colentry[j][1],1)  
end  
end  

for j=minc,c do  
local textindex=0  
for k,v in ipairs(colentry[j]) do  
table.sort(colentry[j][k])  
local ctype  
if k==1 then ctype='header'  
elseif k==2 then ctype='team'  
elseif k==3 then ctype='line'  
elseif k==4 then ctype='text'  
elseif k==5 then ctype='group'  
end  
for n=1,#colentry[j][k] do  
if shift[j]~=0 and colentry[j][k][n]>1 then  
colentry[j][k][n] = colentry[j][k][n]+shift[j]  
end  
local i=2*colentry[j][k][n]-1  
maxrow = math.max(i+2*teams_per_match[j]-1,maxrow)  
if ctype=='team' then  
if entries[j][i-1]==nil and entries[j][i-2]==nil then  
entries[j][i-2]={['ctype']='text',['index']=n}  
entries[j][i-1]={['ctype']='blank'}  
textindex=n  
end  
entries[j][i]={['ctype']=ctype,['index']=teams_per_match[j]*n-(teams_per_match[j]-1),['position']='top'}  
entries[j][i+1]={['ctype']='blank'}  
for m=2,teams_per_match[j] do  
entries[j][i+2*(m-1)]={['ctype']=ctype,['index']=teams_per_match[j]*n-(teams_per_match[j]-m)}  
entries[j][i+2*(m-1)+1]={['ctype']='blank'}  
end  
elseif ctype=='text' then  
entries[j][i]={['ctype']=ctype,['index']=textindex+n}  
entries[j][i+1]={['ctype']='blank'}  
elseif ctype=='line' then  
entries[j][i]={['ctype']=ctype}  
entries[j][i+1]={['ctype']='blank'}  
elseif ctype=='group' then  
entries[j][i]={['ctype']=ctype,['index']=n}  
entries[j][i+1]={['ctype']='blank'}  
else  
entries[j][i]={['ctype']=ctype,['index']=n,['position']='top'}  
entries[j][i+1]={['ctype']='blank'}  
end  
end  
end  
end  
if isempty(r) then  
r = maxrow  
end  
end  

function p.main  
fargs = .args  
pargs = :getParent.args;  

r = tonumber(fargs.rows) or ''  
c = tonumber(fargs.rounds) or 1  
maxc = tonumber(pargs.maxrounds) or tonumber(pargs.maxround) or ''  
minc = tonumber(pargs.minround) or 1  
headerindex = {}  
if notempty(maxc) then c=maxc end  
if fargs.autocol=='yes' or fargs.autocol=='y' then autocol=true end  
local colspacing = tonumber(fargs['col-spacing']) or 5  

maxtpm = 1  
seeds = true  
forceseeds = false  
boldwinner = bargs('boldwinner') or ''  
if bargs('seeds')=='y' or bargs('seeds')=='yes' then forceseeds=true end  
if bargs('seeds')=='n' or bargs('seeds')=='no' then seeds=false end  
if bargs('aggregate')=='y' or bargs('aggregate')=='yes' then aggregate=true end  
if bargs('autolegs')=='y' or bargs('autolegs')=='yes' then autolegs=true end  
if bargs('nowrap')=='y' or bargs('nowrap')=='yes' then nowrap=true end  
if bargs('paramstyle')=='numbered' then  
param  
else  
param  
end  

getCells  
getAltIndices  
assignParams  
matchGroups  
if (boldwinner=='yes' or boldwinner=='y' or boldwinner=='high') then boldWinner end  
getPaths  
if minc==1 then  
getGroups  
end  

for j=minc,c do  
maxlegs[j] = rlegs[j]  
for i=1,r do  
if notempty(entries[j][i]) then  
if notempty(entries[j][i]['legs']) then  
maxlegs[j] = math.max(rlegs[j],entries[j][i]['legs'])  
end  
if autolegs then  
local l=1  
repeat l=l+1  
until isempty(entries[j][i]['score']) or isempty(entries[j][i]['score'][l])  
maxlegs[j] = math.max(maxlegs[j],l-1)  
end  
end  
end  
end  

local tbl = mw.html.create('table')  
:attr('cellpadding','0')  
:attr('cellspacing','0')  
:css('font-size','90%')  
:css('-collapse','collapse')  
:css('margin','1em 2em 1em 1em')  

tbl:tag('tr'):css('visibility','collapse')  
tbl:tag('td'):css('width','')  
for j=minc,c do  
if seeds then  
tbl:tag('td'):css('width',getWidth('seed',''))  
end  
tbl:tag('td'):css('width',getWidth('team',''))  
if maxlegs[j]==0 then  
tbl:tag('td'):css('width',getWidth('score',''))  
else  
for l=1,maxlegs[j] do  
tbl:tag('td'):css('width',getWidth('score',''))  
end  
end  
if aggregate and maxlegs[j]>1 then  
tbl:tag('td'):css('width',getWidth('score',''))  
end  
if j~=c then  
if hascross[j] then  
tbl:tag('td'):css('width',colspacing..'px')  
tbl:tag('td'):css('width','')  
tbl:tag('td'):css('width',colspacing..'px')  
else  
tbl:tag('td'):css('width',colspacing..'px')  
tbl:tag('td'):css('width',colspacing..'px')  
end  
end  
end  

for i=1,r do  
local row = tbl:tag('tr')  
row:tag('td'):css('height','')  
for j=minc,c do  
insertEntry(row,j,i)  
if j~=c then  
insertPath(row,j,i)  
end  
end  
end  

return tostring(tbl)  
end  

return p