// Copyright 2026 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package main import ( "simd/archsimd/_gen/specgen" "slices" ) // A result is one figure: how many of how many. type result struct{ n, d int } func (r result) miss() int { return r.d - r.n } // A tally counts distinct keys at one grain, and how many of them are done. A // key counts as done if any row says so, which is well defined because every // predicate here is a function of the key rather than of the row. type tally[K comparable] struct { all map[K]bool hit map[K]bool } func (t *tally[K]) add(k K, done bool) { if t.all == nil { t.all, t.hit = map[K]bool{}, map[K]bool{} } t.all[k] = true if done { t.hit[k] = true } } func (t *tally[K]) result() result { return result{len(t.hit), len(t.all)} } // has reports whether the key was counted at all; done, whether it is done. func (t *tally[K]) has(k K) bool { return t.all[k] } func (t *tally[K]) done(k K) bool { return t.hit[k] } // A pivot is a tally split by the value of some dimension: the report's // recurring shape is "for each X, what fraction of Y is done". type pivot[K comparable] struct { by map[string]*tally[K] } func (p *pivot[K]) add(dim string, k K, done bool) { if p.by == nil { p.by = map[string]*tally[K]{} } t := p.by[dim] if t == nil { t = new(tally[K]) p.by[dim] = t } t.add(k, done) } // fold merges the tally for one dimension value into another, for reporting a // roll-up (simdgen's per-target runs) alongside its parts. func (p *pivot[K]) fold(from, to string) { src := p.by[from] for k := range src.all { p.add(to, k, src.done(k)) } } func (p *pivot[K]) dims() []string { return sortedKeys(p.by) } func (p *pivot[K]) result(dim string) result { if t := p.by[dim]; t != nil { return t.result() } return result{} } // tallyBy is the common case: one pass over the fact table, keyed at a grain, // scored by a predicate. func tallyBy[K comparable](facts []fact, key func(fact) K, done func(fact) bool) *tally[K] { t := new(tally[K]) for _, f := range facts { t.add(key(f), done(f)) } return t } // pivotBy is tallyBy, split by a dimension. func pivotBy[K comparable](facts []fact, dim func(fact) string, key func(fact) K, done func(fact) bool) *pivot[K] { p := new(pivot[K]) for _, f := range facts { p.add(dim(f), key(f), done(f)) } return p } // countBy counts distinct keys among the rows, for figures that are a plain // count rather than a fraction. func countBy[K comparable](facts []fact, key func(fact) K) int { seen := map[K]bool{} for _, f := range facts { seen[key(f)] = true } return len(seen) } func groupBy[T any, K comparable](s []T, key func(T) K) map[K][]T { m := map[K][]T{} for _, v := range s { m[key(v)] = append(m[key(v)], v) } return m } func sortedKeys[V any](m map[string]V) []string { ks := make([]string, 0, len(m)) for k := range m { ks = append(ks, k) } slices.Sort(ks) return ks } // equalBy reports whether two parameter lists agree under f. Types and names // are compared with the same code because the only difference is the field. func equalBy(a, b []param, f func(param) string) bool { if len(a) != len(b) { return false } for i := range a { if f(a[i]) != f(b[i]) { return false } } return true } func paramType(p param) string { return p.typ } // specParams converts a spec signature's arguments to the same representation // as the API's, so one comparison serves both directions. func specParams(as []specgen.Arg) []param { ps := make([]param, len(as)) for i, a := range as { ps[i] = param{a.Name, a.Type.String()} } return ps } // named reports whether p has a name a doc or body could refer to. As in // specdoc.Fill, the blank identifier does not count. func named(p param) bool { return p.name != "" && p.name != "_" } // namesClash reports whether any argument named in both api and spec has // different names on the two sides. specdoc.Fill reports these as errors. Lists // of different lengths are a type mismatch, not a name one. func namesClash(api, spec []param) bool { if len(api) != len(spec) { return false } for i := range api { if named(api[i]) && named(spec[i]) && api[i].name != spec[i].name { return true } } return false } // namesMissing reports whether any argument spec names is unnamed in api. // specdoc.Fill fills these in from spec. func namesMissing(api, spec []param) bool { if len(api) != len(spec) { return false } for i := range api { if named(spec[i]) && !named(api[i]) { return true } } return false } // namesConsistent reports whether argument lists agree on names, where an // unnamed argument agrees with any name. Lists of different lengths disagree. func namesConsistent(lists [][]param) bool { for _, l := range lists[1:] { if len(l) != len(lists[0]) { return false } } for i := range lists[0] { var name string for _, l := range lists { if !named(l[i]) { continue } if name == "" { name = l[i].name } else if l[i].name != name { return false } } } return true }