| package paramreducer | 
 |  | 
 | /* | 
 |   paramreducer produces a new ParamSet from a ParamSet and a Query against that ParamSet. | 
 |  | 
 | 	Given a corpus of traces that have ids of: | 
 |  | 
 | 		config=8888,cpu=x86,res=ms | 
 | 		config=565,cpu=x86,res=count | 
 | 		config=565,cpu=arm,res=cov | 
 | 		config=gles,cpu=arm,res=bytes | 
 |  | 
 | 	And a query against that corpus: | 
 |  | 
 |     config=565,res=[cov,bytes] | 
 |  | 
 | 	We want to return a paramset that represents all the legal options for queries | 
 | 	against the subset of traces that match the query: | 
 |  | 
 | 		cpu=[arm] | 
 | 		config=[565, gles] | 
 | 		res=[count, cov] | 
 |  | 
 | 	I.e. our query matches the corpus as follows: | 
 |  | 
 | 				 config=8888,cpu=x86,res=ms | 
 | 		**   config=565,cpu=x86,res=count | 
 | 		** * config=565,cpu=arm,res=cov | 
 | 			 * config=gles,cpu=arm,res=bytes | 
 |  | 
 | 		** = matches config=565       => res=[count, cov],   cpu=[arm, x86] | 
 | 		 * = matches res=[cov, bytes] => config=[565, gles], cpu=[arm] | 
 | */ | 
 | import ( | 
 | 	"fmt" | 
 | 	"net/url" | 
 | 	"sort" | 
 |  | 
 | 	"go.skia.org/infra/go/paramtools" | 
 | 	"go.skia.org/infra/go/query" | 
 | 	"go.skia.org/infra/go/util" | 
 | ) | 
 |  | 
 | type SubQuery struct { | 
 | 	q  *query.Query | 
 | 	ps paramtools.ParamSet | 
 | } | 
 |  | 
 | func (s *SubQuery) Add(key string) { | 
 | 	if s.q.Matches(key) { | 
 | 		s.ps.AddParamsFromKey(key) | 
 | 	} | 
 | } | 
 |  | 
 | type Reducer struct { | 
 | 	full       paramtools.ParamSet | 
 | 	subQueries map[string]*SubQuery | 
 | } | 
 |  | 
 | // New returns a new Reducer for the given query and full paramset. | 
 | func New(q url.Values, full paramtools.ParamSet) (*Reducer, error) { | 
 | 	// Break apart the query into sub-queries, one for each key present in the query. | 
 | 	subQueries := map[string]*SubQuery{} | 
 | 	for key, values := range q { | 
 | 		subQuery, err := query.New(url.Values{ | 
 | 			key: values, | 
 | 		}) | 
 | 		if err != nil { | 
 | 			return nil, fmt.Errorf("Invalid query: %s", err) | 
 | 		} | 
 | 		subQueries[key] = &SubQuery{ | 
 | 			q:  subQuery, | 
 | 			ps: paramtools.ParamSet{}, | 
 | 		} | 
 | 	} | 
 | 	return &Reducer{ | 
 | 		full:       full, | 
 | 		subQueries: subQueries, | 
 | 	}, nil | 
 | } | 
 |  | 
 | // Add a structured key from the full corpus. | 
 | func (r *Reducer) Add(key string) { | 
 | 	for _, sub := range r.subQueries { | 
 | 		sub.Add(key) | 
 | 	} | 
 | } | 
 |  | 
 | // Reduce all the data to a final ParamSet that represents | 
 | // all the valid options left in full ParamSet. | 
 | func (r *Reducer) Reduce() paramtools.ParamSet { | 
 | 	ret := paramtools.ParamSet{} | 
 | 	for full_k, full_v := range r.full { | 
 | 		ss := util.NewStringSet(full_v) | 
 | 		for k, sub := range r.subQueries { | 
 | 			if k != full_k { | 
 | 				ss = ss.Intersect(util.NewStringSet(sub.ps[full_k])) | 
 | 			} | 
 | 		} | 
 | 		values := ss.Keys() | 
 | 		sort.Strings(values) | 
 | 		if len(values) > 0 { | 
 | 			ret[full_k] = values | 
 | 		} | 
 | 	} | 
 | 	return ret | 
 | } |