<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-GB">
	<id>https://nssportwiki.com/index.php?action=history&amp;feed=atom&amp;title=Module%3ABuild_bracket%2FLogic</id>
	<title>Module:Build bracket/Logic - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://nssportwiki.com/index.php?action=history&amp;feed=atom&amp;title=Module%3ABuild_bracket%2FLogic"/>
	<link rel="alternate" type="text/html" href="https://nssportwiki.com/index.php?title=Module:Build_bracket/Logic&amp;action=history"/>
	<updated>2026-08-15T12:25:37Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.40.1</generator>
	<entry>
		<id>https://nssportwiki.com/index.php?title=Module:Build_bracket/Logic&amp;diff=76414&amp;oldid=prev</id>
		<title>Osarius: 1 revision imported</title>
		<link rel="alternate" type="text/html" href="https://nssportwiki.com/index.php?title=Module:Build_bracket/Logic&amp;diff=76414&amp;oldid=prev"/>
		<updated>2026-07-24T10:50:38Z</updated>

		<summary type="html">&lt;p&gt;1 revision imported&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en-GB&quot;&gt;
				&lt;td colspan=&quot;1&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;1&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Revision as of 10:50, 24 July 2026&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-notice&quot; lang=&quot;en-GB&quot;&gt;&lt;div class=&quot;mw-diff-empty&quot;&gt;(No difference)&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</summary>
		<author><name>Osarius</name></author>
	</entry>
	<entry>
		<id>https://nssportwiki.com/index.php?title=Module:Build_bracket/Logic&amp;diff=76413&amp;oldid=prev</id>
		<title>wikipedia&gt;Pbrks: allow fraction scores (e.g. ½)</title>
		<link rel="alternate" type="text/html" href="https://nssportwiki.com/index.php?title=Module:Build_bracket/Logic&amp;diff=76413&amp;oldid=prev"/>
		<updated>2025-08-23T21:22:19Z</updated>

		<summary type="html">&lt;p&gt;allow fraction scores (e.g. ½)&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;local Logic = {}&lt;br /&gt;
&lt;br /&gt;
-- =======================&lt;br /&gt;
-- 1) STDLIB ALIASES&lt;br /&gt;
-- =======================&lt;br /&gt;
local m_max = math.max&lt;br /&gt;
local m_ceil = math.ceil&lt;br /&gt;
local s_match = string.match&lt;br /&gt;
local s_find = string.find&lt;br /&gt;
&lt;br /&gt;
-- =======================&lt;br /&gt;
-- 2) MODULE UPVALUES&lt;br /&gt;
-- =======================&lt;br /&gt;
local state, config, Helpers, StateChecks&lt;br /&gt;
local isempty, notempty, teamLegs&lt;br /&gt;
&lt;br /&gt;
local function bind(_state, _config, _Helpers, _StateChecks)&lt;br /&gt;
    state, config, Helpers, StateChecks = _state, _config, _Helpers, _StateChecks&lt;br /&gt;
    isempty = _Helpers and _Helpers.isempty or function(v)&lt;br /&gt;
            return v == nil or v == &amp;quot;&amp;quot;&lt;br /&gt;
        end&lt;br /&gt;
    notempty = _Helpers and _Helpers.notempty or function(v)&lt;br /&gt;
            return v ~= nil and v ~= &amp;quot;&amp;quot;&lt;br /&gt;
        end&lt;br /&gt;
    teamLegs = _StateChecks and _StateChecks.teamLegs&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- =======================&lt;br /&gt;
