Loading
  • 21 Aug, 2019

  • By, Wikipedia

Module:Build Bracket

This module is intended to be used to create and edit intricate sports brackets that cannot be made by Module:RoundN or Module:Team bracket. For example, additional headers (for double-elimination brackets; 3rd-, 5th-, 7th-place matches; etc), singular omitted matches (i.e., having a horizonal line instead of a match), and N-way brackets (N teams per match). The syntax is slightly more complicated than the aforementioned brackets, but simpler than using the standard wikitable code.

Usage

{{#invoke:Build bracket|main
| rounds =

| col1-headers = 
| col1-matches = 

| col2-headers = 
| col2-matches = 

...

| col1-col2-paths = 
| col2-col3-paths = 

...
}}

Parameters

Parameter Description Default
rounds number of rounds (columns). 1
autocol set to yes to automatically set set maximum round to display based on entries. no
rows manually set the number of rows. Automatic
teams-per-match enter the number of teams in each match. Use colm-teams-per-match to set individual columns 2
colm-headers (optional) enter the row numbers where headers are desired in column m. Separate entries with ,. Half integer values are allowed. Automatic
colm-matches enter the row numbers where a match is desired in column m. Matches take up two rows by default. Separate entries with ,. Half integer values are allowed.
RDmh-hide set to yes to hide the (alpha) hth header and all matches beneath it in column m unless any of those entries are non empty. Useful for consolation matches.
colm-colm+1-paths enter the starting and ending row numbers, separated by -, from columns m and m+1 where a path is desired. Separate entries with ,. Half integer values are allowed.
colm-colm+1-cross enter the row number where paths intersect from column m to m+1.
RDm-altname Alternate name for RDm (e.g., if |RD1-altname=first, then first-team1 can be used instead of RD1-team1). Use RDmh-altname for cells under header mh.
text-altname Alternate name for RDm-textk (e.g., if |text-altname=details, then RDm-details1 can be used instead of RDm-text1).
maxround final round to display. This parameter should be omitted unless it is less than the default value set by rounds.
minround first round to display. 1
height the amount of vertical visibility desired for the bracket. Creates a vertical scroll bar. Enter a number with units (e.g., 30em or 480px).
col-spacing the amount of horizontal space between rounds. Enter as a plain number (e.g., 10 for 10px). 5
seed-width the width of the cells for seeds. Plain numbers are assumed to be in px units (e.g., 25 for 25px 2em for 2em) 25
team-width the width of the cells for team names. Plain numbers are assumed to be in px units (e.g., 200 for 200px or 15em for 15em) 150
score-width the width of the cells for scores. Plain numbers are assumed to be in px units (e.g., 25 or 25px 2em for 2em) 25
agg-width the width of the cells for aggregate scores. score-width will change aggregate score widths unless this parameter is used. Plain numbers are assumed to be in px units (e.g., 25 or 25px 2em for 2em) 25
seeds set to no to omit seeds in in all matches. set to yes to show seed cells to show for all matches.
legs the number of legs for all rounds. Use RDm-legs to individually set columns. Use RDm-legsk to individually set teams. 1
autolegs set to yes to automatically generate score cells per team. If legs or RDm-legs is used, autolegs will be set to no . no
byes set to yes to hide any team cells that are empty. Alternatively, set to m to have rounds 1 through m hide any team cells that are empty. Use RDm-byes for just matches under column m. Use RDmh-byes for just matches under header h only in column m. no
show-bye-paths set to yes to replace any team cells that that are hidden byes with a path. no
aggregate set to yes to add an aggregate score box to each match. Only matches with two or more legs will show the aggregate score box. no
boldwinner set to yes to automatically bold the seed/team/score with the higher score in each match. no
shift vertically shifts all of the entries by the number entered. Use RDm-shift for individual in columns. 0
RDm, RDmh The header text of the (alpha hth) header in column m (e.g., RD1 or RD1a for the first header and RD1b for the second header in column 1).
RDm-seedk The seed of the kth team in column m. Alternatively, use RDmh-seedk for the kth team under header mh.
RDm-teamk The name of the kth team in column m. Alternatively, use RDmh-teamk for the kth team under header mh.
RDm-scorek The score of the kth team in column m. Alternatively, use RDmh-scorek for the kth team under header mh. Append the suffix -l for the lth leg or -agg for the aggregate score.
RDm-textk The text above the kth match in column m. Alternatively, use RDmh-textk for the kth match under header mh.
RDm-groupk The text for the kth group in column m. Group text will appear to the left of whenever two paths meet.
RD-shade the background color (in hex format, e.g. #ABCDEF) of all headers. Use RDm-shade or RDmh-shade for individual headers. #F2F2F2
RDm-RD(m+1)-path set to no or 0 to omit the paths from round m to round m+1. yes
paramstyle set to numbered change the parameter name style of RDm-textk, RDm-seedk, RDm-teamk, and RDm-scorek to a numbered notation (|1=,|2=...). Set |seeds=yes add seeds. indexed
  1. ^ Does not currently work for paths under subheaders.
  2. ^ numbered may not be compatible with certain other features, e.g. boldwinner and groups.

Parameter hierarchy

  • Whenever there are multiple headers in a single column, more than one parameter may be assigned to a cell value. For example, in the following bracket, both |RD1-team3= and |RD1b-team1= can be used to assign the third team in the first column. By default, entries with subheader prefixes will override those without. In the below example, RD1b-team1 will override any value that has been set by RD1-team3.
Upper round
Lower round
RD1-team3 or RD1b-team1
  • Parameters used in articles take precedence over parameters used in the template itself. For example, suppose ArticleX used the template NTeamBracket, and suppose NTeamBracket had the parameter |RD1-seed1=1 set. If ArticleX implemented {{NTeamBracket|RD1-seed1=2}}, then the first team in round 1 would have a seed of 2.

Path codes

Path codes are entered in the form a-b, where a is associated match in the first column, and b is associated match in the second column. Path codes can be grouped; for example, (a,b)-c is equivalent to a-c, b-c. To add color, append :color to the end of a path, e.g. 3-5:red. Only one extra color can be used in a bracket.

Example Output
1-3
(1,5)-3
3-1,3-5:red

Examples

Note: These are only examples to illustrate parameters. Standard 4-team, 8-team, etc. brackets are better handled by Module:Team bracket.

Standard 4-team bracket

{{#invoke:Build bracket|main
| rounds=2

| col1-headers = 1
| col2-headers = 1

| col1-matches = 3,7
| col2-matches = 5

| col1-col2-paths = (3,7)-5

<!-- Defaults -->
| RD2 = Grand Final

| RD1-seed1 = 1
| RD1-seed3 = 2
}}
SemifinalsGrand Final
1
2

3-way bracket

{{#invoke:Build bracket|main
| rounds=2
| teams-per-match = 3

| col1-headers = 1
| col2-headers = 1

| col1-matches = 3,7,11
| col2-matches = 7

| col1-col2-paths = (3,7,11)-7
}}
SemifinalsFinal

Double-elimination bracket

{{#invoke:Build bracket|main
| rounds=4

| col1-headers = 1,7
| col2-headers = 1,7
| col3-headers = 7
| col4-headers = 1

| col1-matches = 4,11
| col2-matches = 3,10
| col3-matches = 9
| col4-matches = 6

| col1-col2-paths = 4-3, 11-10
| col2-col3-paths = 3-3, 10-9
| col3-col4-paths = (3,9)-6

<!-- Defaults -->
| RD1 = Upper round 1
| RD2 = Upper final
| RD3b = Lower final
}}
Upper round 1Upper finalFinal
Lower round 1Lower round 2Lower final

Crossing paths

{{#invoke:Build bracket|main
| rounds=2

| col1-matches = 3,7
| col2-matches = 3,7

| col1-col2-paths = 3-7, 7-3
| col1-col2-cross = 5
}}
SemifinalsFinal

Text

{{#invoke:Build bracket|main
| rounds=2
| col1-matches = 3,6
| col2-matches = 4.5
| col1-col2-paths = (3,6)-4.5

| RD1-text1 = Text 1
| RD1-text2 = Text 2
| RD2-text1 = Text 3
}}
SemifinalsFinal
Text 1
Text 3
Text 2

Groups

{{#invoke:Build bracket|main
| rounds=3
| col1-matches = 3,6,9,12
| col2-matches = 4.5,10.5
| col3-matches = 7.5

| col1-col2-paths = (3,6)-4.5, (9,12)-10.5
| col2-col3-paths = (4.5,10.5)-7.5

| RD1-group1 = Group 1
| RD1-group2 = Group 2
| RD2-group1 = Group 3
}}
QuarterfinalsSemifinalsFinal
Group 1
Group 3
Group 2

Legs and aggregate

{{#invoke:Build bracket|main
| rounds=3
| legs = 2
| RD1-legs = 3
| aggregate = y

| col1-matches = 3,6,9,12
| col2-matches = 4.5,10.5
| col3-matches = 7.5

| col1-col2-paths = (3,6)-4.5, (9,12)-10.5
| col2-col3-paths = (4.5,10.5)-7.5
}}
QuarterfinalsSemifinalsFinal

Byes

{{#invoke:Build bracket|main
| rounds=3
| RD1-byes = y

| col1-matches = 3,6,9,12
| col2-matches = 4.5,10.5
| col3-matches = 7.5

| col1-col2-paths = (3,6)-4.5, (9,12)-10.5
| col2-col3-paths = (4.5,10.5)-7.5

| RD1-team1 = Team 1
| RD1-team2 = Team 2
}}
QuarterfinalsSemifinalsFinal
Team 1
Team 2

Numbered parameters

{{#invoke:Build bracket|main
| rounds=2
| col1-matches = 3,7
| col2-matches = 5
| col1-col2-paths = (3,7)-5

| paramstyle = numbered
| seeds = yes

| Text 1 | 1 | Team 1 | 5 | 4 | Team 2 | 11
| Text 2 | 2 | Team 3 | 6 | 3 | Team 4 | 3

| Text 3 | 4 | Team 2 | 2 | 2 | Team 3 | 1 
}}
SemifinalsFinal
Text 1
1 Team 1 5
4 Team 2 11 Text 3
4 Team 2 2
Text 2 2 Team 3 1
2 Team 3 6
3 Team 4 3

Bold winner

{{#invoke:Build bracket|main
| rounds=2
| legs = 3
| boldwinner=y
| col1-matches = 3,7
| col2-matches = 5
| col1-col2-paths = (3,7)-5

| RD1-seed1 = 1 | RD1-team1 = Team 1 | RD1-score1-1 = 5  | RD1-score1-2 = 12 | RD1-score1-3 = 15
| RD1-seed2 = 4 | RD1-team2 = Team 2 | RD1-score2-1 = 11 | RD1-score2-2 = 10 | RD1-score2-3 = 4

| RD1-seed3 = 2 | RD1-team3 = Team 3 | RD1-score3-1 = 6  | RD1-score3-2 = 13 | RD1-score3-3 = {{ndash}}
| RD1-seed4 = 3 | RD1-team4 = Team 4 | RD1-score4-1 = 3  | RD1-score4-2 = 2  | RD1-score4-3 = {{ndash}}

| RD2-seed1 = 4 | RD2-team1 = Team 2 | RD2-score1-1 = 2  | RD2-score1-2 = 2  | RD2-score1-3 = 5
| RD2-seed2 = 2 | RD2-team2 = Team 3 | RD2-score2-1 = 1  | RD2-score2-2 = 7  | RD2-score2-3 = 2
}}
SemifinalsFinal
1Team 151215
4Team 211104
4Team 2225
2Team 3172
2Team 3613
3Team 432

See also

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 nowrap
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 unboldParenthetical(text)
    -- Replace wikilinks with unique placeholders
    local counter = 0
    local placeholders = {}
    text = text:gsub('%[%[(.-)%]%]', function(link)
        counter = counter + 1
        local placeholder = '__WIKILINK__' .. counter .. '__'
        placeholders[placeholder] = link
        return placeholder
    end)

    -- Apply <span style="font-weight:normal"></span> to parenthetical and bracketed text
    text = text:gsub('(%b())', '<span style="font-weight:normal">%1</span>')
               :gsub('(%b[])', '<span style="font-weight:normal">%1</span>')

    -- Restore the original wikilinks
    for placeholder, link in pairs(placeholders) do
        text = text:gsub(placeholder, '[[' .. link .. ']]')
    end

    return text
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
	cell:css('padding','0em 0.3em')
	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=unboldParenthetical(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][1][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][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 
		elseif tonumber(bargs('byes')) then
			if j<=tonumber(bargs('byes')) then
				byes[j][k]=true
			end
		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][1][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,bg,rowspan)
	local color = pathCell[j][i][k]['color']
	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][1]~=0 or a[k][1][2]~=0 or a[k][1][3]~=0 or a[k][1][4]~=0 then
		cell:css('border','solid '..color)
			:css('border-width',2*a[k][1][1]..'px '..2*a[k][1][2]..'px '..2*a[k][1][3]..'px '..2*a[k][1][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
	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][1][n]~=pathCell[j][a][k][1][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,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},['color']='black'}
			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
					pathCell[j][start+1][1][1][1] = 1
					pathCell[j][start+1][1]['color'] = paths[j][k]['color']
					if cross==0 then
						if hascross[j] then
							pathCell[j][start+1][2][1][1] = 1
							pathCell[j][start+1][2]['color'] = paths[j][k]['color']
							for i=start+1,stop do
								pathCell[j][i][2][1][2] = 1
								pathCell[j][i][2]['color'] = paths[j][k]['color']
							end
						else
							for i=start+1,stop do
								pathCell[j][i][1][1][2] = 1
								pathCell[j][i][1]['color'] = paths[j][k]['color']
							end
						end
					else
						crossCell[j][cross]['left'] = {1,paths[j][k]['color']}
						for i=start+1,cross-1 do 
							pathCell[j][i][1][1][2] = 1
							pathCell[j][i][1]['color'] = paths[j][k]['color']
							end
						for i=cross+2,stop do 
							pathCell[j][i][2][1][2] = 1 
							pathCell[j][i][2]['color'] = paths[j][k]['color']
							end	
					end
					pathCell[j][stop][3][1][3] = 1
					pathCell[j][stop][3]['color'] = paths[j][k]['color']
				elseif start>stop then
					if start>r then break end
					pathCell[j][stop+1][3][1][1] = 1
					pathCell[j][stop+1][3]['color'] = paths[j][k]['color']
					if cross==0 then
						if hascross[j] then
							for i=stop+1,start do
								pathCell[j][i][2][1][2] = 1
								pathCell[j][i][2]['color'] = paths[j][k]['color']
							end
							pathCell[j][start][2][1][3] = 1
							pathCell[j][start][2]['color'] = paths[j][k]['color']
						else
							for i=stop+1,start do
								pathCell[j][i][1][1][2] = 1
								pathCell[j][i][1]['color'] = paths[j][k]['color']
							end
						end
					else
						crossCell[j][cross]['right'] = {1,paths[j][k]['color']}
						for i=stop+1,cross-1 do 
							pathCell[j][i][2][1][2] = 1 
							pathCell[j][i][2]['color'] = paths[j][k]['color']
							end
						for i=cross+2,start do 
							pathCell[j][i][1][1][2] = 1 
							pathCell[j][i][1]['color'] = paths[j][k]['color']
							end	
					end
					pathCell[j][start][1][1][3] = 1
					pathCell[j][start][1]['color'] = paths[j][k]['color']
				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][1][3]==0 then
				pathCell[j][i][1][1][3] = 1
				pathCell[j][i][2][1][3] = 1
				pathCell[j][i][3][1][3] = 1
				pathCell[j][i][1]['color'] = color
				pathCell[j][i][2]['color'] = color
				pathCell[j][i][3]['color'] = color
				if pathCell[j][i+1][1][1][1]==0 then
					pathCell[j][i+1][1][1][1] = 0.5
					pathCell[j][i+1][2][1][1] = 0.5
					pathCell[j][i+1][3][1][1] = 0.5
					pathCell[j][i+1][1]['color'] = color
					pathCell[j][i+1][2]['color'] = color
					pathCell[j][i+1][3]['color'] = color
				end
			elseif pathCell[j][i+1][1][1][1]==0 then
				pathCell[j][i+1][1][1][1] = 1
				pathCell[j][i+1][1]['color'] = color
				if hascross[j] then
					pathCell[j][i+1][2][1][1] = 1
					pathCell[j][i+1][2]['color'] = color
				end
				pathCell[j][i+1][3][1][1] = 1
				pathCell[j][i+1][3]['color'] = color
			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][1][3]==1 and pathCell[j][i+1][1][1][1]==0 then
				pathCell[j][i+1][1][1][1] = 0.5*pathCell[j][i][1][1][3]
				pathCell[j][i+1][2][1][1] = 0.5*pathCell[j][i][2][1][3]
				pathCell[j][i+1][1]['color'] = pathCell[j][i][1]['color']
				pathCell[j][i+1][2]['color'] = pathCell[j][i][2]['color']
			elseif i<r and pathCell[j][i][1][1][3]==0 and pathCell[j][i+1][1][1][1]==1 then
				pathCell[j][i][1][1][3] = pathCell[j][i+1][1][1][1]
				pathCell[j][i][2][1][3] = pathCell[j][i+1][2][1][1]
				pathCell[j][i+1][1][1][1] = 0.5*pathCell[j][i][1][1][3]
				pathCell[j][i+1][2][1][1] = 0.5*pathCell[j][i][2][1][3]
				pathCell[j][i][1]['color'] = pathCell[j][i+1][1]['color']
				pathCell[j][i][2]['color'] = pathCell[j][i+1][2]['color']
			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][1][3]==1 and pathCell[j][i+1][3][1][1]==1 and pathCell[j][i][3]['color']==pathCell[j][i+1][3]['color'] then
				pathCell[j][i+1][3][1][1] = 0.5*pathCell[j][i][3][1][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][1][3]~=0 and pathCell[j+1][i][1][1][3]~=0) or (pathCell[j][i+1][3][1][1]~=0 and pathCell[j+1][i+1][1][1][1]~=0) then
					if pathCell[j+1][i][1][1][3]==pathCell[j+1][i][3][1][3] and pathCell[j+1][i+1][1][1][1]==pathCell[j+1][i+1][3][1][1] then
						straightpath=true
					end
					pathCell[j+1][i][1][1][3]=pathCell[j][i][3][1][3]
					pathCell[j+1][i+1][1][1][1]=pathCell[j][i+1][3][1][1]
					pathCell[j+1][i][2][1][3]=pathCell[j][i][3][1][3]
					pathCell[j+1][i+1][2][1][1]=pathCell[j][i+1][3][1][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][1][3]=pathCell[j+1][i][1][1][3]
						pathCell[j+1][i+1][3][1][1]=pathCell[j+1][i+1][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][1][3]==1 or pathCell[j][i+1][3][1][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
	nowrap = 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
	if pargs.nowrap=='n' or pargs.nowrap=='no' then nowrap=false 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 0em 1em')
			
	if nowrap then 
		tbl:css('white-space','nowrap') 
	end
	
	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('agg',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