2022-01-19 17:45:50 +00:00
|
|
|
// Package aghalg contains common generic algorithms and data structures.
|
2021-12-28 14:00:52 +00:00
|
|
|
//
|
2022-08-03 12:36:18 +01:00
|
|
|
// TODO(a.garipov): Move parts of this into golibs.
|
2022-01-19 17:45:50 +00:00
|
|
|
package aghalg
|
2021-12-28 14:00:52 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
|
2022-08-03 12:36:18 +01:00
|
|
|
"golang.org/x/exp/constraints"
|
|
|
|
"golang.org/x/exp/slices"
|
|
|
|
)
|
2021-12-28 14:00:52 +00:00
|
|
|
|
2022-09-06 15:09:54 +01:00
|
|
|
// Coalesce returns the first non-zero value. It is named after function
|
2022-08-16 11:21:25 +01:00
|
|
|
// COALESCE in SQL. If values or all its elements are empty, it returns a zero
|
|
|
|
// value.
|
2022-09-06 15:09:54 +01:00
|
|
|
//
|
|
|
|
// T is comparable, because Go currently doesn't have a comparableWithZeroValue
|
|
|
|
// constraint.
|
|
|
|
//
|
|
|
|
// TODO(a.garipov): Think of ways to merge with [CoalesceSlice].
|
2022-08-16 11:21:25 +01:00
|
|
|
func Coalesce[T comparable](values ...T) (res T) {
|
|
|
|
var zero T
|
|
|
|
for _, v := range values {
|
|
|
|
if v != zero {
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return zero
|
|
|
|
}
|
|
|
|
|
2022-09-06 15:09:54 +01:00
|
|
|
// CoalesceSlice returns the first non-zero value. It is named after function
|
|
|
|
// COALESCE in SQL. If values or all its elements are empty, it returns nil.
|
|
|
|
//
|
|
|
|
// TODO(a.garipov): Think of ways to merge with [Coalesce].
|
|
|
|
func CoalesceSlice[E any, S []E](values ...S) (res S) {
|
|
|
|
for _, v := range values {
|
|
|
|
if v != nil {
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-01-19 17:45:50 +00:00
|
|
|
// UniqChecker allows validating uniqueness of comparable items.
|
2022-08-03 12:36:18 +01:00
|
|
|
//
|
|
|
|
// TODO(a.garipov): The Ordered constraint is only really necessary in Validate.
|
|
|
|
// Consider ways of making this constraint comparable instead.
|
|
|
|
type UniqChecker[T constraints.Ordered] map[T]int64
|
2021-12-28 14:00:52 +00:00
|
|
|
|
|
|
|
// Add adds a value to the validator. v must not be nil.
|
2022-08-03 12:36:18 +01:00
|
|
|
func (uc UniqChecker[T]) Add(elems ...T) {
|
2021-12-28 14:00:52 +00:00
|
|
|
for _, e := range elems {
|
2022-01-19 17:45:50 +00:00
|
|
|
uc[e]++
|
2021-12-28 14:00:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-20 14:19:09 +00:00
|
|
|
// Merge returns a checker containing data from both uc and other.
|
2022-08-03 12:36:18 +01:00
|
|
|
func (uc UniqChecker[T]) Merge(other UniqChecker[T]) (merged UniqChecker[T]) {
|
|
|
|
merged = make(UniqChecker[T], len(uc)+len(other))
|
2022-01-19 17:45:50 +00:00
|
|
|
for elem, num := range uc {
|
2021-12-28 14:00:52 +00:00
|
|
|
merged[elem] += num
|
|
|
|
}
|
|
|
|
|
|
|
|
for elem, num := range other {
|
|
|
|
merged[elem] += num
|
|
|
|
}
|
|
|
|
|
|
|
|
return merged
|
|
|
|
}
|
|
|
|
|
|
|
|
// Validate returns an error enumerating all elements that aren't unique.
|
2022-08-03 12:36:18 +01:00
|
|
|
func (uc UniqChecker[T]) Validate() (err error) {
|
|
|
|
var dup []T
|
2022-01-19 17:45:50 +00:00
|
|
|
for elem, num := range uc {
|
2021-12-28 14:00:52 +00:00
|
|
|
if num > 1 {
|
|
|
|
dup = append(dup, elem)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(dup) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-08-03 12:36:18 +01:00
|
|
|
slices.Sort(dup)
|
2021-12-28 14:00:52 +00:00
|
|
|
|
|
|
|
return fmt.Errorf("duplicated values: %v", dup)
|
|
|
|
}
|