diff --git a/src/cmd/go2go/testdata/go2path/src/alg/alg.go2 b/src/cmd/go2go/testdata/go2path/src/alg/alg.go2 new file mode 100644 index 0000000000..593610d2b1 --- /dev/null +++ b/src/cmd/go2go/testdata/go2path/src/alg/alg.go2 @@ -0,0 +1,24 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package alg provides basic algorithms. +package alg + +import "contracts" + +// Max returns the maximum of two values of some ordered type. +func Max(type T contracts.Ordered)(a, b T) T { + if a < b { + return b + } + return a +} + +// Min returns the minimum of two values of some ordered type. +func Min(type T contracts.Ordered)(a, b T) T { + if a < b { + return a + } + return b +} diff --git a/src/cmd/go2go/testdata/go2path/src/contracts/contracts.go2 b/src/cmd/go2go/testdata/go2path/src/contracts/contracts.go2 index 9ad0b714fd..e84e74ec99 100644 --- a/src/cmd/go2go/testdata/go2path/src/contracts/contracts.go2 +++ b/src/cmd/go2go/testdata/go2path/src/contracts/contracts.go2 @@ -13,3 +13,19 @@ contract Ordered(T) { float32, float64, string } + +// The Integer contract permits any integer type. +contract Integer(T) { + T int, int8, int16, int32, int64, + uint, uint8, uint16, uint32, uint64, uintptr +} + +// The Signed contract permits any signed integer type. +contract Signed(T) { + T int, int8, int16, int32, int64 +} + +// The Unsigned contract permits any unsigned integer type. +contract Unsigned(T) { + T uint, uint8, uint16, uint32, uint64, uintptr +} diff --git a/src/cmd/go2go/testdata/go2path/src/slices/slices.go2 b/src/cmd/go2go/testdata/go2path/src/slices/slices.go2 index 95dba8f65f..456d9222fe 100644 --- a/src/cmd/go2go/testdata/go2path/src/slices/slices.go2 +++ b/src/cmd/go2go/testdata/go2path/src/slices/slices.go2 @@ -2,9 +2,14 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// Package slices provides functions for basic operations on +// slices of any element type. package slices -import "contracts" +import ( + "alg" + "contracts" +) // Equal reports whether two slices are equal: the same length and all // elements equal. All floating point NaNs are considered equal. @@ -38,3 +43,53 @@ func EqualFn(type Elem)(s1, s2 []Elem, eq func(Elem, Elem) bool) bool { } return true } + +// Map turns a []Elem1 to a []Elem2 using a mapping function. +func Map(type Elem1, Elem2)(s []Elem1, f func(Elem1) Elem2) []Elem2 { + r := make([]Elem2, len(s)) + for i, v := range s { + r[i] = f(v) + } + return r +} + +// Reduce reduces a []Elem1 to a single value of type Elem2 using +// a reduction function. +func Reduce(type Elem1, Elem2)(s []Elem1, initializer Elem2, f func(Elem2, Elem1) Elem2) Elem2 { + r := initializer + for _, v := range s { + r = f(r, v) + } + return r +} + +// Filter filters values from a slice using a filter function. +func Filter(type Elem)(s []Elem, f func(Elem) bool) []Elem { + var r []Elem + for _, v := range s { + if f(v) { + r = append(r, v) + } + } + return r +} + +// Max returns the maximum element in a slice of some ordered type. +// If the slice is empty it returns the zero value of the element type. +func Max(type Elem contracts.Ordered)(s []Elem) Elem { + if len(s) == 0 { + var zero Elem + return zero + } + return Reduce(s[1:], s[0], alg.Max(Elem)) +} + +// Min returns the minimum element in a slice of some ordered type. +// If the slice is empty it returns the zero value of the element type. +func Min(type Elem contracts.Ordered)(s []Elem) Elem { + if len(s) == 0 { + var zero Elem + return zero + } + return Reduce(s[1:], s[0], alg.Min(Elem)) +} diff --git a/src/cmd/go2go/testdata/go2path/src/slices/slices_test.go2 b/src/cmd/go2go/testdata/go2path/src/slices/slices_test.go2 new file mode 100644 index 0000000000..a64427a6ec --- /dev/null +++ b/src/cmd/go2go/testdata/go2path/src/slices/slices_test.go2 @@ -0,0 +1,143 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package slices + +import ( + "math" + "strings" + "testing" + + "contracts" +) + +func TestEqual(t *testing.T) { + s1 := []int{1, 2, 3} + if !Equal(s1, s1) { + t.Errorf("Equal(%v, %v) = false, want true", s1, s1) + } + s2 := []int{1, 2, 3} + if !Equal(s1, s2) { + t.Errorf("Equal(%v, %v) = false, want true", s1, s2) + } + s2 = append(s2, 4) + if Equal(s1, s2) { + t.Errorf("Equal(%v, %v) = true, want false", s1, s2) + } + + s3 := []float64{1, 2, math.NaN()} + if !Equal(s3, s3) { + t.Errorf("Equal(%v, %v) = false, want true", s3, s3) + } + + if Equal(s1, nil) { + t.Errorf("Equal(%v, nil) = true, want false", s1) + } + if Equal(nil, s1) { + t.Errorf("Equal(nil, %v) = true, want false", s1) + } + if !Equal(s1[:0], nil) { + t.Errorf("Equal(%v, nil = false, want true", s1[:0]) + } +} + +func offByOne(type Elem contracts.Integer)(a, b Elem) bool { + return a == b + 1 || a == b - 1 +} + +func TestEqualFn(t *testing.T) { + s1 := []int{1, 2, 3} + s2 := []int{2, 3, 4} + if EqualFn(s1, s1, offByOne(int)) { + t.Errorf("EqualFn(%v, %v, offByOne) = true, want false", s1, s1) + } + if !EqualFn(s1, s2, offByOne(int)) { + t.Errorf("EqualFn(%v, %v, offByOne) = false, want true", s1, s2) + } + + s3 := []string{"a", "b", "c"} + s4 := []string{"A", "B", "C"} + if !EqualFn(s3, s4, strings.EqualFold) { + t.Errorf("EqualFn(%v, %v, strings.EqualFold) = false, want true", s3, s4) + } + + if !EqualFn(s1[:0], nil, offByOne(int)) { + t.Errorf("EqualFn(%v, nil, offByOne) = false, want true", s1[:0]) + } +} + +func TestMap(t *testing.T) { + s1 := []int{1, 2, 3} + s2 := Map(s1, func(i int) float64 { return float64(i) * 2.5 }) + if want := []float64{2.5, 5, 7.5}; !Equal(s2, want) { + t.Errorf("Map(%v, ...) = %v, want %v", s1, s2, want) + } + + s3 := []string{"Hello", "World"} + s4 := Map(s3, strings.ToLower) + if want := []string{"hello", "world"}; !Equal(s4, want) { + t.Errorf("Map(%v, strings.ToLower) = %v, want %v", s3, s4, want) + } + + s5 := Map(nil, func(i int) int { return i }) + if len(s5) != 0 { + t.Errorf("Map(nil, identity) = %v, want empty slice", s5) + } +} + +func TestReduce(t *testing.T) { + s1 := []int{1, 2, 3} + r := Reduce(s1, 0, func(f float64, i int) float64 { return float64(i) * 2.5 + f }) + if want := 15.0; r != want { + t.Errorf("Reduce(%v, 0, ...) = %v, want %v", s1, r, want) + } + + if got := Reduce(nil, 0, func(i, j int) int { return i + j}); got != 0 { + t.Errorf("Reduce(nil, 0, add) = %v, want 0", got) + } +} + +func TestFilter(t *testing.T) { + s1 := []int{1, 2, 3} + s2 := Filter(s1, func(i int) bool { return i%2 == 0 }) + if want := []int{2}; !Equal(s2, want) { + t.Errorf("Filter(%v, even) = %v, want %v", s1, s2, want) + } + + if s3 := Filter(s1[:0], func(i int) bool { return true }); len(s3) > 0 { + t.Errorf("Filter(%v, identity) = %v, want empty slice", s1[:0], s3) + } +} + +func TestMax(t *testing.T) { + s1 := []int{1, 2, 3, -5} + if got, want := Max(s1), 3; got != want { + t.Errorf("Max(%v) = %d, want %d", s1, got, want) + } + + s2 := []string{"aaa", "a", "aa", "aaaa"} + if got, want := Max(s2), "aaaa"; got != want { + t.Errorf("Max(%v) = %q, want %q", s2, got, want) + } + + if got, want := Max(s2[:0]), ""; got != want { + t.Errorf("Max(%v) = %q, want %q", s2[:0], got, want) + } +} + +func TestMin(t *testing.T) { + s1 := []int{1, 2, 3, -5} + if got, want := Min(s1), -5; got != want { + t.Errorf("Min(%v) = %d, want %d", s1, got, want) + } + + s2 := []string{"aaa", "a", "aa", "aaaa"} + if got, want := Min(s2), "a"; got != want { + t.Errorf("Min(%v) = %q, want %q", s2, got, want) + } + + if got, want := Min(s2[:0]), ""; got != want { + t.Errorf("Min(%v) = %q, want %q", s2[:0], got, want) + } +}