Regres: Move optimization logic to separate file

There's more to come.

Bug: b/152192800
Change-Id: Icc0928fa0051269f9ca14e9177a9a8ef56d05a34
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/43310
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Tested-by: Ben Clayton <bclayton@google.com>
diff --git a/tests/regres/cov/optimization.go b/tests/regres/cov/optimization.go
new file mode 100644
index 0000000..f2f357f
--- /dev/null
+++ b/tests/regres/cov/optimization.go
@@ -0,0 +1,130 @@
+// Copyright 2020 The SwiftShader Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cov
+
+import (
+	"log"
+	"sort"
+	"sync"
+)
+
+// Optimize optimizes the Tree by de-duplicating common spans into a tree of
+// SpanGroups.
+func (t *Tree) Optimize() {
+	log.Printf("Optimizing coverage tree...")
+
+	// Start by gathering all of the unique spansets
+	wg := sync.WaitGroup{}
+	wg.Add(len(t.files))
+	for _, file := range t.files {
+		file := file
+		go func() {
+			defer wg.Done()
+			o := optimizer{}
+			o.createGroups(file)
+		}()
+	}
+	wg.Wait()
+}
+
+type optimizer struct{}
+
+func (o *optimizer) createGroups(f *treeFile) {
+	const minSpansInGroup = 2
+
+	type spansetKey string
+	spansetMap := map[spansetKey]SpanSet{}
+
+	f.tcm.traverse(func(tc *TestCoverage) {
+		if len(tc.Spans) >= minSpansInGroup {
+			key := spansetKey(tc.Spans.String())
+			if _, ok := spansetMap[key]; !ok {
+				spansetMap[key] = tc.Spans
+			}
+		}
+	})
+
+	if len(spansetMap) == 0 {
+		return
+	}
+
+	type spansetInfo struct {
+		key spansetKey
+		set SpanSet // fully expanded set
+		grp SpanGroup
+		id  SpanGroupID
+	}
+	spansets := make([]*spansetInfo, 0, len(spansetMap))
+	for key, set := range spansetMap {
+		spansets = append(spansets, &spansetInfo{
+			key: key,
+			set: set,
+			grp: SpanGroup{spans: set},
+		})
+	}
+
+	// Sort by number of spans in each sets starting with the largest.
+	sort.Slice(spansets, func(i, j int) bool {
+		a, b := spansets[i].set, spansets[j].set
+		switch {
+		case len(a) > len(b):
+			return true
+		case len(a) < len(b):
+			return false
+		}
+		return a.List().Compare(b.List()) == -1 // Just to keep output stable
+	})
+
+	// Assign IDs now that we have stable order.
+	for i := range spansets {
+		spansets[i].id = SpanGroupID(i)
+	}
+
+	// Loop over the spanGroups starting from the largest, and try to fold them
+	// into the larger sets.
+	// This is O(n^2) complexity.
+nextSpan:
+	for i, a := range spansets[:len(spansets)-1] {
+		for _, b := range spansets[i+1:] {
+			if len(a.set) > len(b.set) && a.set.containsAll(b.set) {
+				extend := b.id // Do not take address of iterator!
+				a.grp.spans = a.set.sub(b.set)
+				a.grp.extend = &extend
+				continue nextSpan
+			}
+		}
+	}
+
+	// Rebuild a map of spansetKey to SpanGroup
+	spangroupMap := make(map[spansetKey]*spansetInfo, len(spansets))
+	for _, s := range spansets {
+		spangroupMap[s.key] = s
+	}
+
+	// Store the groups in the tree
+	f.spangroups = make(map[SpanGroupID]SpanGroup, len(spansets))
+	for _, s := range spansets {
+		f.spangroups[s.id] = s.grp
+	}
+
+	// Update all the uses.
+	f.tcm.traverse(func(tc *TestCoverage) {
+		key := spansetKey(tc.Spans.String())
+		if g, ok := spangroupMap[key]; ok {
+			tc.Spans = nil
+			tc.Group = &g.id
+		}
+	})
+}
diff --git a/tests/regres/cov/tree.go b/tests/regres/cov/tree.go
index 398fd44..7e6c3f2 100644
--- a/tests/regres/cov/tree.go
+++ b/tests/regres/cov/tree.go
@@ -16,10 +16,8 @@
 
 import (
 	"fmt"
-	"log"
 	"sort"
 	"strings"
-	"sync"
 )
 
 type treeFile struct {
@@ -429,109 +427,3 @@
 func indent(s string) string {
 	return strings.TrimSuffix(strings.ReplaceAll(s, "\n", "\n  "), "  ")
 }
-
-// Optimize optimizes the Tree by de-duplicating common spans into a tree of
-// SpanGroups.
-func (t *Tree) Optimize() {
-	log.Printf("Optimizing coverage tree...")
-
-	// Start by gathering all of the unique spansets
-	wg := sync.WaitGroup{}
-	wg.Add(len(t.files))
-	for _, file := range t.files {
-		file := file
-		go func() {
-			defer wg.Done()
-			file.optimize()
-		}()
-	}
-	wg.Wait()
-}
-
-func (f *treeFile) optimize() {
-	const minSpansInGroup = 2
-
-	type spansetKey string
-	spansetMap := map[spansetKey]SpanSet{}
-
-	f.tcm.traverse(func(tc *TestCoverage) {
-		if len(tc.Spans) >= minSpansInGroup {
-			key := spansetKey(tc.Spans.String())
-			if _, ok := spansetMap[key]; !ok {
-				spansetMap[key] = tc.Spans
-			}
-		}
-	})
-
-	if len(spansetMap) == 0 {
-		return
-	}
-
-	type spansetInfo struct {
-		key spansetKey
-		set SpanSet // fully expanded set
-		grp SpanGroup
-		id  SpanGroupID
-	}
-	spansets := make([]*spansetInfo, 0, len(spansetMap))
-	for key, set := range spansetMap {
-		spansets = append(spansets, &spansetInfo{
-			key: key,
-			set: set,
-			grp: SpanGroup{spans: set},
-		})
-	}
-
-	// Sort by number of spans in each sets starting with the largest.
-	sort.Slice(spansets, func(i, j int) bool {
-		a, b := spansets[i].set, spansets[j].set
-		switch {
-		case len(a) > len(b):
-			return true
-		case len(a) < len(b):
-			return false
-		}
-		return a.List().Compare(b.List()) == -1 // Just to keep output stable
-	})
-
-	// Assign IDs now that we have stable order.
-	for i := range spansets {
-		spansets[i].id = SpanGroupID(i)
-	}
-
-	// Loop over the spanGroups starting from the largest, and try to fold them
-	// into the larger sets.
-	// This is O(n^2) complexity.
-nextSpan:
-	for i, a := range spansets[:len(spansets)-1] {
-		for _, b := range spansets[i+1:] {
-			if len(a.set) > len(b.set) && a.set.containsAll(b.set) {
-				extend := b.id // Do not take address of iterator!
-				a.grp.spans = a.set.sub(b.set)
-				a.grp.extend = &extend
-				continue nextSpan
-			}
-		}
-	}
-
-	// Rebuild a map of spansetKey to SpanGroup
-	spangroupMap := make(map[spansetKey]*spansetInfo, len(spansets))
-	for _, s := range spansets {
-		spangroupMap[s.key] = s
-	}
-
-	// Store the groups in the tree
-	f.spangroups = make(map[SpanGroupID]SpanGroup, len(spansets))
-	for _, s := range spansets {
-		f.spangroups[s.id] = s.grp
-	}
-
-	// Update all the uses.
-	f.tcm.traverse(func(tc *TestCoverage) {
-		key := spansetKey(tc.Spans.String())
-		if g, ok := spangroupMap[key]; ok {
-			tc.Spans = nil
-			tc.Group = &g.id
-		}
-	})
-}