diff --git a/cmplxs/cmplxs.go b/cmplxs/cmplxs.go index c945e51a..f208755e 100644 --- a/cmplxs/cmplxs.go +++ b/cmplxs/cmplxs.go @@ -9,7 +9,7 @@ import ( "math" "math/cmplx" - "gonum.org/v1/gonum/floats/scalar" + "gonum.org/v1/gonum/cmplxs/cscalar" "gonum.org/v1/gonum/internal/asm/c128" ) @@ -200,7 +200,7 @@ func EqualApprox(s1, s2 []complex128, tol float64) bool { return false } for i, a := range s1 { - if !EqualWithinAbsOrRel(a, s2[i], tol, tol) { + if !cscalar.EqualWithinAbsOrRel(a, s2[i], tol, tol) { return false } } @@ -221,40 +221,6 @@ func EqualFunc(s1, s2 []complex128, f func(complex128, complex128) bool) bool { return true } -// EqualWithinAbs returns true if a and b have an absolute difference -// of less than tol. -func EqualWithinAbs(a, b complex128, tol float64) bool { - return a == b || cmplx.Abs(a-b) <= tol -} - -const minNormalFloat64 = 2.2250738585072014e-308 - -// EqualWithinRel returns true if the difference between a and b -// is not greater than tol times the greater absolute value of a and b, -// abs(a-b) <= tol * max(abs(a), abs(b)). -func EqualWithinRel(a, b complex128, tol float64) bool { - if a == b { - return true - } - - delta := cmplx.Abs(a - b) - if delta <= minNormalFloat64 { - return delta <= tol*minNormalFloat64 - } - // We depend on the division in this relationship to identify - // infinities. - return delta/math.Max(cmplx.Abs(a), cmplx.Abs(b)) <= tol -} - -// EqualWithinAbsOrRel returns true if parts of a and b are equal to within -// the absolute tolerance. -func EqualWithinAbsOrRel(a, b complex128, absTol, relTol float64) bool { - if EqualWithinAbs(a, b, absTol) { - return true - } - return EqualWithinRel(a, b, relTol) -} - // EqualLengths returns true if all of the slices have equal length, // and false otherwise. Returns true if there are no input slices. func EqualLengths(slices ...[]complex128) bool { @@ -487,19 +453,6 @@ func Norm(s []complex128, L float64) float64 { } } -// ParseWithNA converts the string s to a complex128 in v. -// If s equals missing, w is returned as 0, otherwise 1. -func ParseWithNA(s, missing string) (v complex128, w float64, err error) { - if s == missing { - return 0, 0, nil - } - v, err = parse(s) - if err == nil { - w = 1 - } - return v, w, err -} - // Prod returns the product of the elements of the slice. // Returns 1 if len(s) = 0. func Prod(s []complex128) complex128 { @@ -517,36 +470,6 @@ func Reverse(s []complex128) { } } -// Round returns the half away from zero rounded value of x with prec precision. -// -// Special cases are: -// Round(±0) = +0 -// Round(±Inf) = ±Inf -// Round(NaN) = NaN -func Round(x complex128, prec int) complex128 { - if x == 0 { - // Make sure zero is returned - // without the negative bit set. - return 0 - } - return complex(scalar.Round(real(x), prec), scalar.Round(imag(x), prec)) -} - -// RoundEven returns the half even rounded value of x with prec precision. -// -// Special cases are: -// RoundEven(±0) = +0 -// RoundEven(±Inf) = ±Inf -// RoundEven(NaN) = NaN -func RoundEven(x complex128, prec int) complex128 { - if x == 0 { - // Make sure zero is returned - // without the negative bit set. - return 0 - } - return complex(scalar.RoundEven(real(x), prec), scalar.RoundEven(imag(x), prec)) -} - // Same returns true if the input slices have the same length and all elements // have the same value with NaN treated as the same. func Same(s, t []complex128) bool { diff --git a/cmplxs/cmplxs_test.go b/cmplxs/cmplxs_test.go index 5bbd2e8c..c9489832 100644 --- a/cmplxs/cmplxs_test.go +++ b/cmplxs/cmplxs_test.go @@ -12,6 +12,8 @@ import ( "testing" "golang.org/x/exp/rand" + + "gonum.org/v1/gonum/cmplxs/cscalar" ) const ( @@ -32,7 +34,7 @@ func areSlicesSame(t *testing.T, truth, comp []complex128, 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]) { + if !cscalar.EqualWithinAbsOrRel(a, comp[i], EqTolerance, EqTolerance) && !cscalar.Same(a, comp[i]) { ok = false break } @@ -365,103 +367,6 @@ func TestEqualFunc(t *testing.T) { } } -func TestEqualsRelative(t *testing.T) { - equalityTests := []struct { - a, b float64 - tol float64 - equal bool - }{ - {1000000, 1000001, 0, true}, - {1000001, 1000000, 0, true}, - {10000, 10001, 0, false}, - {10001, 10000, 0, false}, - {-1000000, -1000001, 0, true}, - {-1000001, -1000000, 0, true}, - {-10000, -10001, 0, false}, - {-10001, -10000, 0, false}, - {1.0000001, 1.0000002, 0, true}, - {1.0000002, 1.0000001, 0, true}, - {1.0002, 1.0001, 0, false}, - {1.0001, 1.0002, 0, false}, - {-1.000001, -1.000002, 0, true}, - {-1.000002, -1.000001, 0, true}, - {-1.0001, -1.0002, 0, false}, - {-1.0002, -1.0001, 0, false}, - {0.000000001000001, 0.000000001000002, 0, true}, - {0.000000001000002, 0.000000001000001, 0, true}, - {0.000000000001002, 0.000000000001001, 0, false}, - {0.000000000001001, 0.000000000001002, 0, false}, - {-0.000000001000001, -0.000000001000002, 0, true}, - {-0.000000001000002, -0.000000001000001, 0, true}, - {-0.000000000001002, -0.000000000001001, 0, false}, - {-0.000000000001001, -0.000000000001002, 0, false}, - {0, 0, 0, true}, - {0, -0, 0, true}, - {-0, -0, 0, true}, - {0.00000001, 0, 0, false}, - {0, 0.00000001, 0, false}, - {-0.00000001, 0, 0, false}, - {0, -0.00000001, 0, false}, - {0, 1e-310, 0.01, true}, - {1e-310, 0, 0.01, true}, - {1e-310, 0, 0.000001, false}, - {0, 1e-310, 0.000001, false}, - {0, -1e-310, 0.1, true}, - {-1e-310, 0, 0.1, true}, - {-1e-310, 0, 0.00000001, false}, - {0, -1e-310, 0.00000001, false}, - {math.Inf(1), math.Inf(1), 0, true}, - {math.Inf(1), math.MaxFloat64, 0, false}, - {math.NaN(), math.NaN(), 0, false}, - {math.NaN(), 0, 0, false}, - {-0, math.NaN(), 0, false}, - {math.NaN(), -0, 0, false}, - {0, math.NaN(), 0, false}, - {math.NaN(), math.Inf(1), 0, false}, - {math.Inf(1), math.NaN(), 0, false}, - {math.NaN(), math.MaxFloat64, 0, false}, - {math.MaxFloat64, math.NaN(), 0, false}, - {math.NaN(), -math.MaxFloat64, 0, false}, - {-math.MaxFloat64, math.NaN(), 0, false}, - {math.NaN(), math.SmallestNonzeroFloat64, 0, false}, - {math.SmallestNonzeroFloat64, math.NaN(), 0, false}, - {math.NaN(), -math.SmallestNonzeroFloat64, 0, false}, - {-math.SmallestNonzeroFloat64, math.NaN(), 0, false}, - {1.000000001, -1.0, 0, false}, - {-1.0, 1.000000001, 0, false}, - {-1.000000001, 1.0, 0, false}, - {1.0, -1.000000001, 0, false}, - {10 * math.SmallestNonzeroFloat64, 10 * -math.SmallestNonzeroFloat64, 0, true}, - {1e11 * math.SmallestNonzeroFloat64, 1e11 * -math.SmallestNonzeroFloat64, 0, false}, - {math.SmallestNonzeroFloat64, -math.SmallestNonzeroFloat64, 0, true}, - {-math.SmallestNonzeroFloat64, math.SmallestNonzeroFloat64, 0, true}, - {math.SmallestNonzeroFloat64, 0, 0, true}, - {0, math.SmallestNonzeroFloat64, 0, true}, - {-math.SmallestNonzeroFloat64, 0, 0, true}, - {0, -math.SmallestNonzeroFloat64, 0, true}, - {0.000000001, -math.SmallestNonzeroFloat64, 0, false}, - {0.000000001, math.SmallestNonzeroFloat64, 0, false}, - {math.SmallestNonzeroFloat64, 0.000000001, 0, false}, - {-math.SmallestNonzeroFloat64, 0.000000001, 0, false}, - } - for _, ts := range equalityTests { - if ts.tol == 0 { - ts.tol = 1e-5 - } - - for _, comp := range []struct{ a, b complex128 }{ - {a: complex(ts.a, 0), b: complex(ts.b, 0)}, - {a: complex(0, ts.a), b: complex(0, ts.b)}, - {a: complex(ts.a, ts.a), b: complex(ts.b, ts.b)}, - } { - if equal := EqualWithinRel(comp.a, comp.b, ts.tol); equal != ts.equal { - t.Errorf("Relative equality of %g and %g with tolerance %g returned: %v. Expected: %v", - comp.a, comp.b, ts.tol, equal, ts.equal) - } - } - } -} - func TestEqualLengths(t *testing.T) { s1 := []complex128{1 + 1i, 2 + 2i, 3 + 3i, 4 + 4i} s2 := []complex128{1 + 1i, 2 + 2i, 3 + 3i, 4 + 4i} @@ -641,7 +546,7 @@ func TestMaxAbsAndIdx(t *testing.T) { t.Errorf("Wrong index "+test.desc+": got:%d want:%d", ind, test.wantIdx) } val := MaxAbs(test.in) - if !same(val, test.wantVal) { + if !cscalar.Same(val, test.wantVal) { t.Errorf("Wrong value "+test.desc+": got:%f want:%f", val, test.wantVal) } } @@ -684,7 +589,7 @@ func TestMinAbsAndIdx(t *testing.T) { t.Errorf("Wrong index "+test.desc+": got:%d want:%d", ind, test.wantIdx) } val := MinAbs(test.in) - if !same(val, test.wantVal) { + if !cscalar.Same(val, test.wantVal) { t.Errorf("Wrong value "+test.desc+": got:%f want:%f", val, test.wantVal) } } @@ -893,134 +798,6 @@ func TestReverse(t *testing.T) { } } -func TestRound(t *testing.T) { - for _, test := range []struct { - x complex128 - prec int - want complex128 - }{ - {x: 0, prec: 1, want: 0}, - {x: cmplx.Inf(), prec: 1, want: cmplx.Inf()}, - {x: cmplx.NaN(), prec: 1, want: cmplx.NaN()}, - {x: func() complex128 { var f complex128; return -f }(), prec: 1, want: 0}, - {x: math.MaxFloat64 / 2, prec: 1, want: math.MaxFloat64 / 2}, - {x: 1 << 64, prec: 1, want: 1 << 64}, - {x: 454.4445, prec: 3, want: 454.445}, - {x: 454.44445, prec: 4, want: 454.4445}, - {x: 0.42499, prec: 4, want: 0.425}, - {x: 0.42599, prec: 4, want: 0.426}, - {x: 0.424999999999993, prec: 2, want: 0.42}, - {x: 0.425, prec: 2, want: 0.43}, - {x: 0.425000000000001, prec: 2, want: 0.43}, - {x: 123.4244999999999, prec: 3, want: 123.424}, - {x: 123.4245, prec: 3, want: 123.425}, - {x: 123.4245000000001, prec: 3, want: 123.425}, - - {x: 454.45, prec: 0, want: 454}, - {x: 454.45, prec: 1, want: 454.5}, - {x: 454.45, prec: 2, want: 454.45}, - {x: 454.45, prec: 3, want: 454.45}, - {x: 454.445, prec: 0, want: 454}, - {x: 454.445, prec: 1, want: 454.4}, - {x: 454.445, prec: 2, want: 454.45}, - {x: 454.445, prec: 3, want: 454.445}, - {x: 454.445, prec: 4, want: 454.445}, - {x: 454.55, prec: 0, want: 455}, - {x: 454.55, prec: 1, want: 454.6}, - {x: 454.55, prec: 2, want: 454.55}, - {x: 454.55, prec: 3, want: 454.55}, - {x: 454.455, prec: 0, want: 454}, - {x: 454.455, prec: 1, want: 454.5}, - {x: 454.455, prec: 2, want: 454.46}, - {x: 454.455, prec: 3, want: 454.455}, - {x: 454.455, prec: 4, want: 454.455}, - - // Negative precision. - {x: 454.45, prec: -1, want: 450}, - {x: 454.45, prec: -2, want: 500}, - {x: 500, prec: -3, want: 1000}, - {x: 500, prec: -4, want: 0}, - {x: 1500, prec: -3, want: 2000}, - {x: 1500, prec: -4, want: 0}, - } { - for _, sign := range []complex128{1, -1} { - got := Round(sign*test.x, test.prec) - want := sign * test.want - if want == 0 { - want = 0 - } - // FIXME(kortschak): Complexify this. - if (got != want || math.Signbit(real(got)) != math.Signbit(real(want))) && !(math.IsNaN(real(got)) && math.IsNaN(real(want))) { - t.Errorf("unexpected result for Round(%g, %d): got: %g, want: %g", sign*test.x, test.prec, got, want) - } - } - } -} - -func TestRoundEven(t *testing.T) { - for _, test := range []struct { - x complex128 - prec int - want complex128 - }{ - {x: 0, prec: 1, want: 0}, - {x: cmplx.Inf(), prec: 1, want: cmplx.Inf()}, - {x: cmplx.NaN(), prec: 1, want: cmplx.NaN()}, - {x: func() complex128 { var f complex128; return -f }(), prec: 1, want: 0}, - {x: math.MaxFloat64 / 2, prec: 1, want: math.MaxFloat64 / 2}, - {x: 1 << 64, prec: 1, want: 1 << 64}, - {x: 454.4445, prec: 3, want: 454.444}, - {x: 454.44445, prec: 4, want: 454.4444}, - {x: 0.42499, prec: 4, want: 0.425}, - {x: 0.42599, prec: 4, want: 0.426}, - {x: 0.424999999999993, prec: 2, want: 0.42}, - {x: 0.425, prec: 2, want: 0.42}, - {x: 0.425000000000001, prec: 2, want: 0.43}, - {x: 123.4244999999999, prec: 3, want: 123.424}, - {x: 123.4245, prec: 3, want: 123.424}, - {x: 123.4245000000001, prec: 3, want: 123.425}, - - {x: 454.45, prec: 0, want: 454}, - {x: 454.45, prec: 1, want: 454.4}, - {x: 454.45, prec: 2, want: 454.45}, - {x: 454.45, prec: 3, want: 454.45}, - {x: 454.445, prec: 0, want: 454}, - {x: 454.445, prec: 1, want: 454.4}, - {x: 454.445, prec: 2, want: 454.44}, - {x: 454.445, prec: 3, want: 454.445}, - {x: 454.445, prec: 4, want: 454.445}, - {x: 454.55, prec: 0, want: 455}, - {x: 454.55, prec: 1, want: 454.6}, - {x: 454.55, prec: 2, want: 454.55}, - {x: 454.55, prec: 3, want: 454.55}, - {x: 454.455, prec: 0, want: 454}, - {x: 454.455, prec: 1, want: 454.5}, - {x: 454.455, prec: 2, want: 454.46}, - {x: 454.455, prec: 3, want: 454.455}, - {x: 454.455, prec: 4, want: 454.455}, - - // Negative precision. - {x: 454.45, prec: -1, want: 450}, - {x: 454.45, prec: -2, want: 500}, - {x: 500, prec: -3, want: 0}, - {x: 500, prec: -4, want: 0}, - {x: 1500, prec: -3, want: 2000}, - {x: 1500, prec: -4, want: 0}, - } { - for _, sign := range []complex128{1, -1} { - got := RoundEven(sign*test.x, test.prec) - want := sign * test.want - if want == 0 { - want = 0 - } - // FIXME(kortschak): Complexify this. - if (got != want || math.Signbit(real(got)) != math.Signbit(real(want))) && !(math.IsNaN(real(got)) && math.IsNaN(real(want))) { - t.Errorf("unexpected result for RoundEven(%g, %d): got: %g, want: %g", sign*test.x, test.prec, got, want) - } - } - } -} - func TestSame(t *testing.T) { s1 := []complex128{1 + 1i, 2 + 2i, 3 + 3i, 4 + 4i} s2 := []complex128{1 + 1i, 2 + 2i, 3 + 3i, 4 + 4i} diff --git a/cmplxs/cscalar/cscalar.go b/cmplxs/cscalar/cscalar.go new file mode 100644 index 00000000..1c3e3724 --- /dev/null +++ b/cmplxs/cscalar/cscalar.go @@ -0,0 +1,94 @@ +// Copyright ©2013 The Gonum Authors. All rights reserved. +// Use of this code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cscalar + +import ( + "math" + "math/cmplx" + + "gonum.org/v1/gonum/floats/scalar" +) + +// EqualWithinAbs returns true if a and b have an absolute difference +// of less than tol. +func EqualWithinAbs(a, b complex128, tol float64) bool { + return a == b || cmplx.Abs(a-b) <= tol +} + +const minNormalFloat64 = 2.2250738585072014e-308 + +// EqualWithinRel returns true if the difference between a and b +// is not greater than tol times the greater absolute value of a and b, +// abs(a-b) <= tol * max(abs(a), abs(b)). +func EqualWithinRel(a, b complex128, tol float64) bool { + if a == b { + return true + } + + delta := cmplx.Abs(a - b) + if delta <= minNormalFloat64 { + return delta <= tol*minNormalFloat64 + } + // We depend on the division in this relationship to identify + // infinities. + return delta/math.Max(cmplx.Abs(a), cmplx.Abs(b)) <= tol +} + +// EqualWithinAbsOrRel returns true if parts of a and b are equal to within +// the absolute tolerance. +func EqualWithinAbsOrRel(a, b complex128, absTol, relTol float64) bool { + if EqualWithinAbs(a, b, absTol) { + return true + } + return EqualWithinRel(a, b, relTol) +} + +// ParseWithNA converts the string s to a complex128 in v. +// If s equals missing, w is returned as 0, otherwise 1. +func ParseWithNA(s, missing string) (v complex128, w float64, err error) { + if s == missing { + return 0, 0, nil + } + v, err = parse(s) + if err == nil { + w = 1 + } + return v, w, err +} + +// Round returns the half away from zero rounded value of x with prec precision. +// +// Special cases are: +// Round(±0) = +0 +// Round(±Inf) = ±Inf +// Round(NaN) = NaN +func Round(x complex128, prec int) complex128 { + if x == 0 { + // Make sure zero is returned + // without the negative bit set. + return 0 + } + return complex(scalar.Round(real(x), prec), scalar.Round(imag(x), prec)) +} + +// RoundEven returns the half even rounded value of x with prec precision. +// +// Special cases are: +// RoundEven(±0) = +0 +// RoundEven(±Inf) = ±Inf +// RoundEven(NaN) = NaN +func RoundEven(x complex128, prec int) complex128 { + if x == 0 { + // Make sure zero is returned + // without the negative bit set. + return 0 + } + return complex(scalar.RoundEven(real(x), prec), scalar.RoundEven(imag(x), prec)) +} + +// Same returns true if the inputs have the same value with NaN treated as the same. +func Same(a, b complex128) bool { + return a == b || (cmplx.IsNaN(a) && cmplx.IsNaN(b)) +} diff --git a/cmplxs/cscalar/cscalar_test.go b/cmplxs/cscalar/cscalar_test.go new file mode 100644 index 00000000..d6b59c82 --- /dev/null +++ b/cmplxs/cscalar/cscalar_test.go @@ -0,0 +1,198 @@ +// Copyright ©2013 The Gonum Authors. All rights reserved. +// Use of this code is governed by a BSD-style +// license that can be found in the LICENSE file + +package cscalar + +import ( + "math" + "math/cmplx" + "testing" +) + +func TestEqualsRelative(t *testing.T) { + equalityTests := []struct { + a, b float64 + tol float64 + equal bool + }{ + {1000000, 1000001, 0, true}, + {1000001, 1000000, 0, true}, + {10000, 10001, 0, false}, + {10001, 10000, 0, false}, + {-1000000, -1000001, 0, true}, + {-1000001, -1000000, 0, true}, + {-10000, -10001, 0, false}, + {-10001, -10000, 0, false}, + {1.0000001, 1.0000002, 0, true}, + {1.0000002, 1.0000001, 0, true}, + {1.0002, 1.0001, 0, false}, + {1.0001, 1.0002, 0, false}, + {-1.000001, -1.000002, 0, true}, + {-1.000002, -1.000001, 0, true}, + {-1.0001, -1.0002, 0, false}, + {-1.0002, -1.0001, 0, false}, + {0.000000001000001, 0.000000001000002, 0, true}, + {0.000000001000002, 0.000000001000001, 0, true}, + {0.000000000001002, 0.000000000001001, 0, false}, + {0.000000000001001, 0.000000000001002, 0, false}, + {-0.000000001000001, -0.000000001000002, 0, true}, + {-0.000000001000002, -0.000000001000001, 0, true}, + {-0.000000000001002, -0.000000000001001, 0, false}, + {-0.000000000001001, -0.000000000001002, 0, false}, + {0, 0, 0, true}, + {0, -0, 0, true}, + {-0, -0, 0, true}, + {0.00000001, 0, 0, false}, + {0, 0.00000001, 0, false}, + {-0.00000001, 0, 0, false}, + {0, -0.00000001, 0, false}, + {0, 1e-310, 0.01, true}, + {1e-310, 0, 0.01, true}, + {1e-310, 0, 0.000001, false}, + {0, 1e-310, 0.000001, false}, + {0, -1e-310, 0.1, true}, + {-1e-310, 0, 0.1, true}, + {-1e-310, 0, 0.00000001, false}, + {0, -1e-310, 0.00000001, false}, + {math.Inf(1), math.Inf(1), 0, true}, + {math.Inf(1), math.MaxFloat64, 0, false}, + {math.NaN(), math.NaN(), 0, false}, + {math.NaN(), 0, 0, false}, + {-0, math.NaN(), 0, false}, + {math.NaN(), -0, 0, false}, + {0, math.NaN(), 0, false}, + {math.NaN(), math.Inf(1), 0, false}, + {math.Inf(1), math.NaN(), 0, false}, + {math.NaN(), math.MaxFloat64, 0, false}, + {math.MaxFloat64, math.NaN(), 0, false}, + {math.NaN(), -math.MaxFloat64, 0, false}, + {-math.MaxFloat64, math.NaN(), 0, false}, + {math.NaN(), math.SmallestNonzeroFloat64, 0, false}, + {math.SmallestNonzeroFloat64, math.NaN(), 0, false}, + {math.NaN(), -math.SmallestNonzeroFloat64, 0, false}, + {-math.SmallestNonzeroFloat64, math.NaN(), 0, false}, + {1.000000001, -1.0, 0, false}, + {-1.0, 1.000000001, 0, false}, + {-1.000000001, 1.0, 0, false}, + {1.0, -1.000000001, 0, false}, + {10 * math.SmallestNonzeroFloat64, 10 * -math.SmallestNonzeroFloat64, 0, true}, + {1e11 * math.SmallestNonzeroFloat64, 1e11 * -math.SmallestNonzeroFloat64, 0, false}, + {math.SmallestNonzeroFloat64, -math.SmallestNonzeroFloat64, 0, true}, + {-math.SmallestNonzeroFloat64, math.SmallestNonzeroFloat64, 0, true}, + {math.SmallestNonzeroFloat64, 0, 0, true}, + {0, math.SmallestNonzeroFloat64, 0, true}, + {-math.SmallestNonzeroFloat64, 0, 0, true}, + {0, -math.SmallestNonzeroFloat64, 0, true}, + {0.000000001, -math.SmallestNonzeroFloat64, 0, false}, + {0.000000001, math.SmallestNonzeroFloat64, 0, false}, + {math.SmallestNonzeroFloat64, 0.000000001, 0, false}, + {-math.SmallestNonzeroFloat64, 0.000000001, 0, false}, + } + for _, ts := range equalityTests { + if ts.tol == 0 { + ts.tol = 1e-5 + } + + for _, comp := range []struct{ a, b complex128 }{ + {a: complex(ts.a, 0), b: complex(ts.b, 0)}, + {a: complex(0, ts.a), b: complex(0, ts.b)}, + {a: complex(ts.a, ts.a), b: complex(ts.b, ts.b)}, + } { + if equal := EqualWithinRel(comp.a, comp.b, ts.tol); equal != ts.equal { + t.Errorf("Relative equality of %g and %g with tolerance %g returned: %v. Expected: %v", + comp.a, comp.b, ts.tol, equal, ts.equal) + } + } + } +} + +func TestRoundEven(t *testing.T) { + for _, test := range []struct { + x complex128 + prec int + want complex128 + }{ + {x: 0, prec: 1, want: 0}, + {x: cmplx.Inf(), prec: 1, want: cmplx.Inf()}, + {x: cmplx.NaN(), prec: 1, want: cmplx.NaN()}, + {x: func() complex128 { var f complex128; return -f }(), prec: 1, want: 0}, + {x: math.MaxFloat64 / 2, prec: 1, want: math.MaxFloat64 / 2}, + {x: 1 << 64, prec: 1, want: 1 << 64}, + {x: 454.4445, prec: 3, want: 454.444}, + {x: 454.44445, prec: 4, want: 454.4444}, + {x: 0.42499, prec: 4, want: 0.425}, + {x: 0.42599, prec: 4, want: 0.426}, + {x: 0.424999999999993, prec: 2, want: 0.42}, + {x: 0.425, prec: 2, want: 0.42}, + {x: 0.425000000000001, prec: 2, want: 0.43}, + {x: 123.4244999999999, prec: 3, want: 123.424}, + {x: 123.4245, prec: 3, want: 123.424}, + {x: 123.4245000000001, prec: 3, want: 123.425}, + + {x: 454.45, prec: 0, want: 454}, + {x: 454.45, prec: 1, want: 454.4}, + {x: 454.45, prec: 2, want: 454.45}, + {x: 454.45, prec: 3, want: 454.45}, + {x: 454.445, prec: 0, want: 454}, + {x: 454.445, prec: 1, want: 454.4}, + {x: 454.445, prec: 2, want: 454.44}, + {x: 454.445, prec: 3, want: 454.445}, + {x: 454.445, prec: 4, want: 454.445}, + {x: 454.55, prec: 0, want: 455}, + {x: 454.55, prec: 1, want: 454.6}, + {x: 454.55, prec: 2, want: 454.55}, + {x: 454.55, prec: 3, want: 454.55}, + {x: 454.455, prec: 0, want: 454}, + {x: 454.455, prec: 1, want: 454.5}, + {x: 454.455, prec: 2, want: 454.46}, + {x: 454.455, prec: 3, want: 454.455}, + {x: 454.455, prec: 4, want: 454.455}, + + // Negative precision. + {x: 454.45, prec: -1, want: 450}, + {x: 454.45, prec: -2, want: 500}, + {x: 500, prec: -3, want: 0}, + {x: 500, prec: -4, want: 0}, + {x: 1500, prec: -3, want: 2000}, + {x: 1500, prec: -4, want: 0}, + } { + for _, sign := range []complex128{1, -1} { + got := RoundEven(sign*test.x, test.prec) + want := sign * test.want + if want == 0 { + want = 0 + } + // FIXME(kortschak): Complexify this. + if (got != want || math.Signbit(real(got)) != math.Signbit(real(want))) && !(math.IsNaN(real(got)) && math.IsNaN(real(want))) { + t.Errorf("unexpected result for RoundEven(%g, %d): got: %g, want: %g", sign*test.x, test.prec, got, want) + } + } + } +} + +func TestSame(t *testing.T) { + t.Parallel() + for _, test := range []struct { + a, b complex128 + want bool + }{ + {a: 0, b: 0, want: true}, + {a: 1, b: 1, want: true}, + {a: -1, b: 1, want: false}, + {a: 0, b: 1, want: false}, + {a: 1, b: 0, want: false}, + {a: -1, b: 1, want: false}, + {a: cmplx.NaN(), b: cmplx.NaN(), want: true}, + {a: 1, b: cmplx.NaN(), want: false}, + {a: cmplx.Inf(), b: cmplx.NaN(), want: false}, + {a: cmplx.NaN(), b: cmplx.Inf(), want: false}, + {a: cmplx.NaN(), b: 1, want: false}, + {a: cmplx.Inf(), b: cmplx.Inf(), want: true}, + } { + got := Same(test.a, test.b) + if got != test.want { + t.Errorf("unexpected results for a=%f b=%f: got:%t want:%t", test.a, test.b, got, test.want) + } + } +} diff --git a/cmplxs/cscalar/doc.go b/cmplxs/cscalar/doc.go new file mode 100644 index 00000000..ae7f50ba --- /dev/null +++ b/cmplxs/cscalar/doc.go @@ -0,0 +1,6 @@ +// Copyright ©2020 The Gonum 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 cscalar provides a set of helper routines for dealing with complex128 values. +package cscalar // import "gonum.org/v1/gonum/cmplxs/cscalar" diff --git a/cmplxs/parse.go b/cmplxs/cscalar/parse.go similarity index 99% rename from cmplxs/parse.go rename to cmplxs/cscalar/parse.go index 203d2f12..a0955731 100644 --- a/cmplxs/parse.go +++ b/cmplxs/cscalar/parse.go @@ -2,7 +2,7 @@ // Use of this code is governed by a BSD-style // license that can be found in the LICENSE file -package cmplxs +package cscalar import ( "fmt" diff --git a/cmplxs/parse_example_test.go b/cmplxs/cscalar/parse_example_test.go similarity index 86% rename from cmplxs/parse_example_test.go rename to cmplxs/cscalar/parse_example_test.go index 7a0816ae..02dd2a7b 100644 --- a/cmplxs/parse_example_test.go +++ b/cmplxs/cscalar/parse_example_test.go @@ -2,7 +2,7 @@ // Use of this code is governed by a BSD-style // license that can be found in the LICENSE file -package cmplxs_test +package cscalar_test import ( "bufio" @@ -11,6 +11,7 @@ import ( "strings" "gonum.org/v1/gonum/cmplxs" + "gonum.org/v1/gonum/cmplxs/cscalar" "gonum.org/v1/gonum/floats" ) @@ -28,7 +29,7 @@ missing ) sc := bufio.NewScanner(strings.NewReader(data)) for sc.Scan() { - v, w, err := cmplxs.ParseWithNA(sc.Text(), "missing") + v, w, err := cscalar.ParseWithNA(sc.Text(), "missing") if err != nil { log.Fatal(err) } diff --git a/cmplxs/parse_test.go b/cmplxs/cscalar/parse_test.go similarity index 95% rename from cmplxs/parse_test.go rename to cmplxs/cscalar/parse_test.go index 6acc8bb1..062163e0 100644 --- a/cmplxs/parse_test.go +++ b/cmplxs/cscalar/parse_test.go @@ -2,7 +2,7 @@ // Use of this code is governed by a BSD-style // license that can be found in the LICENSE file -package cmplxs +package cscalar import ( "math" @@ -85,12 +85,8 @@ func TestParse(t *testing.T) { if err != nil { continue } - if !same(got, test.want) { + if !Same(got, test.want) { t.Errorf("unexpected result for Parse(%q): got:%v, want:%v", test.s, got, test.want) } } } - -func same(a, b complex128) bool { - return a == b || (cmplx.IsNaN(a) && cmplx.IsNaN(b)) -}