Modul:Build bracket

local p = {} local entries = {} local pathCell = {} local crossCell = {} local skipPath = {} 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 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 cellBorder(b) return b[1]..'px '..b[2]..'px '..b[3]..'px '..b[4]..'px' end

local function Cell(tbl,j,i,rowspan,colspan,text,align,border,border_width,bg,padding,weight,nwrap) 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(border) then cell:css('border',border) end if notempty(border_width) then cell:css('border-width',cellBorder(border_width)) end if notempty(bg) then cell:css('background-color',bg) end if notempty(align) then cell:css('text-align',align) end if notempty(padding) then cell:css('padding-left',padding) end if weight=='bold' then cell:css('font-weight',weight) end if notempty(text) then cell:wikitext(text) end return cell end

local function teamCell(tbl,k,j,i,l,colspan) local bg = '#F2F2F2' local align local padding local weight local text local nwrap local b={0,0,1,1} if k=='seed' or k=='score' then align='center' end if k~='seed' then bg='#F9F9F9' end if k=='team' then padding='0.3em' if teamLegs(j,i)==0 then b[2]=1 end end if entries[j][i]['position']=='top' then b[1]=1 end if l==teamLegs(j,i) or l=='agg' or k=='seed' then b[2]=1 end if (l==nil and entries[j][i]['weight']=='bold') or entries[j][i]['score']['weight'][l]=='bold' then weight='bold' end if l==nil then text=entries[j][i][k] else text=tostring(entries[j][i][k][l]) end return Cell(tbl,j,i,2,colspan,text,align,'solid #aaa',b,bg,padding,weight,nwrap) 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 Cell(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 Cell(tbl,j,i,2,entry_colspan) end end

if hide[j][entries[j][i]['headerindex']] then return Cell(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 Cell(tbl,j,i,2,entry_colspan,entries[j][i]['header'],'center','1px solid #aaa',nil,entries[j][i]['shade']) end

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

local legs = teamLegs(j,i) local 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 Cell(tbl,j,i,2,entry_colspan,entries[j][i]['text'],nil,nil,nil,nil,'0.3em') 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) return Cell(tbl,j,i,2,colspan,entries[j][i]['group'],'center') end

if entries[j][i]['ctype']=='line' then local b={0,0,0,0} b[3]=2*pathCell[j-1][i+1][3][3] return Cell(tbl,j,i,2,entry_colspan,entries[j][i]['text'],nil,'solid black',b) end

if entries[j][i]['ctype']=='line2' then local b={0,0,0,0} b[1]=2*pathCell[j-1][i][3][1] return Cell(tbl,j,i,2,entry_colspan,entries[j][i]['text'],nil,'solid black',b) 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 noPaths(j,i) local result = true local cols = 2 if hascross[j]==true then cols = 3 end for k=1,cols do for n=1,4 do if pathCell[j][i][k][n]~=0 then result = false return result end end end if hascross[j]==true and (crossCell[j][i]['left'][1]==1 or crossCell[j][i]['right'][1]==1) then result = false return result end return result end

local function generatePathCell(tbl,j,i,k,color,bg,rowspan) if not hascross[j] and k==2 then return end local cell=tbl:tag('td') local a=pathCell[j][i] if rowspan~=1 then cell:attr('rowspan',rowspan) end if notempty(bg) and k==2 then cell:css('background',bg) :css('transform','translate(-1px)') end if a[k][1]~=0 or a[k][2]~=0 or a[k][3]~=0 or a[k][4]~=0 then cell:css('border','solid '..color) :css('border-width',2*a[k][1]..'px '..2*a[k][2]..'px '..2*a[k][3]..'px '..2*a[k][4]..'px') end return cell end

local function insertPath(tbl,j,i) if skipPath[j][i] then return end

local colspan = 2 local rowspan = 1 local angle = 58.2 local pathcolor = pathCell[j][i]['color'] or 'black' local bg = local cross = {,}

if i<r then local function repeatedPath(a) if a>r-1 or skipPath[j][a] then return false end for k=1,3 do for n=1,4 do if pathCell[j][i][k][n]~=pathCell[j][a][k][n] then return false end end end return true end if repeatedPath(i) then local row=i repeat if row~=i and repeatedPath(row) then skipPath[j][row]=true end rowspan=rowspan+1 row=row+1 until row>r or not repeatedPath(row) rowspan=rowspan-1 end end

if i>1 and (crossCell[j][i-1]['left'][1]==1 or crossCell[j][i-1]['right'][1]==1) then return end if hascross[j] then colspan = 3 if crossCell[j][i]['left'][1]==1 or crossCell[j][i]['right'][1]==1 then rowspan = 2 if crossCell[j][i]['left'][1]==1 then cross[1] = 'linear-gradient(to top right, transparent calc(50% - 1px),'..crossCell[j][i]['left'][2]..' calc(50% - 1px),'..crossCell[j][i]['left'][2]..' calc(50% + 1px), transparent calc(50% + 1px))' end if crossCell[j][i]['right'][1]==1 then cross[2] = 'linear-gradient(to bottom right, transparent calc(50% - 1px),'..crossCell[j][i]['right'][2]..' calc(50% - 1px),'..crossCell[j][i]['right'][2]..' calc(50% + 1px), transparent calc(50% + 1px))' end end if notempty(cross[1]) and notempty(cross[2]) then cross[1] = cross[1]..',' end bg = cross[1]..cross[2] end for k=1,3 do generatePathCell(tbl,j,i,k,pathcolor,bg,rowspan) end end

local function parsePaths(j) local result={} 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 array = split(val:gsub("%s+",""):gsub("%)",""):gsub("%(",""),{"-"}) for k,_ in pairs(array) do array[k] = split(array[k],{","}) end if notempty(array[2]) then for m=1,#array[2] do array[3] = {} array[2][m] = split(array[2][m],{":"}) array[3][m] = array[2][m][2] array[2][m] = array[2][m][1] end for n=1,#array[1] do for m=1,#array[2] do table.insert(result,{tonumber(array[1][n]),tonumber(array[2][m]),['color']=array[3][m]}) end end end end return result end

local function isPathHidden(j,i,start,stop) local result=false 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 result=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 result=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 result=true end end return result end

local function getPaths() local paths = {} for j=minc,c-1 do hascross[j] = false if notempty(fargs['col'..j..'-col'..(j+1)..'-cross']) then hascross[j] = true end end for j=minc,c-1 do local straightpaths = {} local outpaths = {} local inpaths = {} paths[j]=parsePaths(j) pathCell[j] = {} crossCell[j] = {} skipPath[j] = {} for i=1,r do pathCell[j][i] = {} crossCell[j][i] = {['left']={0,'black'},['right']={0,'black'}} for k=1,3 do pathCell[j][i][k] = {0,0,0,0} end skipPath[j][i] = false end local crossloc = split((fargs['col'..j..'-col'..(j+1)..'-cross'] or ):gsub("%s+", ""),{","},true) 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 start = 2*(paths[j][k][1]+shift[j])+(teams_per_match[j]-2) local stop = 2*(paths[j][k][2]+shift[j+1])+(teams_per_match[j+1]-2) local mid = {} local cross = 0

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 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 cross = mid[n] end end paths[j][k]['color'] = paths[j][k]['color'] or 'black' table.insert(outpaths,{start,paths[j][k]['color']}) table.insert(inpaths,{stop,paths[j][k]['color']}) if not isPathHidden(j,i,start,stop) then if start==stop then table.insert(straightpaths,{start,paths[j][k]['color']}) elseif start<stop then if stop>r then break end for i=start+1,stop do pathCell[j][i]['color'] = paths[j][k]['color'] end pathCell[j][start+1][1][1] = 1 if cross==0 then if hascross[j] then pathCell[j][start+1][2][1] = 1 for i=start+1,stop do pathCell[j][i][2][2] = 1 end else for i=start+1,stop do pathCell[j][i][1][2] = 1 end end else crossCell[j][cross]['left'] = {1,paths[j][k]['color']} for i=start+1,cross-1 do pathCell[j][i][1][2] = 1 end for i=cross+2,stop do pathCell[j][i][2][2] = 1 end end pathCell[j][stop][3][3] = 1 elseif start>stop then if start>r then break end for i=stop+1,start do pathCell[j][i]['color'] = paths[j][k]['color'] end pathCell[j][stop+1][3][1] = 1 if cross==0 then if hascross[j] then for i=stop+1,start do pathCell[j][i][2][2] = 1 end pathCell[j][start][2][3] = 1 else for i=stop+1,start do pathCell[j][i][1][2] = 1 end end else crossCell[j][cross]['right'] = {1,paths[j][k]['color']} for i=stop+1,cross-1 do pathCell[j][i][2][2] = 1 end for i=cross+2,start do pathCell[j][i][1][2] = 1 end end pathCell[j][start][1][3] = 1 end end end -- Thicken start==stop paths for n=1,#straightpaths do local i = straightpaths[n][1] local color = straightpaths[n][2] if i>r then break end if pathCell[j][i][1][3]==0 then pathCell[j][i]['color'] = color pathCell[j][i][1][3] = 1 pathCell[j][i][2][3] = 1 pathCell[j][i][3][3] = 1 if pathCell[j][i+1][1][1]==0 then pathCell[j][i+1]['color'] = color pathCell[j][i+1][1][1] = 0.5 pathCell[j][i+1][2][1] = 0.5 pathCell[j][i+1][3][1] = 0.5 end elseif pathCell[j][i+1][1][1]==0 then pathCell[j][i+1]['color'] = color pathCell[j][i+1][1][1] = 1 if hascross[j] then pathCell[j][i+1][2][1] = 1 end pathCell[j][i+1][3][1] = 1 end end -- Thicken/Thin out paths for n=1,#outpaths do local i = outpaths[n][1] local color = outpaths[n][2] if i<r and pathCell[j][i][1][3]==1 and pathCell[j][i+1][1][1]==0 then pathCell[j][i+1]['color'] = color pathCell[j][i+1][1][1] = 0.5*pathCell[j][i][1][3] pathCell[j][i+1][2][1] = 0.5*pathCell[j][i][2][3] elseif i<r and pathCell[j][i][1][3]==0 and pathCell[j][i+1][1][1]==1 then pathCell[j][i]['color'] = color pathCell[j][i][1][3] = pathCell[j][i+1][1][1] pathCell[j][i][2][3] = pathCell[j][i+1][2][1] pathCell[j][i+1][1][1] = 0.5*pathCell[j][i][1][3] pathCell[j][i+1][2][1] = 0.5*pathCell[j][i][2][3] end end -- Thin double-in paths for n=1,#inpaths do local i = inpaths[n][1] local color = inpaths[n][2] if i<r and pathCell[j][i][3][3]==1 and pathCell[j][i+1][3][1]==1 and pathCell[j][i]['color']==pathCell[j][i+1]['color'] then pathCell[j][i+1][3][1] = 0.5*pathCell[j][i][3][3] end end end for j=minc,c-1 do for i=1,r-1 do local straightpath=false if (entries[j+1][i-1]==nil or (byes[j+1][entries[j+1][i-1]['headerindex']]) and isBlankEntry(j+1,i-1)) then if (pathCell[j][i][3][3]~=0 and pathCell[j+1][i][1][3]~=0) or (pathCell[j][i+1][3][1]~=0 and pathCell[j+1][i+1][1][1]~=0) then if pathCell[j+1][i][1][3]==pathCell[j+1][i][3][3] and pathCell[j+1][i+1][1][1]==pathCell[j+1][i+1][3][1] then straightpath=true end pathCell[j+1][i][1][3]=pathCell[j][i][3][3] pathCell[j+1][i+1][1][1]=pathCell[j][i+1][3][1] pathCell[j+1][i][2][3]=pathCell[j][i][3][3] pathCell[j+1][i+1][2][1]=pathCell[j][i+1][3][1] entries[j+1][i-1]={['ctype']='line'} entries[j+1][i]={['ctype']='blank'} if notempty(entries[j+1][i+1]) then entries[j+1][i+1]['ctype'] = 'line2' else entries[j+1][i+1]={['ctype']='line2'} end entries[j+1][i+2]={['ctype']='blank'} if straightpath then pathCell[j+1][i][3][3]=pathCell[j+1][i][1][3] pathCell[j+1][i+1][3][1]=pathCell[j+1][i+1][1][1] 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-1 do if pathCell[j][i][3][3]==1 or pathCell[j][i+1][3][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 noPaths(j-k,i) then skipPath[j-k][i] = true skipPath[j-k][i+1] = true end k=k+1 until k>j-1 or not check(j-k,i) or not noPaths(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'} entries[j][i+2]={['ctype']='line2'} entries[j][i+3]={['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(frame) fargs = frame.args pargs = frame: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 local height = bargs('height') or 0

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('paramstyle')=='numbered' then paramstyle = 'numbered' else paramstyle = 'indexed' 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 div = mw.html.create('div')

   		:css('overflow','auto')
   		

if height~=0 then div:css('height',height) end

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

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

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

div:wikitext(tostring(tbl))

return tostring(div) end

return p


local p = {}
local entries = {}
local pathCell = {}
local crossCell = {}
local skipPath = {}
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 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 cellBorder(b)
	return b[1]..'px '..b[2]..'px '..b[3]..'px '..b[4]..'px'
end

local function Cell(tbl,j,i,rowspan,colspan,text,align,border,border_width,bg,padding,weight,nwrap)
	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(border) then
		cell:css('border',border)
	end
	if notempty(border_width) then
		cell:css('border-width',cellBorder(border_width))
	end
	if notempty(bg) then
		cell:css('background-color',bg)
	end
	if notempty(align) then
		cell:css('text-align',align)
	end
	if notempty(padding) then
		cell:css('padding-left',padding)
	end
	if weight=='bold' then
		cell:css('font-weight',weight)
	end
	if notempty(text) then
		cell:wikitext(text)
	end
	return cell
end

local function teamCell(tbl,k,j,i,l,colspan)
	local bg = '#F2F2F2'
	local align
	local padding
	local weight
	local text
	local nwrap
	local b={0,0,1,1}
	if k=='seed' or k=='score' then
		align='center'
	end
	if k~='seed' then
		bg='#F9F9F9'
	end
	if k=='team' then 
		padding='0.3em'
		if teamLegs(j,i)==0 then
			b[2]=1
		end
	end
	if entries[j][i]['position']=='top' then
		b[1]=1
	end
	if l==teamLegs(j,i) or l=='agg' or k=='seed' then
		b[2]=1
	end
	if (l==nil and entries[j][i]['weight']=='bold') or entries[j][i]['score']['weight'][l]=='bold' then
		weight='bold'
	end
	if l==nil then
		text=entries[j][i][k]
	else
		text=tostring(entries[j][i][k][l])
	end
	return Cell(tbl,j,i,2,colspan,text,align,'solid #aaa',b,bg,padding,weight,nwrap)
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 Cell(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 Cell(tbl,j,i,2,entry_colspan)
			end
		end
		
		if hide[j][entries[j][i]['headerindex']] then
			return Cell(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 Cell(tbl,j,i,2,entry_colspan,entries[j][i]['header'],'center','1px solid #aaa',nil,entries[j][i]['shade'])
	end
	
	if entries[j][i]['ctype']=='team' then
		if (byes[j][entries[j][i]['headerindex']] and isBlankEntry(j,i)) or hide[j][entries[j][i]['headerindex']] then
			return Cell(tbl,j,i,2,entry_colspan)
		end
		
		local legs = teamLegs(j,i)
		local 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
		Cell(tbl,j,i,2,entry_colspan,entries[j][i]['text'],nil,nil,nil,nil,'0.3em')
	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)
		return Cell(tbl,j,i,2,colspan,entries[j][i]['group'],'center')
	end
	
	if entries[j][i]['ctype']=='line' then
		local b={0,0,0,0}
		b[3]=2*pathCell[j-1][i+1][3][3]
		return Cell(tbl,j,i,2,entry_colspan,entries[j][i]['text'],nil,'solid black',b)
	end
	
	if entries[j][i]['ctype']=='line2' then
		local b={0,0,0,0}
		b[1]=2*pathCell[j-1][i][3][1]
		return Cell(tbl,j,i,2,entry_colspan,entries[j][i]['text'],nil,'solid black',b)
	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 noPaths(j,i)
	local result = true
	local cols = 2
	if hascross[j]==true then
		cols = 3
	end
	for k=1,cols do
		for n=1,4 do
			if pathCell[j][i][k][n]~=0 then
				result = false
				return result
			end
		end
	end
	if hascross[j]==true and (crossCell[j][i]['left'][1]==1 or crossCell[j][i]['right'][1]==1) then
		result = false
		return result
	end
	return result
end

local function generatePathCell(tbl,j,i,k,color,bg,rowspan)
	if not hascross[j] and k==2 then 
		return 
	end
	local cell=tbl:tag('td')
	local a=pathCell[j][i]
	if rowspan~=1 then
		cell:attr('rowspan',rowspan)
	end
	if notempty(bg) and k==2 then
		cell:css('background',bg)
			:css('transform','translate(-1px)')
	end
	if a[k][1]~=0 or a[k][2]~=0 or a[k][3]~=0 or a[k][4]~=0 then
		cell:css('border','solid '..color)
			:css('border-width',2*a[k][1]..'px '..2*a[k][2]..'px '..2*a[k][3]..'px '..2*a[k][4]..'px')
	end
	return cell
end

local function insertPath(tbl,j,i)
	if skipPath[j][i] then
		return
	end
	
	local colspan = 2
	local rowspan = 1
	local angle = 58.2
	local pathcolor = pathCell[j][i]['color'] or 'black'
	local bg = ''
	local cross = {'',''}
	
	if i<r then
		local function repeatedPath(a)
			if a>r-1 or skipPath[j][a] then
				return false
			end
			for k=1,3 do
				for n=1,4 do
					if pathCell[j][i][k][n]~=pathCell[j][a][k][n] then
						return false
					end
				end
			end
			return true
		end
		if repeatedPath(i) then
			local row=i
			repeat
				if row~=i and repeatedPath(row) then
					skipPath[j][row]=true
				end
				rowspan=rowspan+1
				row=row+1
			until row>r or not repeatedPath(row)
			rowspan=rowspan-1
		end
	end
	
	if i>1 and (crossCell[j][i-1]['left'][1]==1 or crossCell[j][i-1]['right'][1]==1) then
		return
	end
	if hascross[j] then
		colspan = 3
		if crossCell[j][i]['left'][1]==1 or crossCell[j][i]['right'][1]==1 then
			rowspan = 2
			if crossCell[j][i]['left'][1]==1 then
				cross[1] = 'linear-gradient(to top right, transparent calc(50% - 1px),'..crossCell[j][i]['left'][2]..' calc(50% - 1px),'..crossCell[j][i]['left'][2]..' calc(50% + 1px), transparent calc(50% + 1px))'
			end
			if crossCell[j][i]['right'][1]==1 then
				cross[2] = 'linear-gradient(to bottom right, transparent calc(50% - 1px),'..crossCell[j][i]['right'][2]..' calc(50% - 1px),'..crossCell[j][i]['right'][2]..' calc(50% + 1px), transparent calc(50% + 1px))'
			end
		end
		if notempty(cross[1]) and notempty(cross[2]) then
			cross[1] = cross[1]..','
		end
		bg = cross[1]..cross[2]
	end
	for k=1,3 do
		generatePathCell(tbl,j,i,k,pathcolor,bg,rowspan)
	end
end

local function parsePaths(j)
	local result={}
	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 array = split(val:gsub("%s+",""):gsub("%)",""):gsub("%(",""),{"-"})
			for k,_ in pairs(array) do
				array[k] = split(array[k],{","})
			end
			if notempty(array[2]) then
				for m=1,#array[2] do
					array[3] = {}
					array[2][m] = split(array[2][m],{":"})
					array[3][m] = array[2][m][2]
					array[2][m] = array[2][m][1]
				end
				for n=1,#array[1] do
					for m=1,#array[2] do
						table.insert(result,{tonumber(array[1][n]),tonumber(array[2][m]),['color']=array[3][m]})
					end
				end
			end
		end
	return result
end

local function isPathHidden(j,i,start,stop)
	local result=false
	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
			result=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
			result=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
			result=true
		end
	end
	return result
end

local function getPaths()
	local paths = {}
	for j=minc,c-1 do
		hascross[j] = false
		if notempty(fargs['col'..j..'-col'..(j+1)..'-cross']) then
			hascross[j] = true
		end
	end
	for j=minc,c-1 do
		local straightpaths = {}
		local outpaths = {}
		local inpaths = {}
		paths[j]=parsePaths(j)
		pathCell[j] = {}
		crossCell[j] = {}
		skipPath[j] = {}
		for i=1,r do
			pathCell[j][i] = {}
			crossCell[j][i] = {['left']={0,'black'},['right']={0,'black'}}
			for k=1,3 do
				pathCell[j][i][k] = {0,0,0,0}
			end
			skipPath[j][i] = false
			end
		local crossloc = split((fargs['col'..j..'-col'..(j+1)..'-cross'] or ''):gsub("%s+", ""),{","},true)
		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 start = 2*(paths[j][k][1]+shift[j])+(teams_per_match[j]-2)
			local stop = 2*(paths[j][k][2]+shift[j+1])+(teams_per_match[j+1]-2)
			local mid = {}
			local cross = 0
			
			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
			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
					cross = mid[n]
				end
			end
			paths[j][k]['color'] = paths[j][k]['color'] or 'black'
			table.insert(outpaths,{start,paths[j][k]['color']})
			table.insert(inpaths,{stop,paths[j][k]['color']})
			if not isPathHidden(j,i,start,stop) then
				if start==stop then
					table.insert(straightpaths,{start,paths[j][k]['color']})
				elseif start<stop then
					if stop>r then break end
					for i=start+1,stop do
						pathCell[j][i]['color'] = paths[j][k]['color']
					end
					pathCell[j][start+1][1][1] = 1
					if cross==0 then
						if hascross[j] then
							pathCell[j][start+1][2][1] = 1
							for i=start+1,stop do
								pathCell[j][i][2][2] = 1
							end
						else
							for i=start+1,stop do
								pathCell[j][i][1][2] = 1
							end
						end
					else
						crossCell[j][cross]['left'] = {1,paths[j][k]['color']}
						for i=start+1,cross-1 do pathCell[j][i][1][2] = 1 end
						for i=cross+2,stop do pathCell[j][i][2][2] = 1 end	
					end
					pathCell[j][stop][3][3] = 1
				elseif start>stop then
					if start>r then break end
					for i=stop+1,start do
						pathCell[j][i]['color'] = paths[j][k]['color']
					end
					pathCell[j][stop+1][3][1] = 1
					if cross==0 then
						if hascross[j] then
							for i=stop+1,start do
								pathCell[j][i][2][2] = 1
							end
							pathCell[j][start][2][3] = 1
						else
							for i=stop+1,start do
								pathCell[j][i][1][2] = 1
							end
						end
					else
						crossCell[j][cross]['right'] = {1,paths[j][k]['color']}
						for i=stop+1,cross-1 do pathCell[j][i][2][2] = 1 end
						for i=cross+2,start do pathCell[j][i][1][2] = 1 end	
					end
					pathCell[j][start][1][3] = 1
				end
			end
		end
		-- Thicken start==stop paths
		for n=1,#straightpaths do
			local i = straightpaths[n][1]
			local color = straightpaths[n][2]
			if i>r then break end
			if pathCell[j][i][1][3]==0 then
				pathCell[j][i]['color'] = color
				pathCell[j][i][1][3] = 1
				pathCell[j][i][2][3] = 1
				pathCell[j][i][3][3] = 1
				if pathCell[j][i+1][1][1]==0 then
					pathCell[j][i+1]['color'] = color
					pathCell[j][i+1][1][1] = 0.5
					pathCell[j][i+1][2][1] = 0.5
					pathCell[j][i+1][3][1] = 0.5
				end
			elseif pathCell[j][i+1][1][1]==0 then
				pathCell[j][i+1]['color'] = color
				pathCell[j][i+1][1][1] = 1
				if hascross[j] then
					pathCell[j][i+1][2][1] = 1
				end
				pathCell[j][i+1][3][1] = 1
			end
		end
		-- Thicken/Thin out paths
		for n=1,#outpaths do
			local i = outpaths[n][1]
			local color = outpaths[n][2]
			if i<r and pathCell[j][i][1][3]==1 and pathCell[j][i+1][1][1]==0 then
				pathCell[j][i+1]['color'] = color
				pathCell[j][i+1][1][1] = 0.5*pathCell[j][i][1][3]
				pathCell[j][i+1][2][1] = 0.5*pathCell[j][i][2][3]
			elseif i<r and pathCell[j][i][1][3]==0 and pathCell[j][i+1][1][1]==1 then
				pathCell[j][i]['color'] = color
				pathCell[j][i][1][3] = pathCell[j][i+1][1][1]
				pathCell[j][i][2][3] = pathCell[j][i+1][2][1]
				pathCell[j][i+1][1][1] = 0.5*pathCell[j][i][1][3]
				pathCell[j][i+1][2][1] = 0.5*pathCell[j][i][2][3]
			end
		end
		-- Thin double-in paths
		for n=1,#inpaths do
			local i = inpaths[n][1]
			local color = inpaths[n][2]
			if i<r and pathCell[j][i][3][3]==1 and pathCell[j][i+1][3][1]==1 and pathCell[j][i]['color']==pathCell[j][i+1]['color'] then
				pathCell[j][i+1][3][1] = 0.5*pathCell[j][i][3][3]
			end
		end
	end
	for j=minc,c-1 do
		for i=1,r-1 do
			local straightpath=false
			if (entries[j+1][i-1]==nil or (byes[j+1][entries[j+1][i-1]['headerindex']]) and isBlankEntry(j+1,i-1)) then
				if (pathCell[j][i][3][3]~=0 and pathCell[j+1][i][1][3]~=0) or (pathCell[j][i+1][3][1]~=0 and pathCell[j+1][i+1][1][1]~=0) then
					if pathCell[j+1][i][1][3]==pathCell[j+1][i][3][3] and pathCell[j+1][i+1][1][1]==pathCell[j+1][i+1][3][1] then
						straightpath=true
					end
					pathCell[j+1][i][1][3]=pathCell[j][i][3][3]
					pathCell[j+1][i+1][1][1]=pathCell[j][i+1][3][1]
					pathCell[j+1][i][2][3]=pathCell[j][i][3][3]
					pathCell[j+1][i+1][2][1]=pathCell[j][i+1][3][1]
					entries[j+1][i-1]={['ctype']='line'}
					entries[j+1][i]={['ctype']='blank'}
					if notempty(entries[j+1][i+1]) then
						entries[j+1][i+1]['ctype'] = 'line2'
					else
						entries[j+1][i+1]={['ctype']='line2'}
					end
					entries[j+1][i+2]={['ctype']='blank'}
					if straightpath then
						pathCell[j+1][i][3][3]=pathCell[j+1][i][1][3]
						pathCell[j+1][i+1][3][1]=pathCell[j+1][i+1][1][1]
					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-1 do
				if pathCell[j][i][3][3]==1 or pathCell[j][i+1][3][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 noPaths(j-k,i) then
								skipPath[j-k][i] = true
								skipPath[j-k][i+1] = true
							end
							k=k+1
						until k>j-1 or not check(j-k,i) or not noPaths(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'}
					entries[j][i+2]={['ctype']='line2'}
					entries[j][i+3]={['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(frame)
	fargs = frame.args
	pargs = frame: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
	local height = bargs('height') or 0 
	
	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('paramstyle')=='numbered' then 
		paramstyle = 'numbered'
	else
		paramstyle = 'indexed'
	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 div = mw.html.create('div')
    		:css('overflow','auto')
    		
	if height~=0 then
		div:css('height',height)
	end
	
	local tbl = mw.html.create('table')
			:attr('cellpadding','0')
			:attr('cellspacing','0')
			:css('font-size','90%')
			:css('border-collapse','separate')
			:css('margin','1em 2em 1em 1em')
			:css('white-space','nowrap')
	
	tbl:tag('tr'):css('visibility','collapse')
	tbl:tag('td'):css('width','1px')
	for j=minc,c do
		if seeds then
			tbl:tag('td'):css('width',getWidth('seed','25px'))
		end
		tbl:tag('td'):css('width',getWidth('team','150px'))
		if maxlegs[j]==0 then
			tbl:tag('td'):css('width',getWidth('score','25px'))
			else
			for l=1,maxlegs[j] do
				tbl:tag('td'):css('width',getWidth('score','25px'))
			end
		end
		if aggregate and maxlegs[j]>1 then
			tbl:tag('td'):css('width',getWidth('score','25px'))
		end
		if j~=c then
			if hascross[j] then
				tbl:tag('td'):css('width',colspacing+1-4 ..'px')
							:css('padding-left','4px')
				tbl:tag('td'):css('padding-left','5px')
							:css('width','5px')
				tbl:tag('td'):css('width',colspacing-1-2 ..'px')
							:css('padding-right','2px')
			else
				tbl:tag('td'):css('width',colspacing+1-4 ..'px')
							:css('padding-left','4px')
				tbl:tag('td'):css('width',colspacing-1-2 ..'px')
							:css('padding-right','2px')
			end
		end
	end
	
	for i=1,r do
		local row = tbl:tag('tr')
		row:tag('td'):css('height','11px')
		for j=minc,c do
			insertEntry(row,j,i)
			if j~=c then
				insertPath(row,j,i)
			end
		end
	end
	
	div:wikitext(tostring(tbl))
			
	return tostring(div)
end

return p