Skip to content

Commit 2ef0064

Browse files
authored
feat: support trace diff with differing columns (#882)
This enables the "trace diff" command to work even when the columns differ between two traces. In such, case the columns different to each trace are reported, and the diff proceeds on the set of common columns.
1 parent e00da7e commit 2ef0064

File tree

2 files changed

+89
-6
lines changed

2 files changed

+89
-6
lines changed

pkg/cmd/tracediff.go

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ package cmd
33
import (
44
"fmt"
55
"os"
6+
"slices"
67
"sort"
78

89
"github.com/consensys/gnark-crypto/ecc/bls12-377/fr"
910
"github.com/consensys/go-corset/pkg/trace"
11+
"github.com/consensys/go-corset/pkg/util/collection/set"
1012
"github.com/consensys/go-corset/pkg/util/field"
1113
"github.com/spf13/cobra"
1214
)
@@ -28,10 +30,20 @@ var traceDiffCmd = &cobra.Command{
2830
// Read trace files
2931
tracefile1 := ReadTraceFile(filename1)
3032
tracefile2 := ReadTraceFile(filename2)
33+
// Extract column names
34+
trace1cols := extractColumnNames(tracefile1.Columns)
35+
trace2cols := extractColumnNames(tracefile2.Columns)
3136
// Sanity check
32-
if len(tracefile1.Columns) != len(tracefile2.Columns) {
33-
fmt.Printf("differing number of columns (%d v %d)", len(tracefile1.Columns), len(tracefile2.Columns))
34-
os.Exit(2)
37+
if !slices.Equal(trace1cols, trace2cols) {
38+
var common set.SortedSet[string]
39+
//
40+
common.InsertSorted(&trace1cols)
41+
common.Intersect(&trace2cols)
42+
//
43+
reportExtraColumns(filename1, trace1cols, common)
44+
reportExtraColumns(filename2, trace2cols, common)
45+
tracefile1.Columns = filterCommonColumns(tracefile1.Columns, common)
46+
tracefile2.Columns = filterCommonColumns(tracefile2.Columns, common)
3547
}
3648
//
3749
errors := parallelDiff(tracefile1.Columns, tracefile2.Columns)
@@ -42,6 +54,36 @@ var traceDiffCmd = &cobra.Command{
4254
},
4355
}
4456

57+
func extractColumnNames(columns []trace.RawColumn) set.SortedSet[string] {
58+
var names set.SortedSet[string]
59+
//
60+
for _, c := range columns {
61+
names.Insert(c.QualifiedName())
62+
}
63+
//
64+
return names
65+
}
66+
67+
func reportExtraColumns(name string, columns []string, common set.SortedSet[string]) {
68+
for _, c := range columns {
69+
if !common.Contains(c) {
70+
fmt.Printf("column %s only in trace %s\n", c, name)
71+
}
72+
}
73+
}
74+
75+
func filterCommonColumns(columns []trace.RawColumn, common set.SortedSet[string]) []trace.RawColumn {
76+
var ncolumns []trace.RawColumn
77+
//
78+
for _, c := range columns {
79+
if common.Contains(c.QualifiedName()) {
80+
ncolumns = append(ncolumns, c)
81+
}
82+
}
83+
//
84+
return ncolumns
85+
}
86+
4587
func parallelDiff(columns1 []trace.RawColumn, columns2 []trace.RawColumn) []error {
4688
errors := make([]error, 0)
4789
ncols := len(columns1)

pkg/util/collection/set/sorted_set.go

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,27 @@ func (p *SortedSet[T]) InsertSorted(q *SortedSet[T]) {
7979
// Allocate space
8080
ndata := make([]T, len(left)+len(right)-n)
8181
// Merge
82-
mergeSorted(ndata, left, right)
82+
unionSorted(ndata, left, right)
83+
// Finally copy over new data
84+
*p = ndata
85+
}
86+
87+
// Intersect removes all elements from this set which are not in the given set.
88+
func (p *SortedSet[T]) Intersect(q *SortedSet[T]) {
89+
left := *p
90+
right := *q
91+
// Check containment
92+
n := countDuplicates(left, right)
93+
// Check for total inclusion
94+
if n == len(left) {
95+
// Left set completely included within right, so actually there is
96+
// nothing to do.
97+
return
98+
}
99+
// Allocate space
100+
ndata := make([]T, n)
101+
// Intersect
102+
intersectSorted(ndata, left, right)
83103
// Finally copy over new data
84104
*p = ndata
85105
}
@@ -157,9 +177,9 @@ func countDuplicates[T cmp.Ordered](left []T, right []T) int {
157177
return n
158178
}
159179

160-
// Merge two sets of sorted arrays (left and right) into a target array. This
180+
// Union two sets of sorted arrays (left and right) into a target array. This
161181
// assumes the target array is big enough.
162-
func mergeSorted[T cmp.Ordered](target []T, left []T, right []T) {
182+
func unionSorted[T cmp.Ordered](target []T, left []T, right []T) {
163183
i := 0
164184
j := 0
165185
k := 0
@@ -184,3 +204,24 @@ func mergeSorted[T cmp.Ordered](target []T, left []T, right []T) {
184204
copy(target[k:], right[j:])
185205
}
186206
}
207+
208+
// Intersect two sets of sorted arrays (left and right) into a target array.
209+
// This assumes the target array is big enough.
210+
func intersectSorted[T cmp.Ordered](target []T, left []T, right []T) {
211+
i := 0
212+
j := 0
213+
k := 0
214+
// Merge overlap of both sets
215+
for i < len(left) && j < len(right) {
216+
if left[i] < right[j] {
217+
i++
218+
} else if left[i] > right[j] {
219+
j++
220+
} else {
221+
target[k] = left[i]
222+
i++
223+
j++
224+
k++
225+
}
226+
}
227+
}

0 commit comments

Comments
 (0)