github.com/gopherd/gonum@v0.0.4/floats/floats.go (about)

     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  	"errors"
     9  	"math"
    10  	"sort"
    11  
    12  	"github.com/gopherd/gonum/floats/scalar"
    13  	"github.com/gopherd/gonum/internal/asm/f64"
    14  )
    15  
    16  const (
    17  	zeroLength   = "floats: zero length slice"
    18  	shortSpan    = "floats: slice length less than 2"
    19  	badLength    = "floats: slice lengths do not match"
    20  	badDstLength = "floats: destination slice length does not match input"
    21  )
    22  
    23  // Add adds, element-wise, the elements of s and dst, and stores the result in dst.
    24  // It panics if the argument lengths do not match.
    25  func Add(dst, s []float64) {
    26  	if len(dst) != len(s) {
    27  		panic(badDstLength)
    28  	}
    29  	f64.AxpyUnitaryTo(dst, 1, s, dst)
    30  }
    31  
    32  // AddTo adds, element-wise, the elements of s and t and
    33  // stores the result in dst.
    34  // It panics if the argument lengths do not match.
    35  func AddTo(dst, s, t []float64) []float64 {
    36  	if len(s) != len(t) {
    37  		panic(badLength)
    38  	}
    39  	if len(dst) != len(s) {
    40  		panic(badDstLength)
    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  	f64.AddConst(c, dst)
    49  }
    50  
    51  // AddScaled performs dst = dst + alpha * s.
    52  // It panics if the slice argument lengths do not match.
    53  func AddScaled(dst []float64, alpha float64, s []float64) {
    54  	if len(dst) != len(s) {
    55  		panic(badLength)
    56  	}
    57  	f64.AxpyUnitaryTo(dst, alpha, s, dst)
    58  }
    59  
    60  // AddScaledTo performs dst = y + alpha * s, where alpha is a scalar,
    61  // and dst, y and s are all slices.
    62  // It panics if the slice argument lengths do not match.
    63  //
    64  // At the return of the function, dst[i] = y[i] + alpha * s[i]
    65  func AddScaledTo(dst, y []float64, alpha float64, s []float64) []float64 {
    66  	if len(s) != len(y) {
    67  		panic(badLength)
    68  	}
    69  	if len(dst) != len(y) {
    70  		panic(badDstLength)
    71  	}
    72  	f64.AxpyUnitaryTo(dst, alpha, s, y)
    73  	return dst
    74  }
    75  
    76  // argsort is a helper that implements sort.Interface, as used by
    77  // Argsort and ArgsortStable.
    78  type argsort struct {
    79  	s    []float64
    80  	inds []int
    81  }
    82  
    83  func (a argsort) Len() int {
    84  	return len(a.s)
    85  }
    86  
    87  func (a argsort) Less(i, j int) bool {
    88  	return a.s[i] < a.s[j]
    89  }
    90  
    91  func (a argsort) Swap(i, j int) {
    92  	a.s[i], a.s[j] = a.s[j], a.s[i]
    93  	a.inds[i], a.inds[j] = a.inds[j], a.inds[i]
    94  }
    95  
    96  // Argsort sorts the elements of dst while tracking their original order.
    97  // At the conclusion of Argsort, dst will contain the original elements of dst
    98  // but sorted in increasing order, and inds will contain the original position
    99  // of the elements in the slice such that dst[i] = origDst[inds[i]].
   100  // It panics if the argument lengths do not match.
   101  func Argsort(dst []float64, inds []int) {
   102  	if len(dst) != len(inds) {
   103  		panic(badDstLength)
   104  	}
   105  	for i := range dst {
   106  		inds[i] = i
   107  	}
   108  
   109  	a := argsort{s: dst, inds: inds}
   110  	sort.Sort(a)
   111  }
   112  
   113  // ArgsortStable sorts the elements of dst while tracking their original order and
   114  // keeping the original order of equal elements. At the conclusion of ArgsortStable,
   115  // dst will contain the original elements of dst but sorted in increasing order,
   116  // and inds will contain the original position of the elements in the slice such
   117  // that dst[i] = origDst[inds[i]].
   118  // It panics if the argument lengths do not match.
   119  func ArgsortStable(dst []float64, inds []int) {
   120  	if len(dst) != len(inds) {
   121  		panic(badDstLength)
   122  	}
   123  	for i := range dst {
   124  		inds[i] = i
   125  	}
   126  
   127  	a := argsort{s: dst, inds: inds}
   128  	sort.Stable(a)
   129  }
   130  
   131  // Count applies the function f to every element of s and returns the number
   132  // of times the function returned true.
   133  func Count(f func(float64) bool, s []float64) int {
   134  	var n int
   135  	for _, val := range s {
   136  		if f(val) {
   137  			n++
   138  		}
   139  	}
   140  	return n
   141  }
   142  
   143  // CumProd finds the cumulative product of the first i elements in
   144  // s and puts them in place into the ith element of the
   145  // destination dst.
   146  // It panics if the argument lengths do not match.
   147  //
   148  // At the return of the function, dst[i] = s[i] * s[i-1] * s[i-2] * ...
   149  func CumProd(dst, s []float64) []float64 {
   150  	if len(dst) != len(s) {
   151  		panic(badDstLength)
   152  	}
   153  	if len(dst) == 0 {
   154  		return dst
   155  	}
   156  	return f64.CumProd(dst, s)
   157  }
   158  
   159  // CumSum finds the cumulative sum of the first i elements in
   160  // s and puts them in place into the ith element of the
   161  // destination dst.
   162  // It panics if the argument lengths do not match.
   163  //
   164  // At the return of the function, dst[i] = s[i] + s[i-1] + s[i-2] + ...
   165  func CumSum(dst, s []float64) []float64 {
   166  	if len(dst) != len(s) {
   167  		panic(badDstLength)
   168  	}
   169  	if len(dst) == 0 {
   170  		return dst
   171  	}
   172  	return f64.CumSum(dst, s)
   173  }
   174  
   175  // Distance computes the L-norm of s - t. See Norm for special cases.
   176  // It panics if the slice argument lengths do not match.
   177  func Distance(s, t []float64, L float64) float64 {
   178  	if len(s) != len(t) {
   179  		panic(badLength)
   180  	}
   181  	if len(s) == 0 {
   182  		return 0
   183  	}
   184  	if L == 2 {
   185  		return f64.L2DistanceUnitary(s, t)
   186  	}
   187  	var norm float64
   188  	if L == 1 {
   189  		for i, v := range s {
   190  			norm += math.Abs(t[i] - v)
   191  		}
   192  		return norm
   193  	}
   194  	if math.IsInf(L, 1) {
   195  		for i, v := range s {
   196  			absDiff := math.Abs(t[i] - v)
   197  			if absDiff > norm {
   198  				norm = absDiff
   199  			}
   200  		}
   201  		return norm
   202  	}
   203  	for i, v := range s {
   204  		norm += math.Pow(math.Abs(t[i]-v), L)
   205  	}
   206  	return math.Pow(norm, 1/L)
   207  }
   208  
   209  // Div performs element-wise division dst / s
   210  // and stores the value in dst.
   211  // It panics if the argument lengths do not match.
   212  func Div(dst, s []float64) {
   213  	if len(dst) != len(s) {
   214  		panic(badLength)
   215  	}
   216  	f64.Div(dst, s)
   217  }
   218  
   219  // DivTo performs element-wise division s / t
   220  // and stores the value in dst.
   221  // It panics if the argument lengths do not match.
   222  func DivTo(dst, s, t []float64) []float64 {
   223  	if len(s) != len(t) {
   224  		panic(badLength)
   225  	}
   226  	if len(dst) != len(s) {
   227  		panic(badDstLength)
   228  	}
   229  	return f64.DivTo(dst, s, t)
   230  }
   231  
   232  // Dot computes the dot product of s1 and s2, i.e.
   233  // sum_{i = 1}^N s1[i]*s2[i].
   234  // It panics if the argument lengths do not match.
   235  func Dot(s1, s2 []float64) float64 {
   236  	if len(s1) != len(s2) {
   237  		panic(badLength)
   238  	}
   239  	return f64.DotUnitary(s1, s2)
   240  }
   241  
   242  // Equal returns true when the slices have equal lengths and
   243  // all elements are numerically identical.
   244  func Equal(s1, s2 []float64) bool {
   245  	if len(s1) != len(s2) {
   246  		return false
   247  	}
   248  	for i, val := range s1 {
   249  		if s2[i] != val {
   250  			return false
   251  		}
   252  	}
   253  	return true
   254  }
   255  
   256  // EqualApprox returns true when the slices have equal lengths and
   257  // all element pairs have an absolute tolerance less than tol or a
   258  // relative tolerance less than tol.
   259  func EqualApprox(s1, s2 []float64, tol float64) bool {
   260  	if len(s1) != len(s2) {
   261  		return false
   262  	}
   263  	for i, a := range s1 {
   264  		if !scalar.EqualWithinAbsOrRel(a, s2[i], tol, tol) {
   265  			return false
   266  		}
   267  	}
   268  	return true
   269  }
   270  
   271  // EqualFunc returns true when the slices have the same lengths
   272  // and the function returns true for all element pairs.
   273  func EqualFunc(s1, s2 []float64, f func(float64, float64) bool) bool {
   274  	if len(s1) != len(s2) {
   275  		return false
   276  	}
   277  	for i, val := range s1 {
   278  		if !f(val, s2[i]) {
   279  			return false
   280  		}
   281  	}
   282  	return true
   283  }
   284  
   285  // EqualLengths returns true when all of the slices have equal length,
   286  // and false otherwise. It also returns true when there are no input slices.
   287  func EqualLengths(slices ...[]float64) bool {
   288  	// This length check is needed: http://play.golang.org/p/sdty6YiLhM
   289  	if len(slices) == 0 {
   290  		return true
   291  	}
   292  	l := len(slices[0])
   293  	for i := 1; i < len(slices); i++ {
   294  		if len(slices[i]) != l {
   295  			return false
   296  		}
   297  	}
   298  	return true
   299  }
   300  
   301  // Find applies f to every element of s and returns the indices of the first
   302  // k elements for which the f returns true, or all such elements
   303  // if k < 0.
   304  // Find will reslice inds to have 0 length, and will append
   305  // found indices to inds.
   306  // If k > 0 and there are fewer than k elements in s satisfying f,
   307  // all of the found elements will be returned along with an error.
   308  // At the return of the function, the input inds will be in an undetermined state.
   309  func Find(inds []int, f func(float64) bool, s []float64, k int) ([]int, error) {
   310  	// inds is also returned to allow for calling with nil.
   311  
   312  	// Reslice inds to have zero length.
   313  	inds = inds[:0]
   314  
   315  	// If zero elements requested, can just return.
   316  	if k == 0 {
   317  		return inds, nil
   318  	}
   319  
   320  	// If k < 0, return all of the found indices.
   321  	if k < 0 {
   322  		for i, val := range s {
   323  			if f(val) {
   324  				inds = append(inds, i)
   325  			}
   326  		}
   327  		return inds, nil
   328  	}
   329  
   330  	// Otherwise, find the first k elements.
   331  	nFound := 0
   332  	for i, val := range s {
   333  		if f(val) {
   334  			inds = append(inds, i)
   335  			nFound++
   336  			if nFound == k {
   337  				return inds, nil
   338  			}
   339  		}
   340  	}
   341  	// Finished iterating over the loop, which means k elements were not found.
   342  	return inds, errors.New("floats: insufficient elements found")
   343  }
   344  
   345  // HasNaN returns true when the slice s has any values that are NaN and false
   346  // otherwise.
   347  func HasNaN(s []float64) bool {
   348  	for _, v := range s {
   349  		if math.IsNaN(v) {
   350  			return true
   351  		}
   352  	}
   353  	return false
   354  }
   355  
   356  // LogSpan returns a set of n equally spaced points in log space between,
   357  // l and u where N is equal to len(dst). The first element of the
   358  // resulting dst will be l and the final element of dst will be u.
   359  // It panics if the length of dst is less than 2.
   360  // Note that this call will return NaNs if either l or u are negative, and
   361  // will return all zeros if l or u is zero.
   362  // Also returns the mutated slice dst, so that it can be used in range, like:
   363  //
   364  //     for i, x := range LogSpan(dst, l, u) { ... }
   365  func LogSpan(dst []float64, l, u float64) []float64 {
   366  	Span(dst, math.Log(l), math.Log(u))
   367  	for i := range dst {
   368  		dst[i] = math.Exp(dst[i])
   369  	}
   370  	return dst
   371  }
   372  
   373  // LogSumExp returns the log of the sum of the exponentials of the values in s.
   374  // Panics if s is an empty slice.
   375  func LogSumExp(s []float64) float64 {
   376  	// Want to do this in a numerically stable way which avoids
   377  	// overflow and underflow
   378  	// First, find the maximum value in the slice.
   379  	maxval := Max(s)
   380  	if math.IsInf(maxval, 0) {
   381  		// If it's infinity either way, the logsumexp will be infinity as well
   382  		// returning now avoids NaNs
   383  		return maxval
   384  	}
   385  	var lse float64
   386  	// Compute the sumexp part
   387  	for _, val := range s {
   388  		lse += math.Exp(val - maxval)
   389  	}
   390  	// Take the log and add back on the constant taken out
   391  	return math.Log(lse) + maxval
   392  }
   393  
   394  // Max returns the maximum value in the input slice. If the slice is empty, Max will panic.
   395  func Max(s []float64) float64 {
   396  	return s[MaxIdx(s)]
   397  }
   398  
   399  // MaxIdx returns the index of the maximum value in the input slice. If several
   400  // entries have the maximum value, the first such index is returned.
   401  // It panics if s is zero length.
   402  func MaxIdx(s []float64) int {
   403  	if len(s) == 0 {
   404  		panic(zeroLength)
   405  	}
   406  	max := math.NaN()
   407  	var ind int
   408  	for i, v := range s {
   409  		if math.IsNaN(v) {
   410  			continue
   411  		}
   412  		if v > max || math.IsNaN(max) {
   413  			max = v
   414  			ind = i
   415  		}
   416  	}
   417  	return ind
   418  }
   419  
   420  // Min returns the minimum value in the input slice.
   421  // It panics if s is zero length.
   422  func Min(s []float64) float64 {
   423  	return s[MinIdx(s)]
   424  }
   425  
   426  // MinIdx returns the index of the minimum value in the input slice. If several
   427  // entries have the minimum value, the first such index is returned.
   428  // It panics if s is zero length.
   429  func MinIdx(s []float64) int {
   430  	if len(s) == 0 {
   431  		panic(zeroLength)
   432  	}
   433  	min := math.NaN()
   434  	var ind int
   435  	for i, v := range s {
   436  		if math.IsNaN(v) {
   437  			continue
   438  		}
   439  		if v < min || math.IsNaN(min) {
   440  			min = v
   441  			ind = i
   442  		}
   443  	}
   444  	return ind
   445  }
   446  
   447  // Mul performs element-wise multiplication between dst
   448  // and s and stores the value in dst.
   449  // It panics if the argument lengths do not match.
   450  func Mul(dst, s []float64) {
   451  	if len(dst) != len(s) {
   452  		panic(badLength)
   453  	}
   454  	for i, val := range s {
   455  		dst[i] *= val
   456  	}
   457  }
   458  
   459  // MulTo performs element-wise multiplication between s
   460  // and t and stores the value in dst.
   461  // It panics if the argument lengths do not match.
   462  func MulTo(dst, s, t []float64) []float64 {
   463  	if len(s) != len(t) {
   464  		panic(badLength)
   465  	}
   466  	if len(dst) != len(s) {
   467  		panic(badDstLength)
   468  	}
   469  	for i, val := range t {
   470  		dst[i] = val * s[i]
   471  	}
   472  	return dst
   473  }
   474  
   475  // NearestIdx returns the index of the element in s
   476  // whose value is nearest to v. If several such
   477  // elements exist, the lowest index is returned.
   478  // It panics if s is zero length.
   479  func NearestIdx(s []float64, v float64) int {
   480  	if len(s) == 0 {
   481  		panic(zeroLength)
   482  	}
   483  	switch {
   484  	case math.IsNaN(v):
   485  		return 0
   486  	case math.IsInf(v, 1):
   487  		return MaxIdx(s)
   488  	case math.IsInf(v, -1):
   489  		return MinIdx(s)
   490  	}
   491  	var ind int
   492  	dist := math.NaN()
   493  	for i, val := range s {
   494  		newDist := math.Abs(v - val)
   495  		// A NaN distance will not be closer.
   496  		if math.IsNaN(newDist) {
   497  			continue
   498  		}
   499  		if newDist < dist || math.IsNaN(dist) {
   500  			dist = newDist
   501  			ind = i
   502  		}
   503  	}
   504  	return ind
   505  }
   506  
   507  // NearestIdxForSpan return the index of a hypothetical vector created
   508  // by Span with length n and bounds l and u whose value is closest
   509  // to v. That is, NearestIdxForSpan(n, l, u, v) is equivalent to
   510  // Nearest(Span(make([]float64, n),l,u),v) without an allocation.
   511  // It panics if n is less than two.
   512  func NearestIdxForSpan(n int, l, u float64, v float64) int {
   513  	if n < 2 {
   514  		panic(shortSpan)
   515  	}
   516  	if math.IsNaN(v) {
   517  		return 0
   518  	}
   519  
   520  	// Special cases for Inf and NaN.
   521  	switch {
   522  	case math.IsNaN(l) && !math.IsNaN(u):
   523  		return n - 1
   524  	case math.IsNaN(u):
   525  		return 0
   526  	case math.IsInf(l, 0) && math.IsInf(u, 0):
   527  		if l == u {
   528  			return 0
   529  		}
   530  		if n%2 == 1 {
   531  			if !math.IsInf(v, 0) {
   532  				return n / 2
   533  			}
   534  			if math.Copysign(1, v) == math.Copysign(1, l) {
   535  				return 0
   536  			}
   537  			return n/2 + 1
   538  		}
   539  		if math.Copysign(1, v) == math.Copysign(1, l) {
   540  			return 0
   541  		}
   542  		return n / 2
   543  	case math.IsInf(l, 0):
   544  		if v == l {
   545  			return 0
   546  		}
   547  		return n - 1
   548  	case math.IsInf(u, 0):
   549  		if v == u {
   550  			return n - 1
   551  		}
   552  		return 0
   553  	case math.IsInf(v, -1):
   554  		if l <= u {
   555  			return 0
   556  		}
   557  		return n - 1
   558  	case math.IsInf(v, 1):
   559  		if u <= l {
   560  			return 0
   561  		}
   562  		return n - 1
   563  	}
   564  
   565  	// Special cases for v outside (l, u) and (u, l).
   566  	switch {
   567  	case l < u:
   568  		if v <= l {
   569  			return 0
   570  		}
   571  		if v >= u {
   572  			return n - 1
   573  		}
   574  	case l > u:
   575  		if v >= l {
   576  			return 0
   577  		}
   578  		if v <= u {
   579  			return n - 1
   580  		}
   581  	default:
   582  		return 0
   583  	}
   584  
   585  	// Can't guarantee anything about exactly halfway between
   586  	// because of floating point weirdness.
   587  	return int((float64(n)-1)/(u-l)*(v-l) + 0.5)
   588  }
   589  
   590  // Norm returns the L norm of the slice S, defined as
   591  // (sum_{i=1}^N s[i]^L)^{1/L}
   592  // Special cases:
   593  // L = math.Inf(1) gives the maximum absolute value.
   594  // Does not correctly compute the zero norm (use Count).
   595  func Norm(s []float64, L float64) float64 {
   596  	// Should this complain if L is not positive?
   597  	// Should this be done in log space for better numerical stability?
   598  	//	would be more cost
   599  	//	maybe only if L is high?
   600  	if len(s) == 0 {
   601  		return 0
   602  	}
   603  	if L == 2 {
   604  		return f64.L2NormUnitary(s)
   605  	}
   606  	var norm float64
   607  	if L == 1 {
   608  		for _, val := range s {
   609  			norm += math.Abs(val)
   610  		}
   611  		return norm
   612  	}
   613  	if math.IsInf(L, 1) {
   614  		for _, val := range s {
   615  			norm = math.Max(norm, math.Abs(val))
   616  		}
   617  		return norm
   618  	}
   619  	for _, val := range s {
   620  		norm += math.Pow(math.Abs(val), L)
   621  	}
   622  	return math.Pow(norm, 1/L)
   623  }
   624  
   625  // Prod returns the product of the elements of the slice.
   626  // Returns 1 if len(s) = 0.
   627  func Prod(s []float64) float64 {
   628  	prod := 1.0
   629  	for _, val := range s {
   630  		prod *= val
   631  	}
   632  	return prod
   633  }
   634  
   635  // Reverse reverses the order of elements in the slice.
   636  func Reverse(s []float64) {
   637  	for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
   638  		s[i], s[j] = s[j], s[i]
   639  	}
   640  }
   641  
   642  // Same returns true when the input slices have the same length and all
   643  // elements have the same value with NaN treated as the same.
   644  func Same(s, t []float64) bool {
   645  	if len(s) != len(t) {
   646  		return false
   647  	}
   648  	for i, v := range s {
   649  		w := t[i]
   650  		if v != w && !(math.IsNaN(v) && math.IsNaN(w)) {
   651  			return false
   652  		}
   653  	}
   654  	return true
   655  }
   656  
   657  // Scale multiplies every element in dst by the scalar c.
   658  func Scale(c float64, dst []float64) {
   659  	if len(dst) > 0 {
   660  		f64.ScalUnitary(c, dst)
   661  	}
   662  }
   663  
   664  // ScaleTo multiplies the elements in s by c and stores the result in dst.
   665  // It panics if the slice argument lengths do not match.
   666  func ScaleTo(dst []float64, c float64, s []float64) []float64 {
   667  	if len(dst) != len(s) {
   668  		panic(badDstLength)
   669  	}
   670  	if len(dst) > 0 {
   671  		f64.ScalUnitaryTo(dst, c, s)
   672  	}
   673  	return dst
   674  }
   675  
   676  // Span returns a set of N equally spaced points between l and u, where N
   677  // is equal to the length of the destination. The first element of the destination
   678  // is l, the final element of the destination is u.
   679  // It panics if the length of dst is less than 2.
   680  //
   681  // Span also returns the mutated slice dst, so that it can be used in range expressions,
   682  // like:
   683  //
   684  //     for i, x := range Span(dst, l, u) { ... }
   685  func Span(dst []float64, l, u float64) []float64 {
   686  	n := len(dst)
   687  	if n < 2 {
   688  		panic(shortSpan)
   689  	}
   690  
   691  	// Special cases for Inf and NaN.
   692  	switch {
   693  	case math.IsNaN(l):
   694  		for i := range dst[:len(dst)-1] {
   695  			dst[i] = math.NaN()
   696  		}
   697  		dst[len(dst)-1] = u
   698  		return dst
   699  	case math.IsNaN(u):
   700  		for i := range dst[1:] {
   701  			dst[i+1] = math.NaN()
   702  		}
   703  		dst[0] = l
   704  		return dst
   705  	case math.IsInf(l, 0) && math.IsInf(u, 0):
   706  		for i := range dst[:len(dst)/2] {
   707  			dst[i] = l
   708  			dst[len(dst)-i-1] = u
   709  		}
   710  		if len(dst)%2 == 1 {
   711  			if l != u {
   712  				dst[len(dst)/2] = 0
   713  			} else {
   714  				dst[len(dst)/2] = l
   715  			}
   716  		}
   717  		return dst
   718  	case math.IsInf(l, 0):
   719  		for i := range dst[:len(dst)-1] {
   720  			dst[i] = l
   721  		}
   722  		dst[len(dst)-1] = u
   723  		return dst
   724  	case math.IsInf(u, 0):
   725  		for i := range dst[1:] {
   726  			dst[i+1] = u
   727  		}
   728  		dst[0] = l
   729  		return dst
   730  	}
   731  
   732  	step := (u - l) / float64(n-1)
   733  	for i := range dst {
   734  		dst[i] = l + step*float64(i)
   735  	}
   736  	return dst
   737  }
   738  
   739  // Sub subtracts, element-wise, the elements of s from dst.
   740  // It panics if the argument lengths do not match.
   741  func Sub(dst, s []float64) {
   742  	if len(dst) != len(s) {
   743  		panic(badLength)
   744  	}
   745  	f64.AxpyUnitaryTo(dst, -1, s, dst)
   746  }
   747  
   748  // SubTo subtracts, element-wise, the elements of t from s and
   749  // stores the result in dst.
   750  // It panics if the argument lengths do not match.
   751  func SubTo(dst, s, t []float64) []float64 {
   752  	if len(s) != len(t) {
   753  		panic(badLength)
   754  	}
   755  	if len(dst) != len(s) {
   756  		panic(badDstLength)
   757  	}
   758  	f64.AxpyUnitaryTo(dst, -1, t, s)
   759  	return dst
   760  }
   761  
   762  // Sum returns the sum of the elements of the slice.
   763  func Sum(s []float64) float64 {
   764  	return f64.Sum(s)
   765  }
   766  
   767  // Within returns the first index i where s[i] <= v < s[i+1]. Within panics if:
   768  //  - len(s) < 2
   769  //  - s is not sorted
   770  func Within(s []float64, v float64) int {
   771  	if len(s) < 2 {
   772  		panic(shortSpan)
   773  	}
   774  	if !sort.Float64sAreSorted(s) {
   775  		panic("floats: input slice not sorted")
   776  	}
   777  	if v < s[0] || v >= s[len(s)-1] || math.IsNaN(v) {
   778  		return -1
   779  	}
   780  	for i, f := range s[1:] {
   781  		if v < f {
   782  			return i
   783  		}
   784  	}
   785  	return -1
   786  }
   787  
   788  // SumCompensated returns the sum of the elements of the slice calculated with greater
   789  // accuracy than Sum at the expense of additional computation.
   790  func SumCompensated(s []float64) float64 {
   791  	// SumCompensated uses an improved version of Kahan's compensated
   792  	// summation algorithm proposed by Neumaier.
   793  	// See https://en.wikipedia.org/wiki/Kahan_summation_algorithm for details.
   794  	var sum, c float64
   795  	for _, x := range s {
   796  		// This type conversion is here to prevent a sufficiently smart compiler
   797  		// from optimising away these operations.
   798  		t := float64(sum + x)
   799  		if math.Abs(sum) >= math.Abs(x) {
   800  			c += (sum - t) + x
   801  		} else {
   802  			c += (x - t) + sum
   803  		}
   804  		sum = t
   805  	}
   806  	return sum + c
   807  }