diff --git a/README.md b/README.md index 503ce05..7ef0f1d 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,100 @@ tup := tuple.New2(5, "hi!") a, b := tup.Values() ``` +## Comparison + +Tuples are compared from the first element to the last. +For example, the tuple `[1 2 3]` is greater than `[1 2 4]` but less than `[2 2 2]`. + +```go +fmt.Println(tuple.Equal3(tuple.New3(1, 2, 3), tuple.New3(3, 3, 3))) // false. +fmt.Println(tuple.LessThan3(tuple.New3(1, 2, 3), tuple.New3(3, 2, 1))) // true. + +tups := []tuple.T3{ + tuple.New3("foo", 2, -23), + tuple.New3("foo", 72, 15), + tuple.New3("bar", -4, 43), +} +sort.Slice(tups, func (i, j int) { + return tuple.LessThan3(tups[i], tups[j]) +}) + +fmt.Println(tups) // [["bar", -4, 43], ["foo", 2, -23], ["foo", 72, 15]]. +``` + +--- +**NOTE** + +In order to compare tuples, all tuple elements must match `constraints.Ordered`. + +See [Custom comparison](#custom-comparison) in order to see how to compare tuples +with arbitrary element values. + +--- + +### Comparison result + +```go +// Compare* functions return an OrderedComparisonResult value. +result := tuple.Compare3(tuple.New3(1, 2, 3), tuple.New3(3, 2, 1)) + +// OrderedComparisonResult values are wrapped integers. +fmt.Println(result) // -1 + +// OrderedComparisonResult expose various method to see the result +// in a more readable way. +fmt.Println(result.GreaterThan()) // false +``` + +### Custom comparison + +The package provides the `CompareC` comparison functions varation in order to compare tuples of complex +comparable types. + +For a type to be comparable, it must match the `Comparable` or `Equalable` constraints. + +```go +type Comparable[T any] interface { + CompareTo(guest T) OrderedComparisonResult +} + +type Equalable[T any] interface { + Equal(guest T) bool +} +``` + +```go +type person struct { + name string + age int +} + +func (p person) CompareTo(guest person) tuple.OrderedComparisonResult { + if p.name < guest.name { + return -1 + } + if p.name > guest.name { + return 1 + } + return 0 +} + +func main() { + tup1 := tuple.New2(person{name: "foo", age: 20}, person{name: "bar", age: 24}) + tup2 := tuple.New2(person{name: "bar", age: 20}, person{name: "baz", age: 24}) + + fmt.Println(tuple.LessThan2C(tup1, tup2)) // true. +} +``` + +In order to call the complex types variation of the comparable functions, __all__ tuple types must match the `Comparable` constraint. + +While this is not ideal, this a known inconvenience given the current type parameters capabilities in Go. +Some solutions have been porposed for this issue ([lesser](https://github.com/lelysses/lesser), for example, beatifully articulates the issue), +but they still demand features that are not yet implemented by the language. + +Once the language will introduce more convenient ways for generic comparisons, this package will adopt it. + ## Formatting Tuples implement the `Stringer` and `GoStringer` interfaces. diff --git a/comparison.go b/comparison.go new file mode 100644 index 0000000..8e1488c --- /dev/null +++ b/comparison.go @@ -0,0 +1,97 @@ +package tuple + +import ( + "constraints" +) + +// OrderedComparisonResult represents the result of a tuple ordered comparison. +// OrderedComparisonResult == 0 represents that the tuples are equal. +// OrderedComparisonResult < 0 represent that the host tuple is less than the guest tuple. +// OrderedComparisonResult > 0 represent that the host tuple is greater than the guest tuple. +type OrderedComparisonResult int + +// Comparable is a constraint interface for complex tuple elements that can be compared to other instances. +// In order to compare tuples, either all of their elements must be Ordered, or Comparable. +type Comparable[T any] interface { + CompareTo(guest T) OrderedComparisonResult +} + +// Equalable is a constraint interface for complex tuple elements whose equality to other instances can be tested. +type Equalable[T any] interface { + Equal(guest T) bool +} + +// Equal returns whether the compared values are equal. +func (result OrderedComparisonResult) Equal() bool { + return result == 0 +} + +// LessThan returns whether the host is less than the guest. +func (result OrderedComparisonResult) LessThan() bool { + return result < 0 +} + +// LessOrEqual returns whether the host is less than or equal to the guest. +func (result OrderedComparisonResult) LessOrEqual() bool { + return result <= 0 +} + +// GreaterThan returns whether the host is greater than the guest. +func (result OrderedComparisonResult) GreaterThan() bool { + return result > 0 +} + +// GreaterOrEqual returns whether the host is greater than or equal to the guest. +func (result OrderedComparisonResult) GreaterOrEqual() bool { + return result >= 0 +} + +// EQ is short for Equal and returns whether the compared values are equal. +func (result OrderedComparisonResult) EQ() bool { + return result.Equal() +} + +// LT is short for LessThan and returns whether the host is less than the guest. +func (result OrderedComparisonResult) LT() bool { + return result.LessThan() +} + +// LE is short for LessOrEqual and returns whether the host is less than or equal to the guest. +func (result OrderedComparisonResult) LE() bool { + return result.LessOrEqual() +} + +// GT is short for GreaterThan and returns whether the host is greater than the guest. +func (result OrderedComparisonResult) GT() bool { + return result.GreaterThan() +} + +// GE is short for GreaterOrEqual and returns whether the host is greater than or equal to the guest. +func (result OrderedComparisonResult) GE() bool { + return result.GreaterOrEqual() +} + +// multiCompare calls and compares the predicates by order. +// multiCompare will short-circuit once one of the predicates returns a non-equal result, and the rest +// of the predicates will not be called. +func multiCompare(predicates ...func() OrderedComparisonResult) OrderedComparisonResult { + for _, pred := range predicates { + if result := pred(); !result.Equal() { + return result + } + } + + return 0 +} + +// compareOrdered returns the comparison result between the host and guest values provided they match the Ordered constraint. +func compareOrdered[T constraints.Ordered](host, guest T) OrderedComparisonResult { + if host < guest { + return -1 + } + if host > guest { + return 1 + } + + return 0 +} diff --git a/comparison_test.go b/comparison_test.go new file mode 100644 index 0000000..bc242cc --- /dev/null +++ b/comparison_test.go @@ -0,0 +1,22 @@ +package tuple + +// approximationHelper is a helper type for testing type approximation. +type approximationHelper string + +// intEqualable is a wrapper type for int that implements the Equalable constraint. +type intEqualable int + +// stringComparable is a wrapper type for string that implements the Comparable constraint. +type stringComparable string + +// Assert implementation. +var _ Equalable[intEqualable] = (intEqualable)(0) +var _ Comparable[stringComparable] = (stringComparable)("") + +func (i intEqualable) Equal(other intEqualable) bool { + return i == other +} + +func (s stringComparable) CompareTo(other stringComparable) OrderedComparisonResult { + return compareOrdered(s, other) +} diff --git a/go.mod b/go.mod index 55efe90..3b31679 100644 --- a/go.mod +++ b/go.mod @@ -2,9 +2,10 @@ module github.com/barweiss/go-tuple go 1.18 +require github.com/stretchr/testify v1.7.0 + require ( github.com/davecgh/go-spew v1.1.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect - github.com/stretchr/testify v1.7.0 ) diff --git a/go.sum b/go.sum index b380ae4..acb88a4 100644 --- a/go.sum +++ b/go.sum @@ -5,6 +5,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/scripts/gen/main.go b/scripts/gen/main.go index d72ff49..bf83a86 100644 --- a/scripts/gen/main.go +++ b/scripts/gen/main.go @@ -16,7 +16,6 @@ type templateContext struct { Len int TypeName string TypeDecl string - GenericTypesDecl string GenericTypesForward string } @@ -27,6 +26,23 @@ var funcMap = template.FuncMap{ "quote": func(value interface{}) string { return fmt.Sprintf("%q", fmt.Sprint(value)) }, + "inc": func(value int) int { + return value + 1 + }, + "typeRef": func(indexes []int, suffix ...string) string { + if len(suffix) > 1 { + panic(fmt.Errorf("typeRef accepts at most 1 suffix argument")) + } + + var typeNameSuffix string + if len(suffix) == 1 { + typeNameSuffix = suffix[0] + } + + return fmt.Sprintf("T%d%s[%s]", len(indexes), typeNameSuffix, genTypesForward(indexes)) + }, + "genericTypesDecl": genTypesDecl, + "genericTypesDeclGenericConstraint": genTypesDeclGenericConstraint, } //go:embed tuple.tpl @@ -55,15 +71,10 @@ func main() { indexes[index] = index + 1 } - decl := genTypesDecl(indexes) - forward := genTypesForward(indexes) context := templateContext{ Indexes: indexes, Len: tupleLength, - TypeName: fmt.Sprintf("T%d[%s]", tupleLength, forward), - TypeDecl: fmt.Sprintf("T%d[%s]", tupleLength, decl), - GenericTypesDecl: decl, - GenericTypesForward: forward, + GenericTypesForward: genTypesForward(indexes), } filesToGenerate := []struct { @@ -112,18 +123,25 @@ func generateFile(context templateContext, outputFilePath string, tpl *template. } } +func genTypesDeclGenericConstraint(indexes []int, constraint string) string { + sep := make([]string, len(indexes)) + for index, typeIndex := range indexes { + typ := fmt.Sprintf("Ty%d", typeIndex) + sep[index] = fmt.Sprintf("%s %s[%s]", typ, constraint, typ) + } + + return strings.Join(sep, ", ") +} + // genTypesDecl generates a "TypeParamDecl" (https://tip.golang.org/ref/spec#Type_parameter_lists) expression, // used to declare generic types for a type or a function, according to the given element indexes. -func genTypesDecl(indexes []int) string { +func genTypesDecl(indexes []int, constraint string) string { sep := make([]string, len(indexes)) for index, typeIndex := range indexes { sep[index] = fmt.Sprintf("Ty%d", typeIndex) } - // Add constraint to last element. - sep[len(indexes)-1] += " any" - - return strings.Join(sep, ", ") + return strings.Join(sep, ", ") + " " + constraint } // genTypesForward generates a "TypeParamList" (https://tip.golang.org/ref/spec#Type_parameter_lists) expression, diff --git a/scripts/gen/tuple.tpl b/scripts/gen/tuple.tpl index 294741a..37514a4 100644 --- a/scripts/gen/tuple.tpl +++ b/scripts/gen/tuple.tpl @@ -2,25 +2,26 @@ package tuple import ( "fmt" + "constraints" ) -{{/* $typeName can be used when the context of dot changes. */}} -{{$typeName := .TypeName}} +{{/* $typeRef can be used when the context of dot changes. */}} +{{$typeRef := typeRef .Indexes}} // T{{.Len}} is a tuple type holding {{.Len}} generic values. -type T{{.Len}}[{{.GenericTypesDecl}}] struct { +type T{{.Len}}[{{genericTypesDecl .Indexes "any"}}] struct { {{range .Indexes -}} V{{.}} Ty{{.}} {{end -}} } // Len returns the number of values held by the tuple. -func (t {{.TypeName}}) Len() int { +func (t {{$typeRef}}) Len() int { return {{.Len}} } // Values returns the values held by the tuple. -func (t {{.TypeName}}) Values() ({{.GenericTypesForward}}) { +func (t {{$typeRef}}) Values() ({{.GenericTypesForward}}) { return {{range $index, $num := .Indexes -}} {{- if gt $index 0}}, {{end -}} t.V{{$num}} @@ -28,7 +29,7 @@ func (t {{.TypeName}}) Values() ({{.GenericTypesForward}}) { } // Array returns an array of the tuple values. -func (t {{.TypeName}}) Array() [{{.Len}}]any { +func (t {{$typeRef}}) Array() [{{.Len}}]any { return [{{.Len}}]any{ {{ range .Indexes -}} t.V{{.}}, @@ -37,29 +38,29 @@ func (t {{.TypeName}}) Array() [{{.Len}}]any { } // Slice returns a slice of the tuple values. -func (t {{.TypeName}}) Slice() []any { +func (t {{$typeRef}}) Slice() []any { a := t.Array() return a[:] } // String returns the string representation of the tuple. -func (t {{.TypeName}}) String() string { +func (t {{$typeRef}}) String() string { return tupString(t.Slice()) } // GoString returns a Go-syntax representation of the tuple. -func (t {{.TypeName}}) GoString() string { +func (t {{$typeRef}}) GoString() string { return tupGoString(t.Slice()) } // New{{.Len}} creates a new tuple holding {{.Len}} generic values. -func New{{.Len}}[{{.GenericTypesDecl}}]( +func New{{.Len}}[{{genericTypesDecl .Indexes "any"}}]( {{- range $index, $num := .Indexes -}} {{- if gt $index 0}}, {{end -}} v{{.}} Ty{{.}} {{- end -}} -) {{.TypeName}} { - return {{.TypeName}}{ +) {{$typeRef}} { + return {{$typeRef}}{ {{range .Indexes -}} V{{.}}: v{{.}}, {{end}} @@ -68,11 +69,11 @@ func New{{.Len}}[{{.GenericTypesDecl}}]( // FromArray{{.Len}} returns a tuple from an array of length {{.Len}}. // If any of the values can not be converted to the generic type, an error is returned. -func FromArray{{.Len}}[{{.GenericTypesDecl}}](arr [{{.Len}}]any) ({{.TypeName}}, error) { +func FromArray{{.Len}}[{{genericTypesDecl .Indexes "any"}}](arr [{{.Len}}]any) ({{$typeRef}}, error) { {{range $index, $num := .Indexes -}} v{{$num}}, ok := arr[{{$index}}].(Ty{{$num}}) if !ok { - return {{$typeName}}{}, fmt.Errorf("value at array index {{$index}} expected to have type %s but has type %T", typeName[Ty{{$num}}](), arr[{{$index}}]) + return {{$typeRef}}{}, fmt.Errorf("value at array index {{$index}} expected to have type %s but has type %T", typeName[Ty{{$num}}](), arr[{{$index}}]) } {{end}} return New{{.Len}}( @@ -85,21 +86,21 @@ func FromArray{{.Len}}[{{.GenericTypesDecl}}](arr [{{.Len}}]any) ({{.TypeName}}, // FromArray{{.Len}}X returns a tuple from an array of length {{.Len}}. // If any of the values can not be converted to the generic type, the function panics. -func FromArray{{.Len}}X[{{.GenericTypesDecl}}](arr [{{.Len}}]any) {{.TypeName}} { +func FromArray{{.Len}}X[{{genericTypesDecl .Indexes "any"}}](arr [{{.Len}}]any) {{$typeRef}} { return FromSlice{{.Len}}X[{{.GenericTypesForward}}](arr[:]) } // FromSlice{{.Len}} returns a tuple from a slice of length {{.Len}}. // If the length of the slice doesn't match, or any of the values can not be converted to the generic type, an error is returned. -func FromSlice{{.Len}}[{{.GenericTypesDecl}}](values []any) ({{.TypeName}}, error) { +func FromSlice{{.Len}}[{{genericTypesDecl .Indexes "any"}}](values []any) ({{$typeRef}}, error) { if len(values) != {{.Len}} { - return {{.TypeName}}{}, fmt.Errorf("slice length %d must match number of tuple values {{.Len}}", len(values)) + return {{$typeRef}}{}, fmt.Errorf("slice length %d must match number of tuple values {{.Len}}", len(values)) } {{range $index, $num := .Indexes -}} v{{$num}}, ok := values[{{$index}}].(Ty{{$num}}) if !ok { - return {{$typeName}}{}, fmt.Errorf("value at slice index {{$index}} expected to have type %s but has type %T", typeName[Ty{{$num}}](), values[{{$index}}]) + return {{$typeRef}}{}, fmt.Errorf("value at slice index {{$index}} expected to have type %s but has type %T", typeName[Ty{{$num}}](), values[{{$index}}]) } {{end}} return New{{.Len}}( @@ -112,7 +113,7 @@ func FromSlice{{.Len}}[{{.GenericTypesDecl}}](values []any) ({{.TypeName}}, erro // FromSlice{{.Len}}X returns a tuple from a slice of length {{.Len}}. // If the length of the slice doesn't match, or any of the values can not be converted to the generic type, the function panics. -func FromSlice{{.Len}}X[{{.GenericTypesDecl}}](values []any) {{.TypeName}} { +func FromSlice{{.Len}}X[{{genericTypesDecl .Indexes "any"}}](values []any) {{$typeRef}} { if len(values) != {{.Len}} { panic(fmt.Errorf("slice length %d must match number of tuple values {{.Len}}", len(values))) } @@ -127,3 +128,104 @@ func FromSlice{{.Len}}X[{{.GenericTypesDecl}}](values []any) {{.TypeName}} { {{- end -}} ) } + +// Equal{{.Len}} returns whether the host tuple is equal to the other tuple. +// All tuple elements of the host and guest parameters must match the "comparable" built-in constraint. +// To test equality of tuples that hold custom Equalable values, use the Equal{{.Len}}E function. +// To test equality of tuples that hold custom Comparable values, use the Equal{{.Len}}C function. +// Otherwise, use Equal or reflect.DeepEqual to test tuples of any types. +func Equal{{.Len}}[{{genericTypesDecl .Indexes "comparable"}}](host, guest T{{.Len}}[{{.GenericTypesForward}}]) bool { + return {{range $index, $num := .Indexes}}{{if gt $index 0}} && {{end}}host.V{{$num}} == guest.V{{$num}}{{end}} +} + +// Equal{{.Len}}E returns whether the host tuple is semantically equal to the guest tuple. +// All tuple elements of the host and guest parameters must match the Equalable constraint. +// To test equality of tuples that hold built-in "comparable" values, use the Equal{{.Len}} function. +// To test equality of tuples that hold custom Comparable values, use the Equal{{.Len}}C function. +// Otherwise, use Equal or reflect.DeepEqual to test tuples of any types. +func Equal{{.Len}}E[{{genericTypesDeclGenericConstraint .Indexes "Equalable"}}](host, guest T{{.Len}}[{{.GenericTypesForward}}]) bool { + return {{range $index, $num := .Indexes}}{{if gt $index 0}} && {{end}}host.V{{$num}}.Equal(guest.V{{$num}}){{end}} +} + +// Equal{{.Len}}C returns whether the host tuple is semantically less than, equal to, or greater than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To test equality of tuples that hold built-in "comparable" values, use the Equal{{.Len}} function. +// To test equality of tuples that hold custom Equalable values, use the Equal{{.Len}}E function. +// Otherwise, use Equal or reflect.DeepEqual to test tuples of any types. +func Equal{{.Len}}C[{{genericTypesDeclGenericConstraint .Indexes "Comparable"}}](host, guest T{{.Len}}[{{.GenericTypesForward}}]) bool { + return {{range $index, $num := .Indexes}}{{if gt $index 0}} && {{end}}host.V{{$num}}.CompareTo(guest.V{{$num}}).EQ(){{end}} +} + +// Compare{{.Len}} returns whether the host tuple is semantically less than, equal to, or greater than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the Compare{{.Len}}C function. +func Compare{{.Len}}[{{genericTypesDecl .Indexes "constraints.Ordered"}}](host, guest T{{.Len}}[{{.GenericTypesForward}}]) OrderedComparisonResult { + return multiCompare({{range .Indexes}} + func () OrderedComparisonResult { return compareOrdered(host.V{{.}}, guest.V{{.}}) }, + {{end}}) +} + +// Compare{{.Len}}C returns whether the host tuple is semantically less than, equal to, or greater than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the Compare{{.Len}} function. +func Compare{{.Len}}C[{{genericTypesDeclGenericConstraint .Indexes "Comparable"}}](host, guest T{{.Len}}[{{.GenericTypesForward}}]) OrderedComparisonResult { + return multiCompare({{range .Indexes}} + func () OrderedComparisonResult { return host.V{{.}}.CompareTo(guest.V{{.}}) }, + {{end}}) +} + +// LessThan{{.Len}} returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the LessThan{{.Len}}C function. +func LessThan{{.Len}}[{{genericTypesDecl .Indexes "constraints.Ordered"}}](host, guest T{{.Len}}[{{.GenericTypesForward}}]) bool { + return Compare{{.Len}}(host, guest).LT() +} + +// LessThan{{.Len}}C returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the LessThan{{.Len}} function. +func LessThan{{.Len}}C[{{genericTypesDeclGenericConstraint .Indexes "Comparable"}}](host, guest T{{.Len}}[{{.GenericTypesForward}}]) bool { + return Compare{{.Len}}C(host, guest).LT() +} + +// LessOrEqual{{.Len}} returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the LessOrEqual{{.Len}}C function. +func LessOrEqual{{.Len}}[{{genericTypesDecl .Indexes "constraints.Ordered"}}](host, guest T{{.Len}}[{{.GenericTypesForward}}]) bool { + return Compare{{.Len}}(host, guest).LE() +} + +// LessOrEqual{{.Len}}C returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the LessOrEqual{{.Len}} function. +func LessOrEqual{{.Len}}C[{{genericTypesDeclGenericConstraint .Indexes "Comparable"}}](host, guest T{{.Len}}[{{.GenericTypesForward}}]) bool { + return Compare{{.Len}}C(host, guest).LE() +} + +// GreaterThan{{.Len}} returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the GreaterThan{{.Len}}C function. +func GreaterThan{{.Len}}[{{genericTypesDecl .Indexes "constraints.Ordered"}}](host, guest T{{.Len}}[{{.GenericTypesForward}}]) bool { + return Compare{{.Len}}(host, guest).GT() +} + +// GreaterThan{{.Len}}C returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the GreaterThan{{.Len}} function. +func GreaterThan{{.Len}}C[{{genericTypesDeclGenericConstraint .Indexes "Comparable"}}](host, guest T{{.Len}}[{{.GenericTypesForward}}]) bool { + return Compare{{.Len}}C(host, guest).GT() +} + +// GreaterOrEqual{{.Len}} returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the GreaterOrEqual{{.Len}}C function. +func GreaterOrEqual{{.Len}}[{{genericTypesDecl .Indexes "constraints.Ordered"}}](host, guest T{{.Len}}[{{.GenericTypesForward}}]) bool { + return Compare{{.Len}}(host, guest).GE() +} + +// GreaterOrEqual{{.Len}}C returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the GreaterOrEqual{{.Len}} function. +func GreaterOrEqual{{.Len}}C[{{genericTypesDeclGenericConstraint .Indexes "Comparable"}}](host, guest T{{.Len}}[{{.GenericTypesForward}}]) bool { + return Compare{{.Len}}C(host, guest).GE() +} diff --git a/scripts/gen/tuple_test.tpl b/scripts/gen/tuple_test.tpl index 9bc14a6..ec9f408 100644 --- a/scripts/gen/tuple_test.tpl +++ b/scripts/gen/tuple_test.tpl @@ -11,7 +11,7 @@ import ( {{$len := .Len}} func TestT{{.Len}}_New(t *testing.T) { - tup := New{{len .Indexes}}({{range .Indexes}}{{. | quote}},{{end}}) + tup := New{{.Len}}({{range .Indexes}}{{. | quote}},{{end}}) require.Equal(t, T{{.Len}}[{{range $i, $index := .Indexes}}{{if gt $i 0}}, {{end}}string{{end}}]{ {{range .Indexes -}} V{{.}}: {{. | quote}}, @@ -20,25 +20,213 @@ func TestT{{.Len}}_New(t *testing.T) { } func TestT{{.Len}}_Len(t *testing.T) { - tup := New{{len .Indexes}}({{range .Indexes}}{{. | quote}},{{end}}) + tup := New{{.Len}}({{range .Indexes}}{{. | quote}},{{end}}) require.Equal(t, {{.Len}}, tup.Len()) } func TestT{{.Len}}_Values(t *testing.T) { - tup := New{{len .Indexes}}({{range .Indexes}}{{. | quote}},{{end}}) + tup := New{{.Len}}({{range .Indexes}}{{. | quote}},{{end}}) {{range $i, $index := .Indexes}}{{if gt $i 0}}, {{end}}v{{$index}}{{end}} := tup.Values() {{range .Indexes -}} require.Equal(t, {{. | quote}}, v{{.}}) {{end -}} } +func TestT{{.Len}}_Compare(t *testing.T) { + lesser := New{{.Len}}({{range .Indexes}}{{.}},{{end}}) + greater := New{{.Len}}({{range .Indexes}}{{. | inc}},{{end}}) + + tests := []struct{ + name string + host, guest T{{.Len}}[{{range $i, $index := .Indexes}}{{if gt $i 0}}, {{end}}int{{end}}] + want OrderedComparisonResult + wantEQ bool + wantLT bool + wantLE bool + wantGT bool + wantGE bool + }{ + { + name: "less than", + host: lesser, + guest: greater, + want: -1, + wantLT: true, + wantLE: true, + }, + { + name: "greater than", + host: greater, + guest: lesser, + want: 1, + wantGT: true, + wantGE: true, + }, + { + name: "equal", + host: lesser, + guest: lesser, + want: 0, + wantEQ: true, + wantLE: true, + wantGE: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func (t *testing.T) { + got := Compare{{.Len}}(tt.host, tt.guest) + require.Equal(t, tt.want, got) + require.Equal(t, tt.wantEQ, got.EQ()) + require.Equal(t, tt.wantLT, got.LT()) + require.Equal(t, tt.wantLE, got.LE()) + require.Equal(t, tt.wantGT, got.GT()) + require.Equal(t, tt.wantGE, got.GE()) + + require.Equal(t, tt.wantEQ, Equal{{.Len}}(tt.host, tt.guest)) + require.Equal(t, tt.wantLT, LessThan{{.Len}}(tt.host, tt.guest)) + require.Equal(t, tt.wantLE, LessOrEqual{{.Len}}(tt.host, tt.guest)) + require.Equal(t, tt.wantGT, GreaterThan{{.Len}}(tt.host, tt.guest)) + require.Equal(t, tt.wantGE, GreaterOrEqual{{.Len}}(tt.host, tt.guest)) + }) + } +} + +func TestT{{.Len}}_Compare_Approx(t *testing.T) { + lesser := New{{.Len}}({{range .Indexes}}approximationHelper({{. | quote}}),{{end}}) + greater := New{{.Len}}({{range .Indexes}}approximationHelper({{. | inc | quote}}),{{end}}) + + tests := []struct{ + name string + host, guest T{{.Len}}[{{range $i, $index := .Indexes}}{{if gt $i 0}}, {{end}}approximationHelper{{end}}] + want OrderedComparisonResult + wantEQ bool + wantLT bool + wantLE bool + wantGT bool + wantGE bool + }{ + { + name: "less than", + host: lesser, + guest: greater, + want: -1, + wantLT: true, + wantLE: true, + }, + { + name: "greater than", + host: greater, + guest: lesser, + want: 1, + wantGT: true, + wantGE: true, + }, + { + name: "equal", + host: lesser, + guest: lesser, + want: 0, + wantEQ: true, + wantLE: true, + wantGE: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func (t *testing.T) { + got := Compare{{.Len}}(tt.host, tt.guest) + require.Equal(t, tt.want, got) + require.Equal(t, tt.wantEQ, got.EQ()) + require.Equal(t, tt.wantLT, got.LT()) + require.Equal(t, tt.wantLE, got.LE()) + require.Equal(t, tt.wantGT, got.GT()) + require.Equal(t, tt.wantGE, got.GE()) + + require.Equal(t, tt.wantEQ, Equal{{.Len}}(tt.host, tt.guest)) + require.Equal(t, tt.wantLT, LessThan{{.Len}}(tt.host, tt.guest)) + require.Equal(t, tt.wantLE, LessOrEqual{{.Len}}(tt.host, tt.guest)) + require.Equal(t, tt.wantGT, GreaterThan{{.Len}}(tt.host, tt.guest)) + require.Equal(t, tt.wantGE, GreaterOrEqual{{.Len}}(tt.host, tt.guest)) + }) + } +} + +func TestT{{.Len}}_CompareC(t *testing.T) { + lesser := New{{.Len}}({{range .Indexes}}stringComparable({{. | quote}}),{{end}}) + greater := New{{.Len}}({{range .Indexes}}stringComparable({{. | inc | quote}}),{{end}}) + + tests := []struct{ + name string + host, guest T{{.Len}}[{{range $i, $index := .Indexes}}{{if gt $i 0}}, {{end}}stringComparable{{end}}] + want OrderedComparisonResult + wantEQ bool + wantLT bool + wantLE bool + wantGT bool + wantGE bool + }{ + { + name: "less than", + host: lesser, + guest: greater, + want: -1, + wantLT: true, + wantLE: true, + }, + { + name: "greater than", + host: greater, + guest: lesser, + want: 1, + wantGT: true, + wantGE: true, + }, + { + name: "equal", + host: lesser, + guest: lesser, + want: 0, + wantEQ: true, + wantLE: true, + wantGE: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func (t *testing.T) { + got := Compare{{.Len}}C(tt.host, tt.guest) + require.Equal(t, tt.want, got) + require.Equal(t, tt.wantEQ, got.EQ()) + require.Equal(t, tt.wantLT, got.LT()) + require.Equal(t, tt.wantLE, got.LE()) + require.Equal(t, tt.wantGT, got.GT()) + require.Equal(t, tt.wantGE, got.GE()) + + require.Equal(t, tt.wantEQ, Equal{{.Len}}C(tt.host, tt.guest)) + require.Equal(t, tt.wantLT, LessThan{{.Len}}C(tt.host, tt.guest)) + require.Equal(t, tt.wantLE, LessOrEqual{{.Len}}C(tt.host, tt.guest)) + require.Equal(t, tt.wantGT, GreaterThan{{.Len}}C(tt.host, tt.guest)) + require.Equal(t, tt.wantGE, GreaterOrEqual{{.Len}}C(tt.host, tt.guest)) + }) + } +} + +func TestT{{.Len}}_EqualE(t *testing.T) { + a := New{{.Len}}({{range .Indexes}}intEqualable({{.}}),{{end}}) + b := New{{.Len}}({{range .Indexes}}intEqualable({{. | inc}}),{{end}}) + + require.False(t, Equal{{.Len}}E(a, b)) + require.True(t, Equal{{.Len}}E(a, a)) +} + func TestT{{.Len}}_String(t *testing.T) { - tup := New{{len .Indexes}}({{range .Indexes}}{{. | quote}},{{end}}) + tup := New{{.Len}}({{range .Indexes}}{{. | quote}},{{end}}) require.Equal(t, `[{{range $i, $index := .Indexes}}{{if gt $i 0}} {{end}}{{. | quote}}{{end}}]`, tup.String()) } func TestT{{.Len}}_GoString(t *testing.T) { - tup := New{{len .Indexes}}({{range .Indexes}}{{. | quote}},{{end}}) + tup := New{{.Len}}({{range .Indexes}}{{. | quote}},{{end}}) require.Equal(t, `tuple.T{{.Len}}[{{range $i, $index := .Indexes}}{{if gt $i 0}}, {{end}}string{{end}}]{ {{- range $i, $index := .Indexes -}} {{- if gt $i 0}}, {{end -}} @@ -48,14 +236,14 @@ func TestT{{.Len}}_GoString(t *testing.T) { } func TestT{{.Len}}_ToArray(t *testing.T) { - tup := New{{len .Indexes}}({{range .Indexes}}{{. | quote}},{{end}}) + tup := New{{.Len}}({{range .Indexes}}{{. | quote}},{{end}}) require.Equal(t, [{{.Len}}]any{ {{range .Indexes -}}{{. | quote}},{{end}} }, tup.Array()) } func TestT{{.Len}}_ToSlice(t *testing.T) { - tup := New{{len .Indexes}}({{range .Indexes}}{{. | quote}},{{end}}) + tup := New{{.Len}}({{range .Indexes}}{{. | quote}},{{end}}) require.Equal(t, []any{ {{range .Indexes -}}{{. | quote}},{{end}} }, tup.Slice()) @@ -104,7 +292,7 @@ func TestT{{.Len}}_FromArrayX(t *testing.T) { return } - require.Equal(t, New{{len .Indexes}}({{range .Indexes}}{{. | quote}},{{end}}), do()) + require.Equal(t, New{{.Len}}({{range .Indexes}}{{. | quote}},{{end}}), do()) }) } } @@ -148,7 +336,7 @@ func TestT{{.Len}}_FromArray(t *testing.T) { } require.NoError(t, err) - require.Equal(t, New{{len .Indexes}}({{range .Indexes}}{{. | quote}},{{end}}), tup) + require.Equal(t, New{{.Len}}({{range .Indexes}}{{. | quote}},{{end}}), tup) }) } } @@ -216,7 +404,7 @@ func TestT{{.Len}}_FromSliceX(t *testing.T) { return } - require.Equal(t, New{{len .Indexes}}({{range .Indexes}}{{. | quote}},{{end}}), do()) + require.Equal(t, New{{.Len}}({{range .Indexes}}{{. | quote}},{{end}}), do()) }) } } @@ -280,7 +468,7 @@ func TestT{{.Len}}_FromSlice(t *testing.T) { } require.NoError(t, err) - require.Equal(t, New{{len .Indexes}}({{range .Indexes}}{{. | quote}},{{end}}), tup) + require.Equal(t, New{{.Len}}({{range .Indexes}}{{. | quote}},{{end}}), tup) }) } } diff --git a/tuple1.go b/tuple1.go index 18ae6ac..69ed0e6 100644 --- a/tuple1.go +++ b/tuple1.go @@ -1,6 +1,7 @@ package tuple import ( + "constraints" "fmt" ) @@ -92,3 +93,104 @@ func FromSlice1X[Ty1 any](values []any) T1[Ty1] { return New1(v1) } + +// Equal1 returns whether the host tuple is equal to the other tuple. +// All tuple elements of the host and guest parameters must match the "comparable" built-in constraint. +// To test equality of tuples that hold custom Equalable values, use the Equal1E function. +// To test equality of tuples that hold custom Comparable values, use the Equal1C function. +// Otherwise, use Equal or reflect.DeepEqual to test tuples of any types. +func Equal1[Ty1 comparable](host, guest T1[Ty1]) bool { + return host.V1 == guest.V1 +} + +// Equal1E returns whether the host tuple is semantically equal to the guest tuple. +// All tuple elements of the host and guest parameters must match the Equalable constraint. +// To test equality of tuples that hold built-in "comparable" values, use the Equal1 function. +// To test equality of tuples that hold custom Comparable values, use the Equal1C function. +// Otherwise, use Equal or reflect.DeepEqual to test tuples of any types. +func Equal1E[Ty1 Equalable[Ty1]](host, guest T1[Ty1]) bool { + return host.V1.Equal(guest.V1) +} + +// Equal1C returns whether the host tuple is semantically less than, equal to, or greater than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To test equality of tuples that hold built-in "comparable" values, use the Equal1 function. +// To test equality of tuples that hold custom Equalable values, use the Equal1E function. +// Otherwise, use Equal or reflect.DeepEqual to test tuples of any types. +func Equal1C[Ty1 Comparable[Ty1]](host, guest T1[Ty1]) bool { + return host.V1.CompareTo(guest.V1).EQ() +} + +// Compare1 returns whether the host tuple is semantically less than, equal to, or greater than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the Compare1C function. +func Compare1[Ty1 constraints.Ordered](host, guest T1[Ty1]) OrderedComparisonResult { + return multiCompare( + func() OrderedComparisonResult { return compareOrdered(host.V1, guest.V1) }, + ) +} + +// Compare1C returns whether the host tuple is semantically less than, equal to, or greater than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the Compare1 function. +func Compare1C[Ty1 Comparable[Ty1]](host, guest T1[Ty1]) OrderedComparisonResult { + return multiCompare( + func() OrderedComparisonResult { return host.V1.CompareTo(guest.V1) }, + ) +} + +// LessThan1 returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the LessThan1C function. +func LessThan1[Ty1 constraints.Ordered](host, guest T1[Ty1]) bool { + return Compare1(host, guest).LT() +} + +// LessThan1C returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the LessThan1 function. +func LessThan1C[Ty1 Comparable[Ty1]](host, guest T1[Ty1]) bool { + return Compare1C(host, guest).LT() +} + +// LessOrEqual1 returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the LessOrEqual1C function. +func LessOrEqual1[Ty1 constraints.Ordered](host, guest T1[Ty1]) bool { + return Compare1(host, guest).LE() +} + +// LessOrEqual1C returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the LessOrEqual1 function. +func LessOrEqual1C[Ty1 Comparable[Ty1]](host, guest T1[Ty1]) bool { + return Compare1C(host, guest).LE() +} + +// GreaterThan1 returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the GreaterThan1C function. +func GreaterThan1[Ty1 constraints.Ordered](host, guest T1[Ty1]) bool { + return Compare1(host, guest).GT() +} + +// GreaterThan1C returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the GreaterThan1 function. +func GreaterThan1C[Ty1 Comparable[Ty1]](host, guest T1[Ty1]) bool { + return Compare1C(host, guest).GT() +} + +// GreaterOrEqual1 returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the GreaterOrEqual1C function. +func GreaterOrEqual1[Ty1 constraints.Ordered](host, guest T1[Ty1]) bool { + return Compare1(host, guest).GE() +} + +// GreaterOrEqual1C returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the GreaterOrEqual1 function. +func GreaterOrEqual1C[Ty1 Comparable[Ty1]](host, guest T1[Ty1]) bool { + return Compare1C(host, guest).GE() +} diff --git a/tuple1_test.go b/tuple1_test.go index a74c518..6dbd44b 100644 --- a/tuple1_test.go +++ b/tuple1_test.go @@ -24,6 +24,194 @@ func TestT1_Values(t *testing.T) { require.Equal(t, "1", v1) } +func TestT1_Compare(t *testing.T) { + lesser := New1(1) + greater := New1(2) + + tests := []struct { + name string + host, guest T1[int] + want OrderedComparisonResult + wantEQ bool + wantLT bool + wantLE bool + wantGT bool + wantGE bool + }{ + { + name: "less than", + host: lesser, + guest: greater, + want: -1, + wantLT: true, + wantLE: true, + }, + { + name: "greater than", + host: greater, + guest: lesser, + want: 1, + wantGT: true, + wantGE: true, + }, + { + name: "equal", + host: lesser, + guest: lesser, + want: 0, + wantEQ: true, + wantLE: true, + wantGE: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := Compare1(tt.host, tt.guest) + require.Equal(t, tt.want, got) + require.Equal(t, tt.wantEQ, got.EQ()) + require.Equal(t, tt.wantLT, got.LT()) + require.Equal(t, tt.wantLE, got.LE()) + require.Equal(t, tt.wantGT, got.GT()) + require.Equal(t, tt.wantGE, got.GE()) + + require.Equal(t, tt.wantEQ, Equal1(tt.host, tt.guest)) + require.Equal(t, tt.wantLT, LessThan1(tt.host, tt.guest)) + require.Equal(t, tt.wantLE, LessOrEqual1(tt.host, tt.guest)) + require.Equal(t, tt.wantGT, GreaterThan1(tt.host, tt.guest)) + require.Equal(t, tt.wantGE, GreaterOrEqual1(tt.host, tt.guest)) + }) + } +} + +func TestT1_Compare_Approx(t *testing.T) { + lesser := New1(approximationHelper("1")) + greater := New1(approximationHelper("2")) + + tests := []struct { + name string + host, guest T1[approximationHelper] + want OrderedComparisonResult + wantEQ bool + wantLT bool + wantLE bool + wantGT bool + wantGE bool + }{ + { + name: "less than", + host: lesser, + guest: greater, + want: -1, + wantLT: true, + wantLE: true, + }, + { + name: "greater than", + host: greater, + guest: lesser, + want: 1, + wantGT: true, + wantGE: true, + }, + { + name: "equal", + host: lesser, + guest: lesser, + want: 0, + wantEQ: true, + wantLE: true, + wantGE: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := Compare1(tt.host, tt.guest) + require.Equal(t, tt.want, got) + require.Equal(t, tt.wantEQ, got.EQ()) + require.Equal(t, tt.wantLT, got.LT()) + require.Equal(t, tt.wantLE, got.LE()) + require.Equal(t, tt.wantGT, got.GT()) + require.Equal(t, tt.wantGE, got.GE()) + + require.Equal(t, tt.wantEQ, Equal1(tt.host, tt.guest)) + require.Equal(t, tt.wantLT, LessThan1(tt.host, tt.guest)) + require.Equal(t, tt.wantLE, LessOrEqual1(tt.host, tt.guest)) + require.Equal(t, tt.wantGT, GreaterThan1(tt.host, tt.guest)) + require.Equal(t, tt.wantGE, GreaterOrEqual1(tt.host, tt.guest)) + }) + } +} + +func TestT1_CompareC(t *testing.T) { + lesser := New1(stringComparable("1")) + greater := New1(stringComparable("2")) + + tests := []struct { + name string + host, guest T1[stringComparable] + want OrderedComparisonResult + wantEQ bool + wantLT bool + wantLE bool + wantGT bool + wantGE bool + }{ + { + name: "less than", + host: lesser, + guest: greater, + want: -1, + wantLT: true, + wantLE: true, + }, + { + name: "greater than", + host: greater, + guest: lesser, + want: 1, + wantGT: true, + wantGE: true, + }, + { + name: "equal", + host: lesser, + guest: lesser, + want: 0, + wantEQ: true, + wantLE: true, + wantGE: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := Compare1C(tt.host, tt.guest) + require.Equal(t, tt.want, got) + require.Equal(t, tt.wantEQ, got.EQ()) + require.Equal(t, tt.wantLT, got.LT()) + require.Equal(t, tt.wantLE, got.LE()) + require.Equal(t, tt.wantGT, got.GT()) + require.Equal(t, tt.wantGE, got.GE()) + + require.Equal(t, tt.wantEQ, Equal1C(tt.host, tt.guest)) + require.Equal(t, tt.wantLT, LessThan1C(tt.host, tt.guest)) + require.Equal(t, tt.wantLE, LessOrEqual1C(tt.host, tt.guest)) + require.Equal(t, tt.wantGT, GreaterThan1C(tt.host, tt.guest)) + require.Equal(t, tt.wantGE, GreaterOrEqual1C(tt.host, tt.guest)) + }) + } +} + +func TestT1_EqualE(t *testing.T) { + a := New1(intEqualable(1)) + b := New1(intEqualable(2)) + + require.False(t, Equal1E(a, b)) + require.True(t, Equal1E(a, a)) +} + func TestT1_String(t *testing.T) { tup := New1("1") require.Equal(t, `["1"]`, tup.String()) diff --git a/tuple2.go b/tuple2.go index 9ac719d..cff87ae 100644 --- a/tuple2.go +++ b/tuple2.go @@ -1,6 +1,7 @@ package tuple import ( + "constraints" "fmt" ) @@ -104,3 +105,108 @@ func FromSlice2X[Ty1, Ty2 any](values []any) T2[Ty1, Ty2] { return New2(v1, v2) } + +// Equal2 returns whether the host tuple is equal to the other tuple. +// All tuple elements of the host and guest parameters must match the "comparable" built-in constraint. +// To test equality of tuples that hold custom Equalable values, use the Equal2E function. +// To test equality of tuples that hold custom Comparable values, use the Equal2C function. +// Otherwise, use Equal or reflect.DeepEqual to test tuples of any types. +func Equal2[Ty1, Ty2 comparable](host, guest T2[Ty1, Ty2]) bool { + return host.V1 == guest.V1 && host.V2 == guest.V2 +} + +// Equal2E returns whether the host tuple is semantically equal to the guest tuple. +// All tuple elements of the host and guest parameters must match the Equalable constraint. +// To test equality of tuples that hold built-in "comparable" values, use the Equal2 function. +// To test equality of tuples that hold custom Comparable values, use the Equal2C function. +// Otherwise, use Equal or reflect.DeepEqual to test tuples of any types. +func Equal2E[Ty1 Equalable[Ty1], Ty2 Equalable[Ty2]](host, guest T2[Ty1, Ty2]) bool { + return host.V1.Equal(guest.V1) && host.V2.Equal(guest.V2) +} + +// Equal2C returns whether the host tuple is semantically less than, equal to, or greater than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To test equality of tuples that hold built-in "comparable" values, use the Equal2 function. +// To test equality of tuples that hold custom Equalable values, use the Equal2E function. +// Otherwise, use Equal or reflect.DeepEqual to test tuples of any types. +func Equal2C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2]](host, guest T2[Ty1, Ty2]) bool { + return host.V1.CompareTo(guest.V1).EQ() && host.V2.CompareTo(guest.V2).EQ() +} + +// Compare2 returns whether the host tuple is semantically less than, equal to, or greater than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the Compare2C function. +func Compare2[Ty1, Ty2 constraints.Ordered](host, guest T2[Ty1, Ty2]) OrderedComparisonResult { + return multiCompare( + func() OrderedComparisonResult { return compareOrdered(host.V1, guest.V1) }, + + func() OrderedComparisonResult { return compareOrdered(host.V2, guest.V2) }, + ) +} + +// Compare2C returns whether the host tuple is semantically less than, equal to, or greater than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the Compare2 function. +func Compare2C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2]](host, guest T2[Ty1, Ty2]) OrderedComparisonResult { + return multiCompare( + func() OrderedComparisonResult { return host.V1.CompareTo(guest.V1) }, + + func() OrderedComparisonResult { return host.V2.CompareTo(guest.V2) }, + ) +} + +// LessThan2 returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the LessThan2C function. +func LessThan2[Ty1, Ty2 constraints.Ordered](host, guest T2[Ty1, Ty2]) bool { + return Compare2(host, guest).LT() +} + +// LessThan2C returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the LessThan2 function. +func LessThan2C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2]](host, guest T2[Ty1, Ty2]) bool { + return Compare2C(host, guest).LT() +} + +// LessOrEqual2 returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the LessOrEqual2C function. +func LessOrEqual2[Ty1, Ty2 constraints.Ordered](host, guest T2[Ty1, Ty2]) bool { + return Compare2(host, guest).LE() +} + +// LessOrEqual2C returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the LessOrEqual2 function. +func LessOrEqual2C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2]](host, guest T2[Ty1, Ty2]) bool { + return Compare2C(host, guest).LE() +} + +// GreaterThan2 returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the GreaterThan2C function. +func GreaterThan2[Ty1, Ty2 constraints.Ordered](host, guest T2[Ty1, Ty2]) bool { + return Compare2(host, guest).GT() +} + +// GreaterThan2C returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the GreaterThan2 function. +func GreaterThan2C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2]](host, guest T2[Ty1, Ty2]) bool { + return Compare2C(host, guest).GT() +} + +// GreaterOrEqual2 returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the GreaterOrEqual2C function. +func GreaterOrEqual2[Ty1, Ty2 constraints.Ordered](host, guest T2[Ty1, Ty2]) bool { + return Compare2(host, guest).GE() +} + +// GreaterOrEqual2C returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the GreaterOrEqual2 function. +func GreaterOrEqual2C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2]](host, guest T2[Ty1, Ty2]) bool { + return Compare2C(host, guest).GE() +} diff --git a/tuple2_test.go b/tuple2_test.go index a8b1103..090ebf9 100644 --- a/tuple2_test.go +++ b/tuple2_test.go @@ -26,6 +26,194 @@ func TestT2_Values(t *testing.T) { require.Equal(t, "2", v2) } +func TestT2_Compare(t *testing.T) { + lesser := New2(1, 2) + greater := New2(2, 3) + + tests := []struct { + name string + host, guest T2[int, int] + want OrderedComparisonResult + wantEQ bool + wantLT bool + wantLE bool + wantGT bool + wantGE bool + }{ + { + name: "less than", + host: lesser, + guest: greater, + want: -1, + wantLT: true, + wantLE: true, + }, + { + name: "greater than", + host: greater, + guest: lesser, + want: 1, + wantGT: true, + wantGE: true, + }, + { + name: "equal", + host: lesser, + guest: lesser, + want: 0, + wantEQ: true, + wantLE: true, + wantGE: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := Compare2(tt.host, tt.guest) + require.Equal(t, tt.want, got) + require.Equal(t, tt.wantEQ, got.EQ()) + require.Equal(t, tt.wantLT, got.LT()) + require.Equal(t, tt.wantLE, got.LE()) + require.Equal(t, tt.wantGT, got.GT()) + require.Equal(t, tt.wantGE, got.GE()) + + require.Equal(t, tt.wantEQ, Equal2(tt.host, tt.guest)) + require.Equal(t, tt.wantLT, LessThan2(tt.host, tt.guest)) + require.Equal(t, tt.wantLE, LessOrEqual2(tt.host, tt.guest)) + require.Equal(t, tt.wantGT, GreaterThan2(tt.host, tt.guest)) + require.Equal(t, tt.wantGE, GreaterOrEqual2(tt.host, tt.guest)) + }) + } +} + +func TestT2_Compare_Approx(t *testing.T) { + lesser := New2(approximationHelper("1"), approximationHelper("2")) + greater := New2(approximationHelper("2"), approximationHelper("3")) + + tests := []struct { + name string + host, guest T2[approximationHelper, approximationHelper] + want OrderedComparisonResult + wantEQ bool + wantLT bool + wantLE bool + wantGT bool + wantGE bool + }{ + { + name: "less than", + host: lesser, + guest: greater, + want: -1, + wantLT: true, + wantLE: true, + }, + { + name: "greater than", + host: greater, + guest: lesser, + want: 1, + wantGT: true, + wantGE: true, + }, + { + name: "equal", + host: lesser, + guest: lesser, + want: 0, + wantEQ: true, + wantLE: true, + wantGE: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := Compare2(tt.host, tt.guest) + require.Equal(t, tt.want, got) + require.Equal(t, tt.wantEQ, got.EQ()) + require.Equal(t, tt.wantLT, got.LT()) + require.Equal(t, tt.wantLE, got.LE()) + require.Equal(t, tt.wantGT, got.GT()) + require.Equal(t, tt.wantGE, got.GE()) + + require.Equal(t, tt.wantEQ, Equal2(tt.host, tt.guest)) + require.Equal(t, tt.wantLT, LessThan2(tt.host, tt.guest)) + require.Equal(t, tt.wantLE, LessOrEqual2(tt.host, tt.guest)) + require.Equal(t, tt.wantGT, GreaterThan2(tt.host, tt.guest)) + require.Equal(t, tt.wantGE, GreaterOrEqual2(tt.host, tt.guest)) + }) + } +} + +func TestT2_CompareC(t *testing.T) { + lesser := New2(stringComparable("1"), stringComparable("2")) + greater := New2(stringComparable("2"), stringComparable("3")) + + tests := []struct { + name string + host, guest T2[stringComparable, stringComparable] + want OrderedComparisonResult + wantEQ bool + wantLT bool + wantLE bool + wantGT bool + wantGE bool + }{ + { + name: "less than", + host: lesser, + guest: greater, + want: -1, + wantLT: true, + wantLE: true, + }, + { + name: "greater than", + host: greater, + guest: lesser, + want: 1, + wantGT: true, + wantGE: true, + }, + { + name: "equal", + host: lesser, + guest: lesser, + want: 0, + wantEQ: true, + wantLE: true, + wantGE: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := Compare2C(tt.host, tt.guest) + require.Equal(t, tt.want, got) + require.Equal(t, tt.wantEQ, got.EQ()) + require.Equal(t, tt.wantLT, got.LT()) + require.Equal(t, tt.wantLE, got.LE()) + require.Equal(t, tt.wantGT, got.GT()) + require.Equal(t, tt.wantGE, got.GE()) + + require.Equal(t, tt.wantEQ, Equal2C(tt.host, tt.guest)) + require.Equal(t, tt.wantLT, LessThan2C(tt.host, tt.guest)) + require.Equal(t, tt.wantLE, LessOrEqual2C(tt.host, tt.guest)) + require.Equal(t, tt.wantGT, GreaterThan2C(tt.host, tt.guest)) + require.Equal(t, tt.wantGE, GreaterOrEqual2C(tt.host, tt.guest)) + }) + } +} + +func TestT2_EqualE(t *testing.T) { + a := New2(intEqualable(1), intEqualable(2)) + b := New2(intEqualable(2), intEqualable(3)) + + require.False(t, Equal2E(a, b)) + require.True(t, Equal2E(a, a)) +} + func TestT2_String(t *testing.T) { tup := New2("1", "2") require.Equal(t, `["1" "2"]`, tup.String()) diff --git a/tuple3.go b/tuple3.go index 7627d5a..060730c 100644 --- a/tuple3.go +++ b/tuple3.go @@ -1,6 +1,7 @@ package tuple import ( + "constraints" "fmt" ) @@ -116,3 +117,112 @@ func FromSlice3X[Ty1, Ty2, Ty3 any](values []any) T3[Ty1, Ty2, Ty3] { return New3(v1, v2, v3) } + +// Equal3 returns whether the host tuple is equal to the other tuple. +// All tuple elements of the host and guest parameters must match the "comparable" built-in constraint. +// To test equality of tuples that hold custom Equalable values, use the Equal3E function. +// To test equality of tuples that hold custom Comparable values, use the Equal3C function. +// Otherwise, use Equal or reflect.DeepEqual to test tuples of any types. +func Equal3[Ty1, Ty2, Ty3 comparable](host, guest T3[Ty1, Ty2, Ty3]) bool { + return host.V1 == guest.V1 && host.V2 == guest.V2 && host.V3 == guest.V3 +} + +// Equal3E returns whether the host tuple is semantically equal to the guest tuple. +// All tuple elements of the host and guest parameters must match the Equalable constraint. +// To test equality of tuples that hold built-in "comparable" values, use the Equal3 function. +// To test equality of tuples that hold custom Comparable values, use the Equal3C function. +// Otherwise, use Equal or reflect.DeepEqual to test tuples of any types. +func Equal3E[Ty1 Equalable[Ty1], Ty2 Equalable[Ty2], Ty3 Equalable[Ty3]](host, guest T3[Ty1, Ty2, Ty3]) bool { + return host.V1.Equal(guest.V1) && host.V2.Equal(guest.V2) && host.V3.Equal(guest.V3) +} + +// Equal3C returns whether the host tuple is semantically less than, equal to, or greater than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To test equality of tuples that hold built-in "comparable" values, use the Equal3 function. +// To test equality of tuples that hold custom Equalable values, use the Equal3E function. +// Otherwise, use Equal or reflect.DeepEqual to test tuples of any types. +func Equal3C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3]](host, guest T3[Ty1, Ty2, Ty3]) bool { + return host.V1.CompareTo(guest.V1).EQ() && host.V2.CompareTo(guest.V2).EQ() && host.V3.CompareTo(guest.V3).EQ() +} + +// Compare3 returns whether the host tuple is semantically less than, equal to, or greater than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the Compare3C function. +func Compare3[Ty1, Ty2, Ty3 constraints.Ordered](host, guest T3[Ty1, Ty2, Ty3]) OrderedComparisonResult { + return multiCompare( + func() OrderedComparisonResult { return compareOrdered(host.V1, guest.V1) }, + + func() OrderedComparisonResult { return compareOrdered(host.V2, guest.V2) }, + + func() OrderedComparisonResult { return compareOrdered(host.V3, guest.V3) }, + ) +} + +// Compare3C returns whether the host tuple is semantically less than, equal to, or greater than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the Compare3 function. +func Compare3C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3]](host, guest T3[Ty1, Ty2, Ty3]) OrderedComparisonResult { + return multiCompare( + func() OrderedComparisonResult { return host.V1.CompareTo(guest.V1) }, + + func() OrderedComparisonResult { return host.V2.CompareTo(guest.V2) }, + + func() OrderedComparisonResult { return host.V3.CompareTo(guest.V3) }, + ) +} + +// LessThan3 returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the LessThan3C function. +func LessThan3[Ty1, Ty2, Ty3 constraints.Ordered](host, guest T3[Ty1, Ty2, Ty3]) bool { + return Compare3(host, guest).LT() +} + +// LessThan3C returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the LessThan3 function. +func LessThan3C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3]](host, guest T3[Ty1, Ty2, Ty3]) bool { + return Compare3C(host, guest).LT() +} + +// LessOrEqual3 returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the LessOrEqual3C function. +func LessOrEqual3[Ty1, Ty2, Ty3 constraints.Ordered](host, guest T3[Ty1, Ty2, Ty3]) bool { + return Compare3(host, guest).LE() +} + +// LessOrEqual3C returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the LessOrEqual3 function. +func LessOrEqual3C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3]](host, guest T3[Ty1, Ty2, Ty3]) bool { + return Compare3C(host, guest).LE() +} + +// GreaterThan3 returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the GreaterThan3C function. +func GreaterThan3[Ty1, Ty2, Ty3 constraints.Ordered](host, guest T3[Ty1, Ty2, Ty3]) bool { + return Compare3(host, guest).GT() +} + +// GreaterThan3C returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the GreaterThan3 function. +func GreaterThan3C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3]](host, guest T3[Ty1, Ty2, Ty3]) bool { + return Compare3C(host, guest).GT() +} + +// GreaterOrEqual3 returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the GreaterOrEqual3C function. +func GreaterOrEqual3[Ty1, Ty2, Ty3 constraints.Ordered](host, guest T3[Ty1, Ty2, Ty3]) bool { + return Compare3(host, guest).GE() +} + +// GreaterOrEqual3C returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the GreaterOrEqual3 function. +func GreaterOrEqual3C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3]](host, guest T3[Ty1, Ty2, Ty3]) bool { + return Compare3C(host, guest).GE() +} diff --git a/tuple3_test.go b/tuple3_test.go index 6f00359..d6ea7af 100644 --- a/tuple3_test.go +++ b/tuple3_test.go @@ -28,6 +28,194 @@ func TestT3_Values(t *testing.T) { require.Equal(t, "3", v3) } +func TestT3_Compare(t *testing.T) { + lesser := New3(1, 2, 3) + greater := New3(2, 3, 4) + + tests := []struct { + name string + host, guest T3[int, int, int] + want OrderedComparisonResult + wantEQ bool + wantLT bool + wantLE bool + wantGT bool + wantGE bool + }{ + { + name: "less than", + host: lesser, + guest: greater, + want: -1, + wantLT: true, + wantLE: true, + }, + { + name: "greater than", + host: greater, + guest: lesser, + want: 1, + wantGT: true, + wantGE: true, + }, + { + name: "equal", + host: lesser, + guest: lesser, + want: 0, + wantEQ: true, + wantLE: true, + wantGE: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := Compare3(tt.host, tt.guest) + require.Equal(t, tt.want, got) + require.Equal(t, tt.wantEQ, got.EQ()) + require.Equal(t, tt.wantLT, got.LT()) + require.Equal(t, tt.wantLE, got.LE()) + require.Equal(t, tt.wantGT, got.GT()) + require.Equal(t, tt.wantGE, got.GE()) + + require.Equal(t, tt.wantEQ, Equal3(tt.host, tt.guest)) + require.Equal(t, tt.wantLT, LessThan3(tt.host, tt.guest)) + require.Equal(t, tt.wantLE, LessOrEqual3(tt.host, tt.guest)) + require.Equal(t, tt.wantGT, GreaterThan3(tt.host, tt.guest)) + require.Equal(t, tt.wantGE, GreaterOrEqual3(tt.host, tt.guest)) + }) + } +} + +func TestT3_Compare_Approx(t *testing.T) { + lesser := New3(approximationHelper("1"), approximationHelper("2"), approximationHelper("3")) + greater := New3(approximationHelper("2"), approximationHelper("3"), approximationHelper("4")) + + tests := []struct { + name string + host, guest T3[approximationHelper, approximationHelper, approximationHelper] + want OrderedComparisonResult + wantEQ bool + wantLT bool + wantLE bool + wantGT bool + wantGE bool + }{ + { + name: "less than", + host: lesser, + guest: greater, + want: -1, + wantLT: true, + wantLE: true, + }, + { + name: "greater than", + host: greater, + guest: lesser, + want: 1, + wantGT: true, + wantGE: true, + }, + { + name: "equal", + host: lesser, + guest: lesser, + want: 0, + wantEQ: true, + wantLE: true, + wantGE: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := Compare3(tt.host, tt.guest) + require.Equal(t, tt.want, got) + require.Equal(t, tt.wantEQ, got.EQ()) + require.Equal(t, tt.wantLT, got.LT()) + require.Equal(t, tt.wantLE, got.LE()) + require.Equal(t, tt.wantGT, got.GT()) + require.Equal(t, tt.wantGE, got.GE()) + + require.Equal(t, tt.wantEQ, Equal3(tt.host, tt.guest)) + require.Equal(t, tt.wantLT, LessThan3(tt.host, tt.guest)) + require.Equal(t, tt.wantLE, LessOrEqual3(tt.host, tt.guest)) + require.Equal(t, tt.wantGT, GreaterThan3(tt.host, tt.guest)) + require.Equal(t, tt.wantGE, GreaterOrEqual3(tt.host, tt.guest)) + }) + } +} + +func TestT3_CompareC(t *testing.T) { + lesser := New3(stringComparable("1"), stringComparable("2"), stringComparable("3")) + greater := New3(stringComparable("2"), stringComparable("3"), stringComparable("4")) + + tests := []struct { + name string + host, guest T3[stringComparable, stringComparable, stringComparable] + want OrderedComparisonResult + wantEQ bool + wantLT bool + wantLE bool + wantGT bool + wantGE bool + }{ + { + name: "less than", + host: lesser, + guest: greater, + want: -1, + wantLT: true, + wantLE: true, + }, + { + name: "greater than", + host: greater, + guest: lesser, + want: 1, + wantGT: true, + wantGE: true, + }, + { + name: "equal", + host: lesser, + guest: lesser, + want: 0, + wantEQ: true, + wantLE: true, + wantGE: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := Compare3C(tt.host, tt.guest) + require.Equal(t, tt.want, got) + require.Equal(t, tt.wantEQ, got.EQ()) + require.Equal(t, tt.wantLT, got.LT()) + require.Equal(t, tt.wantLE, got.LE()) + require.Equal(t, tt.wantGT, got.GT()) + require.Equal(t, tt.wantGE, got.GE()) + + require.Equal(t, tt.wantEQ, Equal3C(tt.host, tt.guest)) + require.Equal(t, tt.wantLT, LessThan3C(tt.host, tt.guest)) + require.Equal(t, tt.wantLE, LessOrEqual3C(tt.host, tt.guest)) + require.Equal(t, tt.wantGT, GreaterThan3C(tt.host, tt.guest)) + require.Equal(t, tt.wantGE, GreaterOrEqual3C(tt.host, tt.guest)) + }) + } +} + +func TestT3_EqualE(t *testing.T) { + a := New3(intEqualable(1), intEqualable(2), intEqualable(3)) + b := New3(intEqualable(2), intEqualable(3), intEqualable(4)) + + require.False(t, Equal3E(a, b)) + require.True(t, Equal3E(a, a)) +} + func TestT3_String(t *testing.T) { tup := New3("1", "2", "3") require.Equal(t, `["1" "2" "3"]`, tup.String()) diff --git a/tuple4.go b/tuple4.go index cbce815..0f74f91 100644 --- a/tuple4.go +++ b/tuple4.go @@ -1,6 +1,7 @@ package tuple import ( + "constraints" "fmt" ) @@ -128,3 +129,116 @@ func FromSlice4X[Ty1, Ty2, Ty3, Ty4 any](values []any) T4[Ty1, Ty2, Ty3, Ty4] { return New4(v1, v2, v3, v4) } + +// Equal4 returns whether the host tuple is equal to the other tuple. +// All tuple elements of the host and guest parameters must match the "comparable" built-in constraint. +// To test equality of tuples that hold custom Equalable values, use the Equal4E function. +// To test equality of tuples that hold custom Comparable values, use the Equal4C function. +// Otherwise, use Equal or reflect.DeepEqual to test tuples of any types. +func Equal4[Ty1, Ty2, Ty3, Ty4 comparable](host, guest T4[Ty1, Ty2, Ty3, Ty4]) bool { + return host.V1 == guest.V1 && host.V2 == guest.V2 && host.V3 == guest.V3 && host.V4 == guest.V4 +} + +// Equal4E returns whether the host tuple is semantically equal to the guest tuple. +// All tuple elements of the host and guest parameters must match the Equalable constraint. +// To test equality of tuples that hold built-in "comparable" values, use the Equal4 function. +// To test equality of tuples that hold custom Comparable values, use the Equal4C function. +// Otherwise, use Equal or reflect.DeepEqual to test tuples of any types. +func Equal4E[Ty1 Equalable[Ty1], Ty2 Equalable[Ty2], Ty3 Equalable[Ty3], Ty4 Equalable[Ty4]](host, guest T4[Ty1, Ty2, Ty3, Ty4]) bool { + return host.V1.Equal(guest.V1) && host.V2.Equal(guest.V2) && host.V3.Equal(guest.V3) && host.V4.Equal(guest.V4) +} + +// Equal4C returns whether the host tuple is semantically less than, equal to, or greater than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To test equality of tuples that hold built-in "comparable" values, use the Equal4 function. +// To test equality of tuples that hold custom Equalable values, use the Equal4E function. +// Otherwise, use Equal or reflect.DeepEqual to test tuples of any types. +func Equal4C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3], Ty4 Comparable[Ty4]](host, guest T4[Ty1, Ty2, Ty3, Ty4]) bool { + return host.V1.CompareTo(guest.V1).EQ() && host.V2.CompareTo(guest.V2).EQ() && host.V3.CompareTo(guest.V3).EQ() && host.V4.CompareTo(guest.V4).EQ() +} + +// Compare4 returns whether the host tuple is semantically less than, equal to, or greater than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the Compare4C function. +func Compare4[Ty1, Ty2, Ty3, Ty4 constraints.Ordered](host, guest T4[Ty1, Ty2, Ty3, Ty4]) OrderedComparisonResult { + return multiCompare( + func() OrderedComparisonResult { return compareOrdered(host.V1, guest.V1) }, + + func() OrderedComparisonResult { return compareOrdered(host.V2, guest.V2) }, + + func() OrderedComparisonResult { return compareOrdered(host.V3, guest.V3) }, + + func() OrderedComparisonResult { return compareOrdered(host.V4, guest.V4) }, + ) +} + +// Compare4C returns whether the host tuple is semantically less than, equal to, or greater than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the Compare4 function. +func Compare4C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3], Ty4 Comparable[Ty4]](host, guest T4[Ty1, Ty2, Ty3, Ty4]) OrderedComparisonResult { + return multiCompare( + func() OrderedComparisonResult { return host.V1.CompareTo(guest.V1) }, + + func() OrderedComparisonResult { return host.V2.CompareTo(guest.V2) }, + + func() OrderedComparisonResult { return host.V3.CompareTo(guest.V3) }, + + func() OrderedComparisonResult { return host.V4.CompareTo(guest.V4) }, + ) +} + +// LessThan4 returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the LessThan4C function. +func LessThan4[Ty1, Ty2, Ty3, Ty4 constraints.Ordered](host, guest T4[Ty1, Ty2, Ty3, Ty4]) bool { + return Compare4(host, guest).LT() +} + +// LessThan4C returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the LessThan4 function. +func LessThan4C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3], Ty4 Comparable[Ty4]](host, guest T4[Ty1, Ty2, Ty3, Ty4]) bool { + return Compare4C(host, guest).LT() +} + +// LessOrEqual4 returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the LessOrEqual4C function. +func LessOrEqual4[Ty1, Ty2, Ty3, Ty4 constraints.Ordered](host, guest T4[Ty1, Ty2, Ty3, Ty4]) bool { + return Compare4(host, guest).LE() +} + +// LessOrEqual4C returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the LessOrEqual4 function. +func LessOrEqual4C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3], Ty4 Comparable[Ty4]](host, guest T4[Ty1, Ty2, Ty3, Ty4]) bool { + return Compare4C(host, guest).LE() +} + +// GreaterThan4 returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the GreaterThan4C function. +func GreaterThan4[Ty1, Ty2, Ty3, Ty4 constraints.Ordered](host, guest T4[Ty1, Ty2, Ty3, Ty4]) bool { + return Compare4(host, guest).GT() +} + +// GreaterThan4C returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the GreaterThan4 function. +func GreaterThan4C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3], Ty4 Comparable[Ty4]](host, guest T4[Ty1, Ty2, Ty3, Ty4]) bool { + return Compare4C(host, guest).GT() +} + +// GreaterOrEqual4 returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the GreaterOrEqual4C function. +func GreaterOrEqual4[Ty1, Ty2, Ty3, Ty4 constraints.Ordered](host, guest T4[Ty1, Ty2, Ty3, Ty4]) bool { + return Compare4(host, guest).GE() +} + +// GreaterOrEqual4C returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the GreaterOrEqual4 function. +func GreaterOrEqual4C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3], Ty4 Comparable[Ty4]](host, guest T4[Ty1, Ty2, Ty3, Ty4]) bool { + return Compare4C(host, guest).GE() +} diff --git a/tuple4_test.go b/tuple4_test.go index 88b9f6b..9d7cddf 100644 --- a/tuple4_test.go +++ b/tuple4_test.go @@ -30,6 +30,194 @@ func TestT4_Values(t *testing.T) { require.Equal(t, "4", v4) } +func TestT4_Compare(t *testing.T) { + lesser := New4(1, 2, 3, 4) + greater := New4(2, 3, 4, 5) + + tests := []struct { + name string + host, guest T4[int, int, int, int] + want OrderedComparisonResult + wantEQ bool + wantLT bool + wantLE bool + wantGT bool + wantGE bool + }{ + { + name: "less than", + host: lesser, + guest: greater, + want: -1, + wantLT: true, + wantLE: true, + }, + { + name: "greater than", + host: greater, + guest: lesser, + want: 1, + wantGT: true, + wantGE: true, + }, + { + name: "equal", + host: lesser, + guest: lesser, + want: 0, + wantEQ: true, + wantLE: true, + wantGE: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := Compare4(tt.host, tt.guest) + require.Equal(t, tt.want, got) + require.Equal(t, tt.wantEQ, got.EQ()) + require.Equal(t, tt.wantLT, got.LT()) + require.Equal(t, tt.wantLE, got.LE()) + require.Equal(t, tt.wantGT, got.GT()) + require.Equal(t, tt.wantGE, got.GE()) + + require.Equal(t, tt.wantEQ, Equal4(tt.host, tt.guest)) + require.Equal(t, tt.wantLT, LessThan4(tt.host, tt.guest)) + require.Equal(t, tt.wantLE, LessOrEqual4(tt.host, tt.guest)) + require.Equal(t, tt.wantGT, GreaterThan4(tt.host, tt.guest)) + require.Equal(t, tt.wantGE, GreaterOrEqual4(tt.host, tt.guest)) + }) + } +} + +func TestT4_Compare_Approx(t *testing.T) { + lesser := New4(approximationHelper("1"), approximationHelper("2"), approximationHelper("3"), approximationHelper("4")) + greater := New4(approximationHelper("2"), approximationHelper("3"), approximationHelper("4"), approximationHelper("5")) + + tests := []struct { + name string + host, guest T4[approximationHelper, approximationHelper, approximationHelper, approximationHelper] + want OrderedComparisonResult + wantEQ bool + wantLT bool + wantLE bool + wantGT bool + wantGE bool + }{ + { + name: "less than", + host: lesser, + guest: greater, + want: -1, + wantLT: true, + wantLE: true, + }, + { + name: "greater than", + host: greater, + guest: lesser, + want: 1, + wantGT: true, + wantGE: true, + }, + { + name: "equal", + host: lesser, + guest: lesser, + want: 0, + wantEQ: true, + wantLE: true, + wantGE: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := Compare4(tt.host, tt.guest) + require.Equal(t, tt.want, got) + require.Equal(t, tt.wantEQ, got.EQ()) + require.Equal(t, tt.wantLT, got.LT()) + require.Equal(t, tt.wantLE, got.LE()) + require.Equal(t, tt.wantGT, got.GT()) + require.Equal(t, tt.wantGE, got.GE()) + + require.Equal(t, tt.wantEQ, Equal4(tt.host, tt.guest)) + require.Equal(t, tt.wantLT, LessThan4(tt.host, tt.guest)) + require.Equal(t, tt.wantLE, LessOrEqual4(tt.host, tt.guest)) + require.Equal(t, tt.wantGT, GreaterThan4(tt.host, tt.guest)) + require.Equal(t, tt.wantGE, GreaterOrEqual4(tt.host, tt.guest)) + }) + } +} + +func TestT4_CompareC(t *testing.T) { + lesser := New4(stringComparable("1"), stringComparable("2"), stringComparable("3"), stringComparable("4")) + greater := New4(stringComparable("2"), stringComparable("3"), stringComparable("4"), stringComparable("5")) + + tests := []struct { + name string + host, guest T4[stringComparable, stringComparable, stringComparable, stringComparable] + want OrderedComparisonResult + wantEQ bool + wantLT bool + wantLE bool + wantGT bool + wantGE bool + }{ + { + name: "less than", + host: lesser, + guest: greater, + want: -1, + wantLT: true, + wantLE: true, + }, + { + name: "greater than", + host: greater, + guest: lesser, + want: 1, + wantGT: true, + wantGE: true, + }, + { + name: "equal", + host: lesser, + guest: lesser, + want: 0, + wantEQ: true, + wantLE: true, + wantGE: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := Compare4C(tt.host, tt.guest) + require.Equal(t, tt.want, got) + require.Equal(t, tt.wantEQ, got.EQ()) + require.Equal(t, tt.wantLT, got.LT()) + require.Equal(t, tt.wantLE, got.LE()) + require.Equal(t, tt.wantGT, got.GT()) + require.Equal(t, tt.wantGE, got.GE()) + + require.Equal(t, tt.wantEQ, Equal4C(tt.host, tt.guest)) + require.Equal(t, tt.wantLT, LessThan4C(tt.host, tt.guest)) + require.Equal(t, tt.wantLE, LessOrEqual4C(tt.host, tt.guest)) + require.Equal(t, tt.wantGT, GreaterThan4C(tt.host, tt.guest)) + require.Equal(t, tt.wantGE, GreaterOrEqual4C(tt.host, tt.guest)) + }) + } +} + +func TestT4_EqualE(t *testing.T) { + a := New4(intEqualable(1), intEqualable(2), intEqualable(3), intEqualable(4)) + b := New4(intEqualable(2), intEqualable(3), intEqualable(4), intEqualable(5)) + + require.False(t, Equal4E(a, b)) + require.True(t, Equal4E(a, a)) +} + func TestT4_String(t *testing.T) { tup := New4("1", "2", "3", "4") require.Equal(t, `["1" "2" "3" "4"]`, tup.String()) diff --git a/tuple5.go b/tuple5.go index 5ca7e7f..5630d9d 100644 --- a/tuple5.go +++ b/tuple5.go @@ -1,6 +1,7 @@ package tuple import ( + "constraints" "fmt" ) @@ -140,3 +141,120 @@ func FromSlice5X[Ty1, Ty2, Ty3, Ty4, Ty5 any](values []any) T5[Ty1, Ty2, Ty3, Ty return New5(v1, v2, v3, v4, v5) } + +// Equal5 returns whether the host tuple is equal to the other tuple. +// All tuple elements of the host and guest parameters must match the "comparable" built-in constraint. +// To test equality of tuples that hold custom Equalable values, use the Equal5E function. +// To test equality of tuples that hold custom Comparable values, use the Equal5C function. +// Otherwise, use Equal or reflect.DeepEqual to test tuples of any types. +func Equal5[Ty1, Ty2, Ty3, Ty4, Ty5 comparable](host, guest T5[Ty1, Ty2, Ty3, Ty4, Ty5]) bool { + return host.V1 == guest.V1 && host.V2 == guest.V2 && host.V3 == guest.V3 && host.V4 == guest.V4 && host.V5 == guest.V5 +} + +// Equal5E returns whether the host tuple is semantically equal to the guest tuple. +// All tuple elements of the host and guest parameters must match the Equalable constraint. +// To test equality of tuples that hold built-in "comparable" values, use the Equal5 function. +// To test equality of tuples that hold custom Comparable values, use the Equal5C function. +// Otherwise, use Equal or reflect.DeepEqual to test tuples of any types. +func Equal5E[Ty1 Equalable[Ty1], Ty2 Equalable[Ty2], Ty3 Equalable[Ty3], Ty4 Equalable[Ty4], Ty5 Equalable[Ty5]](host, guest T5[Ty1, Ty2, Ty3, Ty4, Ty5]) bool { + return host.V1.Equal(guest.V1) && host.V2.Equal(guest.V2) && host.V3.Equal(guest.V3) && host.V4.Equal(guest.V4) && host.V5.Equal(guest.V5) +} + +// Equal5C returns whether the host tuple is semantically less than, equal to, or greater than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To test equality of tuples that hold built-in "comparable" values, use the Equal5 function. +// To test equality of tuples that hold custom Equalable values, use the Equal5E function. +// Otherwise, use Equal or reflect.DeepEqual to test tuples of any types. +func Equal5C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3], Ty4 Comparable[Ty4], Ty5 Comparable[Ty5]](host, guest T5[Ty1, Ty2, Ty3, Ty4, Ty5]) bool { + return host.V1.CompareTo(guest.V1).EQ() && host.V2.CompareTo(guest.V2).EQ() && host.V3.CompareTo(guest.V3).EQ() && host.V4.CompareTo(guest.V4).EQ() && host.V5.CompareTo(guest.V5).EQ() +} + +// Compare5 returns whether the host tuple is semantically less than, equal to, or greater than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the Compare5C function. +func Compare5[Ty1, Ty2, Ty3, Ty4, Ty5 constraints.Ordered](host, guest T5[Ty1, Ty2, Ty3, Ty4, Ty5]) OrderedComparisonResult { + return multiCompare( + func() OrderedComparisonResult { return compareOrdered(host.V1, guest.V1) }, + + func() OrderedComparisonResult { return compareOrdered(host.V2, guest.V2) }, + + func() OrderedComparisonResult { return compareOrdered(host.V3, guest.V3) }, + + func() OrderedComparisonResult { return compareOrdered(host.V4, guest.V4) }, + + func() OrderedComparisonResult { return compareOrdered(host.V5, guest.V5) }, + ) +} + +// Compare5C returns whether the host tuple is semantically less than, equal to, or greater than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the Compare5 function. +func Compare5C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3], Ty4 Comparable[Ty4], Ty5 Comparable[Ty5]](host, guest T5[Ty1, Ty2, Ty3, Ty4, Ty5]) OrderedComparisonResult { + return multiCompare( + func() OrderedComparisonResult { return host.V1.CompareTo(guest.V1) }, + + func() OrderedComparisonResult { return host.V2.CompareTo(guest.V2) }, + + func() OrderedComparisonResult { return host.V3.CompareTo(guest.V3) }, + + func() OrderedComparisonResult { return host.V4.CompareTo(guest.V4) }, + + func() OrderedComparisonResult { return host.V5.CompareTo(guest.V5) }, + ) +} + +// LessThan5 returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the LessThan5C function. +func LessThan5[Ty1, Ty2, Ty3, Ty4, Ty5 constraints.Ordered](host, guest T5[Ty1, Ty2, Ty3, Ty4, Ty5]) bool { + return Compare5(host, guest).LT() +} + +// LessThan5C returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the LessThan5 function. +func LessThan5C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3], Ty4 Comparable[Ty4], Ty5 Comparable[Ty5]](host, guest T5[Ty1, Ty2, Ty3, Ty4, Ty5]) bool { + return Compare5C(host, guest).LT() +} + +// LessOrEqual5 returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the LessOrEqual5C function. +func LessOrEqual5[Ty1, Ty2, Ty3, Ty4, Ty5 constraints.Ordered](host, guest T5[Ty1, Ty2, Ty3, Ty4, Ty5]) bool { + return Compare5(host, guest).LE() +} + +// LessOrEqual5C returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the LessOrEqual5 function. +func LessOrEqual5C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3], Ty4 Comparable[Ty4], Ty5 Comparable[Ty5]](host, guest T5[Ty1, Ty2, Ty3, Ty4, Ty5]) bool { + return Compare5C(host, guest).LE() +} + +// GreaterThan5 returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the GreaterThan5C function. +func GreaterThan5[Ty1, Ty2, Ty3, Ty4, Ty5 constraints.Ordered](host, guest T5[Ty1, Ty2, Ty3, Ty4, Ty5]) bool { + return Compare5(host, guest).GT() +} + +// GreaterThan5C returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the GreaterThan5 function. +func GreaterThan5C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3], Ty4 Comparable[Ty4], Ty5 Comparable[Ty5]](host, guest T5[Ty1, Ty2, Ty3, Ty4, Ty5]) bool { + return Compare5C(host, guest).GT() +} + +// GreaterOrEqual5 returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the GreaterOrEqual5C function. +func GreaterOrEqual5[Ty1, Ty2, Ty3, Ty4, Ty5 constraints.Ordered](host, guest T5[Ty1, Ty2, Ty3, Ty4, Ty5]) bool { + return Compare5(host, guest).GE() +} + +// GreaterOrEqual5C returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the GreaterOrEqual5 function. +func GreaterOrEqual5C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3], Ty4 Comparable[Ty4], Ty5 Comparable[Ty5]](host, guest T5[Ty1, Ty2, Ty3, Ty4, Ty5]) bool { + return Compare5C(host, guest).GE() +} diff --git a/tuple5_test.go b/tuple5_test.go index d23c42f..74ff5b5 100644 --- a/tuple5_test.go +++ b/tuple5_test.go @@ -32,6 +32,194 @@ func TestT5_Values(t *testing.T) { require.Equal(t, "5", v5) } +func TestT5_Compare(t *testing.T) { + lesser := New5(1, 2, 3, 4, 5) + greater := New5(2, 3, 4, 5, 6) + + tests := []struct { + name string + host, guest T5[int, int, int, int, int] + want OrderedComparisonResult + wantEQ bool + wantLT bool + wantLE bool + wantGT bool + wantGE bool + }{ + { + name: "less than", + host: lesser, + guest: greater, + want: -1, + wantLT: true, + wantLE: true, + }, + { + name: "greater than", + host: greater, + guest: lesser, + want: 1, + wantGT: true, + wantGE: true, + }, + { + name: "equal", + host: lesser, + guest: lesser, + want: 0, + wantEQ: true, + wantLE: true, + wantGE: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := Compare5(tt.host, tt.guest) + require.Equal(t, tt.want, got) + require.Equal(t, tt.wantEQ, got.EQ()) + require.Equal(t, tt.wantLT, got.LT()) + require.Equal(t, tt.wantLE, got.LE()) + require.Equal(t, tt.wantGT, got.GT()) + require.Equal(t, tt.wantGE, got.GE()) + + require.Equal(t, tt.wantEQ, Equal5(tt.host, tt.guest)) + require.Equal(t, tt.wantLT, LessThan5(tt.host, tt.guest)) + require.Equal(t, tt.wantLE, LessOrEqual5(tt.host, tt.guest)) + require.Equal(t, tt.wantGT, GreaterThan5(tt.host, tt.guest)) + require.Equal(t, tt.wantGE, GreaterOrEqual5(tt.host, tt.guest)) + }) + } +} + +func TestT5_Compare_Approx(t *testing.T) { + lesser := New5(approximationHelper("1"), approximationHelper("2"), approximationHelper("3"), approximationHelper("4"), approximationHelper("5")) + greater := New5(approximationHelper("2"), approximationHelper("3"), approximationHelper("4"), approximationHelper("5"), approximationHelper("6")) + + tests := []struct { + name string + host, guest T5[approximationHelper, approximationHelper, approximationHelper, approximationHelper, approximationHelper] + want OrderedComparisonResult + wantEQ bool + wantLT bool + wantLE bool + wantGT bool + wantGE bool + }{ + { + name: "less than", + host: lesser, + guest: greater, + want: -1, + wantLT: true, + wantLE: true, + }, + { + name: "greater than", + host: greater, + guest: lesser, + want: 1, + wantGT: true, + wantGE: true, + }, + { + name: "equal", + host: lesser, + guest: lesser, + want: 0, + wantEQ: true, + wantLE: true, + wantGE: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := Compare5(tt.host, tt.guest) + require.Equal(t, tt.want, got) + require.Equal(t, tt.wantEQ, got.EQ()) + require.Equal(t, tt.wantLT, got.LT()) + require.Equal(t, tt.wantLE, got.LE()) + require.Equal(t, tt.wantGT, got.GT()) + require.Equal(t, tt.wantGE, got.GE()) + + require.Equal(t, tt.wantEQ, Equal5(tt.host, tt.guest)) + require.Equal(t, tt.wantLT, LessThan5(tt.host, tt.guest)) + require.Equal(t, tt.wantLE, LessOrEqual5(tt.host, tt.guest)) + require.Equal(t, tt.wantGT, GreaterThan5(tt.host, tt.guest)) + require.Equal(t, tt.wantGE, GreaterOrEqual5(tt.host, tt.guest)) + }) + } +} + +func TestT5_CompareC(t *testing.T) { + lesser := New5(stringComparable("1"), stringComparable("2"), stringComparable("3"), stringComparable("4"), stringComparable("5")) + greater := New5(stringComparable("2"), stringComparable("3"), stringComparable("4"), stringComparable("5"), stringComparable("6")) + + tests := []struct { + name string + host, guest T5[stringComparable, stringComparable, stringComparable, stringComparable, stringComparable] + want OrderedComparisonResult + wantEQ bool + wantLT bool + wantLE bool + wantGT bool + wantGE bool + }{ + { + name: "less than", + host: lesser, + guest: greater, + want: -1, + wantLT: true, + wantLE: true, + }, + { + name: "greater than", + host: greater, + guest: lesser, + want: 1, + wantGT: true, + wantGE: true, + }, + { + name: "equal", + host: lesser, + guest: lesser, + want: 0, + wantEQ: true, + wantLE: true, + wantGE: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := Compare5C(tt.host, tt.guest) + require.Equal(t, tt.want, got) + require.Equal(t, tt.wantEQ, got.EQ()) + require.Equal(t, tt.wantLT, got.LT()) + require.Equal(t, tt.wantLE, got.LE()) + require.Equal(t, tt.wantGT, got.GT()) + require.Equal(t, tt.wantGE, got.GE()) + + require.Equal(t, tt.wantEQ, Equal5C(tt.host, tt.guest)) + require.Equal(t, tt.wantLT, LessThan5C(tt.host, tt.guest)) + require.Equal(t, tt.wantLE, LessOrEqual5C(tt.host, tt.guest)) + require.Equal(t, tt.wantGT, GreaterThan5C(tt.host, tt.guest)) + require.Equal(t, tt.wantGE, GreaterOrEqual5C(tt.host, tt.guest)) + }) + } +} + +func TestT5_EqualE(t *testing.T) { + a := New5(intEqualable(1), intEqualable(2), intEqualable(3), intEqualable(4), intEqualable(5)) + b := New5(intEqualable(2), intEqualable(3), intEqualable(4), intEqualable(5), intEqualable(6)) + + require.False(t, Equal5E(a, b)) + require.True(t, Equal5E(a, a)) +} + func TestT5_String(t *testing.T) { tup := New5("1", "2", "3", "4", "5") require.Equal(t, `["1" "2" "3" "4" "5"]`, tup.String()) diff --git a/tuple6.go b/tuple6.go index 08a07fe..d48cd07 100644 --- a/tuple6.go +++ b/tuple6.go @@ -1,6 +1,7 @@ package tuple import ( + "constraints" "fmt" ) @@ -152,3 +153,124 @@ func FromSlice6X[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6 any](values []any) T6[Ty1, Ty2, Ty return New6(v1, v2, v3, v4, v5, v6) } + +// Equal6 returns whether the host tuple is equal to the other tuple. +// All tuple elements of the host and guest parameters must match the "comparable" built-in constraint. +// To test equality of tuples that hold custom Equalable values, use the Equal6E function. +// To test equality of tuples that hold custom Comparable values, use the Equal6C function. +// Otherwise, use Equal or reflect.DeepEqual to test tuples of any types. +func Equal6[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6 comparable](host, guest T6[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6]) bool { + return host.V1 == guest.V1 && host.V2 == guest.V2 && host.V3 == guest.V3 && host.V4 == guest.V4 && host.V5 == guest.V5 && host.V6 == guest.V6 +} + +// Equal6E returns whether the host tuple is semantically equal to the guest tuple. +// All tuple elements of the host and guest parameters must match the Equalable constraint. +// To test equality of tuples that hold built-in "comparable" values, use the Equal6 function. +// To test equality of tuples that hold custom Comparable values, use the Equal6C function. +// Otherwise, use Equal or reflect.DeepEqual to test tuples of any types. +func Equal6E[Ty1 Equalable[Ty1], Ty2 Equalable[Ty2], Ty3 Equalable[Ty3], Ty4 Equalable[Ty4], Ty5 Equalable[Ty5], Ty6 Equalable[Ty6]](host, guest T6[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6]) bool { + return host.V1.Equal(guest.V1) && host.V2.Equal(guest.V2) && host.V3.Equal(guest.V3) && host.V4.Equal(guest.V4) && host.V5.Equal(guest.V5) && host.V6.Equal(guest.V6) +} + +// Equal6C returns whether the host tuple is semantically less than, equal to, or greater than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To test equality of tuples that hold built-in "comparable" values, use the Equal6 function. +// To test equality of tuples that hold custom Equalable values, use the Equal6E function. +// Otherwise, use Equal or reflect.DeepEqual to test tuples of any types. +func Equal6C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3], Ty4 Comparable[Ty4], Ty5 Comparable[Ty5], Ty6 Comparable[Ty6]](host, guest T6[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6]) bool { + return host.V1.CompareTo(guest.V1).EQ() && host.V2.CompareTo(guest.V2).EQ() && host.V3.CompareTo(guest.V3).EQ() && host.V4.CompareTo(guest.V4).EQ() && host.V5.CompareTo(guest.V5).EQ() && host.V6.CompareTo(guest.V6).EQ() +} + +// Compare6 returns whether the host tuple is semantically less than, equal to, or greater than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the Compare6C function. +func Compare6[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6 constraints.Ordered](host, guest T6[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6]) OrderedComparisonResult { + return multiCompare( + func() OrderedComparisonResult { return compareOrdered(host.V1, guest.V1) }, + + func() OrderedComparisonResult { return compareOrdered(host.V2, guest.V2) }, + + func() OrderedComparisonResult { return compareOrdered(host.V3, guest.V3) }, + + func() OrderedComparisonResult { return compareOrdered(host.V4, guest.V4) }, + + func() OrderedComparisonResult { return compareOrdered(host.V5, guest.V5) }, + + func() OrderedComparisonResult { return compareOrdered(host.V6, guest.V6) }, + ) +} + +// Compare6C returns whether the host tuple is semantically less than, equal to, or greater than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the Compare6 function. +func Compare6C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3], Ty4 Comparable[Ty4], Ty5 Comparable[Ty5], Ty6 Comparable[Ty6]](host, guest T6[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6]) OrderedComparisonResult { + return multiCompare( + func() OrderedComparisonResult { return host.V1.CompareTo(guest.V1) }, + + func() OrderedComparisonResult { return host.V2.CompareTo(guest.V2) }, + + func() OrderedComparisonResult { return host.V3.CompareTo(guest.V3) }, + + func() OrderedComparisonResult { return host.V4.CompareTo(guest.V4) }, + + func() OrderedComparisonResult { return host.V5.CompareTo(guest.V5) }, + + func() OrderedComparisonResult { return host.V6.CompareTo(guest.V6) }, + ) +} + +// LessThan6 returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the LessThan6C function. +func LessThan6[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6 constraints.Ordered](host, guest T6[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6]) bool { + return Compare6(host, guest).LT() +} + +// LessThan6C returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the LessThan6 function. +func LessThan6C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3], Ty4 Comparable[Ty4], Ty5 Comparable[Ty5], Ty6 Comparable[Ty6]](host, guest T6[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6]) bool { + return Compare6C(host, guest).LT() +} + +// LessOrEqual6 returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the LessOrEqual6C function. +func LessOrEqual6[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6 constraints.Ordered](host, guest T6[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6]) bool { + return Compare6(host, guest).LE() +} + +// LessOrEqual6C returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the LessOrEqual6 function. +func LessOrEqual6C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3], Ty4 Comparable[Ty4], Ty5 Comparable[Ty5], Ty6 Comparable[Ty6]](host, guest T6[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6]) bool { + return Compare6C(host, guest).LE() +} + +// GreaterThan6 returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the GreaterThan6C function. +func GreaterThan6[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6 constraints.Ordered](host, guest T6[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6]) bool { + return Compare6(host, guest).GT() +} + +// GreaterThan6C returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the GreaterThan6 function. +func GreaterThan6C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3], Ty4 Comparable[Ty4], Ty5 Comparable[Ty5], Ty6 Comparable[Ty6]](host, guest T6[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6]) bool { + return Compare6C(host, guest).GT() +} + +// GreaterOrEqual6 returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the GreaterOrEqual6C function. +func GreaterOrEqual6[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6 constraints.Ordered](host, guest T6[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6]) bool { + return Compare6(host, guest).GE() +} + +// GreaterOrEqual6C returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the GreaterOrEqual6 function. +func GreaterOrEqual6C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3], Ty4 Comparable[Ty4], Ty5 Comparable[Ty5], Ty6 Comparable[Ty6]](host, guest T6[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6]) bool { + return Compare6C(host, guest).GE() +} diff --git a/tuple6_test.go b/tuple6_test.go index 2872d6b..05ab09a 100644 --- a/tuple6_test.go +++ b/tuple6_test.go @@ -34,6 +34,194 @@ func TestT6_Values(t *testing.T) { require.Equal(t, "6", v6) } +func TestT6_Compare(t *testing.T) { + lesser := New6(1, 2, 3, 4, 5, 6) + greater := New6(2, 3, 4, 5, 6, 7) + + tests := []struct { + name string + host, guest T6[int, int, int, int, int, int] + want OrderedComparisonResult + wantEQ bool + wantLT bool + wantLE bool + wantGT bool + wantGE bool + }{ + { + name: "less than", + host: lesser, + guest: greater, + want: -1, + wantLT: true, + wantLE: true, + }, + { + name: "greater than", + host: greater, + guest: lesser, + want: 1, + wantGT: true, + wantGE: true, + }, + { + name: "equal", + host: lesser, + guest: lesser, + want: 0, + wantEQ: true, + wantLE: true, + wantGE: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := Compare6(tt.host, tt.guest) + require.Equal(t, tt.want, got) + require.Equal(t, tt.wantEQ, got.EQ()) + require.Equal(t, tt.wantLT, got.LT()) + require.Equal(t, tt.wantLE, got.LE()) + require.Equal(t, tt.wantGT, got.GT()) + require.Equal(t, tt.wantGE, got.GE()) + + require.Equal(t, tt.wantEQ, Equal6(tt.host, tt.guest)) + require.Equal(t, tt.wantLT, LessThan6(tt.host, tt.guest)) + require.Equal(t, tt.wantLE, LessOrEqual6(tt.host, tt.guest)) + require.Equal(t, tt.wantGT, GreaterThan6(tt.host, tt.guest)) + require.Equal(t, tt.wantGE, GreaterOrEqual6(tt.host, tt.guest)) + }) + } +} + +func TestT6_Compare_Approx(t *testing.T) { + lesser := New6(approximationHelper("1"), approximationHelper("2"), approximationHelper("3"), approximationHelper("4"), approximationHelper("5"), approximationHelper("6")) + greater := New6(approximationHelper("2"), approximationHelper("3"), approximationHelper("4"), approximationHelper("5"), approximationHelper("6"), approximationHelper("7")) + + tests := []struct { + name string + host, guest T6[approximationHelper, approximationHelper, approximationHelper, approximationHelper, approximationHelper, approximationHelper] + want OrderedComparisonResult + wantEQ bool + wantLT bool + wantLE bool + wantGT bool + wantGE bool + }{ + { + name: "less than", + host: lesser, + guest: greater, + want: -1, + wantLT: true, + wantLE: true, + }, + { + name: "greater than", + host: greater, + guest: lesser, + want: 1, + wantGT: true, + wantGE: true, + }, + { + name: "equal", + host: lesser, + guest: lesser, + want: 0, + wantEQ: true, + wantLE: true, + wantGE: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := Compare6(tt.host, tt.guest) + require.Equal(t, tt.want, got) + require.Equal(t, tt.wantEQ, got.EQ()) + require.Equal(t, tt.wantLT, got.LT()) + require.Equal(t, tt.wantLE, got.LE()) + require.Equal(t, tt.wantGT, got.GT()) + require.Equal(t, tt.wantGE, got.GE()) + + require.Equal(t, tt.wantEQ, Equal6(tt.host, tt.guest)) + require.Equal(t, tt.wantLT, LessThan6(tt.host, tt.guest)) + require.Equal(t, tt.wantLE, LessOrEqual6(tt.host, tt.guest)) + require.Equal(t, tt.wantGT, GreaterThan6(tt.host, tt.guest)) + require.Equal(t, tt.wantGE, GreaterOrEqual6(tt.host, tt.guest)) + }) + } +} + +func TestT6_CompareC(t *testing.T) { + lesser := New6(stringComparable("1"), stringComparable("2"), stringComparable("3"), stringComparable("4"), stringComparable("5"), stringComparable("6")) + greater := New6(stringComparable("2"), stringComparable("3"), stringComparable("4"), stringComparable("5"), stringComparable("6"), stringComparable("7")) + + tests := []struct { + name string + host, guest T6[stringComparable, stringComparable, stringComparable, stringComparable, stringComparable, stringComparable] + want OrderedComparisonResult + wantEQ bool + wantLT bool + wantLE bool + wantGT bool + wantGE bool + }{ + { + name: "less than", + host: lesser, + guest: greater, + want: -1, + wantLT: true, + wantLE: true, + }, + { + name: "greater than", + host: greater, + guest: lesser, + want: 1, + wantGT: true, + wantGE: true, + }, + { + name: "equal", + host: lesser, + guest: lesser, + want: 0, + wantEQ: true, + wantLE: true, + wantGE: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := Compare6C(tt.host, tt.guest) + require.Equal(t, tt.want, got) + require.Equal(t, tt.wantEQ, got.EQ()) + require.Equal(t, tt.wantLT, got.LT()) + require.Equal(t, tt.wantLE, got.LE()) + require.Equal(t, tt.wantGT, got.GT()) + require.Equal(t, tt.wantGE, got.GE()) + + require.Equal(t, tt.wantEQ, Equal6C(tt.host, tt.guest)) + require.Equal(t, tt.wantLT, LessThan6C(tt.host, tt.guest)) + require.Equal(t, tt.wantLE, LessOrEqual6C(tt.host, tt.guest)) + require.Equal(t, tt.wantGT, GreaterThan6C(tt.host, tt.guest)) + require.Equal(t, tt.wantGE, GreaterOrEqual6C(tt.host, tt.guest)) + }) + } +} + +func TestT6_EqualE(t *testing.T) { + a := New6(intEqualable(1), intEqualable(2), intEqualable(3), intEqualable(4), intEqualable(5), intEqualable(6)) + b := New6(intEqualable(2), intEqualable(3), intEqualable(4), intEqualable(5), intEqualable(6), intEqualable(7)) + + require.False(t, Equal6E(a, b)) + require.True(t, Equal6E(a, a)) +} + func TestT6_String(t *testing.T) { tup := New6("1", "2", "3", "4", "5", "6") require.Equal(t, `["1" "2" "3" "4" "5" "6"]`, tup.String()) diff --git a/tuple7.go b/tuple7.go index 73be093..b38e0cf 100644 --- a/tuple7.go +++ b/tuple7.go @@ -1,6 +1,7 @@ package tuple import ( + "constraints" "fmt" ) @@ -164,3 +165,128 @@ func FromSlice7X[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7 any](values []any) T7[Ty1, Ty return New7(v1, v2, v3, v4, v5, v6, v7) } + +// Equal7 returns whether the host tuple is equal to the other tuple. +// All tuple elements of the host and guest parameters must match the "comparable" built-in constraint. +// To test equality of tuples that hold custom Equalable values, use the Equal7E function. +// To test equality of tuples that hold custom Comparable values, use the Equal7C function. +// Otherwise, use Equal or reflect.DeepEqual to test tuples of any types. +func Equal7[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7 comparable](host, guest T7[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7]) bool { + return host.V1 == guest.V1 && host.V2 == guest.V2 && host.V3 == guest.V3 && host.V4 == guest.V4 && host.V5 == guest.V5 && host.V6 == guest.V6 && host.V7 == guest.V7 +} + +// Equal7E returns whether the host tuple is semantically equal to the guest tuple. +// All tuple elements of the host and guest parameters must match the Equalable constraint. +// To test equality of tuples that hold built-in "comparable" values, use the Equal7 function. +// To test equality of tuples that hold custom Comparable values, use the Equal7C function. +// Otherwise, use Equal or reflect.DeepEqual to test tuples of any types. +func Equal7E[Ty1 Equalable[Ty1], Ty2 Equalable[Ty2], Ty3 Equalable[Ty3], Ty4 Equalable[Ty4], Ty5 Equalable[Ty5], Ty6 Equalable[Ty6], Ty7 Equalable[Ty7]](host, guest T7[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7]) bool { + return host.V1.Equal(guest.V1) && host.V2.Equal(guest.V2) && host.V3.Equal(guest.V3) && host.V4.Equal(guest.V4) && host.V5.Equal(guest.V5) && host.V6.Equal(guest.V6) && host.V7.Equal(guest.V7) +} + +// Equal7C returns whether the host tuple is semantically less than, equal to, or greater than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To test equality of tuples that hold built-in "comparable" values, use the Equal7 function. +// To test equality of tuples that hold custom Equalable values, use the Equal7E function. +// Otherwise, use Equal or reflect.DeepEqual to test tuples of any types. +func Equal7C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3], Ty4 Comparable[Ty4], Ty5 Comparable[Ty5], Ty6 Comparable[Ty6], Ty7 Comparable[Ty7]](host, guest T7[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7]) bool { + return host.V1.CompareTo(guest.V1).EQ() && host.V2.CompareTo(guest.V2).EQ() && host.V3.CompareTo(guest.V3).EQ() && host.V4.CompareTo(guest.V4).EQ() && host.V5.CompareTo(guest.V5).EQ() && host.V6.CompareTo(guest.V6).EQ() && host.V7.CompareTo(guest.V7).EQ() +} + +// Compare7 returns whether the host tuple is semantically less than, equal to, or greater than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the Compare7C function. +func Compare7[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7 constraints.Ordered](host, guest T7[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7]) OrderedComparisonResult { + return multiCompare( + func() OrderedComparisonResult { return compareOrdered(host.V1, guest.V1) }, + + func() OrderedComparisonResult { return compareOrdered(host.V2, guest.V2) }, + + func() OrderedComparisonResult { return compareOrdered(host.V3, guest.V3) }, + + func() OrderedComparisonResult { return compareOrdered(host.V4, guest.V4) }, + + func() OrderedComparisonResult { return compareOrdered(host.V5, guest.V5) }, + + func() OrderedComparisonResult { return compareOrdered(host.V6, guest.V6) }, + + func() OrderedComparisonResult { return compareOrdered(host.V7, guest.V7) }, + ) +} + +// Compare7C returns whether the host tuple is semantically less than, equal to, or greater than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the Compare7 function. +func Compare7C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3], Ty4 Comparable[Ty4], Ty5 Comparable[Ty5], Ty6 Comparable[Ty6], Ty7 Comparable[Ty7]](host, guest T7[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7]) OrderedComparisonResult { + return multiCompare( + func() OrderedComparisonResult { return host.V1.CompareTo(guest.V1) }, + + func() OrderedComparisonResult { return host.V2.CompareTo(guest.V2) }, + + func() OrderedComparisonResult { return host.V3.CompareTo(guest.V3) }, + + func() OrderedComparisonResult { return host.V4.CompareTo(guest.V4) }, + + func() OrderedComparisonResult { return host.V5.CompareTo(guest.V5) }, + + func() OrderedComparisonResult { return host.V6.CompareTo(guest.V6) }, + + func() OrderedComparisonResult { return host.V7.CompareTo(guest.V7) }, + ) +} + +// LessThan7 returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the LessThan7C function. +func LessThan7[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7 constraints.Ordered](host, guest T7[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7]) bool { + return Compare7(host, guest).LT() +} + +// LessThan7C returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the LessThan7 function. +func LessThan7C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3], Ty4 Comparable[Ty4], Ty5 Comparable[Ty5], Ty6 Comparable[Ty6], Ty7 Comparable[Ty7]](host, guest T7[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7]) bool { + return Compare7C(host, guest).LT() +} + +// LessOrEqual7 returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the LessOrEqual7C function. +func LessOrEqual7[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7 constraints.Ordered](host, guest T7[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7]) bool { + return Compare7(host, guest).LE() +} + +// LessOrEqual7C returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the LessOrEqual7 function. +func LessOrEqual7C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3], Ty4 Comparable[Ty4], Ty5 Comparable[Ty5], Ty6 Comparable[Ty6], Ty7 Comparable[Ty7]](host, guest T7[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7]) bool { + return Compare7C(host, guest).LE() +} + +// GreaterThan7 returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the GreaterThan7C function. +func GreaterThan7[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7 constraints.Ordered](host, guest T7[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7]) bool { + return Compare7(host, guest).GT() +} + +// GreaterThan7C returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the GreaterThan7 function. +func GreaterThan7C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3], Ty4 Comparable[Ty4], Ty5 Comparable[Ty5], Ty6 Comparable[Ty6], Ty7 Comparable[Ty7]](host, guest T7[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7]) bool { + return Compare7C(host, guest).GT() +} + +// GreaterOrEqual7 returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the GreaterOrEqual7C function. +func GreaterOrEqual7[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7 constraints.Ordered](host, guest T7[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7]) bool { + return Compare7(host, guest).GE() +} + +// GreaterOrEqual7C returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the GreaterOrEqual7 function. +func GreaterOrEqual7C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3], Ty4 Comparable[Ty4], Ty5 Comparable[Ty5], Ty6 Comparable[Ty6], Ty7 Comparable[Ty7]](host, guest T7[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7]) bool { + return Compare7C(host, guest).GE() +} diff --git a/tuple7_test.go b/tuple7_test.go index 8d5c447..b17a20c 100644 --- a/tuple7_test.go +++ b/tuple7_test.go @@ -36,6 +36,194 @@ func TestT7_Values(t *testing.T) { require.Equal(t, "7", v7) } +func TestT7_Compare(t *testing.T) { + lesser := New7(1, 2, 3, 4, 5, 6, 7) + greater := New7(2, 3, 4, 5, 6, 7, 8) + + tests := []struct { + name string + host, guest T7[int, int, int, int, int, int, int] + want OrderedComparisonResult + wantEQ bool + wantLT bool + wantLE bool + wantGT bool + wantGE bool + }{ + { + name: "less than", + host: lesser, + guest: greater, + want: -1, + wantLT: true, + wantLE: true, + }, + { + name: "greater than", + host: greater, + guest: lesser, + want: 1, + wantGT: true, + wantGE: true, + }, + { + name: "equal", + host: lesser, + guest: lesser, + want: 0, + wantEQ: true, + wantLE: true, + wantGE: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := Compare7(tt.host, tt.guest) + require.Equal(t, tt.want, got) + require.Equal(t, tt.wantEQ, got.EQ()) + require.Equal(t, tt.wantLT, got.LT()) + require.Equal(t, tt.wantLE, got.LE()) + require.Equal(t, tt.wantGT, got.GT()) + require.Equal(t, tt.wantGE, got.GE()) + + require.Equal(t, tt.wantEQ, Equal7(tt.host, tt.guest)) + require.Equal(t, tt.wantLT, LessThan7(tt.host, tt.guest)) + require.Equal(t, tt.wantLE, LessOrEqual7(tt.host, tt.guest)) + require.Equal(t, tt.wantGT, GreaterThan7(tt.host, tt.guest)) + require.Equal(t, tt.wantGE, GreaterOrEqual7(tt.host, tt.guest)) + }) + } +} + +func TestT7_Compare_Approx(t *testing.T) { + lesser := New7(approximationHelper("1"), approximationHelper("2"), approximationHelper("3"), approximationHelper("4"), approximationHelper("5"), approximationHelper("6"), approximationHelper("7")) + greater := New7(approximationHelper("2"), approximationHelper("3"), approximationHelper("4"), approximationHelper("5"), approximationHelper("6"), approximationHelper("7"), approximationHelper("8")) + + tests := []struct { + name string + host, guest T7[approximationHelper, approximationHelper, approximationHelper, approximationHelper, approximationHelper, approximationHelper, approximationHelper] + want OrderedComparisonResult + wantEQ bool + wantLT bool + wantLE bool + wantGT bool + wantGE bool + }{ + { + name: "less than", + host: lesser, + guest: greater, + want: -1, + wantLT: true, + wantLE: true, + }, + { + name: "greater than", + host: greater, + guest: lesser, + want: 1, + wantGT: true, + wantGE: true, + }, + { + name: "equal", + host: lesser, + guest: lesser, + want: 0, + wantEQ: true, + wantLE: true, + wantGE: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := Compare7(tt.host, tt.guest) + require.Equal(t, tt.want, got) + require.Equal(t, tt.wantEQ, got.EQ()) + require.Equal(t, tt.wantLT, got.LT()) + require.Equal(t, tt.wantLE, got.LE()) + require.Equal(t, tt.wantGT, got.GT()) + require.Equal(t, tt.wantGE, got.GE()) + + require.Equal(t, tt.wantEQ, Equal7(tt.host, tt.guest)) + require.Equal(t, tt.wantLT, LessThan7(tt.host, tt.guest)) + require.Equal(t, tt.wantLE, LessOrEqual7(tt.host, tt.guest)) + require.Equal(t, tt.wantGT, GreaterThan7(tt.host, tt.guest)) + require.Equal(t, tt.wantGE, GreaterOrEqual7(tt.host, tt.guest)) + }) + } +} + +func TestT7_CompareC(t *testing.T) { + lesser := New7(stringComparable("1"), stringComparable("2"), stringComparable("3"), stringComparable("4"), stringComparable("5"), stringComparable("6"), stringComparable("7")) + greater := New7(stringComparable("2"), stringComparable("3"), stringComparable("4"), stringComparable("5"), stringComparable("6"), stringComparable("7"), stringComparable("8")) + + tests := []struct { + name string + host, guest T7[stringComparable, stringComparable, stringComparable, stringComparable, stringComparable, stringComparable, stringComparable] + want OrderedComparisonResult + wantEQ bool + wantLT bool + wantLE bool + wantGT bool + wantGE bool + }{ + { + name: "less than", + host: lesser, + guest: greater, + want: -1, + wantLT: true, + wantLE: true, + }, + { + name: "greater than", + host: greater, + guest: lesser, + want: 1, + wantGT: true, + wantGE: true, + }, + { + name: "equal", + host: lesser, + guest: lesser, + want: 0, + wantEQ: true, + wantLE: true, + wantGE: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := Compare7C(tt.host, tt.guest) + require.Equal(t, tt.want, got) + require.Equal(t, tt.wantEQ, got.EQ()) + require.Equal(t, tt.wantLT, got.LT()) + require.Equal(t, tt.wantLE, got.LE()) + require.Equal(t, tt.wantGT, got.GT()) + require.Equal(t, tt.wantGE, got.GE()) + + require.Equal(t, tt.wantEQ, Equal7C(tt.host, tt.guest)) + require.Equal(t, tt.wantLT, LessThan7C(tt.host, tt.guest)) + require.Equal(t, tt.wantLE, LessOrEqual7C(tt.host, tt.guest)) + require.Equal(t, tt.wantGT, GreaterThan7C(tt.host, tt.guest)) + require.Equal(t, tt.wantGE, GreaterOrEqual7C(tt.host, tt.guest)) + }) + } +} + +func TestT7_EqualE(t *testing.T) { + a := New7(intEqualable(1), intEqualable(2), intEqualable(3), intEqualable(4), intEqualable(5), intEqualable(6), intEqualable(7)) + b := New7(intEqualable(2), intEqualable(3), intEqualable(4), intEqualable(5), intEqualable(6), intEqualable(7), intEqualable(8)) + + require.False(t, Equal7E(a, b)) + require.True(t, Equal7E(a, a)) +} + func TestT7_String(t *testing.T) { tup := New7("1", "2", "3", "4", "5", "6", "7") require.Equal(t, `["1" "2" "3" "4" "5" "6" "7"]`, tup.String()) diff --git a/tuple8.go b/tuple8.go index 2365d9b..36805da 100644 --- a/tuple8.go +++ b/tuple8.go @@ -1,6 +1,7 @@ package tuple import ( + "constraints" "fmt" ) @@ -176,3 +177,132 @@ func FromSlice8X[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8 any](values []any) T8[Ty return New8(v1, v2, v3, v4, v5, v6, v7, v8) } + +// Equal8 returns whether the host tuple is equal to the other tuple. +// All tuple elements of the host and guest parameters must match the "comparable" built-in constraint. +// To test equality of tuples that hold custom Equalable values, use the Equal8E function. +// To test equality of tuples that hold custom Comparable values, use the Equal8C function. +// Otherwise, use Equal or reflect.DeepEqual to test tuples of any types. +func Equal8[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8 comparable](host, guest T8[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8]) bool { + return host.V1 == guest.V1 && host.V2 == guest.V2 && host.V3 == guest.V3 && host.V4 == guest.V4 && host.V5 == guest.V5 && host.V6 == guest.V6 && host.V7 == guest.V7 && host.V8 == guest.V8 +} + +// Equal8E returns whether the host tuple is semantically equal to the guest tuple. +// All tuple elements of the host and guest parameters must match the Equalable constraint. +// To test equality of tuples that hold built-in "comparable" values, use the Equal8 function. +// To test equality of tuples that hold custom Comparable values, use the Equal8C function. +// Otherwise, use Equal or reflect.DeepEqual to test tuples of any types. +func Equal8E[Ty1 Equalable[Ty1], Ty2 Equalable[Ty2], Ty3 Equalable[Ty3], Ty4 Equalable[Ty4], Ty5 Equalable[Ty5], Ty6 Equalable[Ty6], Ty7 Equalable[Ty7], Ty8 Equalable[Ty8]](host, guest T8[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8]) bool { + return host.V1.Equal(guest.V1) && host.V2.Equal(guest.V2) && host.V3.Equal(guest.V3) && host.V4.Equal(guest.V4) && host.V5.Equal(guest.V5) && host.V6.Equal(guest.V6) && host.V7.Equal(guest.V7) && host.V8.Equal(guest.V8) +} + +// Equal8C returns whether the host tuple is semantically less than, equal to, or greater than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To test equality of tuples that hold built-in "comparable" values, use the Equal8 function. +// To test equality of tuples that hold custom Equalable values, use the Equal8E function. +// Otherwise, use Equal or reflect.DeepEqual to test tuples of any types. +func Equal8C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3], Ty4 Comparable[Ty4], Ty5 Comparable[Ty5], Ty6 Comparable[Ty6], Ty7 Comparable[Ty7], Ty8 Comparable[Ty8]](host, guest T8[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8]) bool { + return host.V1.CompareTo(guest.V1).EQ() && host.V2.CompareTo(guest.V2).EQ() && host.V3.CompareTo(guest.V3).EQ() && host.V4.CompareTo(guest.V4).EQ() && host.V5.CompareTo(guest.V5).EQ() && host.V6.CompareTo(guest.V6).EQ() && host.V7.CompareTo(guest.V7).EQ() && host.V8.CompareTo(guest.V8).EQ() +} + +// Compare8 returns whether the host tuple is semantically less than, equal to, or greater than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the Compare8C function. +func Compare8[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8 constraints.Ordered](host, guest T8[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8]) OrderedComparisonResult { + return multiCompare( + func() OrderedComparisonResult { return compareOrdered(host.V1, guest.V1) }, + + func() OrderedComparisonResult { return compareOrdered(host.V2, guest.V2) }, + + func() OrderedComparisonResult { return compareOrdered(host.V3, guest.V3) }, + + func() OrderedComparisonResult { return compareOrdered(host.V4, guest.V4) }, + + func() OrderedComparisonResult { return compareOrdered(host.V5, guest.V5) }, + + func() OrderedComparisonResult { return compareOrdered(host.V6, guest.V6) }, + + func() OrderedComparisonResult { return compareOrdered(host.V7, guest.V7) }, + + func() OrderedComparisonResult { return compareOrdered(host.V8, guest.V8) }, + ) +} + +// Compare8C returns whether the host tuple is semantically less than, equal to, or greater than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the Compare8 function. +func Compare8C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3], Ty4 Comparable[Ty4], Ty5 Comparable[Ty5], Ty6 Comparable[Ty6], Ty7 Comparable[Ty7], Ty8 Comparable[Ty8]](host, guest T8[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8]) OrderedComparisonResult { + return multiCompare( + func() OrderedComparisonResult { return host.V1.CompareTo(guest.V1) }, + + func() OrderedComparisonResult { return host.V2.CompareTo(guest.V2) }, + + func() OrderedComparisonResult { return host.V3.CompareTo(guest.V3) }, + + func() OrderedComparisonResult { return host.V4.CompareTo(guest.V4) }, + + func() OrderedComparisonResult { return host.V5.CompareTo(guest.V5) }, + + func() OrderedComparisonResult { return host.V6.CompareTo(guest.V6) }, + + func() OrderedComparisonResult { return host.V7.CompareTo(guest.V7) }, + + func() OrderedComparisonResult { return host.V8.CompareTo(guest.V8) }, + ) +} + +// LessThan8 returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the LessThan8C function. +func LessThan8[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8 constraints.Ordered](host, guest T8[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8]) bool { + return Compare8(host, guest).LT() +} + +// LessThan8C returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the LessThan8 function. +func LessThan8C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3], Ty4 Comparable[Ty4], Ty5 Comparable[Ty5], Ty6 Comparable[Ty6], Ty7 Comparable[Ty7], Ty8 Comparable[Ty8]](host, guest T8[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8]) bool { + return Compare8C(host, guest).LT() +} + +// LessOrEqual8 returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the LessOrEqual8C function. +func LessOrEqual8[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8 constraints.Ordered](host, guest T8[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8]) bool { + return Compare8(host, guest).LE() +} + +// LessOrEqual8C returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the LessOrEqual8 function. +func LessOrEqual8C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3], Ty4 Comparable[Ty4], Ty5 Comparable[Ty5], Ty6 Comparable[Ty6], Ty7 Comparable[Ty7], Ty8 Comparable[Ty8]](host, guest T8[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8]) bool { + return Compare8C(host, guest).LE() +} + +// GreaterThan8 returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the GreaterThan8C function. +func GreaterThan8[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8 constraints.Ordered](host, guest T8[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8]) bool { + return Compare8(host, guest).GT() +} + +// GreaterThan8C returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the GreaterThan8 function. +func GreaterThan8C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3], Ty4 Comparable[Ty4], Ty5 Comparable[Ty5], Ty6 Comparable[Ty6], Ty7 Comparable[Ty7], Ty8 Comparable[Ty8]](host, guest T8[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8]) bool { + return Compare8C(host, guest).GT() +} + +// GreaterOrEqual8 returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the GreaterOrEqual8C function. +func GreaterOrEqual8[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8 constraints.Ordered](host, guest T8[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8]) bool { + return Compare8(host, guest).GE() +} + +// GreaterOrEqual8C returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the GreaterOrEqual8 function. +func GreaterOrEqual8C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3], Ty4 Comparable[Ty4], Ty5 Comparable[Ty5], Ty6 Comparable[Ty6], Ty7 Comparable[Ty7], Ty8 Comparable[Ty8]](host, guest T8[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8]) bool { + return Compare8C(host, guest).GE() +} diff --git a/tuple8_test.go b/tuple8_test.go index 11ee074..94aebc6 100644 --- a/tuple8_test.go +++ b/tuple8_test.go @@ -38,6 +38,194 @@ func TestT8_Values(t *testing.T) { require.Equal(t, "8", v8) } +func TestT8_Compare(t *testing.T) { + lesser := New8(1, 2, 3, 4, 5, 6, 7, 8) + greater := New8(2, 3, 4, 5, 6, 7, 8, 9) + + tests := []struct { + name string + host, guest T8[int, int, int, int, int, int, int, int] + want OrderedComparisonResult + wantEQ bool + wantLT bool + wantLE bool + wantGT bool + wantGE bool + }{ + { + name: "less than", + host: lesser, + guest: greater, + want: -1, + wantLT: true, + wantLE: true, + }, + { + name: "greater than", + host: greater, + guest: lesser, + want: 1, + wantGT: true, + wantGE: true, + }, + { + name: "equal", + host: lesser, + guest: lesser, + want: 0, + wantEQ: true, + wantLE: true, + wantGE: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := Compare8(tt.host, tt.guest) + require.Equal(t, tt.want, got) + require.Equal(t, tt.wantEQ, got.EQ()) + require.Equal(t, tt.wantLT, got.LT()) + require.Equal(t, tt.wantLE, got.LE()) + require.Equal(t, tt.wantGT, got.GT()) + require.Equal(t, tt.wantGE, got.GE()) + + require.Equal(t, tt.wantEQ, Equal8(tt.host, tt.guest)) + require.Equal(t, tt.wantLT, LessThan8(tt.host, tt.guest)) + require.Equal(t, tt.wantLE, LessOrEqual8(tt.host, tt.guest)) + require.Equal(t, tt.wantGT, GreaterThan8(tt.host, tt.guest)) + require.Equal(t, tt.wantGE, GreaterOrEqual8(tt.host, tt.guest)) + }) + } +} + +func TestT8_Compare_Approx(t *testing.T) { + lesser := New8(approximationHelper("1"), approximationHelper("2"), approximationHelper("3"), approximationHelper("4"), approximationHelper("5"), approximationHelper("6"), approximationHelper("7"), approximationHelper("8")) + greater := New8(approximationHelper("2"), approximationHelper("3"), approximationHelper("4"), approximationHelper("5"), approximationHelper("6"), approximationHelper("7"), approximationHelper("8"), approximationHelper("9")) + + tests := []struct { + name string + host, guest T8[approximationHelper, approximationHelper, approximationHelper, approximationHelper, approximationHelper, approximationHelper, approximationHelper, approximationHelper] + want OrderedComparisonResult + wantEQ bool + wantLT bool + wantLE bool + wantGT bool + wantGE bool + }{ + { + name: "less than", + host: lesser, + guest: greater, + want: -1, + wantLT: true, + wantLE: true, + }, + { + name: "greater than", + host: greater, + guest: lesser, + want: 1, + wantGT: true, + wantGE: true, + }, + { + name: "equal", + host: lesser, + guest: lesser, + want: 0, + wantEQ: true, + wantLE: true, + wantGE: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := Compare8(tt.host, tt.guest) + require.Equal(t, tt.want, got) + require.Equal(t, tt.wantEQ, got.EQ()) + require.Equal(t, tt.wantLT, got.LT()) + require.Equal(t, tt.wantLE, got.LE()) + require.Equal(t, tt.wantGT, got.GT()) + require.Equal(t, tt.wantGE, got.GE()) + + require.Equal(t, tt.wantEQ, Equal8(tt.host, tt.guest)) + require.Equal(t, tt.wantLT, LessThan8(tt.host, tt.guest)) + require.Equal(t, tt.wantLE, LessOrEqual8(tt.host, tt.guest)) + require.Equal(t, tt.wantGT, GreaterThan8(tt.host, tt.guest)) + require.Equal(t, tt.wantGE, GreaterOrEqual8(tt.host, tt.guest)) + }) + } +} + +func TestT8_CompareC(t *testing.T) { + lesser := New8(stringComparable("1"), stringComparable("2"), stringComparable("3"), stringComparable("4"), stringComparable("5"), stringComparable("6"), stringComparable("7"), stringComparable("8")) + greater := New8(stringComparable("2"), stringComparable("3"), stringComparable("4"), stringComparable("5"), stringComparable("6"), stringComparable("7"), stringComparable("8"), stringComparable("9")) + + tests := []struct { + name string + host, guest T8[stringComparable, stringComparable, stringComparable, stringComparable, stringComparable, stringComparable, stringComparable, stringComparable] + want OrderedComparisonResult + wantEQ bool + wantLT bool + wantLE bool + wantGT bool + wantGE bool + }{ + { + name: "less than", + host: lesser, + guest: greater, + want: -1, + wantLT: true, + wantLE: true, + }, + { + name: "greater than", + host: greater, + guest: lesser, + want: 1, + wantGT: true, + wantGE: true, + }, + { + name: "equal", + host: lesser, + guest: lesser, + want: 0, + wantEQ: true, + wantLE: true, + wantGE: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := Compare8C(tt.host, tt.guest) + require.Equal(t, tt.want, got) + require.Equal(t, tt.wantEQ, got.EQ()) + require.Equal(t, tt.wantLT, got.LT()) + require.Equal(t, tt.wantLE, got.LE()) + require.Equal(t, tt.wantGT, got.GT()) + require.Equal(t, tt.wantGE, got.GE()) + + require.Equal(t, tt.wantEQ, Equal8C(tt.host, tt.guest)) + require.Equal(t, tt.wantLT, LessThan8C(tt.host, tt.guest)) + require.Equal(t, tt.wantLE, LessOrEqual8C(tt.host, tt.guest)) + require.Equal(t, tt.wantGT, GreaterThan8C(tt.host, tt.guest)) + require.Equal(t, tt.wantGE, GreaterOrEqual8C(tt.host, tt.guest)) + }) + } +} + +func TestT8_EqualE(t *testing.T) { + a := New8(intEqualable(1), intEqualable(2), intEqualable(3), intEqualable(4), intEqualable(5), intEqualable(6), intEqualable(7), intEqualable(8)) + b := New8(intEqualable(2), intEqualable(3), intEqualable(4), intEqualable(5), intEqualable(6), intEqualable(7), intEqualable(8), intEqualable(9)) + + require.False(t, Equal8E(a, b)) + require.True(t, Equal8E(a, a)) +} + func TestT8_String(t *testing.T) { tup := New8("1", "2", "3", "4", "5", "6", "7", "8") require.Equal(t, `["1" "2" "3" "4" "5" "6" "7" "8"]`, tup.String()) diff --git a/tuple9.go b/tuple9.go index d9d485d..e1cfa04 100644 --- a/tuple9.go +++ b/tuple9.go @@ -1,6 +1,7 @@ package tuple import ( + "constraints" "fmt" ) @@ -188,3 +189,136 @@ func FromSlice9X[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8, Ty9 any](values []any) return New9(v1, v2, v3, v4, v5, v6, v7, v8, v9) } + +// Equal9 returns whether the host tuple is equal to the other tuple. +// All tuple elements of the host and guest parameters must match the "comparable" built-in constraint. +// To test equality of tuples that hold custom Equalable values, use the Equal9E function. +// To test equality of tuples that hold custom Comparable values, use the Equal9C function. +// Otherwise, use Equal or reflect.DeepEqual to test tuples of any types. +func Equal9[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8, Ty9 comparable](host, guest T9[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8, Ty9]) bool { + return host.V1 == guest.V1 && host.V2 == guest.V2 && host.V3 == guest.V3 && host.V4 == guest.V4 && host.V5 == guest.V5 && host.V6 == guest.V6 && host.V7 == guest.V7 && host.V8 == guest.V8 && host.V9 == guest.V9 +} + +// Equal9E returns whether the host tuple is semantically equal to the guest tuple. +// All tuple elements of the host and guest parameters must match the Equalable constraint. +// To test equality of tuples that hold built-in "comparable" values, use the Equal9 function. +// To test equality of tuples that hold custom Comparable values, use the Equal9C function. +// Otherwise, use Equal or reflect.DeepEqual to test tuples of any types. +func Equal9E[Ty1 Equalable[Ty1], Ty2 Equalable[Ty2], Ty3 Equalable[Ty3], Ty4 Equalable[Ty4], Ty5 Equalable[Ty5], Ty6 Equalable[Ty6], Ty7 Equalable[Ty7], Ty8 Equalable[Ty8], Ty9 Equalable[Ty9]](host, guest T9[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8, Ty9]) bool { + return host.V1.Equal(guest.V1) && host.V2.Equal(guest.V2) && host.V3.Equal(guest.V3) && host.V4.Equal(guest.V4) && host.V5.Equal(guest.V5) && host.V6.Equal(guest.V6) && host.V7.Equal(guest.V7) && host.V8.Equal(guest.V8) && host.V9.Equal(guest.V9) +} + +// Equal9C returns whether the host tuple is semantically less than, equal to, or greater than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To test equality of tuples that hold built-in "comparable" values, use the Equal9 function. +// To test equality of tuples that hold custom Equalable values, use the Equal9E function. +// Otherwise, use Equal or reflect.DeepEqual to test tuples of any types. +func Equal9C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3], Ty4 Comparable[Ty4], Ty5 Comparable[Ty5], Ty6 Comparable[Ty6], Ty7 Comparable[Ty7], Ty8 Comparable[Ty8], Ty9 Comparable[Ty9]](host, guest T9[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8, Ty9]) bool { + return host.V1.CompareTo(guest.V1).EQ() && host.V2.CompareTo(guest.V2).EQ() && host.V3.CompareTo(guest.V3).EQ() && host.V4.CompareTo(guest.V4).EQ() && host.V5.CompareTo(guest.V5).EQ() && host.V6.CompareTo(guest.V6).EQ() && host.V7.CompareTo(guest.V7).EQ() && host.V8.CompareTo(guest.V8).EQ() && host.V9.CompareTo(guest.V9).EQ() +} + +// Compare9 returns whether the host tuple is semantically less than, equal to, or greater than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the Compare9C function. +func Compare9[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8, Ty9 constraints.Ordered](host, guest T9[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8, Ty9]) OrderedComparisonResult { + return multiCompare( + func() OrderedComparisonResult { return compareOrdered(host.V1, guest.V1) }, + + func() OrderedComparisonResult { return compareOrdered(host.V2, guest.V2) }, + + func() OrderedComparisonResult { return compareOrdered(host.V3, guest.V3) }, + + func() OrderedComparisonResult { return compareOrdered(host.V4, guest.V4) }, + + func() OrderedComparisonResult { return compareOrdered(host.V5, guest.V5) }, + + func() OrderedComparisonResult { return compareOrdered(host.V6, guest.V6) }, + + func() OrderedComparisonResult { return compareOrdered(host.V7, guest.V7) }, + + func() OrderedComparisonResult { return compareOrdered(host.V8, guest.V8) }, + + func() OrderedComparisonResult { return compareOrdered(host.V9, guest.V9) }, + ) +} + +// Compare9C returns whether the host tuple is semantically less than, equal to, or greater than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the Compare9 function. +func Compare9C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3], Ty4 Comparable[Ty4], Ty5 Comparable[Ty5], Ty6 Comparable[Ty6], Ty7 Comparable[Ty7], Ty8 Comparable[Ty8], Ty9 Comparable[Ty9]](host, guest T9[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8, Ty9]) OrderedComparisonResult { + return multiCompare( + func() OrderedComparisonResult { return host.V1.CompareTo(guest.V1) }, + + func() OrderedComparisonResult { return host.V2.CompareTo(guest.V2) }, + + func() OrderedComparisonResult { return host.V3.CompareTo(guest.V3) }, + + func() OrderedComparisonResult { return host.V4.CompareTo(guest.V4) }, + + func() OrderedComparisonResult { return host.V5.CompareTo(guest.V5) }, + + func() OrderedComparisonResult { return host.V6.CompareTo(guest.V6) }, + + func() OrderedComparisonResult { return host.V7.CompareTo(guest.V7) }, + + func() OrderedComparisonResult { return host.V8.CompareTo(guest.V8) }, + + func() OrderedComparisonResult { return host.V9.CompareTo(guest.V9) }, + ) +} + +// LessThan9 returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the LessThan9C function. +func LessThan9[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8, Ty9 constraints.Ordered](host, guest T9[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8, Ty9]) bool { + return Compare9(host, guest).LT() +} + +// LessThan9C returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the LessThan9 function. +func LessThan9C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3], Ty4 Comparable[Ty4], Ty5 Comparable[Ty5], Ty6 Comparable[Ty6], Ty7 Comparable[Ty7], Ty8 Comparable[Ty8], Ty9 Comparable[Ty9]](host, guest T9[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8, Ty9]) bool { + return Compare9C(host, guest).LT() +} + +// LessOrEqual9 returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the LessOrEqual9C function. +func LessOrEqual9[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8, Ty9 constraints.Ordered](host, guest T9[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8, Ty9]) bool { + return Compare9(host, guest).LE() +} + +// LessOrEqual9C returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the LessOrEqual9 function. +func LessOrEqual9C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3], Ty4 Comparable[Ty4], Ty5 Comparable[Ty5], Ty6 Comparable[Ty6], Ty7 Comparable[Ty7], Ty8 Comparable[Ty8], Ty9 Comparable[Ty9]](host, guest T9[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8, Ty9]) bool { + return Compare9C(host, guest).LE() +} + +// GreaterThan9 returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the GreaterThan9C function. +func GreaterThan9[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8, Ty9 constraints.Ordered](host, guest T9[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8, Ty9]) bool { + return Compare9(host, guest).GT() +} + +// GreaterThan9C returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the GreaterThan9 function. +func GreaterThan9C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3], Ty4 Comparable[Ty4], Ty5 Comparable[Ty5], Ty6 Comparable[Ty6], Ty7 Comparable[Ty7], Ty8 Comparable[Ty8], Ty9 Comparable[Ty9]](host, guest T9[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8, Ty9]) bool { + return Compare9C(host, guest).GT() +} + +// GreaterOrEqual9 returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the "Ordered" constraint. +// To compare tuples that hold custom comparable values, use the GreaterOrEqual9C function. +func GreaterOrEqual9[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8, Ty9 constraints.Ordered](host, guest T9[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8, Ty9]) bool { + return Compare9(host, guest).GE() +} + +// GreaterOrEqual9C returns whether the host tuple is semantically less than the guest tuple. +// All tuple elements of the host and guest parameters must match the Comparable constraint. +// To compare tuples that hold built-in "Ordered" values, use the GreaterOrEqual9 function. +func GreaterOrEqual9C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3], Ty4 Comparable[Ty4], Ty5 Comparable[Ty5], Ty6 Comparable[Ty6], Ty7 Comparable[Ty7], Ty8 Comparable[Ty8], Ty9 Comparable[Ty9]](host, guest T9[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8, Ty9]) bool { + return Compare9C(host, guest).GE() +} diff --git a/tuple9_test.go b/tuple9_test.go index 37f8b16..042f46c 100644 --- a/tuple9_test.go +++ b/tuple9_test.go @@ -40,6 +40,194 @@ func TestT9_Values(t *testing.T) { require.Equal(t, "9", v9) } +func TestT9_Compare(t *testing.T) { + lesser := New9(1, 2, 3, 4, 5, 6, 7, 8, 9) + greater := New9(2, 3, 4, 5, 6, 7, 8, 9, 10) + + tests := []struct { + name string + host, guest T9[int, int, int, int, int, int, int, int, int] + want OrderedComparisonResult + wantEQ bool + wantLT bool + wantLE bool + wantGT bool + wantGE bool + }{ + { + name: "less than", + host: lesser, + guest: greater, + want: -1, + wantLT: true, + wantLE: true, + }, + { + name: "greater than", + host: greater, + guest: lesser, + want: 1, + wantGT: true, + wantGE: true, + }, + { + name: "equal", + host: lesser, + guest: lesser, + want: 0, + wantEQ: true, + wantLE: true, + wantGE: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := Compare9(tt.host, tt.guest) + require.Equal(t, tt.want, got) + require.Equal(t, tt.wantEQ, got.EQ()) + require.Equal(t, tt.wantLT, got.LT()) + require.Equal(t, tt.wantLE, got.LE()) + require.Equal(t, tt.wantGT, got.GT()) + require.Equal(t, tt.wantGE, got.GE()) + + require.Equal(t, tt.wantEQ, Equal9(tt.host, tt.guest)) + require.Equal(t, tt.wantLT, LessThan9(tt.host, tt.guest)) + require.Equal(t, tt.wantLE, LessOrEqual9(tt.host, tt.guest)) + require.Equal(t, tt.wantGT, GreaterThan9(tt.host, tt.guest)) + require.Equal(t, tt.wantGE, GreaterOrEqual9(tt.host, tt.guest)) + }) + } +} + +func TestT9_Compare_Approx(t *testing.T) { + lesser := New9(approximationHelper("1"), approximationHelper("2"), approximationHelper("3"), approximationHelper("4"), approximationHelper("5"), approximationHelper("6"), approximationHelper("7"), approximationHelper("8"), approximationHelper("9")) + greater := New9(approximationHelper("2"), approximationHelper("3"), approximationHelper("4"), approximationHelper("5"), approximationHelper("6"), approximationHelper("7"), approximationHelper("8"), approximationHelper("9"), approximationHelper("10")) + + tests := []struct { + name string + host, guest T9[approximationHelper, approximationHelper, approximationHelper, approximationHelper, approximationHelper, approximationHelper, approximationHelper, approximationHelper, approximationHelper] + want OrderedComparisonResult + wantEQ bool + wantLT bool + wantLE bool + wantGT bool + wantGE bool + }{ + { + name: "less than", + host: lesser, + guest: greater, + want: -1, + wantLT: true, + wantLE: true, + }, + { + name: "greater than", + host: greater, + guest: lesser, + want: 1, + wantGT: true, + wantGE: true, + }, + { + name: "equal", + host: lesser, + guest: lesser, + want: 0, + wantEQ: true, + wantLE: true, + wantGE: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := Compare9(tt.host, tt.guest) + require.Equal(t, tt.want, got) + require.Equal(t, tt.wantEQ, got.EQ()) + require.Equal(t, tt.wantLT, got.LT()) + require.Equal(t, tt.wantLE, got.LE()) + require.Equal(t, tt.wantGT, got.GT()) + require.Equal(t, tt.wantGE, got.GE()) + + require.Equal(t, tt.wantEQ, Equal9(tt.host, tt.guest)) + require.Equal(t, tt.wantLT, LessThan9(tt.host, tt.guest)) + require.Equal(t, tt.wantLE, LessOrEqual9(tt.host, tt.guest)) + require.Equal(t, tt.wantGT, GreaterThan9(tt.host, tt.guest)) + require.Equal(t, tt.wantGE, GreaterOrEqual9(tt.host, tt.guest)) + }) + } +} + +func TestT9_CompareC(t *testing.T) { + lesser := New9(stringComparable("1"), stringComparable("2"), stringComparable("3"), stringComparable("4"), stringComparable("5"), stringComparable("6"), stringComparable("7"), stringComparable("8"), stringComparable("9")) + greater := New9(stringComparable("2"), stringComparable("3"), stringComparable("4"), stringComparable("5"), stringComparable("6"), stringComparable("7"), stringComparable("8"), stringComparable("9"), stringComparable("10")) + + tests := []struct { + name string + host, guest T9[stringComparable, stringComparable, stringComparable, stringComparable, stringComparable, stringComparable, stringComparable, stringComparable, stringComparable] + want OrderedComparisonResult + wantEQ bool + wantLT bool + wantLE bool + wantGT bool + wantGE bool + }{ + { + name: "less than", + host: lesser, + guest: greater, + want: -1, + wantLT: true, + wantLE: true, + }, + { + name: "greater than", + host: greater, + guest: lesser, + want: 1, + wantGT: true, + wantGE: true, + }, + { + name: "equal", + host: lesser, + guest: lesser, + want: 0, + wantEQ: true, + wantLE: true, + wantGE: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := Compare9C(tt.host, tt.guest) + require.Equal(t, tt.want, got) + require.Equal(t, tt.wantEQ, got.EQ()) + require.Equal(t, tt.wantLT, got.LT()) + require.Equal(t, tt.wantLE, got.LE()) + require.Equal(t, tt.wantGT, got.GT()) + require.Equal(t, tt.wantGE, got.GE()) + + require.Equal(t, tt.wantEQ, Equal9C(tt.host, tt.guest)) + require.Equal(t, tt.wantLT, LessThan9C(tt.host, tt.guest)) + require.Equal(t, tt.wantLE, LessOrEqual9C(tt.host, tt.guest)) + require.Equal(t, tt.wantGT, GreaterThan9C(tt.host, tt.guest)) + require.Equal(t, tt.wantGE, GreaterOrEqual9C(tt.host, tt.guest)) + }) + } +} + +func TestT9_EqualE(t *testing.T) { + a := New9(intEqualable(1), intEqualable(2), intEqualable(3), intEqualable(4), intEqualable(5), intEqualable(6), intEqualable(7), intEqualable(8), intEqualable(9)) + b := New9(intEqualable(2), intEqualable(3), intEqualable(4), intEqualable(5), intEqualable(6), intEqualable(7), intEqualable(8), intEqualable(9), intEqualable(10)) + + require.False(t, Equal9E(a, b)) + require.True(t, Equal9E(a, a)) +} + func TestT9_String(t *testing.T) { tup := New9("1", "2", "3", "4", "5", "6", "7", "8", "9") require.Equal(t, `["1" "2" "3" "4" "5" "6" "7" "8" "9"]`, tup.String())