-- 3) SMALL UTILITIES&lt;br /&gt;
-- =======================&lt;br /&gt;
-- Map common Unicode fraction characters to decimal&lt;br /&gt;
local fraction_map = {&lt;br /&gt;
    [&amp;quot;½&amp;quot;] = &amp;quot;.5&amp;quot;,&lt;br /&gt;
    [&amp;quot;⅓&amp;quot;] = &amp;quot;.333&amp;quot;,&lt;br /&gt;
    [&amp;quot;⅔&amp;quot;] = &amp;quot;.667&amp;quot;,&lt;br /&gt;
    [&amp;quot;¼&amp;quot;] = &amp;quot;.25&amp;quot;,&lt;br /&gt;
    [&amp;quot;¾&amp;quot;] = &amp;quot;.75&amp;quot;,&lt;br /&gt;
    [&amp;quot;⅕&amp;quot;] = &amp;quot;.2&amp;quot;,&lt;br /&gt;
    [&amp;quot;⅖&amp;quot;] = &amp;quot;.4&amp;quot;,&lt;br /&gt;
    [&amp;quot;⅗&amp;quot;] = &amp;quot;.6&amp;quot;,&lt;br /&gt;
    [&amp;quot;⅘&amp;quot;] = &amp;quot;.8&amp;quot;,&lt;br /&gt;
    [&amp;quot;⅙&amp;quot;] = &amp;quot;.167&amp;quot;,&lt;br /&gt;
    [&amp;quot;⅚&amp;quot;] = &amp;quot;.833&amp;quot;,&lt;br /&gt;
    [&amp;quot;⅛&amp;quot;] = &amp;quot;.125&amp;quot;,&lt;br /&gt;
    [&amp;quot;⅜&amp;quot;] = &amp;quot;.375&amp;quot;,&lt;br /&gt;
    [&amp;quot;⅝&amp;quot;] = &amp;quot;.625&amp;quot;,&lt;br /&gt;
    [&amp;quot;⅞&amp;quot;] = &amp;quot;.875&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
-- Normalize fractions like &amp;quot;1½&amp;quot; to &amp;quot;1.5&amp;quot;&lt;br /&gt;
local function normalizeFractions(s)&lt;br /&gt;
    -- Replace integer + fraction (e.g. &amp;quot;1½&amp;quot; → &amp;quot;1.5&amp;quot;)&lt;br /&gt;
    s =&lt;br /&gt;
        s:gsub(&lt;br /&gt;
        &amp;quot;(%d+)%s*([%z\1-\127\194-\244][\128-\191]*)&amp;quot;,&lt;br /&gt;
        function(d, frac)&lt;br /&gt;
            local repl = fraction_map[frac]&lt;br /&gt;
            if repl then&lt;br /&gt;
                return d .. repl&lt;br /&gt;
            else&lt;br /&gt;
                return d .. frac&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    -- Replace standalone fraction (e.g. &amp;quot;½&amp;quot; → &amp;quot;0.5&amp;quot;)&lt;br /&gt;
    s =&lt;br /&gt;
        s:gsub(&lt;br /&gt;
        &amp;quot;([%z\1-\127\194-\244][\128-\191]*)&amp;quot;,&lt;br /&gt;
        function(frac)&lt;br /&gt;
            local repl = fraction_map[frac]&lt;br /&gt;
            if repl then&lt;br /&gt;
                return &amp;quot;0&amp;quot; .. repl&lt;br /&gt;
            else&lt;br /&gt;
                return frac&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    return s&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Leading integer from a value; supports string or number; nil if none.&lt;br /&gt;
local function numlead(v)&lt;br /&gt;
    if v == nil then&lt;br /&gt;
        return nil&lt;br /&gt;
    end&lt;br /&gt;
    if type(v) == &amp;quot;number&amp;quot; then&lt;br /&gt;
        return v&lt;br /&gt;
    end&lt;br /&gt;
    local s = tostring(v)&lt;br /&gt;
    if s == &amp;quot;&amp;quot; then&lt;br /&gt;
        return nil&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    -- 1) strip leading spaces and wiki bold/italic quotes (&amp;#039;&amp;#039; or &amp;#039;&amp;#039;&amp;#039;)&lt;br /&gt;
    s = s:match(&amp;quot;^%s*&amp;#039;*%s*(.*)&amp;quot;) or s&lt;br /&gt;
&lt;br /&gt;
    -- 2) strip *leading* simple wrappers (tags/templates/links), repeatedly, but only if they occur before the first digit.&lt;br /&gt;
    local advanced = true -- set false if you want the minimal version&lt;br /&gt;
    if advanced then&lt;br /&gt;
        local changed = true&lt;br /&gt;
        while changed do&lt;br /&gt;
            changed = false&lt;br /&gt;
            local s2 = s:gsub(&amp;quot;^%s*&amp;lt;[^&amp;gt;]-&amp;gt;%s*&amp;quot;, &amp;quot;&amp;quot;, 1) -- leading HTML tag&lt;br /&gt;
            if s2 ~= s then&lt;br /&gt;
                s, changed = s2, true&lt;br /&gt;
            end&lt;br /&gt;
            s2 = s:gsub(&amp;quot;^%s*{{.-}}%s*&amp;quot;, &amp;quot;&amp;quot;, 1) -- leading template&lt;br /&gt;
            if s2 ~= s then&lt;br /&gt;
                s, changed = s2, true&lt;br /&gt;
            end&lt;br /&gt;
            s2 = s:gsub(&amp;quot;^%s*%[%[[^%]]-%]%]%s*&amp;quot;, &amp;quot;&amp;quot;, 1) -- leading wikilink&lt;br /&gt;
            if s2 ~= s then&lt;br /&gt;
                s, changed = s2, true&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    -- 3) capture leading digits only (stops at first non-digit)&lt;br /&gt;
    s = normalizeFractions(s)&lt;br /&gt;
    local n = s:match(&amp;quot;^(%d+%.?%d*)&amp;quot;)&lt;br /&gt;
    return n and tonumber(n) or nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ===========================&lt;br /&gt;
-- 4) MATCH GROUPING (PER RD)&lt;br /&gt;
-- ===========================&lt;br /&gt;
local function _matchGroups()&lt;br /&gt;
    state.matchgroup = state.matchgroup or {}&lt;br /&gt;
    local MINC, C, R = config.minc, config.c, config.r&lt;br /&gt;
    for j = MINC, C do&lt;br /&gt;
        local mgj = {}&lt;br /&gt;
        state.matchgroup[j] = mgj&lt;br /&gt;
