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