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