floats: fix nearest, span and min/max index behaviours

* Fix nearest and span behaviours
* Fix max and min behaviour for NaN-containing slices
* Unexport exported test helpers
This commit is contained in:
Dan Kortschak
2018-02-25 13:57:12 +10:30
committed by GitHub
parent 37b2756f7a
commit e558b37b29
2 changed files with 505 additions and 97 deletions

View File

@@ -433,10 +433,13 @@ func MaxIdx(s []float64) int {
if len(s) == 0 {
panic("floats: zero slice length")
}
max := s[0]
max := math.NaN()
var ind int
for i, v := range s {
if v > max {
if math.IsNaN(v) {
continue
}
if v > max || math.IsNaN(max) {
max = v
ind = i
}
@@ -453,10 +456,16 @@ func Min(s []float64) float64 {
// entries have the maximum value, the first such index is returned. If the slice
// is empty, MinIdx will panic.
func MinIdx(s []float64) int {
min := s[0]
if len(s) == 0 {
panic("floats: zero slice length")
}
min := math.NaN()
var ind int
for i, v := range s {
if v < min {
if math.IsNaN(v) {
continue
}
if v < min || math.IsNaN(min) {
min = v
ind = i
}
@@ -512,16 +521,31 @@ func NaNPayload(f float64) (payload uint64, ok bool) {
return b &^ nanMask, true
}
// Nearest returns the index of the element in s
// NearestIdx returns the index of the element in s
// whose value is nearest to v. If several such
// elements exist, the lowest index is returned.
// Panics if len(s) == 0.
func Nearest(s []float64, v float64) int {
// NearestIdx panics if len(s) == 0.
func NearestIdx(s []float64, v float64) int {
if len(s) == 0 {
panic("floats: zero length slice")
}
switch {
case math.IsNaN(v):
return 0
case math.IsInf(v, 1):
return MaxIdx(s)
case math.IsInf(v, -1):
return MinIdx(s)
}
var ind int
dist := math.Abs(v - s[0])
dist := math.NaN()
for i, val := range s {
newDist := math.Abs(v - val)
if newDist < dist {
// A NaN distance will not be closer.
if math.IsNaN(newDist) {
continue
}
if newDist < dist || math.IsNaN(dist) {
dist = newDist
ind = i
}
@@ -529,17 +553,84 @@ func Nearest(s []float64, v float64) int {
return ind
}
// NearestWithinSpan return the index of a hypothetical vector created
// NearestIdxForSpan return the index of a hypothetical vector created
// by Span with length n and bounds l and u whose value is closest
// to v. NearestWithinSpan panics if u < l. If the value is greater than u or
// less than l, the function returns -1.
func NearestWithinSpan(n int, l, u float64, v float64) int {
if u < l {
panic("floats: upper bound greater than lower bound")
// to v. That is, NearestIdxForSpan(n, l, u, v) is equivalent to
// Nearest(Span(make([]float64, n),l,u),v) without an allocation.
// NearestIdxForSpan panics if n is less than two.
func NearestIdxForSpan(n int, l, u float64, v float64) int {
if n <= 1 {
panic("floats: span must have length >1")
}
if v < l || v > u {
return -1
if math.IsNaN(v) {
return 0
}
// Special cases for Inf and NaN.
switch {
case math.IsNaN(l) && !math.IsNaN(u):
return n - 1
case math.IsNaN(u):
return 0
case math.IsInf(l, 0) && math.IsInf(u, 0):
if l == u {
return 0
}
if n%2 == 1 {
if !math.IsInf(v, 0) {
return n / 2
}
if math.Copysign(1, v) == math.Copysign(1, l) {
return 0
}
return n/2 + 1
}
if math.Copysign(1, v) == math.Copysign(1, l) {
return 0
}
return n / 2
case math.IsInf(l, 0):
if v == l {
return 0
}
return n - 1
case math.IsInf(u, 0):
if v == u {
return n - 1
}
return 0
case math.IsInf(v, -1):
if l <= u {
return 0
}
return n - 1
case math.IsInf(v, 1):
if u <= l {
return 0
}
return n - 1
}
// Special cases for v outside (l, u) and (u, l).
switch {
case l < u:
if v <= l {
return 0
}
if v >= u {
return n - 1
}
case l > u:
if v >= l {
return 0
}
if v <= u {
return n - 1
}
default:
return 0
}
// Can't guarantee anything about exactly halfway between
// because of floating point weirdness.
return int((float64(n)-1)/(u-l)*(v-l) + 0.5)
@@ -723,9 +814,11 @@ func Scale(c float64, dst []float64) {
// Span returns a set of N equally spaced points between l and u, where N
// is equal to the length of the destination. The first element of the destination
// is l, the final element of the destination is u.
//
// Panics if len(dst) < 2.
//
// Also returns the mutated slice dst, so that it can be used in range expressions, like:
// Span also returns the mutated slice dst, so that it can be used in range expressions,
// like:
//
// for i, x := range Span(dst, l, u) { ... }
func Span(dst []float64, l, u float64) []float64 {
@@ -733,6 +826,48 @@ func Span(dst []float64, l, u float64) []float64 {
if n < 2 {
panic("floats: destination must have length >1")
}
// Special cases for Inf and NaN.
switch {
case math.IsNaN(l):
for i := range dst[:len(dst)-1] {
dst[i] = math.NaN()
}
dst[len(dst)-1] = u
return dst
case math.IsNaN(u):
for i := range dst[1:] {
dst[i+1] = math.NaN()
}
dst[0] = l
return dst
case math.IsInf(l, 0) && math.IsInf(u, 0):
for i := range dst[:len(dst)/2] {
dst[i] = l
dst[len(dst)-i-1] = u
}
if len(dst)%2 == 1 {
if l != u {
dst[len(dst)/2] = 0
} else {
dst[len(dst)/2] = l
}
}
return dst
case math.IsInf(l, 0):
for i := range dst[:len(dst)-1] {
dst[i] = l
}
dst[len(dst)-1] = u
return dst
case math.IsInf(u, 0):
for i := range dst[1:] {
dst[i+1] = u
}
dst[0] = l
return dst
}
step := (u - l) / float64(n-1)
for i := range dst {
dst[i] = l + step*float64(i)

View File

@@ -5,6 +5,7 @@
package floats
import (
"fmt"
"math"
"strconv"
"testing"
@@ -20,12 +21,32 @@ const (
Huge = 10000000
)
func AreSlicesEqual(t *testing.T, truth, comp []float64, str string) {
func areSlicesEqual(t *testing.T, truth, comp []float64, str string) {
if !EqualApprox(comp, truth, EqTolerance) {
t.Errorf(str+". Expected %v, returned %v", truth, comp)
}
}
func areSlicesSame(t *testing.T, truth, comp []float64, str string) {
ok := len(truth) == len(comp)
if ok {
for i, a := range truth {
if !EqualWithinAbsOrRel(a, comp[i], EqTolerance, EqTolerance) && !same(a, comp[i]) {
ok = false
break
}
}
}
if !ok {
t.Errorf(str+". Expected %v, returned %v", truth, comp)
}
}
func same(a, b float64) bool {
return a == b || (math.IsNaN(a) && math.IsNaN(b))
}
func Panics(fun func()) (b bool) {
defer func() {
err := recover()
@@ -47,10 +68,10 @@ func TestAdd(t *testing.T) {
Add(n, a)
Add(n, b)
Add(n, c)
AreSlicesEqual(t, truth, n, "Wrong addition of slices new receiver")
areSlicesEqual(t, truth, n, "Wrong addition of slices new receiver")
Add(a, b)
Add(a, c)
AreSlicesEqual(t, truth, n, "Wrong addition of slices for no new receiver")
areSlicesEqual(t, truth, n, "Wrong addition of slices for no new receiver")
// Test that it panics
if !Panics(func() { Add(make([]float64, 2), make([]float64, 3)) }) {
@@ -65,8 +86,8 @@ func TestAddTo(t *testing.T) {
n1 := make([]float64, len(a))
n2 := AddTo(n1, a, b)
AreSlicesEqual(t, truth, n1, "Bad addition from mutator")
AreSlicesEqual(t, truth, n2, "Bad addition from returned slice")
areSlicesEqual(t, truth, n1, "Bad addition from mutator")
areSlicesEqual(t, truth, n2, "Bad addition from returned slice")
// Test that it panics
if !Panics(func() { AddTo(make([]float64, 2), make([]float64, 3), make([]float64, 3)) }) {
@@ -83,7 +104,7 @@ func TestAddConst(t *testing.T) {
c := 6.0
truth := []float64{9, 10, 7, 13, 11}
AddConst(c, s)
AreSlicesEqual(t, truth, s, "Wrong addition of constant")
areSlicesEqual(t, truth, s, "Wrong addition of constant")
}
func TestAddScaled(t *testing.T) {
@@ -172,10 +193,10 @@ func TestCumProd(t *testing.T) {
receiver := make([]float64, len(s))
result := CumProd(receiver, s)
truth := []float64{3, 12, 12, 84, 420}
AreSlicesEqual(t, truth, receiver, "Wrong cumprod mutated with new receiver")
AreSlicesEqual(t, truth, result, "Wrong cumprod result with new receiver")
areSlicesEqual(t, truth, receiver, "Wrong cumprod mutated with new receiver")
areSlicesEqual(t, truth, result, "Wrong cumprod result with new receiver")
CumProd(receiver, s)
AreSlicesEqual(t, truth, receiver, "Wrong cumprod returned with reused receiver")
areSlicesEqual(t, truth, receiver, "Wrong cumprod returned with reused receiver")
// Test that it panics
if !Panics(func() { CumProd(make([]float64, 2), make([]float64, 3)) }) {
@@ -186,7 +207,7 @@ func TestCumProd(t *testing.T) {
emptyReceiver := make([]float64, 0)
truth = []float64{}
CumProd(emptyReceiver, emptyReceiver)
AreSlicesEqual(t, truth, emptyReceiver, "Wrong cumprod returned with empty receiver")
areSlicesEqual(t, truth, emptyReceiver, "Wrong cumprod returned with empty receiver")
}
@@ -195,10 +216,10 @@ func TestCumSum(t *testing.T) {
receiver := make([]float64, len(s))
result := CumSum(receiver, s)
truth := []float64{3, 7, 8, 15, 20}
AreSlicesEqual(t, truth, receiver, "Wrong cumsum mutated with new receiver")
AreSlicesEqual(t, truth, result, "Wrong cumsum returned with new receiver")
areSlicesEqual(t, truth, receiver, "Wrong cumsum mutated with new receiver")
areSlicesEqual(t, truth, result, "Wrong cumsum returned with new receiver")
CumSum(receiver, s)
AreSlicesEqual(t, truth, receiver, "Wrong cumsum returned with reused receiver")
areSlicesEqual(t, truth, receiver, "Wrong cumsum returned with reused receiver")
// Test that it panics
if !Panics(func() { CumSum(make([]float64, 2), make([]float64, 3)) }) {
@@ -209,7 +230,7 @@ func TestCumSum(t *testing.T) {
emptyReceiver := make([]float64, 0)
truth = []float64{}
CumSum(emptyReceiver, emptyReceiver)
AreSlicesEqual(t, truth, emptyReceiver, "Wrong cumsum returned with empty receiver")
areSlicesEqual(t, truth, emptyReceiver, "Wrong cumsum returned with empty receiver")
}
@@ -626,12 +647,12 @@ func TestLogSpan(t *testing.T) {
for i := range comp {
comp[i] = 1
}
AreSlicesEqual(t, comp, tst, "Improper logspace from mutator")
areSlicesEqual(t, comp, tst, "Improper logspace from mutator")
for i := range truth {
tst[i] = receiver2[i] / truth[i]
}
AreSlicesEqual(t, comp, tst, "Improper logspace from returned slice")
areSlicesEqual(t, comp, tst, "Improper logspace from returned slice")
if !Panics(func() { LogSpan(nil, 1, 5) }) {
t.Errorf("Span accepts nil argument")
@@ -682,26 +703,100 @@ func TestLogSumExp(t *testing.T) {
}
func TestMaxAndIdx(t *testing.T) {
s := []float64{3, 4, 1, 7, 5}
ind := MaxIdx(s)
val := Max(s)
if val != 7 {
t.Errorf("Wrong value returned")
for _, test := range []struct {
in []float64
wantIdx int
wantVal float64
desc string
}{
{
in: []float64{3, 4, 1, 7, 5},
wantIdx: 3,
wantVal: 7,
desc: "with only finite entries",
},
{
in: []float64{math.NaN(), 4, 1, 7, 5},
wantIdx: 3,
wantVal: 7,
desc: "with leading NaN",
},
{
in: []float64{math.NaN(), math.NaN(), math.NaN()},
wantIdx: 0,
wantVal: math.NaN(),
desc: "when only NaN elements exist",
},
{
in: []float64{math.NaN(), math.Inf(-1)},
wantIdx: 1,
wantVal: math.Inf(-1),
desc: "leading NaN followed by -Inf",
},
{
in: []float64{math.NaN(), math.Inf(1)},
wantIdx: 1,
wantVal: math.Inf(1),
desc: "leading NaN followed by +Inf",
},
} {
ind := MaxIdx(test.in)
if ind != test.wantIdx {
t.Errorf("Wrong index "+test.desc+": got:%d want:%d", ind, test.wantIdx)
}
val := Max(test.in)
if !same(val, test.wantVal) {
t.Errorf("Wrong value "+test.desc+": got:%f want:%f", val, test.wantVal)
}
if ind != 3 {
t.Errorf("Wrong index returned")
}
}
func TestMinAndIdx(t *testing.T) {
s := []float64{3, 4, 1, 7, 5}
ind := MinIdx(s)
val := Min(s)
if val != 1 {
t.Errorf("Wrong value returned")
for _, test := range []struct {
in []float64
wantIdx int
wantVal float64
desc string
}{
{
in: []float64{3, 4, 1, 7, 5},
wantIdx: 2,
wantVal: 1,
desc: "with only finite entries",
},
{
in: []float64{math.NaN(), 4, 1, 7, 5},
wantIdx: 2,
wantVal: 1,
desc: "with leading NaN",
},
{
in: []float64{math.NaN(), math.NaN(), math.NaN()},
wantIdx: 0,
wantVal: math.NaN(),
desc: "when only NaN elements exist",
},
{
in: []float64{math.NaN(), math.Inf(-1)},
wantIdx: 1,
wantVal: math.Inf(-1),
desc: "leading NaN followed by -Inf",
},
{
in: []float64{math.NaN(), math.Inf(1)},
wantIdx: 1,
wantVal: math.Inf(1),
desc: "leading NaN followed by +Inf",
},
} {
ind := MinIdx(test.in)
if ind != test.wantIdx {
t.Errorf("Wrong index "+test.desc+": got:%d want:%d", ind, test.wantIdx)
}
val := Min(test.in)
if !same(val, test.wantVal) {
t.Errorf("Wrong value "+test.desc+": got:%f want:%f", val, test.wantVal)
}
if ind != 2 {
t.Errorf("Wrong index returned")
}
}
@@ -823,46 +918,100 @@ func TestNaNPayload(t *testing.T) {
}
}
func TestNearest(t *testing.T) {
s := []float64{6.2, 3, 5, 6.2, 8}
ind := Nearest(s, 2.0)
if ind != 1 {
t.Errorf("Wrong index returned when value is less than all of elements")
func TestNearestIdx(t *testing.T) {
for _, test := range []struct {
in []float64
query float64
want int
desc string
}{
{
in: []float64{6.2, 3, 5, 6.2, 8},
query: 2,
want: 1,
desc: "Wrong index returned when value is less than all of elements",
},
{
in: []float64{6.2, 3, 5, 6.2, 8},
query: 9,
want: 4,
desc: "Wrong index returned when value is greater than all of elements",
},
{
in: []float64{6.2, 3, 5, 6.2, 8},
query: 3.1,
want: 1,
desc: "Wrong index returned when value is greater than closest element",
},
{
in: []float64{6.2, 3, 5, 6.2, 8},
query: 2.9,
want: 1,
desc: "Wrong index returned when value is less than closest element",
},
{
in: []float64{6.2, 3, 5, 6.2, 8},
query: 3,
want: 1,
desc: "Wrong index returned when value is equal to element",
},
{
in: []float64{6.2, 3, 5, 6.2, 8},
query: 6.2,
want: 0,
desc: "Wrong index returned when value is equal to several elements",
},
{
in: []float64{6.2, 3, 5, 6.2, 8},
query: 4,
want: 1,
desc: "Wrong index returned when value is exactly between two closest elements",
},
{
in: []float64{math.NaN(), 3, 2, -1},
query: 2,
want: 2,
desc: "Wrong index returned when initial element is NaN",
},
{
in: []float64{0, math.NaN(), -1, 2},
query: math.NaN(),
want: 0,
desc: "Wrong index returned when query is NaN and a NaN element exists",
},
{
in: []float64{0, math.NaN(), -1, 2},
query: math.Inf(1),
want: 3,
desc: "Wrong index returned when query is +Inf and no +Inf element exists",
},
{
in: []float64{0, math.NaN(), -1, 2},
query: math.Inf(-1),
want: 2,
desc: "Wrong index returned when query is -Inf and no -Inf element exists",
},
{
in: []float64{math.NaN(), math.NaN(), math.NaN()},
query: 1,
want: 0,
desc: "Wrong index returned when query is a number and only NaN elements exist",
},
{
in: []float64{math.NaN(), math.Inf(-1)},
query: 1,
want: 1,
desc: "Wrong index returned when query is a number and single NaN preceeds -Inf",
},
} {
ind := NearestIdx(test.in, test.query)
if ind != test.want {
t.Errorf(test.desc+": got:%d want:%d", ind, test.want)
}
ind = Nearest(s, 9.0)
if ind != 4 {
t.Errorf("Wrong index returned when value is greater than all of elements")
}
ind = Nearest(s, 3.1)
if ind != 1 {
t.Errorf("Wrong index returned when value is greater than closest element")
}
ind = Nearest(s, 3.1)
if ind != 1 {
t.Errorf("Wrong index returned when value is greater than closest element")
}
ind = Nearest(s, 2.9)
if ind != 1 {
t.Errorf("Wrong index returned when value is less than closest element")
}
ind = Nearest(s, 3)
if ind != 1 {
t.Errorf("Wrong index returned when value is equal to element")
}
ind = Nearest(s, 6.2)
if ind != 0 {
t.Errorf("Wrong index returned when value is equal to several elements")
}
ind = Nearest(s, 4)
if ind != 1 {
t.Errorf("Wrong index returned when value is exactly between two closest elements")
}
}
func TestNearestWithinSpan(t *testing.T) {
if !Panics(func() { NearestWithinSpan(10, 8, 2, 4.5) }) {
t.Errorf("Did not panic when upper bound is lower than greater bound")
}
func TestNearestIdxForSpan(t *testing.T) {
for i, test := range []struct {
length int
lower float64
@@ -875,14 +1024,14 @@ func TestNearestWithinSpan(t *testing.T) {
lower: 7,
upper: 8.2,
value: 6,
idx: -1,
idx: 0,
},
{
length: 13,
lower: 7,
upper: 8.2,
value: 10,
idx: -1,
idx: 12,
},
{
length: 13,
@@ -919,8 +1068,57 @@ func TestNearestWithinSpan(t *testing.T) {
value: 7.249,
idx: 2,
},
{
length: 4,
lower: math.Inf(-1),
upper: math.Inf(1),
value: math.Copysign(0, -1),
idx: 0,
},
{
length: 5,
lower: math.Inf(-1),
upper: math.Inf(1),
value: 0,
idx: 2,
},
{
length: 4,
lower: math.Inf(-1),
upper: math.Inf(1),
value: 0,
idx: 2,
},
{
length: 4,
lower: math.Inf(-1),
upper: math.Inf(1),
value: math.Inf(1),
idx: 2,
},
{
length: 4,
lower: math.Inf(-1),
upper: math.Inf(1),
value: math.Inf(-1),
idx: 0,
},
{
length: 5,
lower: math.Inf(1),
upper: math.Inf(1),
value: 1,
idx: 0,
},
{
length: 5,
lower: math.NaN(),
upper: math.NaN(),
value: 1,
idx: 0,
},
} {
if idx := NearestWithinSpan(test.length, test.lower, test.upper, test.value); test.idx != idx {
if idx := NearestIdxForSpan(test.length, test.lower, test.upper, test.value); test.idx != idx {
t.Errorf("Case %v mismatch: Want: %v, Got: %v", i, test.idx, idx)
}
}
@@ -1140,25 +1338,100 @@ func TestScale(t *testing.T) {
c := 5.0
truth := []float64{15, 20, 5, 35, 25}
Scale(c, s)
AreSlicesEqual(t, truth, s, "Bad scaling")
areSlicesEqual(t, truth, s, "Bad scaling")
}
func TestSpan(t *testing.T) {
receiver1 := make([]float64, 5)
truth := []float64{1, 2, 3, 4, 5}
receiver2 := Span(receiver1, 1, 5)
AreSlicesEqual(t, truth, receiver1, "Improper linspace from mutator")
AreSlicesEqual(t, truth, receiver2, "Improper linspace from returned slice")
areSlicesEqual(t, truth, receiver1, "Improper linspace from mutator")
areSlicesEqual(t, truth, receiver2, "Improper linspace from returned slice")
receiver1 = make([]float64, 6)
truth = []float64{0, 0.2, 0.4, 0.6, 0.8, 1.0}
Span(receiver1, 0, 1)
AreSlicesEqual(t, truth, receiver1, "Improper linspace")
areSlicesEqual(t, truth, receiver1, "Improper linspace")
if !Panics(func() { Span(nil, 1, 5) }) {
t.Errorf("Span accepts nil argument")
}
if !Panics(func() { Span(make([]float64, 1), 1, 5) }) {
t.Errorf("Span accepts argument of len = 1")
}
for _, test := range []struct {
n int
l, u float64
want []float64
}{
{
n: 4, l: math.Inf(-1), u: math.Inf(1),
want: []float64{math.Inf(-1), math.Inf(-1), math.Inf(1), math.Inf(1)},
},
{
n: 4, l: math.Inf(1), u: math.Inf(-1),
want: []float64{math.Inf(1), math.Inf(1), math.Inf(-1), math.Inf(-1)},
},
{
n: 5, l: math.Inf(-1), u: math.Inf(1),
want: []float64{math.Inf(-1), math.Inf(-1), 0, math.Inf(1), math.Inf(1)},
},
{
n: 5, l: math.Inf(1), u: math.Inf(-1),
want: []float64{math.Inf(1), math.Inf(1), 0, math.Inf(-1), math.Inf(-1)},
},
{
n: 5, l: math.Inf(1), u: math.Inf(1),
want: []float64{math.Inf(1), math.Inf(1), math.Inf(1), math.Inf(1), math.Inf(1)},
},
{
n: 5, l: math.Inf(-1), u: math.Inf(-1),
want: []float64{math.Inf(-1), math.Inf(-1), math.Inf(-1), math.Inf(-1), math.Inf(-1)},
},
{
n: 5, l: math.Inf(-1), u: math.NaN(),
want: []float64{math.Inf(-1), math.NaN(), math.NaN(), math.NaN(), math.NaN()},
},
{
n: 5, l: math.Inf(1), u: math.NaN(),
want: []float64{math.Inf(1), math.NaN(), math.NaN(), math.NaN(), math.NaN()},
},
{
n: 5, l: math.NaN(), u: math.Inf(-1),
want: []float64{math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.Inf(-1)},
},
{
n: 5, l: math.NaN(), u: math.Inf(1),
want: []float64{math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.Inf(1)},
},
{
n: 5, l: 42, u: math.Inf(-1),
want: []float64{42, math.Inf(-1), math.Inf(-1), math.Inf(-1), math.Inf(-1)},
},
{
n: 5, l: 42, u: math.Inf(1),
want: []float64{42, math.Inf(1), math.Inf(1), math.Inf(1), math.Inf(1)},
},
{
n: 5, l: 42, u: math.NaN(),
want: []float64{42, math.NaN(), math.NaN(), math.NaN(), math.NaN()},
},
{
n: 5, l: math.Inf(-1), u: 42,
want: []float64{math.Inf(-1), math.Inf(-1), math.Inf(-1), math.Inf(-1), 42},
},
{
n: 5, l: math.Inf(1), u: 42,
want: []float64{math.Inf(1), math.Inf(1), math.Inf(1), math.Inf(1), 42},
},
{
n: 5, l: math.NaN(), u: 42,
want: []float64{math.NaN(), math.NaN(), math.NaN(), math.NaN(), 42},
},
} {
got := Span(make([]float64, test.n), test.l, test.u)
areSlicesSame(t, test.want, got,
fmt.Sprintf("Unexpected slice of length %d for %f to %f", test.n, test.l, test.u))
}
}
func TestSub(t *testing.T) {
@@ -1166,7 +1439,7 @@ func TestSub(t *testing.T) {
v := []float64{1, 2, 3, 4, 5}
truth := []float64{2, 2, -2, 3, 0}
Sub(s, v)
AreSlicesEqual(t, truth, s, "Bad subtract")
areSlicesEqual(t, truth, s, "Bad subtract")
// Test that it panics
if !Panics(func() { Sub(make([]float64, 2), make([]float64, 3)) }) {
t.Errorf("Did not panic with length mismatch")
@@ -1179,8 +1452,8 @@ func TestSubTo(t *testing.T) {
truth := []float64{2, 2, -2, 3, 0}
dst1 := make([]float64, len(s))
dst2 := SubTo(dst1, s, v)
AreSlicesEqual(t, truth, dst1, "Bad subtract from mutator")
AreSlicesEqual(t, truth, dst2, "Bad subtract from returned slice")
areSlicesEqual(t, truth, dst1, "Bad subtract from mutator")
areSlicesEqual(t, truth, dst2, "Bad subtract from returned slice")
// Test that all mismatch combinations panic
if !Panics(func() { SubTo(make([]float64, 2), make([]float64, 3), make([]float64, 3)) }) {
t.Errorf("Did not panic with dst different length")