├── .github ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .travis.yml ├── .travis └── test-coverage.sh ├── README.md ├── examples_test.go ├── floats.go └── floats_test.go /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### This repository is no longer actively maintained. 2 | 3 | Development of the packages in this repository has moved to https://github.com/gonum/gonum. 4 | Please file issues [there](https://github.com/gonum/gonum/issues) after having checked that your issue has not been fixed. 5 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### This repository is no longer actively maintained. 2 | 3 | Development of the packages in this repository has moved to https://github.com/gonum/gonum. 4 | Please send pull requests [there](https://github.com/gonum/gonum/pulls) after having checked that your addition has not already been made. 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | # Versions of go that are explicitly supported by gonum. 4 | go: 5 | - 1.5.4 6 | - 1.6.3 7 | - 1.7.3 8 | 9 | # Required for coverage. 10 | before_install: 11 | - go get golang.org/x/tools/cmd/cover 12 | - go get github.com/mattn/goveralls 13 | 14 | # Get deps, build, test, and ensure the code is gofmt'ed. 15 | # If we are building as gonum, then we have access to the coveralls api key, so we can run coverage as well. 16 | script: 17 | - go get -d -t -v ./... 18 | - go build -a -v ./... 19 | - go build -a -tags noasm -v ./... 20 | - go test -a -v ./... 21 | - go test -a -tags noasm -v ./... 22 | - test -z "$(gofmt -d .)" 23 | - if [[ $TRAVIS_SECURE_ENV_VARS = "true" ]]; then bash ./.travis/test-coverage.sh; fi 24 | -------------------------------------------------------------------------------- /.travis/test-coverage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | PROFILE_OUT=$PWD/profile.out 4 | ACC_OUT=$PWD/acc.out 5 | 6 | testCover() { 7 | # set the return value to 0 (succesful) 8 | retval=0 9 | # get the directory to check from the parameter. Default to '.' 10 | d=${1:-.} 11 | # skip if there are no Go files here 12 | ls $d/*.go &> /dev/null || return $retval 13 | # switch to the directory to check 14 | pushd $d > /dev/null 15 | # create the coverage profile 16 | coverageresult=`go test -v -coverprofile=$PROFILE_OUT` 17 | # output the result so we can check the shell output 18 | echo ${coverageresult} 19 | # append the results to acc.out if coverage didn't fail, else set the retval to 1 (failed) 20 | ( [[ ${coverageresult} == *FAIL* ]] && retval=1 ) || ( [ -f $PROFILE_OUT ] && grep -v "mode: set" $PROFILE_OUT >> $ACC_OUT ) 21 | # return to our working dir 22 | popd > /dev/null 23 | # return our return value 24 | return $retval 25 | } 26 | 27 | # Init acc.out 28 | echo "mode: set" > $ACC_OUT 29 | 30 | # Run test coverage on all directories containing go files 31 | find . -maxdepth 10 -type d | while read d; do testCover $d || exit; done 32 | 33 | # Upload the coverage profile to coveralls.io 34 | [ -n "$COVERALLS_TOKEN" ] && goveralls -coverprofile=$ACC_OUT -service=travis-ci -repotoken $COVERALLS_TOKEN 35 | 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gonum floats [![travis-build-status](https://travis-ci.org/gonum/floats.svg?branch=master)](https://travis-ci.org/gonum/floats) [![Coverage Status](https://coveralls.io/repos/gonum/floats/badge.svg?branch=master&service=github)](https://coveralls.io/github/gonum/floats?branch=master) [![GoDoc](https://godoc.org/github.com/gonum/floats?status.svg)](https://godoc.org/github.com/gonum/floats) 2 | 3 | # This repository is no longer maintained. Development has moved to https://github.com/gonum/gonum. 4 | 5 | package floats provides a set of helper routines for dealing with slices of float64. The functions avoid allocations to allow for use within tight loops without garbage collection overhead. 6 | 7 | ## Issues 8 | 9 | If you find any bugs, feel free to file an issue on the github [issue tracker for gonum/gonum](https://github.com/gonum/gonum/issues) if the bug exists in that reposity; no code changes will be made to this repository. Other discussions should be taken to the gonum-dev Google Group. 10 | 11 | https://groups.google.com/forum/#!forum/gonum-dev 12 | 13 | ## License 14 | 15 | Please see github.com/gonum/license for general license information, contributors, authors, etc on the Gonum suite of packages. 16 | -------------------------------------------------------------------------------- /examples_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gonum Authors. All rights reserved. 2 | // Use of this code is governed by a BSD-style 3 | // license that can be found in the LICENSE file 4 | 5 | package floats 6 | 7 | import ( 8 | "fmt" 9 | ) 10 | 11 | // Set of examples for all the functions 12 | 13 | func ExampleAdd_simple() { 14 | // Adding three slices together. Note that 15 | // the result is stored in the first slice 16 | s1 := []float64{1, 2, 3, 4} 17 | s2 := []float64{5, 6, 7, 8} 18 | s3 := []float64{1, 1, 1, 1} 19 | Add(s1, s2) 20 | Add(s1, s3) 21 | 22 | fmt.Println("s1 =", s1) 23 | fmt.Println("s2 =", s2) 24 | fmt.Println("s3 =", s3) 25 | // Output: 26 | // s1 = [7 9 11 13] 27 | // s2 = [5 6 7 8] 28 | // s3 = [1 1 1 1] 29 | } 30 | 31 | func ExampleAdd_newslice() { 32 | // If one wants to store the result in a 33 | // new container, just make a new slice 34 | s1 := []float64{1, 2, 3, 4} 35 | s2 := []float64{5, 6, 7, 8} 36 | s3 := []float64{1, 1, 1, 1} 37 | dst := make([]float64, len(s1)) 38 | 39 | AddTo(dst, s1, s2) 40 | Add(dst, s3) 41 | 42 | fmt.Println("dst =", dst) 43 | fmt.Println("s1 =", s1) 44 | fmt.Println("s2 =", s2) 45 | fmt.Println("s3 =", s3) 46 | // Output: 47 | // dst = [7 9 11 13] 48 | // s1 = [1 2 3 4] 49 | // s2 = [5 6 7 8] 50 | // s3 = [1 1 1 1] 51 | } 52 | 53 | func ExampleAdd_unequallengths() { 54 | // If the lengths of the slices are unknown, 55 | // use Eqlen to check 56 | s1 := []float64{1, 2, 3} 57 | s2 := []float64{5, 6, 7, 8} 58 | 59 | eq := EqualLengths(s1, s2) 60 | if eq { 61 | Add(s1, s2) 62 | } else { 63 | fmt.Println("Unequal lengths") 64 | } 65 | // Output: 66 | // Unequal lengths 67 | } 68 | 69 | func ExampleAddConst() { 70 | s := []float64{1, -2, 3, -4} 71 | c := 5.0 72 | 73 | AddConst(c, s) 74 | 75 | fmt.Println("s =", s) 76 | // Output: 77 | // s = [6 3 8 1] 78 | } 79 | 80 | func ExampleCumProd() { 81 | s := []float64{1, -2, 3, -4} 82 | dst := make([]float64, len(s)) 83 | 84 | CumProd(dst, s) 85 | 86 | fmt.Println("dst =", dst) 87 | fmt.Println("s =", s) 88 | // Output: 89 | // dst = [1 -2 -6 24] 90 | // s = [1 -2 3 -4] 91 | } 92 | 93 | func ExampleCumSum() { 94 | s := []float64{1, -2, 3, -4} 95 | dst := make([]float64, len(s)) 96 | 97 | CumSum(dst, s) 98 | 99 | fmt.Println("dst =", dst) 100 | fmt.Println("s =", s) 101 | // Output: 102 | // dst = [1 -1 2 -2] 103 | // s = [1 -2 3 -4] 104 | } 105 | -------------------------------------------------------------------------------- /floats.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gonum Authors. All rights reserved. 2 | // Use of this code is governed by a BSD-style 3 | // license that can be found in the LICENSE file 4 | 5 | // This repository is no longer maintained. 6 | // Development has moved to https://github.com/gonum/gonum. 7 | // 8 | // Package floats provides a set of helper routines for dealing with slices 9 | // of float64. The functions avoid allocations to allow for use within tight 10 | // loops without garbage collection overhead. 11 | // 12 | // The convention used is that when a slice is being modified in place, it has 13 | // the name dst. 14 | package floats 15 | 16 | import ( 17 | "errors" 18 | "math" 19 | "sort" 20 | 21 | "github.com/gonum/internal/asm/f64" 22 | ) 23 | 24 | // Add adds, element-wise, the elements of s and dst, and stores in dst. 25 | // Panics if the lengths of dst and s do not match. 26 | func Add(dst, s []float64) { 27 | if len(dst) != len(s) { 28 | panic("floats: length of the slices do not match") 29 | } 30 | f64.AxpyUnitaryTo(dst, 1, s, dst) 31 | } 32 | 33 | // AddTo adds, element-wise, the elements of s and t and 34 | // stores the result in dst. Panics if the lengths of s, t and dst do not match. 35 | func AddTo(dst, s, t []float64) []float64 { 36 | if len(s) != len(t) { 37 | panic("floats: length of adders do not match") 38 | } 39 | if len(dst) != len(s) { 40 | panic("floats: length of destination does not match length of adder") 41 | } 42 | f64.AxpyUnitaryTo(dst, 1, s, t) 43 | return dst 44 | } 45 | 46 | // AddConst adds the scalar c to all of the values in dst. 47 | func AddConst(c float64, dst []float64) { 48 | for i := range dst { 49 | dst[i] += c 50 | } 51 | } 52 | 53 | // AddScaled performs dst = dst + alpha * s. 54 | // It panics if the lengths of dst and s are not equal. 55 | func AddScaled(dst []float64, alpha float64, s []float64) { 56 | if len(dst) != len(s) { 57 | panic("floats: length of destination and source to not match") 58 | } 59 | f64.AxpyUnitaryTo(dst, alpha, s, dst) 60 | } 61 | 62 | // AddScaledTo performs dst = y + alpha * s, where alpha is a scalar, 63 | // and dst, y and s are all slices. 64 | // It panics if the lengths of dst, y, and s are not equal. 65 | // 66 | // At the return of the function, dst[i] = y[i] + alpha * s[i] 67 | func AddScaledTo(dst, y []float64, alpha float64, s []float64) []float64 { 68 | if len(dst) != len(s) || len(dst) != len(y) { 69 | panic("floats: lengths of slices do not match") 70 | } 71 | f64.AxpyUnitaryTo(dst, alpha, s, y) 72 | return dst 73 | } 74 | 75 | // argsort is a helper that implements sort.Interface, as used by 76 | // Argsort. 77 | type argsort struct { 78 | s []float64 79 | inds []int 80 | } 81 | 82 | func (a argsort) Len() int { 83 | return len(a.s) 84 | } 85 | 86 | func (a argsort) Less(i, j int) bool { 87 | return a.s[i] < a.s[j] 88 | } 89 | 90 | func (a argsort) Swap(i, j int) { 91 | a.s[i], a.s[j] = a.s[j], a.s[i] 92 | a.inds[i], a.inds[j] = a.inds[j], a.inds[i] 93 | } 94 | 95 | // Argsort sorts the elements of s while tracking their original order. 96 | // At the conclusion of Argsort, s will contain the original elements of s 97 | // but sorted in increasing order, and inds will contain the original position 98 | // of the elements in the slice such that dst[i] = origDst[inds[i]]. 99 | // It panics if the lengths of dst and inds do not match. 100 | func Argsort(dst []float64, inds []int) { 101 | if len(dst) != len(inds) { 102 | panic("floats: length of inds does not match length of slice") 103 | } 104 | for i := range dst { 105 | inds[i] = i 106 | } 107 | 108 | a := argsort{s: dst, inds: inds} 109 | sort.Sort(a) 110 | } 111 | 112 | // Count applies the function f to every element of s and returns the number 113 | // of times the function returned true. 114 | func Count(f func(float64) bool, s []float64) int { 115 | var n int 116 | for _, val := range s { 117 | if f(val) { 118 | n++ 119 | } 120 | } 121 | return n 122 | } 123 | 124 | // CumProd finds the cumulative product of the first i elements in 125 | // s and puts them in place into the ith element of the 126 | // destination dst. A panic will occur if the lengths of arguments 127 | // do not match. 128 | // 129 | // At the return of the function, dst[i] = s[i] * s[i-1] * s[i-2] * ... 130 | func CumProd(dst, s []float64) []float64 { 131 | if len(dst) != len(s) { 132 | panic("floats: length of destination does not match length of the source") 133 | } 134 | if len(dst) == 0 { 135 | return dst 136 | } 137 | return f64.CumProd(dst, s) 138 | } 139 | 140 | // CumSum finds the cumulative sum of the first i elements in 141 | // s and puts them in place into the ith element of the 142 | // destination dst. A panic will occur if the lengths of arguments 143 | // do not match. 144 | // 145 | // At the return of the function, dst[i] = s[i] + s[i-1] + s[i-2] + ... 146 | func CumSum(dst, s []float64) []float64 { 147 | if len(dst) != len(s) { 148 | panic("floats: length of destination does not match length of the source") 149 | } 150 | if len(dst) == 0 { 151 | return dst 152 | } 153 | return f64.CumSum(dst, s) 154 | } 155 | 156 | // Distance computes the L-norm of s - t. See Norm for special cases. 157 | // A panic will occur if the lengths of s and t do not match. 158 | func Distance(s, t []float64, L float64) float64 { 159 | if len(s) != len(t) { 160 | panic("floats: slice lengths do not match") 161 | } 162 | if len(s) == 0 { 163 | return 0 164 | } 165 | var norm float64 166 | if L == 2 { 167 | for i, v := range s { 168 | diff := t[i] - v 169 | norm = math.Hypot(norm, diff) 170 | } 171 | return norm 172 | } 173 | if L == 1 { 174 | for i, v := range s { 175 | norm += math.Abs(t[i] - v) 176 | } 177 | return norm 178 | } 179 | if math.IsInf(L, 1) { 180 | for i, v := range s { 181 | absDiff := math.Abs(t[i] - v) 182 | if absDiff > norm { 183 | norm = absDiff 184 | } 185 | } 186 | return norm 187 | } 188 | for i, v := range s { 189 | norm += math.Pow(math.Abs(t[i]-v), L) 190 | } 191 | return math.Pow(norm, 1/L) 192 | } 193 | 194 | // Div performs element-wise division dst / s 195 | // and stores the value in dst. It panics if the 196 | // lengths of s and t are not equal. 197 | func Div(dst, s []float64) { 198 | if len(dst) != len(s) { 199 | panic("floats: slice lengths do not match") 200 | } 201 | f64.Div(dst, s) 202 | } 203 | 204 | // DivTo performs element-wise division s / t 205 | // and stores the value in dst. It panics if the 206 | // lengths of s, t, and dst are not equal. 207 | func DivTo(dst, s, t []float64) []float64 { 208 | if len(s) != len(t) || len(dst) != len(t) { 209 | panic("floats: slice lengths do not match") 210 | } 211 | return f64.DivTo(dst, s, t) 212 | } 213 | 214 | // Dot computes the dot product of s1 and s2, i.e. 215 | // sum_{i = 1}^N s1[i]*s2[i]. 216 | // A panic will occur if lengths of arguments do not match. 217 | func Dot(s1, s2 []float64) float64 { 218 | if len(s1) != len(s2) { 219 | panic("floats: lengths of the slices do not match") 220 | } 221 | return f64.DotUnitary(s1, s2) 222 | } 223 | 224 | // Equal returns true if the slices have equal lengths and 225 | // all elements are numerically identical. 226 | func Equal(s1, s2 []float64) bool { 227 | if len(s1) != len(s2) { 228 | return false 229 | } 230 | for i, val := range s1 { 231 | if s2[i] != val { 232 | return false 233 | } 234 | } 235 | return true 236 | } 237 | 238 | // EqualApprox returns true if the slices have equal lengths and 239 | // all element pairs have an absolute tolerance less than tol or a 240 | // relative tolerance less than tol. 241 | func EqualApprox(s1, s2 []float64, tol float64) bool { 242 | if len(s1) != len(s2) { 243 | return false 244 | } 245 | for i, a := range s1 { 246 | if !EqualWithinAbsOrRel(a, s2[i], tol, tol) { 247 | return false 248 | } 249 | } 250 | return true 251 | } 252 | 253 | // EqualFunc returns true if the slices have the same lengths 254 | // and the function returns true for all element pairs. 255 | func EqualFunc(s1, s2 []float64, f func(float64, float64) bool) bool { 256 | if len(s1) != len(s2) { 257 | return false 258 | } 259 | for i, val := range s1 { 260 | if !f(val, s2[i]) { 261 | return false 262 | } 263 | } 264 | return true 265 | } 266 | 267 | // EqualWithinAbs returns true if a and b have an absolute 268 | // difference of less than tol. 269 | func EqualWithinAbs(a, b, tol float64) bool { 270 | return a == b || math.Abs(a-b) <= tol 271 | } 272 | 273 | const minNormalFloat64 = 2.2250738585072014e-308 274 | 275 | // EqualWithinRel returns true if the difference between a and b 276 | // is not greater than tol times the greater value. 277 | func EqualWithinRel(a, b, tol float64) bool { 278 | if a == b { 279 | return true 280 | } 281 | delta := math.Abs(a - b) 282 | if delta <= minNormalFloat64 { 283 | return delta <= tol*minNormalFloat64 284 | } 285 | // We depend on the division in this relationship to identify 286 | // infinities (we rely on the NaN to fail the test) otherwise 287 | // we compare Infs of the same sign and evaluate Infs as equal 288 | // independent of sign. 289 | return delta/math.Max(math.Abs(a), math.Abs(b)) <= tol 290 | } 291 | 292 | // EqualWithinAbsOrRel returns true if a and b are equal to within 293 | // the absolute tolerance. 294 | func EqualWithinAbsOrRel(a, b, absTol, relTol float64) bool { 295 | if EqualWithinAbs(a, b, absTol) { 296 | return true 297 | } 298 | return EqualWithinRel(a, b, relTol) 299 | } 300 | 301 | // EqualWithinULP returns true if a and b are equal to within 302 | // the specified number of floating point units in the last place. 303 | func EqualWithinULP(a, b float64, ulp uint) bool { 304 | if a == b { 305 | return true 306 | } 307 | if math.IsNaN(a) || math.IsNaN(b) { 308 | return false 309 | } 310 | if math.Signbit(a) != math.Signbit(b) { 311 | return math.Float64bits(math.Abs(a))+math.Float64bits(math.Abs(b)) <= uint64(ulp) 312 | } 313 | return ulpDiff(math.Float64bits(a), math.Float64bits(b)) <= uint64(ulp) 314 | } 315 | 316 | func ulpDiff(a, b uint64) uint64 { 317 | if a > b { 318 | return a - b 319 | } 320 | return b - a 321 | } 322 | 323 | // EqualLengths returns true if all of the slices have equal length, 324 | // and false otherwise. Returns true if there are no input slices. 325 | func EqualLengths(slices ...[]float64) bool { 326 | // This length check is needed: http://play.golang.org/p/sdty6YiLhM 327 | if len(slices) == 0 { 328 | return true 329 | } 330 | l := len(slices[0]) 331 | for i := 1; i < len(slices); i++ { 332 | if len(slices[i]) != l { 333 | return false 334 | } 335 | } 336 | return true 337 | } 338 | 339 | // Find applies f to every element of s and returns the indices of the first 340 | // k elements for which the f returns true, or all such elements 341 | // if k < 0. 342 | // Find will reslice inds to have 0 length, and will append 343 | // found indices to inds. 344 | // If k > 0 and there are fewer than k elements in s satisfying f, 345 | // all of the found elements will be returned along with an error. 346 | // At the return of the function, the input inds will be in an undetermined state. 347 | func Find(inds []int, f func(float64) bool, s []float64, k int) ([]int, error) { 348 | 349 | // inds is also returned to allow for calling with nil 350 | 351 | // Reslice inds to have zero length 352 | inds = inds[:0] 353 | 354 | // If zero elements requested, can just return 355 | if k == 0 { 356 | return inds, nil 357 | } 358 | 359 | // If k < 0, return all of the found indices 360 | if k < 0 { 361 | for i, val := range s { 362 | if f(val) { 363 | inds = append(inds, i) 364 | } 365 | } 366 | return inds, nil 367 | } 368 | 369 | // Otherwise, find the first k elements 370 | nFound := 0 371 | for i, val := range s { 372 | if f(val) { 373 | inds = append(inds, i) 374 | nFound++ 375 | if nFound == k { 376 | return inds, nil 377 | } 378 | } 379 | } 380 | // Finished iterating over the loop, which means k elements were not found 381 | return inds, errors.New("floats: insufficient elements found") 382 | } 383 | 384 | // HasNaN returns true if the slice s has any values that are NaN and false 385 | // otherwise. 386 | func HasNaN(s []float64) bool { 387 | for _, v := range s { 388 | if math.IsNaN(v) { 389 | return true 390 | } 391 | } 392 | return false 393 | } 394 | 395 | // LogSpan returns a set of n equally spaced points in log space between, 396 | // l and u where N is equal to len(dst). The first element of the 397 | // resulting dst will be l and the final element of dst will be u. 398 | // Panics if len(dst) < 2 399 | // Note that this call will return NaNs if either l or u are negative, and 400 | // will return all zeros if l or u is zero. 401 | // Also returns the mutated slice dst, so that it can be used in range, like: 402 | // 403 | // for i, x := range LogSpan(dst, l, u) { ... } 404 | func LogSpan(dst []float64, l, u float64) []float64 { 405 | Span(dst, math.Log(l), math.Log(u)) 406 | for i := range dst { 407 | dst[i] = math.Exp(dst[i]) 408 | } 409 | return dst 410 | } 411 | 412 | // LogSumExp returns the log of the sum of the exponentials of the values in s. 413 | // Panics if s is an empty slice. 414 | func LogSumExp(s []float64) float64 { 415 | // Want to do this in a numerically stable way which avoids 416 | // overflow and underflow 417 | // First, find the maximum value in the slice. 418 | maxval := Max(s) 419 | if math.IsInf(maxval, 0) { 420 | // If it's infinity either way, the logsumexp will be infinity as well 421 | // returning now avoids NaNs 422 | return maxval 423 | } 424 | var lse float64 425 | // Compute the sumexp part 426 | for _, val := range s { 427 | lse += math.Exp(val - maxval) 428 | } 429 | // Take the log and add back on the constant taken out 430 | return math.Log(lse) + maxval 431 | } 432 | 433 | // Max returns the maximum value in the input slice. If the slice is empty, Max will panic. 434 | func Max(s []float64) float64 { 435 | return s[MaxIdx(s)] 436 | } 437 | 438 | // MaxIdx returns the index of the maximum value in the input slice. If several 439 | // entries have the maximum value, the first such index is returned. If the slice 440 | // is empty, MaxIdx will panic. 441 | func MaxIdx(s []float64) int { 442 | if len(s) == 0 { 443 | panic("floats: zero slice length") 444 | } 445 | max := s[0] 446 | var ind int 447 | for i, v := range s { 448 | if v > max { 449 | max = v 450 | ind = i 451 | } 452 | } 453 | return ind 454 | } 455 | 456 | // Min returns the maximum value in the input slice. If the slice is empty, Min will panic. 457 | func Min(s []float64) float64 { 458 | return s[MinIdx(s)] 459 | } 460 | 461 | // MinIdx returns the index of the minimum value in the input slice. If several 462 | // entries have the maximum value, the first such index is returned. If the slice 463 | // is empty, MinIdx will panic. 464 | func MinIdx(s []float64) int { 465 | min := s[0] 466 | var ind int 467 | for i, v := range s { 468 | if v < min { 469 | min = v 470 | ind = i 471 | } 472 | } 473 | return ind 474 | } 475 | 476 | // Mul performs element-wise multiplication between dst 477 | // and s and stores the value in dst. Panics if the 478 | // lengths of s and t are not equal. 479 | func Mul(dst, s []float64) { 480 | if len(dst) != len(s) { 481 | panic("floats: slice lengths do not match") 482 | } 483 | for i, val := range s { 484 | dst[i] *= val 485 | } 486 | } 487 | 488 | // MulTo performs element-wise multiplication between s 489 | // and t and stores the value in dst. Panics if the 490 | // lengths of s, t, and dst are not equal. 491 | func MulTo(dst, s, t []float64) []float64 { 492 | if len(s) != len(t) || len(dst) != len(t) { 493 | panic("floats: slice lengths do not match") 494 | } 495 | for i, val := range t { 496 | dst[i] = val * s[i] 497 | } 498 | return dst 499 | } 500 | 501 | // Nearest returns the index of the element in s 502 | // whose value is nearest to v. If several such 503 | // elements exist, the lowest index is returned. 504 | // Panics if len(s) == 0. 505 | func Nearest(s []float64, v float64) int { 506 | var ind int 507 | dist := math.Abs(v - s[0]) 508 | for i, val := range s { 509 | newDist := math.Abs(v - val) 510 | if newDist < dist { 511 | dist = newDist 512 | ind = i 513 | } 514 | } 515 | return ind 516 | } 517 | 518 | // NearestWithinSpan return the index of a hypothetical vector created 519 | // by Span with length n and bounds l and u whose value is closest 520 | // to v. NearestWithinSpan panics if u < l. If the value is greater than u or 521 | // less than l, the function returns -1. 522 | func NearestWithinSpan(n int, l, u float64, v float64) int { 523 | if u < l { 524 | panic("floats: upper bound greater than lower bound") 525 | } 526 | if v < l || v > u { 527 | return -1 528 | } 529 | // Can't guarantee anything about exactly halfway between 530 | // because of floating point weirdness. 531 | return int((float64(n)-1)/(u-l)*(v-l) + 0.5) 532 | } 533 | 534 | // Norm returns the L norm of the slice S, defined as 535 | // (sum_{i=1}^N s[i]^L)^{1/L} 536 | // Special cases: 537 | // L = math.Inf(1) gives the maximum absolute value. 538 | // Does not correctly compute the zero norm (use Count). 539 | func Norm(s []float64, L float64) float64 { 540 | // Should this complain if L is not positive? 541 | // Should this be done in log space for better numerical stability? 542 | // would be more cost 543 | // maybe only if L is high? 544 | if len(s) == 0 { 545 | return 0 546 | } 547 | if L == 2 { 548 | twoNorm := math.Abs(s[0]) 549 | for i := 1; i < len(s); i++ { 550 | twoNorm = math.Hypot(twoNorm, s[i]) 551 | } 552 | return twoNorm 553 | } 554 | var norm float64 555 | if L == 1 { 556 | for _, val := range s { 557 | norm += math.Abs(val) 558 | } 559 | return norm 560 | } 561 | if math.IsInf(L, 1) { 562 | for _, val := range s { 563 | norm = math.Max(norm, math.Abs(val)) 564 | } 565 | return norm 566 | } 567 | for _, val := range s { 568 | norm += math.Pow(math.Abs(val), L) 569 | } 570 | return math.Pow(norm, 1/L) 571 | } 572 | 573 | // Prod returns the product of the elements of the slice. 574 | // Returns 1 if len(s) = 0. 575 | func Prod(s []float64) float64 { 576 | prod := 1.0 577 | for _, val := range s { 578 | prod *= val 579 | } 580 | return prod 581 | } 582 | 583 | // Reverse reverses the order of elements in the slice. 584 | func Reverse(s []float64) { 585 | for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { 586 | s[i], s[j] = s[j], s[i] 587 | } 588 | } 589 | 590 | // Round returns the half away from zero rounded value of x with prec precision. 591 | // 592 | // Special cases are: 593 | // Round(±0) = +0 594 | // Round(±Inf) = ±Inf 595 | // Round(NaN) = NaN 596 | func Round(x float64, prec int) float64 { 597 | if x == 0 { 598 | // Make sure zero is returned 599 | // without the negative bit set. 600 | return 0 601 | } 602 | // Fast path for positive precision on integers. 603 | if prec >= 0 && x == math.Trunc(x) { 604 | return x 605 | } 606 | pow := math.Pow10(prec) 607 | intermed := x * pow 608 | if math.IsInf(intermed, 0) { 609 | return x 610 | } 611 | if x < 0 { 612 | x = math.Ceil(intermed - 0.5) 613 | } else { 614 | x = math.Floor(intermed + 0.5) 615 | } 616 | 617 | if x == 0 { 618 | return 0 619 | } 620 | 621 | return x / pow 622 | } 623 | 624 | // RoundEven returns the half even rounded value of x with prec precision. 625 | // 626 | // Special cases are: 627 | // RoundEven(±0) = +0 628 | // RoundEven(±Inf) = ±Inf 629 | // RoundEven(NaN) = NaN 630 | func RoundEven(x float64, prec int) float64 { 631 | if x == 0 { 632 | // Make sure zero is returned 633 | // without the negative bit set. 634 | return 0 635 | } 636 | // Fast path for positive precision on integers. 637 | if prec >= 0 && x == math.Trunc(x) { 638 | return x 639 | } 640 | pow := math.Pow10(prec) 641 | intermed := x * pow 642 | if math.IsInf(intermed, 0) { 643 | return x 644 | } 645 | if isHalfway(intermed) { 646 | correction, _ := math.Modf(math.Mod(intermed, 2)) 647 | intermed += correction 648 | if intermed > 0 { 649 | x = math.Floor(intermed) 650 | } else { 651 | x = math.Ceil(intermed) 652 | } 653 | } else { 654 | if x < 0 { 655 | x = math.Ceil(intermed - 0.5) 656 | } else { 657 | x = math.Floor(intermed + 0.5) 658 | } 659 | } 660 | 661 | if x == 0 { 662 | return 0 663 | } 664 | 665 | return x / pow 666 | } 667 | 668 | func isHalfway(x float64) bool { 669 | _, frac := math.Modf(x) 670 | frac = math.Abs(frac) 671 | return frac == 0.5 || (math.Nextafter(frac, math.Inf(-1)) < 0.5 && math.Nextafter(frac, math.Inf(1)) > 0.5) 672 | } 673 | 674 | // Same returns true if the input slices have the same length and the all elements 675 | // have the same value with NaN treated as the same. 676 | func Same(s, t []float64) bool { 677 | if len(s) != len(t) { 678 | return false 679 | } 680 | for i, v := range s { 681 | w := t[i] 682 | if v != w && !math.IsNaN(v) && !math.IsNaN(w) { 683 | return false 684 | } 685 | } 686 | return true 687 | } 688 | 689 | // Scale multiplies every element in dst by the scalar c. 690 | func Scale(c float64, dst []float64) { 691 | if len(dst) > 0 { 692 | f64.ScalUnitary(c, dst) 693 | } 694 | } 695 | 696 | // Span returns a set of N equally spaced points between l and u, where N 697 | // is equal to the length of the destination. The first element of the destination 698 | // is l, the final element of the destination is u. 699 | // Panics if len(dst) < 2. 700 | // 701 | // Also returns the mutated slice dst, so that it can be used in range expressions, like: 702 | // 703 | // for i, x := range Span(dst, l, u) { ... } 704 | func Span(dst []float64, l, u float64) []float64 { 705 | n := len(dst) 706 | if n < 2 { 707 | panic("floats: destination must have length >1") 708 | } 709 | step := (u - l) / float64(n-1) 710 | for i := range dst { 711 | dst[i] = l + step*float64(i) 712 | } 713 | return dst 714 | } 715 | 716 | // Sub subtracts, element-wise, the elements of s from dst. Panics if 717 | // the lengths of dst and s do not match. 718 | func Sub(dst, s []float64) { 719 | if len(dst) != len(s) { 720 | panic("floats: length of the slices do not match") 721 | } 722 | f64.AxpyUnitaryTo(dst, -1, s, dst) 723 | } 724 | 725 | // SubTo subtracts, element-wise, the elements of t from s and 726 | // stores the result in dst. Panics if the lengths of s, t and dst do not match. 727 | func SubTo(dst, s, t []float64) []float64 { 728 | if len(s) != len(t) { 729 | panic("floats: length of subtractor and subtractee do not match") 730 | } 731 | if len(dst) != len(s) { 732 | panic("floats: length of destination does not match length of subtractor") 733 | } 734 | f64.AxpyUnitaryTo(dst, -1, t, s) 735 | return dst 736 | } 737 | 738 | // Sum returns the sum of the elements of the slice. 739 | func Sum(s []float64) float64 { 740 | var sum float64 741 | for _, val := range s { 742 | sum += val 743 | } 744 | return sum 745 | } 746 | 747 | // Within returns the first index i where s[i] <= v < s[i+1]. Within panics if: 748 | // - len(s) < 2 749 | // - s is not sorted 750 | func Within(s []float64, v float64) int { 751 | if len(s) < 2 { 752 | panic("floats: slice length less than 2") 753 | } 754 | if !sort.Float64sAreSorted(s) { 755 | panic("floats: input slice not sorted") 756 | } 757 | if v < s[0] || v >= s[len(s)-1] || math.IsNaN(v) { 758 | return -1 759 | } 760 | for i, f := range s[1:] { 761 | if v < f { 762 | return i 763 | } 764 | } 765 | return -1 766 | } 767 | -------------------------------------------------------------------------------- /floats_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gonum Authors. All rights reserved. 2 | // Use of this code is governed by a BSD-style 3 | // license that can be found in the LICENSE file 4 | 5 | package floats 6 | 7 | import ( 8 | "math" 9 | "math/rand" 10 | "strconv" 11 | "testing" 12 | "testing/quick" 13 | ) 14 | 15 | const ( 16 | EqTolerance = 1E-14 17 | Small = 10 18 | Medium = 1000 19 | Large = 100000 20 | Huge = 10000000 21 | ) 22 | 23 | func AreSlicesEqual(t *testing.T, truth, comp []float64, str string) { 24 | if !EqualApprox(comp, truth, EqTolerance) { 25 | t.Errorf(str+". Expected %v, returned %v", truth, comp) 26 | } 27 | } 28 | 29 | func Panics(fun func()) (b bool) { 30 | defer func() { 31 | err := recover() 32 | if err != nil { 33 | b = true 34 | } 35 | }() 36 | fun() 37 | return 38 | } 39 | 40 | func TestAdd(t *testing.T) { 41 | a := []float64{1, 2, 3} 42 | b := []float64{4, 5, 6} 43 | c := []float64{7, 8, 9} 44 | truth := []float64{12, 15, 18} 45 | n := make([]float64, len(a)) 46 | 47 | Add(n, a) 48 | Add(n, b) 49 | Add(n, c) 50 | AreSlicesEqual(t, truth, n, "Wrong addition of slices new receiver") 51 | Add(a, b) 52 | Add(a, c) 53 | AreSlicesEqual(t, truth, n, "Wrong addition of slices for no new receiver") 54 | 55 | // Test that it panics 56 | if !Panics(func() { Add(make([]float64, 2), make([]float64, 3)) }) { 57 | t.Errorf("Did not panic with length mismatch") 58 | } 59 | } 60 | 61 | func TestAddTo(t *testing.T) { 62 | a := []float64{1, 2, 3} 63 | b := []float64{4, 5, 6} 64 | truth := []float64{5, 7, 9} 65 | n1 := make([]float64, len(a)) 66 | 67 | n2 := AddTo(n1, a, b) 68 | AreSlicesEqual(t, truth, n1, "Bad addition from mutator") 69 | AreSlicesEqual(t, truth, n2, "Bad addition from returned slice") 70 | 71 | // Test that it panics 72 | if !Panics(func() { AddTo(make([]float64, 2), make([]float64, 3), make([]float64, 3)) }) { 73 | t.Errorf("Did not panic with length mismatch") 74 | } 75 | if !Panics(func() { AddTo(make([]float64, 3), make([]float64, 3), make([]float64, 2)) }) { 76 | t.Errorf("Did not panic with length mismatch") 77 | } 78 | 79 | } 80 | 81 | func TestAddConst(t *testing.T) { 82 | s := []float64{3, 4, 1, 7, 5} 83 | c := 6.0 84 | truth := []float64{9, 10, 7, 13, 11} 85 | AddConst(c, s) 86 | AreSlicesEqual(t, truth, s, "Wrong addition of constant") 87 | } 88 | 89 | func TestAddScaled(t *testing.T) { 90 | s := []float64{3, 4, 1, 7, 5} 91 | alpha := 6.0 92 | dst := []float64{1, 2, 3, 4, 5} 93 | ans := []float64{19, 26, 9, 46, 35} 94 | AddScaled(dst, alpha, s) 95 | if !EqualApprox(dst, ans, EqTolerance) { 96 | t.Errorf("Adding scaled did not match") 97 | } 98 | short := []float64{1} 99 | if !Panics(func() { AddScaled(dst, alpha, short) }) { 100 | t.Errorf("Doesn't panic if s is smaller than dst") 101 | } 102 | if !Panics(func() { AddScaled(short, alpha, s) }) { 103 | t.Errorf("Doesn't panic if dst is smaller than s") 104 | } 105 | } 106 | 107 | func TestAddScaledTo(t *testing.T) { 108 | s := []float64{3, 4, 1, 7, 5} 109 | alpha := 6.0 110 | y := []float64{1, 2, 3, 4, 5} 111 | dst1 := make([]float64, 5) 112 | ans := []float64{19, 26, 9, 46, 35} 113 | dst2 := AddScaledTo(dst1, y, alpha, s) 114 | if !EqualApprox(dst1, ans, EqTolerance) { 115 | t.Errorf("AddScaledTo did not match for mutator") 116 | } 117 | if !EqualApprox(dst2, ans, EqTolerance) { 118 | t.Errorf("AddScaledTo did not match for returned slice") 119 | } 120 | AddScaledTo(dst1, y, alpha, s) 121 | if !EqualApprox(dst1, ans, EqTolerance) { 122 | t.Errorf("Reusing dst did not match") 123 | } 124 | short := []float64{1} 125 | if !Panics(func() { AddScaledTo(dst1, y, alpha, short) }) { 126 | t.Errorf("Doesn't panic if s is smaller than dst") 127 | } 128 | if !Panics(func() { AddScaledTo(short, y, alpha, s) }) { 129 | t.Errorf("Doesn't panic if dst is smaller than s") 130 | } 131 | if !Panics(func() { AddScaledTo(dst1, short, alpha, s) }) { 132 | t.Errorf("Doesn't panic if y is smaller than dst") 133 | } 134 | } 135 | 136 | func TestArgsort(t *testing.T) { 137 | s := []float64{3, 4, 1, 7, 5} 138 | inds := make([]int, len(s)) 139 | 140 | Argsort(s, inds) 141 | 142 | sortedS := []float64{1, 3, 4, 5, 7} 143 | trueInds := []int{2, 0, 1, 4, 3} 144 | 145 | if !Equal(s, sortedS) { 146 | t.Error("elements not sorted correctly") 147 | } 148 | for i := range trueInds { 149 | if trueInds[i] != inds[i] { 150 | t.Error("inds not correct") 151 | } 152 | } 153 | 154 | inds = []int{1, 2} 155 | if !Panics(func() { Argsort(s, inds) }) { 156 | t.Error("does not panic if lengths do not match") 157 | } 158 | } 159 | 160 | func TestCount(t *testing.T) { 161 | s := []float64{3, 4, 1, 7, 5} 162 | f := func(v float64) bool { return v > 3.5 } 163 | truth := 3 164 | n := Count(f, s) 165 | if n != truth { 166 | t.Errorf("Wrong number of elements counted") 167 | } 168 | } 169 | 170 | func TestCumProd(t *testing.T) { 171 | s := []float64{3, 4, 1, 7, 5} 172 | receiver := make([]float64, len(s)) 173 | result := CumProd(receiver, s) 174 | truth := []float64{3, 12, 12, 84, 420} 175 | AreSlicesEqual(t, truth, receiver, "Wrong cumprod mutated with new receiver") 176 | AreSlicesEqual(t, truth, result, "Wrong cumprod result with new receiver") 177 | CumProd(receiver, s) 178 | AreSlicesEqual(t, truth, receiver, "Wrong cumprod returned with reused receiver") 179 | 180 | // Test that it panics 181 | if !Panics(func() { CumProd(make([]float64, 2), make([]float64, 3)) }) { 182 | t.Errorf("Did not panic with length mismatch") 183 | } 184 | 185 | // Test empty CumProd 186 | emptyReceiver := make([]float64, 0) 187 | truth = []float64{} 188 | CumProd(emptyReceiver, emptyReceiver) 189 | AreSlicesEqual(t, truth, emptyReceiver, "Wrong cumprod returned with emtpy receiver") 190 | 191 | } 192 | 193 | func TestCumSum(t *testing.T) { 194 | s := []float64{3, 4, 1, 7, 5} 195 | receiver := make([]float64, len(s)) 196 | result := CumSum(receiver, s) 197 | truth := []float64{3, 7, 8, 15, 20} 198 | AreSlicesEqual(t, truth, receiver, "Wrong cumsum mutated with new receiver") 199 | AreSlicesEqual(t, truth, result, "Wrong cumsum returned with new receiver") 200 | CumSum(receiver, s) 201 | AreSlicesEqual(t, truth, receiver, "Wrong cumsum returned with reused receiver") 202 | 203 | // Test that it panics 204 | if !Panics(func() { CumSum(make([]float64, 2), make([]float64, 3)) }) { 205 | t.Errorf("Did not panic with length mismatch") 206 | } 207 | 208 | // Test empty CumSum 209 | emptyReceiver := make([]float64, 0) 210 | truth = []float64{} 211 | CumSum(emptyReceiver, emptyReceiver) 212 | AreSlicesEqual(t, truth, emptyReceiver, "Wrong cumsum returned with emtpy receiver") 213 | 214 | } 215 | 216 | func TestDistance(t *testing.T) { 217 | norms := []float64{1, 2, 4, math.Inf(1)} 218 | slices := []struct { 219 | s []float64 220 | t []float64 221 | }{ 222 | { 223 | nil, 224 | nil, 225 | }, 226 | { 227 | []float64{8, 9, 10, -12}, 228 | []float64{8, 9, 10, -12}, 229 | }, 230 | { 231 | []float64{1, 2, 3, -4, -5, 8}, 232 | []float64{-9.2, -6.8, 9, -3, -2, 1}, 233 | }, 234 | } 235 | 236 | for j, test := range slices { 237 | tmp := make([]float64, len(test.s)) 238 | for i, L := range norms { 239 | dist := Distance(test.s, test.t, L) 240 | copy(tmp, test.s) 241 | Sub(tmp, test.t) 242 | norm := Norm(tmp, L) 243 | if dist != norm { // Use equality because they should be identical 244 | t.Errorf("Distance does not match norm for case %v, %v. Expected %v, Found %v.", i, j, norm, dist) 245 | } 246 | } 247 | } 248 | 249 | if !Panics(func() { Distance([]float64{}, norms, 1) }) { 250 | t.Errorf("Did not panic with unequal lengths") 251 | } 252 | 253 | } 254 | 255 | func TestDiv(t *testing.T) { 256 | s1 := []float64{5, 12, 27} 257 | s2 := []float64{1, 2, 3} 258 | ans := []float64{5, 6, 9} 259 | Div(s1, s2) 260 | if !EqualApprox(s1, ans, EqTolerance) { 261 | t.Errorf("Mul doesn't give correct answer") 262 | } 263 | s1short := []float64{1} 264 | if !Panics(func() { Div(s1short, s2) }) { 265 | t.Errorf("Did not panic with unequal lengths") 266 | } 267 | s2short := []float64{1} 268 | if !Panics(func() { Div(s1, s2short) }) { 269 | t.Errorf("Did not panic with unequal lengths") 270 | } 271 | } 272 | 273 | func TestDivTo(t *testing.T) { 274 | s1 := []float64{5, 12, 27} 275 | s1orig := []float64{5, 12, 27} 276 | s2 := []float64{1, 2, 3} 277 | s2orig := []float64{1, 2, 3} 278 | dst1 := make([]float64, 3) 279 | ans := []float64{5, 6, 9} 280 | dst2 := DivTo(dst1, s1, s2) 281 | if !EqualApprox(dst1, ans, EqTolerance) { 282 | t.Errorf("DivTo doesn't give correct answer in mutated slice") 283 | } 284 | if !EqualApprox(dst2, ans, EqTolerance) { 285 | t.Errorf("DivTo doesn't give correct answer in returned slice") 286 | } 287 | if !EqualApprox(s1, s1orig, EqTolerance) { 288 | t.Errorf("S1 changes during multo") 289 | } 290 | if !EqualApprox(s2, s2orig, EqTolerance) { 291 | t.Errorf("s2 changes during multo") 292 | } 293 | DivTo(dst1, s1, s2) 294 | if !EqualApprox(dst1, ans, EqTolerance) { 295 | t.Errorf("DivTo doesn't give correct answer reusing dst") 296 | } 297 | dstShort := []float64{1} 298 | if !Panics(func() { DivTo(dstShort, s1, s2) }) { 299 | t.Errorf("Did not panic with s1 wrong length") 300 | } 301 | s1short := []float64{1} 302 | if !Panics(func() { DivTo(dst1, s1short, s2) }) { 303 | t.Errorf("Did not panic with s1 wrong length") 304 | } 305 | s2short := []float64{1} 306 | if !Panics(func() { DivTo(dst1, s1, s2short) }) { 307 | t.Errorf("Did not panic with s2 wrong length") 308 | } 309 | } 310 | 311 | func TestDot(t *testing.T) { 312 | s1 := []float64{1, 2, 3, 4} 313 | s2 := []float64{-3, 4, 5, -6} 314 | truth := -4.0 315 | ans := Dot(s1, s2) 316 | if ans != truth { 317 | t.Errorf("Dot product computed incorrectly") 318 | } 319 | 320 | // Test that it panics 321 | if !Panics(func() { Dot(make([]float64, 2), make([]float64, 3)) }) { 322 | t.Errorf("Did not panic with length mismatch") 323 | } 324 | } 325 | 326 | func TestEquals(t *testing.T) { 327 | s1 := []float64{1, 2, 3, 4} 328 | s2 := []float64{1, 2, 3, 4} 329 | if !Equal(s1, s2) { 330 | t.Errorf("Equal slices returned as unequal") 331 | } 332 | s2 = []float64{1, 2, 3, 4 + 1e-14} 333 | if Equal(s1, s2) { 334 | t.Errorf("Unequal slices returned as equal") 335 | } 336 | if Equal(s1, []float64{}) { 337 | t.Errorf("Unequal slice lengths returned as equal") 338 | } 339 | } 340 | 341 | func TestEqualApprox(t *testing.T) { 342 | s1 := []float64{1, 2, 3, 4} 343 | s2 := []float64{1, 2, 3, 4 + 1e-10} 344 | if EqualApprox(s1, s2, 1e-13) { 345 | t.Errorf("Unequal slices returned as equal for absolute") 346 | } 347 | if !EqualApprox(s1, s2, 1e-5) { 348 | t.Errorf("Equal slices returned as unequal for absolute") 349 | } 350 | s1 = []float64{1, 2, 3, 1000} 351 | s2 = []float64{1, 2, 3, 1000 * (1 + 1e-7)} 352 | if EqualApprox(s1, s2, 1e-8) { 353 | t.Errorf("Unequal slices returned as equal for relative") 354 | } 355 | if !EqualApprox(s1, s2, 1e-5) { 356 | t.Errorf("Equal slices returned as unequal for relative") 357 | } 358 | if EqualApprox(s1, []float64{}, 1e-5) { 359 | t.Errorf("Unequal slice lengths returned as equal") 360 | } 361 | } 362 | 363 | func TestEqualFunc(t *testing.T) { 364 | s1 := []float64{1, 2, 3, 4} 365 | s2 := []float64{1, 2, 3, 4} 366 | eq := func(x, y float64) bool { return x == y } 367 | if !EqualFunc(s1, s2, eq) { 368 | t.Errorf("Equal slices returned as unequal") 369 | } 370 | s2 = []float64{1, 2, 3, 4 + 1e-14} 371 | if EqualFunc(s1, s2, eq) { 372 | t.Errorf("Unequal slices returned as equal") 373 | } 374 | if EqualFunc(s1, []float64{}, eq) { 375 | t.Errorf("Unequal slice lengths returned as equal") 376 | } 377 | } 378 | 379 | func TestEqualsRelative(t *testing.T) { 380 | var equalityTests = []struct { 381 | a, b float64 382 | tol float64 383 | equal bool 384 | }{ 385 | {1000000, 1000001, 0, true}, 386 | {1000001, 1000000, 0, true}, 387 | {10000, 10001, 0, false}, 388 | {10001, 10000, 0, false}, 389 | {-1000000, -1000001, 0, true}, 390 | {-1000001, -1000000, 0, true}, 391 | {-10000, -10001, 0, false}, 392 | {-10001, -10000, 0, false}, 393 | {1.0000001, 1.0000002, 0, true}, 394 | {1.0000002, 1.0000001, 0, true}, 395 | {1.0002, 1.0001, 0, false}, 396 | {1.0001, 1.0002, 0, false}, 397 | {-1.000001, -1.000002, 0, true}, 398 | {-1.000002, -1.000001, 0, true}, 399 | {-1.0001, -1.0002, 0, false}, 400 | {-1.0002, -1.0001, 0, false}, 401 | {0.000000001000001, 0.000000001000002, 0, true}, 402 | {0.000000001000002, 0.000000001000001, 0, true}, 403 | {0.000000000001002, 0.000000000001001, 0, false}, 404 | {0.000000000001001, 0.000000000001002, 0, false}, 405 | {-0.000000001000001, -0.000000001000002, 0, true}, 406 | {-0.000000001000002, -0.000000001000001, 0, true}, 407 | {-0.000000000001002, -0.000000000001001, 0, false}, 408 | {-0.000000000001001, -0.000000000001002, 0, false}, 409 | {0, 0, 0, true}, 410 | {0, -0, 0, true}, 411 | {-0, -0, 0, true}, 412 | {0.00000001, 0, 0, false}, 413 | {0, 0.00000001, 0, false}, 414 | {-0.00000001, 0, 0, false}, 415 | {0, -0.00000001, 0, false}, 416 | {0, 1e-310, 0.01, true}, 417 | {1e-310, 0, 0.01, true}, 418 | {1e-310, 0, 0.000001, false}, 419 | {0, 1e-310, 0.000001, false}, 420 | {0, -1e-310, 0.1, true}, 421 | {-1e-310, 0, 0.1, true}, 422 | {-1e-310, 0, 0.00000001, false}, 423 | {0, -1e-310, 0.00000001, false}, 424 | {math.Inf(1), math.Inf(1), 0, true}, 425 | {math.Inf(-1), math.Inf(-1), 0, true}, 426 | {math.Inf(-1), math.Inf(1), 0, false}, 427 | {math.Inf(1), math.MaxFloat64, 0, false}, 428 | {math.Inf(-1), -math.MaxFloat64, 0, false}, 429 | {math.NaN(), math.NaN(), 0, false}, 430 | {math.NaN(), 0, 0, false}, 431 | {-0, math.NaN(), 0, false}, 432 | {math.NaN(), -0, 0, false}, 433 | {0, math.NaN(), 0, false}, 434 | {math.NaN(), math.Inf(1), 0, false}, 435 | {math.Inf(1), math.NaN(), 0, false}, 436 | {math.NaN(), math.Inf(-1), 0, false}, 437 | {math.Inf(-1), math.NaN(), 0, false}, 438 | {math.NaN(), math.MaxFloat64, 0, false}, 439 | {math.MaxFloat64, math.NaN(), 0, false}, 440 | {math.NaN(), -math.MaxFloat64, 0, false}, 441 | {-math.MaxFloat64, math.NaN(), 0, false}, 442 | {math.NaN(), math.SmallestNonzeroFloat64, 0, false}, 443 | {math.SmallestNonzeroFloat64, math.NaN(), 0, false}, 444 | {math.NaN(), -math.SmallestNonzeroFloat64, 0, false}, 445 | {-math.SmallestNonzeroFloat64, math.NaN(), 0, false}, 446 | {1.000000001, -1.0, 0, false}, 447 | {-1.0, 1.000000001, 0, false}, 448 | {-1.000000001, 1.0, 0, false}, 449 | {1.0, -1.000000001, 0, false}, 450 | {10 * math.SmallestNonzeroFloat64, 10 * -math.SmallestNonzeroFloat64, 0, true}, 451 | {1e11 * math.SmallestNonzeroFloat64, 1e11 * -math.SmallestNonzeroFloat64, 0, false}, 452 | {math.SmallestNonzeroFloat64, -math.SmallestNonzeroFloat64, 0, true}, 453 | {-math.SmallestNonzeroFloat64, math.SmallestNonzeroFloat64, 0, true}, 454 | {math.SmallestNonzeroFloat64, 0, 0, true}, 455 | {0, math.SmallestNonzeroFloat64, 0, true}, 456 | {-math.SmallestNonzeroFloat64, 0, 0, true}, 457 | {0, -math.SmallestNonzeroFloat64, 0, true}, 458 | {0.000000001, -math.SmallestNonzeroFloat64, 0, false}, 459 | {0.000000001, math.SmallestNonzeroFloat64, 0, false}, 460 | {math.SmallestNonzeroFloat64, 0.000000001, 0, false}, 461 | {-math.SmallestNonzeroFloat64, 0.000000001, 0, false}, 462 | } 463 | for _, ts := range equalityTests { 464 | if ts.tol == 0 { 465 | ts.tol = 1e-5 466 | } 467 | if equal := EqualWithinRel(ts.a, ts.b, ts.tol); equal != ts.equal { 468 | t.Errorf("Relative equality of %g and %g with tolerance %g returned: %v. Expected: %v", 469 | ts.a, ts.b, ts.tol, equal, ts.equal) 470 | } 471 | } 472 | } 473 | 474 | func nextAfterN(x, y float64, n int) float64 { 475 | for i := 0; i < n; i++ { 476 | x = math.Nextafter(x, y) 477 | } 478 | return x 479 | } 480 | 481 | func TestEqualsULP(t *testing.T) { 482 | if f := 67329.242; !EqualWithinULP(f, nextAfterN(f, math.Inf(1), 10), 10) { 483 | t.Errorf("Equal values returned as unequal") 484 | } 485 | if f := 67329.242; EqualWithinULP(f, nextAfterN(f, math.Inf(1), 5), 1) { 486 | t.Errorf("Unequal values returned as equal") 487 | } 488 | if f := 67329.242; EqualWithinULP(nextAfterN(f, math.Inf(1), 5), f, 1) { 489 | t.Errorf("Unequal values returned as equal") 490 | } 491 | if f := nextAfterN(0, math.Inf(1), 2); !EqualWithinULP(f, nextAfterN(f, math.Inf(-1), 5), 10) { 492 | t.Errorf("Equal values returned as unequal") 493 | } 494 | if !EqualWithinULP(67329.242, 67329.242, 10) { 495 | t.Errorf("Equal float64s not returned as equal") 496 | } 497 | if EqualWithinULP(1, math.NaN(), 10) { 498 | t.Errorf("NaN returned as equal") 499 | } 500 | 501 | } 502 | 503 | func TestEqualLengths(t *testing.T) { 504 | s1 := []float64{1, 2, 3, 4} 505 | s2 := []float64{1, 2, 3, 4} 506 | s3 := []float64{1, 2, 3} 507 | if !EqualLengths(s1, s2) { 508 | t.Errorf("Equal lengths returned as unequal") 509 | } 510 | if EqualLengths(s1, s3) { 511 | t.Errorf("Unequal lengths returned as equal") 512 | } 513 | if !EqualLengths(s1) { 514 | t.Errorf("Single slice returned as unequal") 515 | } 516 | if !EqualLengths() { 517 | t.Errorf("No slices returned as unequal") 518 | } 519 | } 520 | 521 | func eqIntSlice(one, two []int) string { 522 | if len(one) != len(two) { 523 | return "Length mismatch" 524 | } 525 | for i, val := range one { 526 | if val != two[i] { 527 | return "Index " + strconv.Itoa(i) + " mismatch" 528 | } 529 | } 530 | return "" 531 | } 532 | 533 | func TestFind(t *testing.T) { 534 | s := []float64{3, 4, 1, 7, 5} 535 | f := func(v float64) bool { return v > 3.5 } 536 | allTrueInds := []int{1, 3, 4} 537 | 538 | // Test finding first two elements 539 | inds, err := Find(nil, f, s, 2) 540 | if err != nil { 541 | t.Errorf("Find first two: Improper error return") 542 | } 543 | trueInds := allTrueInds[:2] 544 | str := eqIntSlice(inds, trueInds) 545 | if str != "" { 546 | t.Errorf("Find first two: " + str) 547 | } 548 | 549 | // Test finding no elements with non nil slice 550 | inds = []int{1, 2, 3, 4, 5, 6} 551 | inds, err = Find(inds, f, s, 0) 552 | if err != nil { 553 | t.Errorf("Find no elements: Improper error return") 554 | } 555 | str = eqIntSlice(inds, []int{}) 556 | if str != "" { 557 | t.Errorf("Find no non-nil: " + str) 558 | } 559 | 560 | // Test finding first two elements with non nil slice 561 | inds = []int{1, 2, 3, 4, 5, 6} 562 | inds, err = Find(inds, f, s, 2) 563 | if err != nil { 564 | t.Errorf("Find first two non-nil: Improper error return") 565 | } 566 | str = eqIntSlice(inds, trueInds) 567 | if str != "" { 568 | t.Errorf("Find first two non-nil: " + str) 569 | } 570 | 571 | // Test finding too many elements 572 | inds, err = Find(inds, f, s, 4) 573 | if err == nil { 574 | t.Errorf("Request too many: No error returned") 575 | } 576 | str = eqIntSlice(inds, allTrueInds) 577 | if str != "" { 578 | t.Errorf("Request too many: Does not match all of the inds: " + str) 579 | } 580 | 581 | // Test finding all elements 582 | inds, err = Find(nil, f, s, -1) 583 | if err != nil { 584 | t.Errorf("Find all: Improper error returned") 585 | } 586 | str = eqIntSlice(inds, allTrueInds) 587 | if str != "" { 588 | t.Errorf("Find all: Does not match all of the inds: " + str) 589 | } 590 | } 591 | 592 | func TestHasNaN(t *testing.T) { 593 | for i, test := range []struct { 594 | s []float64 595 | ans bool 596 | }{ 597 | {}, 598 | { 599 | s: []float64{1, 2, 3, 4}, 600 | }, 601 | { 602 | s: []float64{1, math.NaN(), 3, 4}, 603 | ans: true, 604 | }, 605 | { 606 | s: []float64{1, 2, 3, math.NaN()}, 607 | ans: true, 608 | }, 609 | } { 610 | b := HasNaN(test.s) 611 | if b != test.ans { 612 | t.Errorf("HasNaN mismatch case %d. Expected %v, Found %v", i, test.ans, b) 613 | } 614 | } 615 | } 616 | 617 | func TestLogSpan(t *testing.T) { 618 | receiver1 := make([]float64, 6) 619 | truth := []float64{0.001, 0.01, 0.1, 1, 10, 100} 620 | receiver2 := LogSpan(receiver1, 0.001, 100) 621 | tst := make([]float64, 6) 622 | for i := range truth { 623 | tst[i] = receiver1[i] / truth[i] 624 | } 625 | comp := make([]float64, 6) 626 | for i := range comp { 627 | comp[i] = 1 628 | } 629 | AreSlicesEqual(t, comp, tst, "Improper logspace from mutator") 630 | 631 | for i := range truth { 632 | tst[i] = receiver2[i] / truth[i] 633 | } 634 | AreSlicesEqual(t, comp, tst, "Improper logspace from returned slice") 635 | 636 | if !Panics(func() { LogSpan(nil, 1, 5) }) { 637 | t.Errorf("Span accepts nil argument") 638 | } 639 | if !Panics(func() { LogSpan(make([]float64, 1), 1, 5) }) { 640 | t.Errorf("Span accepts argument of len = 1") 641 | } 642 | } 643 | 644 | func TestLogSumExp(t *testing.T) { 645 | s := []float64{1, 2, 3, 4, 5} 646 | val := LogSumExp(s) 647 | // http://www.wolframalpha.com/input/?i=log%28exp%281%29+%2B+exp%282%29+%2B+exp%283%29+%2B+exp%284%29+%2B+exp%285%29%29 648 | truth := 5.4519143959375933331957225109748087179338972737576824 649 | if math.Abs(val-truth) > EqTolerance { 650 | t.Errorf("Wrong logsumexp for many values") 651 | } 652 | s = []float64{1, 2} 653 | // http://www.wolframalpha.com/input/?i=log%28exp%281%29+%2B+exp%282%29%29 654 | truth = 2.3132616875182228340489954949678556419152800856703483 655 | val = LogSumExp(s) 656 | if math.Abs(val-truth) > EqTolerance { 657 | t.Errorf("Wrong logsumexp for two values. %v expected, %v found", truth, val) 658 | } 659 | // This case would normally underflow 660 | s = []float64{-1001, -1002, -1003, -1004, -1005} 661 | // http://www.wolframalpha.com/input/?i=log%28exp%28-1001%29%2Bexp%28-1002%29%2Bexp%28-1003%29%2Bexp%28-1004%29%2Bexp%28-1005%29%29 662 | truth = -1000.54808560406240666680427748902519128206610272624 663 | val = LogSumExp(s) 664 | if math.Abs(val-truth) > EqTolerance { 665 | t.Errorf("Doesn't match for underflow case. %v expected, %v found", truth, val) 666 | } 667 | // positive infinite case 668 | s = []float64{1, 2, 3, 4, 5, math.Inf(1)} 669 | val = LogSumExp(s) 670 | truth = math.Inf(1) 671 | if val != truth { 672 | t.Errorf("Doesn't match for pos Infinity case. %v expected, %v found", truth, val) 673 | } 674 | // negative infinite case 675 | s = []float64{1, 2, 3, 4, 5, math.Inf(-1)} 676 | val = LogSumExp(s) 677 | truth = 5.4519143959375933331957225109748087179338972737576824 // same as first case 678 | if math.Abs(val-truth) > EqTolerance { 679 | t.Errorf("Wrong logsumexp for values with negative infinity") 680 | } 681 | 682 | } 683 | 684 | func TestMaxAndIdx(t *testing.T) { 685 | s := []float64{3, 4, 1, 7, 5} 686 | ind := MaxIdx(s) 687 | val := Max(s) 688 | if val != 7 { 689 | t.Errorf("Wrong value returned") 690 | } 691 | if ind != 3 { 692 | t.Errorf("Wrong index returned") 693 | } 694 | } 695 | 696 | func TestMinAndIdx(t *testing.T) { 697 | s := []float64{3, 4, 1, 7, 5} 698 | ind := MinIdx(s) 699 | val := Min(s) 700 | if val != 1 { 701 | t.Errorf("Wrong value returned") 702 | } 703 | if ind != 2 { 704 | t.Errorf("Wrong index returned") 705 | } 706 | } 707 | 708 | func TestMul(t *testing.T) { 709 | s1 := []float64{1, 2, 3} 710 | s2 := []float64{1, 2, 3} 711 | ans := []float64{1, 4, 9} 712 | Mul(s1, s2) 713 | if !EqualApprox(s1, ans, EqTolerance) { 714 | t.Errorf("Mul doesn't give correct answer") 715 | } 716 | s1short := []float64{1} 717 | if !Panics(func() { Mul(s1short, s2) }) { 718 | t.Errorf("Did not panic with unequal lengths") 719 | } 720 | s2short := []float64{1} 721 | if !Panics(func() { Mul(s1, s2short) }) { 722 | t.Errorf("Did not panic with unequal lengths") 723 | } 724 | } 725 | 726 | func TestMulTo(t *testing.T) { 727 | s1 := []float64{1, 2, 3} 728 | s1orig := []float64{1, 2, 3} 729 | s2 := []float64{1, 2, 3} 730 | s2orig := []float64{1, 2, 3} 731 | dst1 := make([]float64, 3) 732 | ans := []float64{1, 4, 9} 733 | dst2 := MulTo(dst1, s1, s2) 734 | if !EqualApprox(dst1, ans, EqTolerance) { 735 | t.Errorf("MulTo doesn't give correct answer in mutated slice") 736 | } 737 | if !EqualApprox(dst2, ans, EqTolerance) { 738 | t.Errorf("MulTo doesn't give correct answer in returned slice") 739 | } 740 | if !EqualApprox(s1, s1orig, EqTolerance) { 741 | t.Errorf("S1 changes during multo") 742 | } 743 | if !EqualApprox(s2, s2orig, EqTolerance) { 744 | t.Errorf("s2 changes during multo") 745 | } 746 | MulTo(dst1, s1, s2) 747 | if !EqualApprox(dst1, ans, EqTolerance) { 748 | t.Errorf("MulTo doesn't give correct answer reusing dst") 749 | } 750 | dstShort := []float64{1} 751 | if !Panics(func() { MulTo(dstShort, s1, s2) }) { 752 | t.Errorf("Did not panic with s1 wrong length") 753 | } 754 | s1short := []float64{1} 755 | if !Panics(func() { MulTo(dst1, s1short, s2) }) { 756 | t.Errorf("Did not panic with s1 wrong length") 757 | } 758 | s2short := []float64{1} 759 | if !Panics(func() { MulTo(dst1, s1, s2short) }) { 760 | t.Errorf("Did not panic with s2 wrong length") 761 | } 762 | } 763 | 764 | func TestNearest(t *testing.T) { 765 | s := []float64{6.2, 3, 5, 6.2, 8} 766 | ind := Nearest(s, 2.0) 767 | if ind != 1 { 768 | t.Errorf("Wrong index returned when value is less than all of elements") 769 | } 770 | ind = Nearest(s, 9.0) 771 | if ind != 4 { 772 | t.Errorf("Wrong index returned when value is greater than all of elements") 773 | } 774 | ind = Nearest(s, 3.1) 775 | if ind != 1 { 776 | t.Errorf("Wrong index returned when value is greater than closest element") 777 | } 778 | ind = Nearest(s, 3.1) 779 | if ind != 1 { 780 | t.Errorf("Wrong index returned when value is greater than closest element") 781 | } 782 | ind = Nearest(s, 2.9) 783 | if ind != 1 { 784 | t.Errorf("Wrong index returned when value is less than closest element") 785 | } 786 | ind = Nearest(s, 3) 787 | if ind != 1 { 788 | t.Errorf("Wrong index returned when value is equal to element") 789 | } 790 | ind = Nearest(s, 6.2) 791 | if ind != 0 { 792 | t.Errorf("Wrong index returned when value is equal to several elements") 793 | } 794 | ind = Nearest(s, 4) 795 | if ind != 1 { 796 | t.Errorf("Wrong index returned when value is exactly between two closest elements") 797 | } 798 | } 799 | 800 | func TestNearestWithinSpan(t *testing.T) { 801 | if !Panics(func() { NearestWithinSpan(10, 8, 2, 4.5) }) { 802 | t.Errorf("Did not panic when upper bound is lower than greater bound") 803 | } 804 | for i, test := range []struct { 805 | length int 806 | lower float64 807 | upper float64 808 | value float64 809 | idx int 810 | }{ 811 | { 812 | length: 13, 813 | lower: 7, 814 | upper: 8.2, 815 | value: 6, 816 | idx: -1, 817 | }, 818 | { 819 | length: 13, 820 | lower: 7, 821 | upper: 8.2, 822 | value: 10, 823 | idx: -1, 824 | }, 825 | { 826 | length: 13, 827 | lower: 7, 828 | upper: 8.2, 829 | value: 7.19, 830 | idx: 2, 831 | }, 832 | { 833 | length: 13, 834 | lower: 7, 835 | upper: 8.2, 836 | value: 7.21, 837 | idx: 2, 838 | }, 839 | { 840 | length: 13, 841 | lower: 7, 842 | upper: 8.2, 843 | value: 7.2, 844 | idx: 2, 845 | }, 846 | { 847 | length: 13, 848 | lower: 7, 849 | upper: 8.2, 850 | value: 7.151, 851 | idx: 2, 852 | }, 853 | { 854 | length: 13, 855 | lower: 7, 856 | upper: 8.2, 857 | value: 7.249, 858 | idx: 2, 859 | }, 860 | } { 861 | if idx := NearestWithinSpan(test.length, test.lower, test.upper, test.value); test.idx != idx { 862 | t.Errorf("Case %v mismatch: Want: %v, Got: %v", i, test.idx, idx) 863 | } 864 | } 865 | } 866 | 867 | func TestNorm(t *testing.T) { 868 | s := []float64{-1, -3.4, 5, -6} 869 | val := Norm(s, math.Inf(1)) 870 | truth := 6.0 871 | if math.Abs(val-truth) > EqTolerance { 872 | t.Errorf("Doesn't match for inf norm. %v expected, %v found", truth, val) 873 | } 874 | // http://www.wolframalpha.com/input/?i=%28%28-1%29%5E2+%2B++%28-3.4%29%5E2+%2B+5%5E2%2B++6%5E2%29%5E%281%2F2%29 875 | val = Norm(s, 2) 876 | truth = 8.5767126569566267590651614132751986658027271236078592 877 | if math.Abs(val-truth) > EqTolerance { 878 | t.Errorf("Doesn't match for inf norm. %v expected, %v found", truth, val) 879 | } 880 | // http://www.wolframalpha.com/input/?i=%28%28%7C-1%7C%29%5E3+%2B++%28%7C-3.4%7C%29%5E3+%2B+%7C5%7C%5E3%2B++%7C6%7C%5E3%29%5E%281%2F3%29 881 | val = Norm(s, 3) 882 | truth = 7.2514321388020228478109121239004816430071237369356233 883 | if math.Abs(val-truth) > EqTolerance { 884 | t.Errorf("Doesn't match for inf norm. %v expected, %v found", truth, val) 885 | } 886 | 887 | //http://www.wolframalpha.com/input/?i=%7C-1%7C+%2B+%7C-3.4%7C+%2B+%7C5%7C%2B++%7C6%7C 888 | val = Norm(s, 1) 889 | truth = 15.4 890 | if math.Abs(val-truth) > EqTolerance { 891 | t.Errorf("Doesn't match for inf norm. %v expected, %v found", truth, val) 892 | } 893 | } 894 | 895 | func TestProd(t *testing.T) { 896 | s := []float64{} 897 | val := Prod(s) 898 | if val != 1 { 899 | t.Errorf("Val not returned as default when slice length is zero") 900 | } 901 | s = []float64{3, 4, 1, 7, 5} 902 | val = Prod(s) 903 | if val != 420 { 904 | t.Errorf("Wrong prod returned. Expected %v returned %v", 420, val) 905 | } 906 | } 907 | 908 | func TestReverse(t *testing.T) { 909 | for _, s := range [][]float64{ 910 | {0}, 911 | {1, 0}, 912 | {2, 1, 0}, 913 | {3, 2, 1, 0}, 914 | {9, 8, 7, 6, 5, 4, 3, 2, 1, 0}, 915 | } { 916 | Reverse(s) 917 | for i, v := range s { 918 | if v != float64(i) { 919 | t.Errorf("unexpected values for element %d: got:%v want:%v", i, v, i) 920 | } 921 | } 922 | } 923 | } 924 | 925 | func roundFloat(x float64, prec int) float64 { 926 | f, _ := strconv.ParseFloat(strconv.FormatFloat(x, 'f', prec, 64), 64) 927 | if f == 0 { 928 | return math.Abs(f) 929 | } 930 | return f 931 | } 932 | 933 | func TestRound(t *testing.T) { 934 | for _, x := range []float64{ 935 | 0, 936 | math.Inf(1), 937 | math.NaN(), 938 | func() float64 { var f float64; return -f }(), 939 | math.MaxFloat64 / 2, 940 | 1 << 64, 941 | 454.4445, 942 | 454.44445, 943 | 0.42499, 944 | 0.42599, 945 | 0.424999999999993, 946 | 0.425000000000001, 947 | 123.42499999999993, 948 | 949 | // FIXME(kortschak): These values fail due to roundFloat rounding 950 | // 454.45 to ±454.4 when prec=1 and 454.445 to ±454.44 when prec=2. 951 | // This is a result of fmt's rendering of the value. 952 | // 454.45, 953 | // 454.445, 954 | } { 955 | for _, sign := range []float64{1, -1} { 956 | for prec := 0; prec < 10; prec++ { 957 | got := Round(sign*x, prec) 958 | want := roundFloat(sign*x, prec) 959 | if (got != want || math.Signbit(got) != math.Signbit(want)) && !(math.IsNaN(got) && math.IsNaN(want)) { 960 | t.Errorf("unexpected result for Round(%g, %d): got: %g, want: %g", x, prec, got, want) 961 | } 962 | } 963 | } 964 | } 965 | 966 | // Special cases. 967 | for _, test := range []struct { 968 | x float64 969 | prec int 970 | want float64 971 | }{ 972 | // Failing cases above. 973 | {x: 454.45, prec: 0, want: 454}, 974 | {x: 454.45, prec: 1, want: 454.5}, 975 | {x: 454.45, prec: 2, want: 454.45}, 976 | {x: 454.45, prec: 3, want: 454.45}, 977 | {x: 454.445, prec: 0, want: 454}, 978 | {x: 454.445, prec: 1, want: 454.4}, 979 | {x: 454.445, prec: 2, want: 454.45}, 980 | {x: 454.445, prec: 3, want: 454.445}, 981 | {x: 454.445, prec: 4, want: 454.445}, 982 | 983 | // Negative precision. 984 | {x: 454.45, prec: -1, want: 450}, 985 | {x: 454.45, prec: -2, want: 500}, 986 | {x: 500, prec: -3, want: 1000}, 987 | {x: 500, prec: -4, want: 0}, 988 | } { 989 | for _, sign := range []float64{1, -1} { 990 | got := Round(sign*test.x, test.prec) 991 | want := sign * test.want 992 | if want == 0 { 993 | want = 0 994 | } 995 | if got != want || math.Signbit(got) != math.Signbit(want) { 996 | t.Errorf("unexpected result for Round(%g, %d): got: %g, want: %g", test.x, test.prec, got, test.want) 997 | } 998 | } 999 | } 1000 | 1001 | // Test many large numbers. We hit float precision 1002 | // issues below 1e16 so we avoid that domain. 1003 | err := quick.Check(func(x float64, prec int) bool { 1004 | prec %= 20 1005 | if prec < 0 { 1006 | prec = -prec 1007 | } 1008 | for x > 1e16 { 1009 | got := Round(x, prec) 1010 | want := roundFloat(x, prec) 1011 | if (got != want || math.Signbit(got) != math.Signbit(want)) && !(math.IsNaN(got) && math.IsNaN(want)) { 1012 | t.Logf("big numbers failed with prec=%d x=%f got=%f want=%f", prec, x, got, want) 1013 | return false 1014 | } 1015 | x /= 10 1016 | } 1017 | return true 1018 | }, nil) 1019 | if err != nil { 1020 | t.Error(err) 1021 | } 1022 | 1023 | // Test many small numbers. 1024 | err = quick.Check(func(mant, exp int, prec int) bool { 1025 | prec %= 20 1026 | if prec < 0 { 1027 | prec = -prec 1028 | } 1029 | mant %= 1e10 1030 | exp %= 40 1031 | if exp > 0 { 1032 | exp = -exp 1033 | } 1034 | x := float64(mant) * math.Pow10(exp) 1035 | _, x = math.Modf(x) 1036 | if prec > -exp { 1037 | prec = -exp 1038 | } 1039 | got := strconv.FormatFloat(Round(x, prec), 'g', prec, 64) 1040 | want := strconv.FormatFloat(roundFloat(x, prec), 'g', prec, 64) 1041 | if got != want { 1042 | t.Logf("small numbers failed with prec=%d x=%f got=%s want=%s", prec, x, got, want) 1043 | return false 1044 | } 1045 | return true 1046 | }, nil) 1047 | if err != nil { 1048 | t.Error(err) 1049 | } 1050 | } 1051 | 1052 | func TestRoundEven(t *testing.T) { 1053 | for _, test := range []struct { 1054 | x float64 1055 | prec int 1056 | want float64 1057 | }{ 1058 | {x: 0, prec: 1, want: 0}, 1059 | {x: math.Inf(1), prec: 1, want: math.Inf(1)}, 1060 | {x: math.NaN(), prec: 1, want: math.NaN()}, 1061 | {x: func() float64 { var f float64; return -f }(), prec: 1, want: 0}, 1062 | {x: math.MaxFloat64 / 2, prec: 1, want: math.MaxFloat64 / 2}, 1063 | {x: 1 << 64, prec: 1, want: 1 << 64}, 1064 | {x: 454.4445, prec: 3, want: 454.444}, 1065 | {x: 454.44445, prec: 4, want: 454.4444}, 1066 | {x: 0.42499, prec: 4, want: 0.425}, 1067 | {x: 0.42599, prec: 4, want: 0.426}, 1068 | {x: 0.424999999999993, prec: 2, want: 0.42}, 1069 | {x: 0.425, prec: 2, want: 0.42}, 1070 | {x: 0.425000000000001, prec: 2, want: 0.43}, 1071 | {x: 123.4244999999999, prec: 3, want: 123.424}, 1072 | {x: 123.4245, prec: 3, want: 123.424}, 1073 | {x: 123.4245000000001, prec: 3, want: 123.425}, 1074 | 1075 | {x: 454.45, prec: 0, want: 454}, 1076 | {x: 454.45, prec: 1, want: 454.4}, 1077 | {x: 454.45, prec: 2, want: 454.45}, 1078 | {x: 454.45, prec: 3, want: 454.45}, 1079 | {x: 454.445, prec: 0, want: 454}, 1080 | {x: 454.445, prec: 1, want: 454.4}, 1081 | {x: 454.445, prec: 2, want: 454.44}, 1082 | {x: 454.445, prec: 3, want: 454.445}, 1083 | {x: 454.445, prec: 4, want: 454.445}, 1084 | {x: 454.55, prec: 0, want: 455}, 1085 | {x: 454.55, prec: 1, want: 454.6}, 1086 | {x: 454.55, prec: 2, want: 454.55}, 1087 | {x: 454.55, prec: 3, want: 454.55}, 1088 | {x: 454.455, prec: 0, want: 454}, 1089 | {x: 454.455, prec: 1, want: 454.5}, 1090 | {x: 454.455, prec: 2, want: 454.46}, 1091 | {x: 454.455, prec: 3, want: 454.455}, 1092 | {x: 454.455, prec: 4, want: 454.455}, 1093 | 1094 | // Negative precision. 1095 | {x: 454.45, prec: -1, want: 450}, 1096 | {x: 454.45, prec: -2, want: 500}, 1097 | {x: 500, prec: -3, want: 0}, 1098 | {x: 500, prec: -4, want: 0}, 1099 | {x: 1500, prec: -3, want: 2000}, 1100 | {x: 1500, prec: -4, want: 0}, 1101 | } { 1102 | for _, sign := range []float64{1, -1} { 1103 | got := RoundEven(sign*test.x, test.prec) 1104 | want := sign * test.want 1105 | if want == 0 { 1106 | want = 0 1107 | } 1108 | if (got != want || math.Signbit(got) != math.Signbit(want)) && !(math.IsNaN(got) && math.IsNaN(want)) { 1109 | t.Errorf("unexpected result for RoundEven(%g, %d): got: %g, want: %g", sign*test.x, test.prec, got, want) 1110 | } 1111 | } 1112 | } 1113 | } 1114 | 1115 | func TestSame(t *testing.T) { 1116 | s1 := []float64{1, 2, 3, 4} 1117 | s2 := []float64{1, 2, 3, 4} 1118 | if !Same(s1, s2) { 1119 | t.Errorf("Equal slices returned as unequal") 1120 | } 1121 | s2 = []float64{1, 2, 3, 4 + 1e-14} 1122 | if Same(s1, s2) { 1123 | t.Errorf("Unequal slices returned as equal") 1124 | } 1125 | if Same(s1, []float64{}) { 1126 | t.Errorf("Unequal slice lengths returned as equal") 1127 | } 1128 | s1 = []float64{1, 2, math.NaN(), 4} 1129 | s2 = []float64{1, 2, math.NaN(), 4} 1130 | if !Same(s1, s2) { 1131 | t.Errorf("Slices with matching NaN values returned as unequal") 1132 | } 1133 | s1 = []float64{1, 2, math.NaN(), 4} 1134 | s2 = []float64{1, math.NaN(), 3, 4} 1135 | if !Same(s1, s2) { 1136 | t.Errorf("Slices with unmatching NaN values returned as equal") 1137 | } 1138 | } 1139 | 1140 | func TestScale(t *testing.T) { 1141 | s := []float64{3, 4, 1, 7, 5} 1142 | c := 5.0 1143 | truth := []float64{15, 20, 5, 35, 25} 1144 | Scale(c, s) 1145 | AreSlicesEqual(t, truth, s, "Bad scaling") 1146 | } 1147 | 1148 | func TestSpan(t *testing.T) { 1149 | receiver1 := make([]float64, 5) 1150 | truth := []float64{1, 2, 3, 4, 5} 1151 | receiver2 := Span(receiver1, 1, 5) 1152 | AreSlicesEqual(t, truth, receiver1, "Improper linspace from mutator") 1153 | AreSlicesEqual(t, truth, receiver2, "Improper linspace from returned slice") 1154 | receiver1 = make([]float64, 6) 1155 | truth = []float64{0, 0.2, 0.4, 0.6, 0.8, 1.0} 1156 | Span(receiver1, 0, 1) 1157 | AreSlicesEqual(t, truth, receiver1, "Improper linspace") 1158 | if !Panics(func() { Span(nil, 1, 5) }) { 1159 | t.Errorf("Span accepts nil argument") 1160 | } 1161 | if !Panics(func() { Span(make([]float64, 1), 1, 5) }) { 1162 | t.Errorf("Span accepts argument of len = 1") 1163 | } 1164 | } 1165 | 1166 | func TestSub(t *testing.T) { 1167 | s := []float64{3, 4, 1, 7, 5} 1168 | v := []float64{1, 2, 3, 4, 5} 1169 | truth := []float64{2, 2, -2, 3, 0} 1170 | Sub(s, v) 1171 | AreSlicesEqual(t, truth, s, "Bad subtract") 1172 | // Test that it panics 1173 | if !Panics(func() { Sub(make([]float64, 2), make([]float64, 3)) }) { 1174 | t.Errorf("Did not panic with length mismatch") 1175 | } 1176 | } 1177 | 1178 | func TestSubTo(t *testing.T) { 1179 | s := []float64{3, 4, 1, 7, 5} 1180 | v := []float64{1, 2, 3, 4, 5} 1181 | truth := []float64{2, 2, -2, 3, 0} 1182 | dst1 := make([]float64, len(s)) 1183 | dst2 := SubTo(dst1, s, v) 1184 | AreSlicesEqual(t, truth, dst1, "Bad subtract from mutator") 1185 | AreSlicesEqual(t, truth, dst2, "Bad subtract from returned slice") 1186 | // Test that all mismatch combinations panic 1187 | if !Panics(func() { SubTo(make([]float64, 2), make([]float64, 3), make([]float64, 3)) }) { 1188 | t.Errorf("Did not panic with dst different length") 1189 | } 1190 | if !Panics(func() { SubTo(make([]float64, 3), make([]float64, 2), make([]float64, 3)) }) { 1191 | t.Errorf("Did not panic with subtractor different length") 1192 | } 1193 | if !Panics(func() { SubTo(make([]float64, 3), make([]float64, 3), make([]float64, 2)) }) { 1194 | t.Errorf("Did not panic with subtractee different length") 1195 | } 1196 | } 1197 | 1198 | func TestSum(t *testing.T) { 1199 | s := []float64{} 1200 | val := Sum(s) 1201 | if val != 0 { 1202 | t.Errorf("Val not returned as default when slice length is zero") 1203 | } 1204 | s = []float64{3, 4, 1, 7, 5} 1205 | val = Sum(s) 1206 | if val != 20 { 1207 | t.Errorf("Wrong sum returned") 1208 | } 1209 | } 1210 | 1211 | func TestWithin(t *testing.T) { 1212 | for i, test := range []struct { 1213 | s []float64 1214 | v float64 1215 | idx int 1216 | panics bool 1217 | }{ 1218 | { 1219 | s: []float64{1, 2, 5, 9}, 1220 | v: 1, 1221 | idx: 0, 1222 | }, 1223 | { 1224 | s: []float64{1, 2, 5, 9}, 1225 | v: 9, 1226 | idx: -1, 1227 | }, 1228 | { 1229 | s: []float64{1, 2, 5, 9}, 1230 | v: 1.5, 1231 | idx: 0, 1232 | }, 1233 | { 1234 | s: []float64{1, 2, 5, 9}, 1235 | v: 2, 1236 | idx: 1, 1237 | }, 1238 | { 1239 | s: []float64{1, 2, 5, 9}, 1240 | v: 2.5, 1241 | idx: 1, 1242 | }, 1243 | { 1244 | s: []float64{1, 2, 5, 9}, 1245 | v: -3, 1246 | idx: -1, 1247 | }, 1248 | { 1249 | s: []float64{1, 2, 5, 9}, 1250 | v: 15, 1251 | idx: -1, 1252 | }, 1253 | { 1254 | s: []float64{1, 2, 5, 9}, 1255 | v: math.NaN(), 1256 | idx: -1, 1257 | }, 1258 | { 1259 | s: []float64{5, 2, 6}, 1260 | panics: true, 1261 | }, 1262 | { 1263 | panics: true, 1264 | }, 1265 | { 1266 | s: []float64{1}, 1267 | panics: true, 1268 | }, 1269 | } { 1270 | var idx int 1271 | panics := Panics(func() { idx = Within(test.s, test.v) }) 1272 | if panics { 1273 | if !test.panics { 1274 | t.Errorf("Case %v: bad panic", i) 1275 | } 1276 | continue 1277 | } 1278 | if test.panics { 1279 | if !panics { 1280 | t.Errorf("Case %v: did not panic when it should", i) 1281 | } 1282 | continue 1283 | } 1284 | if idx != test.idx { 1285 | t.Errorf("Case %v: Idx mismatch. Want: %v, got: %v", i, test.idx, idx) 1286 | } 1287 | } 1288 | 1289 | } 1290 | 1291 | func randomSlice(l int) []float64 { 1292 | s := make([]float64, l) 1293 | for i := range s { 1294 | s[i] = rand.Float64() 1295 | } 1296 | return s 1297 | } 1298 | 1299 | func benchmarkMin(b *testing.B, size int) { 1300 | s := randomSlice(size) 1301 | b.ResetTimer() 1302 | for i := 0; i < b.N; i++ { 1303 | Min(s) 1304 | } 1305 | } 1306 | func BenchmarkMinSmall(b *testing.B) { benchmarkMin(b, Small) } 1307 | func BenchmarkMinMed(b *testing.B) { benchmarkMin(b, Medium) } 1308 | func BenchmarkMinLarge(b *testing.B) { benchmarkMin(b, Large) } 1309 | func BenchmarkMinHuge(b *testing.B) { benchmarkMin(b, Huge) } 1310 | 1311 | func benchmarkAdd(b *testing.B, size int) { 1312 | s1 := randomSlice(size) 1313 | s2 := randomSlice(size) 1314 | b.ResetTimer() 1315 | for i := 0; i < b.N; i++ { 1316 | Add(s1, s2) 1317 | } 1318 | } 1319 | func BenchmarkAddSmall(b *testing.B) { benchmarkAdd(b, Small) } 1320 | func BenchmarkAddMed(b *testing.B) { benchmarkAdd(b, Medium) } 1321 | func BenchmarkAddLarge(b *testing.B) { benchmarkAdd(b, Large) } 1322 | func BenchmarkAddHuge(b *testing.B) { benchmarkAdd(b, Huge) } 1323 | 1324 | func benchmarkAddTo(b *testing.B, size int) { 1325 | s1 := randomSlice(size) 1326 | s2 := randomSlice(size) 1327 | dst := randomSlice(size) 1328 | b.ResetTimer() 1329 | for i := 0; i < b.N; i++ { 1330 | AddTo(dst, s1, s2) 1331 | } 1332 | } 1333 | func BenchmarkAddToSmall(b *testing.B) { benchmarkAddTo(b, Small) } 1334 | func BenchmarkAddToMed(b *testing.B) { benchmarkAddTo(b, Medium) } 1335 | func BenchmarkAddToLarge(b *testing.B) { benchmarkAddTo(b, Large) } 1336 | func BenchmarkAddToHuge(b *testing.B) { benchmarkAddTo(b, Huge) } 1337 | 1338 | func benchmarkCumProd(b *testing.B, size int) { 1339 | s := randomSlice(size) 1340 | dst := randomSlice(size) 1341 | b.ResetTimer() 1342 | for i := 0; i < b.N; i++ { 1343 | CumProd(dst, s) 1344 | } 1345 | } 1346 | func BenchmarkCumProdSmall(b *testing.B) { benchmarkCumProd(b, Small) } 1347 | func BenchmarkCumProdMed(b *testing.B) { benchmarkCumProd(b, Medium) } 1348 | func BenchmarkCumProdLarge(b *testing.B) { benchmarkCumProd(b, Large) } 1349 | func BenchmarkCumProdHuge(b *testing.B) { benchmarkCumProd(b, Huge) } 1350 | 1351 | func benchmarkCumSum(b *testing.B, size int) { 1352 | s := randomSlice(size) 1353 | dst := randomSlice(size) 1354 | b.ResetTimer() 1355 | for i := 0; i < b.N; i++ { 1356 | CumSum(dst, s) 1357 | } 1358 | } 1359 | func BenchmarkCumSumSmall(b *testing.B) { benchmarkCumSum(b, Small) } 1360 | func BenchmarkCumSumMed(b *testing.B) { benchmarkCumSum(b, Medium) } 1361 | func BenchmarkCumSumLarge(b *testing.B) { benchmarkCumSum(b, Large) } 1362 | func BenchmarkCumSumHuge(b *testing.B) { benchmarkCumSum(b, Huge) } 1363 | 1364 | func benchmarkDiv(b *testing.B, size int) { 1365 | s := randomSlice(size) 1366 | dst := randomSlice(size) 1367 | b.ResetTimer() 1368 | for i := 0; i < b.N; i++ { 1369 | Div(dst, s) 1370 | } 1371 | } 1372 | func BenchmarkDivSmall(b *testing.B) { benchmarkDiv(b, Small) } 1373 | func BenchmarkDivMed(b *testing.B) { benchmarkDiv(b, Medium) } 1374 | func BenchmarkDivLarge(b *testing.B) { benchmarkDiv(b, Large) } 1375 | func BenchmarkDivHuge(b *testing.B) { benchmarkDiv(b, Huge) } 1376 | 1377 | func benchmarkDivTo(b *testing.B, size int) { 1378 | s1 := randomSlice(size) 1379 | s2 := randomSlice(size) 1380 | dst := randomSlice(size) 1381 | b.ResetTimer() 1382 | for i := 0; i < b.N; i++ { 1383 | DivTo(dst, s1, s2) 1384 | } 1385 | } 1386 | func BenchmarkDivToSmall(b *testing.B) { benchmarkDivTo(b, Small) } 1387 | func BenchmarkDivToMed(b *testing.B) { benchmarkDivTo(b, Medium) } 1388 | func BenchmarkDivToLarge(b *testing.B) { benchmarkDivTo(b, Large) } 1389 | func BenchmarkDivToHuge(b *testing.B) { benchmarkDivTo(b, Huge) } 1390 | 1391 | func benchmarkSub(b *testing.B, size int) { 1392 | s1 := randomSlice(size) 1393 | s2 := randomSlice(size) 1394 | b.ResetTimer() 1395 | for i := 0; i < b.N; i++ { 1396 | Sub(s1, s2) 1397 | } 1398 | } 1399 | func BenchmarkSubSmall(b *testing.B) { benchmarkSub(b, Small) } 1400 | func BenchmarkSubMed(b *testing.B) { benchmarkSub(b, Medium) } 1401 | func BenchmarkSubLarge(b *testing.B) { benchmarkSub(b, Large) } 1402 | func BenchmarkSubHuge(b *testing.B) { benchmarkSub(b, Huge) } 1403 | 1404 | func benchmarkSubTo(b *testing.B, size int) { 1405 | s1 := randomSlice(size) 1406 | s2 := randomSlice(size) 1407 | dst := randomSlice(size) 1408 | b.ResetTimer() 1409 | for i := 0; i < b.N; i++ { 1410 | SubTo(dst, s1, s2) 1411 | } 1412 | } 1413 | func BenchmarkSubToSmall(b *testing.B) { benchmarkSubTo(b, Small) } 1414 | func BenchmarkSubToMed(b *testing.B) { benchmarkSubTo(b, Medium) } 1415 | func BenchmarkSubToLarge(b *testing.B) { benchmarkSubTo(b, Large) } 1416 | func BenchmarkSubToHuge(b *testing.B) { benchmarkSubTo(b, Huge) } 1417 | 1418 | func benchmarkLogSumExp(b *testing.B, size int) { 1419 | s := randomSlice(size) 1420 | b.ResetTimer() 1421 | for i := 0; i < b.N; i++ { 1422 | LogSumExp(s) 1423 | } 1424 | } 1425 | func BenchmarkLogSumExpSmall(b *testing.B) { benchmarkLogSumExp(b, Small) } 1426 | func BenchmarkLogSumExpMed(b *testing.B) { benchmarkLogSumExp(b, Medium) } 1427 | func BenchmarkLogSumExpLarge(b *testing.B) { benchmarkLogSumExp(b, Large) } 1428 | func BenchmarkLogSumExpHuge(b *testing.B) { benchmarkLogSumExp(b, Huge) } 1429 | 1430 | func benchmarkDot(b *testing.B, size int) { 1431 | s1 := randomSlice(size) 1432 | s2 := randomSlice(size) 1433 | b.ResetTimer() 1434 | for i := 0; i < b.N; i++ { 1435 | Dot(s1, s2) 1436 | } 1437 | } 1438 | func BenchmarkDotSmall(b *testing.B) { benchmarkDot(b, Small) } 1439 | func BenchmarkDotMed(b *testing.B) { benchmarkDot(b, Medium) } 1440 | func BenchmarkDotLarge(b *testing.B) { benchmarkDot(b, Large) } 1441 | func BenchmarkDotHuge(b *testing.B) { benchmarkDot(b, Huge) } 1442 | 1443 | func benchmarkAddScaledTo(b *testing.B, size int) { 1444 | dst := randomSlice(size) 1445 | y := randomSlice(size) 1446 | s := randomSlice(size) 1447 | b.ResetTimer() 1448 | for i := 0; i < b.N; i++ { 1449 | AddScaledTo(dst, y, 2.3, s) 1450 | } 1451 | } 1452 | func BenchmarkAddScaledToSmall(b *testing.B) { benchmarkAddScaledTo(b, Small) } 1453 | func BenchmarkAddScaledToMedium(b *testing.B) { benchmarkAddScaledTo(b, Medium) } 1454 | func BenchmarkAddScaledToLarge(b *testing.B) { benchmarkAddScaledTo(b, Large) } 1455 | func BenchmarkAddScaledToHuge(b *testing.B) { benchmarkAddScaledTo(b, Huge) } 1456 | 1457 | func benchmarkScale(b *testing.B, size int) { 1458 | dst := randomSlice(size) 1459 | b.ResetTimer() 1460 | for i := 0; i < b.N; i += 2 { 1461 | Scale(2.0, dst) 1462 | Scale(0.5, dst) 1463 | } 1464 | } 1465 | func BenchmarkScaleSmall(b *testing.B) { benchmarkScale(b, Small) } 1466 | func BenchmarkScaleMedium(b *testing.B) { benchmarkScale(b, Medium) } 1467 | func BenchmarkScaleLarge(b *testing.B) { benchmarkScale(b, Large) } 1468 | func BenchmarkScaleHuge(b *testing.B) { benchmarkScale(b, Huge) } 1469 | --------------------------------------------------------------------------------