go.mway.dev/math@v0.3.4-0.20220903004814-3c8fcf9df0ca/math.go (about)

     1  // Copyright (c) 2022 Matt Way
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to
     5  // deal in the Software without restriction, including without limitation the
     6  // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
     7  // sell copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    18  // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
    19  // IN THE THE SOFTWARE.
    20  
    21  // Package math provides math-related utilities.
    22  package math
    23  
    24  import (
    25  	"math"
    26  	_ "unsafe" // for go:linkname
    27  
    28  	"golang.org/x/exp/constraints"
    29  )
    30  
    31  // Numeric is a constraint that permits any integer or floating point number.
    32  type Numeric interface {
    33  	constraints.Integer | constraints.Float
    34  }
    35  
    36  // SignedNumeric is a constraint that permits any signed integer or floating
    37  // point number.
    38  type SignedNumeric interface {
    39  	constraints.Signed | constraints.Float
    40  }
    41  
    42  // Unsigned32 is a constraint that permits any type that can fit a 32-bit
    43  // unsigned integer.
    44  type Unsigned32 interface {
    45  	~int | ~int64 | ~uint | ~uint32 | ~uint64 | constraints.Float
    46  }
    47  
    48  // Abs returns the absolute value of the given signed number.
    49  func Abs[T SignedNumeric](x T) T {
    50  	if x < 0 {
    51  		return -x
    52  	}
    53  
    54  	return x
    55  }
    56  
    57  // Min returns the minimum value of the two given numbers.
    58  func Min[T Numeric](x T, y T) T {
    59  	return MinN(x, y)
    60  }
    61  
    62  // MinN returns the minimum value of the given numbers.
    63  //
    64  //nolint:gocyclo
    65  func MinN[T Numeric](x ...T) T {
    66  	switch len(x) {
    67  	case 0:
    68  		return 0
    69  	case 1:
    70  		return x[0]
    71  	case 2:
    72  		if x[0] < x[1] {
    73  			return x[0]
    74  		}
    75  		return x[1]
    76  	case 3:
    77  		y := x[0]
    78  		if x[1] < y {
    79  			y = x[1]
    80  		}
    81  		if x[2] < y {
    82  			y = x[2]
    83  		}
    84  		return y
    85  	case 4:
    86  		y := x[0]
    87  		if x[1] < y {
    88  			y = x[1]
    89  		}
    90  		if x[2] < y {
    91  			y = x[2]
    92  		}
    93  		if x[3] < y {
    94  			y = x[3]
    95  		}
    96  		return y
    97  	default:
    98  		min := x[0]
    99  		for _, n := range x[1:] {
   100  			if n < min {
   101  				min = n
   102  			}
   103  		}
   104  
   105  		return min
   106  	}
   107  }
   108  
   109  // Max returns the maximum value of the two given numbers.
   110  func Max[T Numeric](x T, y T) T {
   111  	return MaxN(x, y)
   112  }
   113  
   114  // MaxN returns the maximum value of the given numbers.
   115  //
   116  //nolint:gocyclo
   117  func MaxN[T Numeric](x ...T) T {
   118  	switch len(x) {
   119  	case 0:
   120  		return 0
   121  	case 1:
   122  		return x[0]
   123  	case 2:
   124  		if x[0] > x[1] {
   125  			return x[0]
   126  		}
   127  		return x[1]
   128  	case 3:
   129  		y := x[0]
   130  		if x[1] > y {
   131  			y = x[1]
   132  		}
   133  		if x[2] > y {
   134  			y = x[2]
   135  		}
   136  		return y
   137  	case 4:
   138  		y := x[0]
   139  		if x[1] > y {
   140  			y = x[1]
   141  		}
   142  		if x[2] > y {
   143  			y = x[2]
   144  		}
   145  		if x[3] > y {
   146  			y = x[3]
   147  		}
   148  		return y
   149  	default:
   150  		max := x[0]
   151  		for _, n := range x[1:] {
   152  			if n > max {
   153  				max = n
   154  			}
   155  		}
   156  
   157  		return max
   158  	}
   159  }
   160  
   161  // Mean returns the truncated average value of all given numbers.
   162  func Mean[T Numeric](x ...T) T {
   163  	var (
   164  		size  = len(x)
   165  		total T
   166  	)
   167  
   168  	for len(x) >= 8 {
   169  		total += x[0] + x[1] + x[2] + x[3] + x[4] + x[5] + x[6] + x[7]
   170  		x = x[8:]
   171  	}
   172  
   173  	for _, n := range x {
   174  		total += n
   175  	}
   176  
   177  	return total / T(size)
   178  }
   179  
   180  // MeanFloat64 returns the average value of all given numbers.
   181  func MeanFloat64[T Numeric](x ...T) float64 {
   182  	var (
   183  		size  = len(x)
   184  		total T
   185  	)
   186  
   187  	for len(x) >= 8 {
   188  		total += x[0] + x[1] + x[2] + x[3] + x[4] + x[5] + x[6] + x[7]
   189  		x = x[8:]
   190  	}
   191  
   192  	for _, n := range x {
   193  		total += n
   194  	}
   195  
   196  	return float64(total) / float64(size)
   197  }
   198  
   199  // Clamp clamps the given value between [min,max] (inclusive).
   200  func Clamp[T Numeric](x T, min T, max T) T {
   201  	switch {
   202  	case x > max:
   203  		return max
   204  	case x < min:
   205  		return min
   206  	default:
   207  		return x
   208  	}
   209  }
   210  
   211  // ClampMin clamps the given value such that it is not less than min (inclusive).
   212  func ClampMin[T Numeric](x T, min T) T {
   213  	switch {
   214  	case x < min:
   215  		return min
   216  	default:
   217  		return x
   218  	}
   219  }
   220  
   221  // ClampMax clamps the given value such that it is not greater than max
   222  // (inclusive).
   223  func ClampMax[T Numeric](x T, max T) T {
   224  	switch {
   225  	case x > max:
   226  		return max
   227  	default:
   228  		return x
   229  	}
   230  }
   231  
   232  // NextPowerOf2 returns the next T greater than x that is a power of 2. If x is
   233  // a power of 2 itself, x is returned.
   234  func NextPowerOf2[T Numeric](x T) T {
   235  	if x <= 1 {
   236  		return 1
   237  	}
   238  
   239  	var (
   240  		l2 = math.Log2(float64(x))
   241  		hi = T(int(1) << int(math.Ceil(l2)))
   242  	)
   243  
   244  	return hi
   245  }
   246  
   247  // ClosestPowerOf2 returns the next T greater than x that is a power of 2.
   248  func ClosestPowerOf2[T Numeric](x T) T {
   249  	if x <= 1 {
   250  		return 1
   251  	}
   252  
   253  	var (
   254  		l2 = math.Log2(float64(x))
   255  		lo = T(int(1) << int(math.Floor(l2)))
   256  		hi = T(int(1) << int(math.Ceil(l2)))
   257  	)
   258  
   259  	if hi-x > x-lo {
   260  		return lo
   261  	}
   262  
   263  	return hi
   264  }
   265  
   266  // Precision truncates x to the given precision. If precision is < 0, x is
   267  // returned unchanged; if x == 0, x is rounded to the nearest integer;
   268  // otherwise, the precision of x is changed and the resulting value rounded.
   269  func Precision[T constraints.Float](x T, precision int) T {
   270  	if precision < 0 {
   271  		return x
   272  	}
   273  
   274  	if precision == 0 {
   275  		return T(math.Round(float64(x)))
   276  	}
   277  
   278  	var (
   279  		coeff = math.Pow(10.0, float64(precision))
   280  		tmp   = math.Round(float64(x) * coeff)
   281  	)
   282  
   283  	return T(tmp / coeff)
   284  }
   285  
   286  // Fastrand returns a pseudorandom T in the range [0, 1<<32-1).
   287  func Fastrand[T Unsigned32]() T {
   288  	return T(fastrand())
   289  }
   290  
   291  // Fastrandn returns a pseudorandom T in the range [0, min(x, 1<<32-1)). If x
   292  // is greater than 1<<32-1, it will be clamped.
   293  func Fastrandn[T Unsigned32](x T) T {
   294  	return T(fastrandn(uint32(Clamp(x, 0, math.MaxUint32))))
   295  }
   296  
   297  //go:linkname fastrand runtime.fastrand
   298  func fastrand() uint32
   299  
   300  //go:linkname fastrandn runtime.fastrandn
   301  func fastrandn(uint32) uint32