Adds headers to the input data, such that the first line lists the candidates. This makes comparing results between different datasets (with some candidates added or removed) easier.
3 files changed, 20 insertions(+), 41 deletions(-)

M input-1.csv
M input-2.csv
M main.go
M input-1.csv +1 -0
@@ 1,3 1,4 @@ 
+A,B,C
 5,4,0
 4,1,5
 5,4,0

          
M input-2.csv +1 -0
@@ 1,3 1,4 @@ 
+A,B,C
 5,4,0
 4,1,5
 5,4,0

          
M main.go +18 -41
@@ 23,7 23,8 @@ func main() {
 		panic(err)
 	}
 	// All of the row/column values are strings. Turn them into ints
-	ints, badVotes := toInts(lines)
+	// The first line is the header, and lists the candidates, so is excluded
+	ints, badVotes := toInts(lines[1:])
 	if len(badVotes) > 0 {
 		fmt.Println("Vote errors encountered:")
 		// We'll print info about the votes at the end.

          
@@ 118,9 119,6 @@ func diff(lines [][]int, lidx, r int) []
 		lls := row[lidx]
 		dr := make([]int, len(row))
 		for c, cell := range row {
-			if c == lidx {
-				continue
-			}
 			switch {
 			case cell < lls:
 				dr[c] = -r

          
@@ 165,22 163,24 @@ func total(lines [][]int, diffs [][]int)
 
 // printDetails cut out from main() to keep main terse; nothing here is
 // relevant to the voting calculation, and is only output.
-func printDetails(wc int, lines, badVotes [][]string, ints [][]int, diffs [][]int, ttl []int) {
+func printDetails(wc int, hlines, badVotes [][]string, ints [][]int, diffs [][]int, ttl []int) {
+	header := hlines[0]
+	lines := hlines[1:]
 	// The rest of this is just dumping out the calculations.
 	fmt.Printf("Total votes in input: %d\n", len(lines))
-	printWithHeader("Summary of distributions", true, uniq(lines))
+	printWithCandidates("Summary of distributions", true, header, uniq(lines))
 	subttl := subtotal(ints)
-	printWithHeader("Subtotals", false, subttl)
+	printWithCandidates("Subtotals", false, header, subttl)
 	subWc := max(subttl)
-	fmt.Printf("Winner by score: %s with %d\n", toS(subWc), subttl[subWc])
-	printWithHeader("Summary of diffs", true, uniq(toString(diffs)))
-	printWithHeader("Modifiers", false, subtotal(diffs))
-	printWithHeader("Totals", false, ttl)
-	fmt.Printf("Highest combined total: %s with %d\n", toS(wc), ttl[wc])
+	fmt.Printf("Winner by score: %s with %d\n", header[subWc], subttl[subWc])
+	printWithCandidates("Summary of diffs", true, header, uniq(toString(diffs)))
+	printWithCandidates("Modifiers", false, header, subtotal(diffs))
+	printWithCandidates("Totals", false, header, ttl)
+	fmt.Printf("Highest combined total: %s with %d\n", header[wc], ttl[wc])
 
 	if len(badVotes) > 0 {
 		fmt.Printf("\n%d records were invalid.", len(badVotes))
-		printWithHeader("The # column is the line number in the input file.", true, badVotes)
+		printWithCandidates("The # column is the line number in the input file.", true, header, badVotes)
 	}
 }
 

          
@@ 244,11 244,11 @@ func toString(lines [][]int) [][]string 
 	return strs
 }
 
-// printWithHeader is a utility function to print out a matrix, with an
+// printWithCandidates is a utility function to print out a matrix, with an
 // optional header where each column is a candidate (A, B, C, etc). Optionally,
 // the first column is a count of the number of votes that voted exactly
 // the same way.
-func printWithHeader(d string, count bool, v interface{}) {
+func printWithCandidates(d string, count bool, cands []string, v interface{}) {
 	fmt.Println(d)
 	switch a := v.(type) {
 	case [][]string:

          
@@ 257,7 257,7 @@ func printWithHeader(d string, count boo
 				return a[i][0] > a[j][0]
 			})
 		}
-		printHeader(len(a[0]), count)
+		fmt.Printf(" \t%s\n", strings.Join(cands, "\t"))
 		for _, r := range a {
 			if !count {
 				fmt.Printf(" \t")

          
@@ 270,7 270,7 @@ func printWithHeader(d string, count boo
 				return a[i][0] > a[j][0]
 			})
 		}
-		printHeader(len(a[0]), count)
+		fmt.Printf(" \t%s\n", strings.Join(cands, "\t"))
 		for _, r := range a {
 			if !count {
 				fmt.Printf(" \t")

          
@@ 286,7 286,7 @@ func printWithHeader(d string, count boo
 				return a[i] > a[j]
 			})
 		}
-		printHeader(len(a), count)
+		fmt.Printf(" \t%s\n", strings.Join(cands, "\t"))
 		if !count {
 			fmt.Printf(" \t")
 		}

          
@@ 297,26 297,3 @@ func printWithHeader(d string, count boo
 	default:
 	}
 }
-
-// printHeader prints a line of n candidates starting with candidate A. c
-// determines whether or not the array contains a count as the first column
-// of the number of votes in the first column.
-func printHeader(n int, c bool) {
-	if c {
-		n -= 1
-	}
-	fmt.Printf("#\t")
-	a := byte('A')
-	var i byte
-	m := byte(n)
-	for i = 0; i < m; i++ {
-		fmt.Printf("%s\t", string([]byte{a + i}))
-	}
-	fmt.Println()
-}
-
-// toS converts a column index to a candidate string; column 0 is candidate A,
-// column 1 is candidate B, and so on. 0-based.
-func toS(b int) string {
-	return string([]byte{'A' + byte(b)})
-}