&lt;br /&gt;
        local tpm = tonumber(state.teamsPerMatch[j]) or 2&lt;br /&gt;
        if tpm &amp;lt; 1 then&lt;br /&gt;
            tpm = 2&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        local col = state.entries[j]&lt;br /&gt;
        if col then&lt;br /&gt;
            for i = 1, R do&lt;br /&gt;
                local e = col[i]&lt;br /&gt;
                if e and e.ctype == &amp;quot;team&amp;quot; then&lt;br /&gt;
                    local idx = tonumber(e.index) or tonumber(e.altindex) or i&lt;br /&gt;
                    local g = m_ceil(idx / tpm)&lt;br /&gt;
                    mgj[i] = g&lt;br /&gt;
                    e.group = g&lt;br /&gt;
                end&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Build ordered lists once per round: gid -&amp;gt; {row indices}&lt;br /&gt;
local function buildGroupsForRound(j, R)&lt;br /&gt;
    local groups = {}&lt;br /&gt;
    local mg = (state.matchgroup and state.matchgroup[j]) or {}&lt;br /&gt;
    local col = state.entries[j]&lt;br /&gt;
    if not col then&lt;br /&gt;
        return groups&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    for i = 1, R do&lt;br /&gt;
        local e = col[i]&lt;br /&gt;
        if e and e.ctype == &amp;quot;team&amp;quot; then&lt;br /&gt;
            local gid = mg[i]&lt;br /&gt;
            if gid ~= nil then&lt;br /&gt;
                local t = groups[gid]&lt;br /&gt;
                if not t then&lt;br /&gt;
                    t = {}&lt;br /&gt;
                    groups[gid] = t&lt;br /&gt;
                end&lt;br /&gt;
                t[#t + 1] = i&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    return groups&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Pre-parse leg numbers for each team (per round), only when agg &amp;amp; &amp;gt;1 legs.&lt;br /&gt;
-- Returns: i -&amp;gt; { [l] = number|nil }&lt;br /&gt;
local function preparseLegs(j, R)&lt;br /&gt;
    local legNums = {}&lt;br /&gt;
    local col = state.entries[j]&lt;br /&gt;
    if not col then&lt;br /&gt;
        return legNums&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    for i = 1, R do&lt;br /&gt;
        local e = col[i]&lt;br /&gt;
        if e and e.ctype == &amp;quot;team&amp;quot; then&lt;br /&gt;
            local L = teamLegs(j, i)&lt;br /&gt;
            if config.aggregate and L &amp;gt; 1 and e.score then&lt;br /&gt;
                local t = {}&lt;br /&gt;
                for l = 1, L do&lt;br /&gt;
                    t[l] = numlead(e.score[l])&lt;br /&gt;
                end&lt;br /&gt;
                legNums[i] = t&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    return legNums&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ==========================================&lt;br /&gt;
-- 5) COMPUTE AGGREGATES (score/legs/sets)&lt;br /&gt;
-- ==========================================&lt;br /&gt;
local function _computeAggregate()&lt;br /&gt;
    if config.aggregate_mode == &amp;quot;off&amp;quot; or config.aggregate_mode == &amp;quot;manual&amp;quot; then&lt;br /&gt;
        return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local MINC, C, R = config.minc, config.c, config.r&lt;br /&gt;
    local modeLow = (config.boldwinner_mode == &amp;quot;low&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    for j = MINC, C do&lt;br /&gt;
        local groups = buildGroupsForRound(j, R)&lt;br /&gt;
        local legNums = preparseLegs(j, R)&lt;br /&gt;
&lt;br /&gt;
        if config.aggregate_mode == &amp;quot;score&amp;quot; then&lt;br /&gt;
            -- Sum per-leg scores; operate directly on parsed legNums.&lt;br /&gt;
            for i, nums in pairs(legNums) do&lt;br /&gt;
                local e = state.entries[j][i]&lt;br /&gt;
                if e and e.ctype == &amp;quot;team&amp;quot; and config.aggregate and teamLegs(j, i) &amp;gt; 1 then&lt;br /&gt;
                    local sc = e.score&lt;br /&gt;
                    if sc and isempty(sc.agg) then&lt;br /&gt;
                        local sum = 0&lt;br /&gt;
                        for _, v in ipairs(nums) do&lt;br /&gt;
                            if v then&lt;br /&gt;
                                sum = sum + v&lt;br /&gt;
                            end&lt;br /&gt;
                        end&lt;br /&gt;
                        sc.agg = tostring(sum)&lt;br /&gt;
                    end&lt;br /&gt;
                end&lt;br /&gt;
            end&lt;br /&gt;
        else&lt;br /&gt;
            -- &amp;#039;sets&amp;#039;/&amp;#039;legs&amp;#039;: count wins per-leg using high/low rule; ties yield no win.&lt;br /&gt;
            for _, members in pairs(groups) do&lt;br /&gt;
                local wins = {} -- row index -&amp;gt; wins&lt;br /&gt;
&lt;br /&gt;
                -- Comparable leg count across the group = min(#numeric arrays)&lt;br /&gt;
                local commonLegs = math.huge&lt;br /&gt;
                for _, i in ipairs(members) do&lt;br /&gt;
                    local nums = legNums[i]&lt;br /&gt;
                    local L = (nums and #nums) or 0&lt;br /&gt;
                    if L == 0 then&lt;br /&gt;
                        commonLegs = 0&lt;br /&gt;
                        break&lt;br /&gt;
                    end&lt;br /&gt;
                    if L &amp;lt; commonLegs then&lt;br /&gt;
                        commonLegs = L&lt;br /&gt;
                    end&lt;br /&gt;
                end&lt;br /&gt;
&lt;br /&gt;
                for l = 1, commonLegs do&lt;br /&gt;
                    local allNumeric = true&lt;br /&gt;
                    for _, i in ipairs(members) do&lt;br /&gt;
                        local v = legNums[i] and legNums[i][l]&lt;br /&gt;
                        if v == nil then&lt;br /&gt;
                            allNumeric = false&lt;br /&gt;
                            break&lt;br /&gt;
                        end&lt;br /&gt;
                    end&lt;br /&gt;
                    if allNumeric then&lt;br /&gt;
                        local best, bestIdx, tie&lt;br /&gt;
                        for _, i in ipairs(members) do&lt;br /&gt;
                            local v = legNums[i][l]&lt;br /&gt;
                            if best == nil then&lt;br /&gt;
                                best, bestIdx, tie = v, i, false&lt;br /&gt;
                            else&lt;br /&gt;
                                if (modeLow and v &amp;lt; best) or (not modeLow and v &amp;gt; best) then&lt;br /&gt;
                                    best, bestIdx, tie = v, i, false&lt;br /&gt;
                                elseif v == best then&lt;br /&gt;
                                    tie = true&lt;br /&gt;
                                end&lt;br /&gt;
                            end&lt;br /&gt;
                        end&lt;br /&gt;
                        if not tie and bestIdx then&lt;br /&gt;
                            wins[bestIdx] = (wins[bestIdx] or 0) + 1&lt;br /&gt;
                        end&lt;br /&gt;
                    end&lt;br /&gt;
                end&lt;br /&gt;
&lt;br /&gt;
                -- Write aggregates if still empty&lt;br /&gt;
                for _, i in ipairs(members) do&lt;br /&gt;
                    local e = state.entries[j][i]&lt;br /&gt;
                    if e and e.ctype == &amp;quot;team&amp;quot; and config.aggregate and teamLegs(j, i) &amp;gt; 1 then&lt;br /&gt;
                        local sc = e.score&lt;br /&gt;
                        if sc and isempty(sc.agg) then&lt;br /&gt;
                            sc.agg = tostring(wins[i] or 0)&lt;br /&gt;
                        end&lt;br /&gt;
                    end&lt;br /&gt;
                end&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ==========================================&lt;br /&gt;
-- 6) BOLD WINNERS (per cells &amp;amp; whole rows)&lt;br /&gt;
-- ==========================================&lt;br /&gt;
local function _boldWinner()&lt;br /&gt;
    local function isWin(mine, theirs)&lt;br /&gt;
        return modeLow and (mine &amp;lt; theirs) or (not modeLow and mine &amp;gt; theirs)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function isAggWin(mine, theirs, colKey)&lt;br /&gt;
        -- For aggregate counts (legs/sets won), higher is always better&lt;br /&gt;
        if colKey == &amp;quot;agg&amp;quot; and (config.aggregate_mode ~= &amp;quot;score&amp;quot;) then&lt;br /&gt;
            return mine &amp;gt; theirs&lt;br /&gt;
        end&lt;br /&gt;
        return isWin(mine, theirs)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if not config or config.boldwinner_mode == &amp;quot;off&amp;quot; then&lt;br /&gt;
        -- Normalize all weights (defensive; avoids leftovers across calls)&lt;br /&gt;
        for j = config.minc, config.c do&lt;br /&gt;
            for i = 1, config.r do&lt;br /&gt;
                local e = state.entries[j] and state.entries[j][i]&lt;br /&gt;
                if e and e.ctype == &amp;quot;team&amp;quot; then&lt;br /&gt;
                    e.weight = &amp;quot;normal&amp;quot;&lt;br /&gt;
                    if e.score and e.score.weight then&lt;br /&gt;
                        for k, _ in pairs(e.score.weight) do&lt;br /&gt;
                            e.score.weight[k] = &amp;quot;normal&amp;quot;&lt;br /&gt;
                        end&lt;br /&gt;
                    end&lt;br /&gt;
                end&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local MINC, C, R = config.minc, config.c, config.r&lt;br /&gt;
    local modeLow = (config.boldwinner_mode == &amp;quot;low&amp;quot;)&lt;br /&gt;
    local aggOnly = config.boldwinner_aggonly&lt;br /&gt;
&lt;br /&gt;
    local function isWin(mine, theirs)&lt;br /&gt;
        return modeLow and (mine &amp;lt; theirs) or (not modeLow and mine &amp;gt; theirs)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function isAggWin(mine, theirs, colKey)&lt;br /&gt;
        if colKey == &amp;quot;agg&amp;quot; and config.aggregate_mode == &amp;quot;sets&amp;quot; then&lt;br /&gt;
            -- Sets/legs won: larger count wins regardless of low/high sport&lt;br /&gt;
            return mine &amp;gt; theirs&lt;br /&gt;
        end&lt;br /&gt;
        return isWin(mine, theirs)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function hasAllScores(e, legs)&lt;br /&gt;
        local sc = e.score&lt;br /&gt;
        for l = 1, legs do&lt;br /&gt;
            local sv = sc and sc[l]&lt;br /&gt;
            if isempty(sv) or s_find(sv or &amp;quot;&amp;quot;, &amp;quot;nbsp&amp;quot;) then -- use alias&lt;br /&gt;
                return false&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
        return true&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    for j = MINC, C do&lt;br /&gt;
        local groups = buildGroupsForRound(j, R)&lt;br /&gt;
&lt;br /&gt;
        -- Reset counters &amp;amp; ensure score/weight tables exist&lt;br /&gt;
        for i = 1, R do&lt;br /&gt;
            local e = state.entries[j] and state.entries[j][i]&lt;br /&gt;
            if e and e.ctype == &amp;quot;team&amp;quot; then&lt;br /&gt;
                e.wins, e.aggwins = 0, 0&lt;br /&gt;
                e.score = e.score or {}&lt;br /&gt;
                e.score.weight = e.score.weight or {}&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        for _, members in pairs(groups) do&lt;br /&gt;
            -- Parse per-leg and aggregate numbers ONCE for this group (works for 1+ legs)&lt;br /&gt;
            local perLegNum = {} -- row -&amp;gt; { l -&amp;gt; number|nil }&lt;br /&gt;
            local aggNum = {} -- row -&amp;gt; number|nil&lt;br /&gt;
&lt;br /&gt;
            for _, i in ipairs(members) do&lt;br /&gt;
                local e = state.entries[j][i]&lt;br /&gt;
                local legs = teamLegs(j, i)&lt;br /&gt;
                local arr = {}&lt;br /&gt;
                for l = 1, legs do&lt;br /&gt;
                    arr[l] = numlead(e.score and e.score[l])&lt;br /&gt;
                end&lt;br /&gt;
                perLegNum[i] = arr&lt;br /&gt;
                aggNum[i] = numlead(e.score and e.score.agg)&lt;br /&gt;
            end&lt;br /&gt;
&lt;br /&gt;
            -- Per-score bolding (skip entirely if agg-only)&lt;br /&gt;
            if not aggOnly then&lt;br /&gt;
                -- iterate to the max leg count in the group&lt;br /&gt;
                local maxL = 0&lt;br /&gt;
                for _, i in ipairs(members) do&lt;br /&gt;
                    local L = teamLegs(j, i)&lt;br /&gt;
                    if L &amp;gt; maxL then&lt;br /&gt;
                        maxL = L&lt;br /&gt;
                    end&lt;br /&gt;
                end&lt;br /&gt;
&lt;br /&gt;
                for l = 1, maxL do&lt;br /&gt;
                    local allNumeric = true&lt;br /&gt;
                    local best, bestIdx, tie&lt;br /&gt;
                    for _, i in ipairs(members) do&lt;br /&gt;
                        local v = perLegNum[i] and perLegNum[i][l]&lt;br /&gt;
                        if v == nil then&lt;br /&gt;
                            allNumeric = false&lt;br /&gt;
                            break&lt;br /&gt;
                        end&lt;br /&gt;
                        if best == nil then&lt;br /&gt;
                            best, bestIdx, tie = v, i, false&lt;br /&gt;
                        else&lt;br /&gt;
                            if (modeLow and v &amp;lt; best) or (not modeLow and v &amp;gt; best) then&lt;br /&gt;
                                best, bestIdx, tie = v, i, false&lt;br /&gt;
                            elseif v == best then&lt;br /&gt;
                                tie = true&lt;br /&gt;
                            end&lt;br /&gt;
                        end&lt;br /&gt;
                    end&lt;br /&gt;
&lt;br /&gt;
                    if allNumeric and not tie and bestIdx then&lt;br /&gt;
                        -- set the winner bold + increment wins&lt;br /&gt;
                        local e = state.entries[j][bestIdx]&lt;br /&gt;
                        e.score.weight[l] = &amp;quot;bold&amp;quot;&lt;br /&gt;
                        e.wins = (e.wins or 0) + 1&lt;br /&gt;
                        -- normalize others on this leg&lt;br /&gt;
                        for _, i in ipairs(members) do&lt;br /&gt;
                            if i ~= bestIdx then&lt;br /&gt;
                                state.entries[j][i].score.weight[l] = &amp;quot;normal&amp;quot;&lt;br /&gt;
                            end&lt;br /&gt;
                        end&lt;br /&gt;
                    else&lt;br /&gt;
                        -- no unique winner: normalize everyone for this leg&lt;br /&gt;
                        for _, i in ipairs(members) do&lt;br /&gt;
                            state.entries[j][i].score.weight[l] = &amp;quot;normal&amp;quot;&lt;br /&gt;
                        end&lt;br /&gt;
                    end&lt;br /&gt;
                end&lt;br /&gt;
            end&lt;br /&gt;
&lt;br /&gt;
            -- Aggregate column (if configured &amp;amp; multi-leg)&lt;br /&gt;
            do&lt;br /&gt;
                local needAgg = false&lt;br /&gt;
                for _, i in ipairs(members) do&lt;br /&gt;
                    if config.aggregate and teamLegs(j, i) &amp;gt; 1 then&lt;br /&gt;
                        needAgg = true&lt;br /&gt;
                        break&lt;br /&gt;
                    end&lt;br /&gt;
                end&lt;br /&gt;
&lt;br /&gt;
                if needAgg then&lt;br /&gt;
                    local allNumeric = true&lt;br /&gt;
                    local best, bestIdx, tie&lt;br /&gt;
&lt;br /&gt;
                    -- comparator: for legs/sets counts, higher always wins&lt;br /&gt;
                    local function betterAgg(a, b)&lt;br /&gt;
                        if config.aggregate_mode ~= &amp;quot;score&amp;quot; then&lt;br /&gt;
                            return a &amp;gt; b&lt;br /&gt;
                        else&lt;br /&gt;
                            return (modeLow and a &amp;lt; b) or (not modeLow and a &amp;gt; b)&lt;br /&gt;
                        end&lt;br /&gt;
                    end&lt;br /&gt;
&lt;br /&gt;
                    for _, i in ipairs(members) do&lt;br /&gt;
                        local v = aggNum[i]&lt;br /&gt;
                        if v == nil then&lt;br /&gt;
                            allNumeric = false&lt;br /&gt;
                            break&lt;br /&gt;
                        end&lt;br /&gt;
                        if best == nil then&lt;br /&gt;
                            best, bestIdx, tie = v, i, false&lt;br /&gt;
                        else&lt;br /&gt;
                            if betterAgg(v, best) then&lt;br /&gt;
                                best, bestIdx, tie = v, i, false&lt;br /&gt;
                            elseif v == best then&lt;br /&gt;
                                tie = true&lt;br /&gt;
                            end&lt;br /&gt;
                        end&lt;br /&gt;
                    end&lt;br /&gt;
&lt;br /&gt;
                    if allNumeric and not tie and bestIdx then&lt;br /&gt;
                        local e = state.entries[j][bestIdx]&lt;br /&gt;
                        e.score.weight.agg = &amp;quot;bold&amp;quot;&lt;br /&gt;
                        e.aggwins = 1&lt;br /&gt;
                        for _, i in ipairs(members) do&lt;br /&gt;
                            if i ~= bestIdx then&lt;br /&gt;
                                local o = state.entries[j][i]&lt;br /&gt;
                                o.score.weight.agg = &amp;quot;normal&amp;quot;&lt;br /&gt;
                                o.aggwins = 0&lt;br /&gt;
                            end&lt;br /&gt;
                        end&lt;br /&gt;
                    else&lt;br /&gt;
                        for _, i in ipairs(members) do&lt;br /&gt;
                            local e = state.entries[j][i]&lt;br /&gt;
                            if e.score then&lt;br /&gt;
                                e.score.weight.agg = &amp;quot;normal&amp;quot;&lt;br /&gt;
                            end&lt;br /&gt;
                            e.aggwins = 0&lt;br /&gt;
                        end&lt;br /&gt;
                    end&lt;br /&gt;
                end&lt;br /&gt;
            end&lt;br /&gt;
&lt;br /&gt;
            -- Whole-team bolding (skip if agg-only so only agg cell bolds)&lt;br /&gt;
            if not aggOnly then&lt;br /&gt;
                for _, i in ipairs(members) do&lt;br /&gt;
                    local e = state.entries[j][i]&lt;br /&gt;
                    local legs = teamLegs(j, i)&lt;br /&gt;
                    local useAggregate = config.aggregate and legs &amp;gt; 1&lt;br /&gt;
                    local winsKey = useAggregate and &amp;quot;aggwins&amp;quot; or &amp;quot;wins&amp;quot;&lt;br /&gt;
&lt;br /&gt;
                    if not useAggregate then&lt;br /&gt;
                        if (e[winsKey] or 0) &amp;gt; legs / 2 then&lt;br /&gt;
                            e.weight = &amp;quot;bold&amp;quot;&lt;br /&gt;
                        else&lt;br /&gt;
                            e.weight = hasAllScores(e, legs) and &amp;quot;bold&amp;quot; or &amp;quot;normal&amp;quot;&lt;br /&gt;
                        end&lt;br /&gt;
                    end&lt;br /&gt;
&lt;br /&gt;
                    -- Must strictly beat any opponent on winsKey&lt;br /&gt;
                    for _, oi in ipairs(members) do&lt;br /&gt;
                        if oi ~= i then&lt;br /&gt;
                            local opp = state.entries[j][oi]&lt;br /&gt;
                            if (e[winsKey] or 0) &amp;lt;= tonumber(opp[winsKey] or 0) then&lt;br /&gt;
                                e.weight = &amp;quot;normal&amp;quot;&lt;br /&gt;
                                break&lt;br /&gt;
                            end&lt;br /&gt;
                        end&lt;br /&gt;
                    end&lt;br /&gt;
&lt;br /&gt;
                    if useAggregate then&lt;br /&gt;
                        -- when using aggregate, team weight follows aggwins comparison&lt;br /&gt;
                        e.weight = ((e[winsKey] or 0) &amp;gt; 0) and &amp;quot;bold&amp;quot; or &amp;quot;normal&amp;quot;&lt;br /&gt;
                        for _, oi in ipairs(members) do&lt;br /&gt;
                            if oi ~= i then&lt;br /&gt;
                                local opp = state.entries[j][oi]&lt;br /&gt;
                                if (e[winsKey] or 0) &amp;lt;= tonumber(opp[winsKey] or 0) then&lt;br /&gt;
                                    e.weight = &amp;quot;normal&amp;quot;&lt;br /&gt;
                                    break&lt;br /&gt;
                                end&lt;br /&gt;
                            end&lt;br /&gt;
                        end&lt;br /&gt;
                    end&lt;br /&gt;
                end&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ==============================&lt;br /&gt;
-- 7) UPDATE PER-ROUND MAX LEGS&lt;br /&gt;
-- ==============================&lt;br /&gt;
local function _updateMaxLegs()&lt;br /&gt;
    local MINC, C, R = config.minc, config.c, config.r&lt;br /&gt;
    for j = MINC, C do&lt;br /&gt;
        local col = state.entries[j]&lt;br /&gt;
        local rj = state.rlegs[j]&lt;br /&gt;
        local mj = rj&lt;br /&gt;
        if col then&lt;br /&gt;
            for i = 1, R do&lt;br /&gt;
                local e = col[i]&lt;br /&gt;
                if e then&lt;br /&gt;
                    if notempty(e.legs) then&lt;br /&gt;
                        mj = m_max(rj, e.legs)&lt;br /&gt;
                    end&lt;br /&gt;
                    if config.autolegs and e.score then&lt;br /&gt;
                        local l = 1&lt;br /&gt;
                        while e.score[l] and not isempty(e.score[l]) do&lt;br /&gt;
                            l = l + 1&lt;br /&gt;
                        end&lt;br /&gt;
                        mj = m_max(mj, l - 1)&lt;br /&gt;
                    end&lt;br /&gt;
                end&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
        state.maxlegs[j] = mj&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ==============&lt;br /&gt;
-- 8) PUBLIC API&lt;br /&gt;
-- ==============&lt;br /&gt;
function Logic.matchGroups(_state, _config)&lt;br /&gt;
    bind(_state, _config)&lt;br /&gt;
    _matchGroups()&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Logic.computeAggregate(_state, _config, _Helpers, _StateChecks)&lt;br /&gt;
    bind(_state, _config, _Helpers, _StateChecks)&lt;br /&gt;
    _computeAggregate()&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Logic.boldWinner(_state, _config, _Helpers, _StateChecks)&lt;br /&gt;
    bind(_state, _config, _Helpers, _StateChecks)&lt;br /&gt;
    _boldWinner()&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Logic.updateMaxLegs(_state, _config, _Helpers)&lt;br /&gt;
    bind(_state, _config, _Helpers)&lt;br /&gt;
    _updateMaxLegs()&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return Logic&lt;/div&gt;</summary>
		<author><name>wikipedia&gt;Pbrks</name></author>
	</entry>
</feed>