github.com/arnodel/golua@v0.0.0-20230215163904-e0b5347eaaa1/lib/mathlib/mathlib.go (about) 1 package mathlib 2 3 import ( 4 crypto "crypto/rand" 5 "encoding/binary" 6 "errors" 7 "math" 8 "math/rand" 9 10 "github.com/arnodel/golua/lib/packagelib" 11 rt "github.com/arnodel/golua/runtime" 12 ) 13 14 var LibLoader = packagelib.Loader{ 15 Load: load, 16 Name: "math", 17 } 18 19 func load(r *rt.Runtime) (rt.Value, func()) { 20 pkg := rt.NewTable() 21 r.SetEnv(pkg, "huge", rt.FloatValue(math.Inf(1))) 22 r.SetEnv(pkg, "maxinteger", rt.IntValue(math.MaxInt64)) 23 r.SetEnv(pkg, "mininteger", rt.IntValue(math.MinInt64)) 24 r.SetEnv(pkg, "pi", rt.FloatValue(math.Pi)) 25 26 rt.SolemnlyDeclareCompliance( 27 rt.ComplyCpuSafe|rt.ComplyMemSafe|rt.ComplyTimeSafe|rt.ComplyIoSafe, 28 29 r.SetEnvGoFunc(pkg, "abs", abs, 1, false), 30 r.SetEnvGoFunc(pkg, "acos", acos, 1, false), 31 r.SetEnvGoFunc(pkg, "asin", asin, 1, false), 32 r.SetEnvGoFunc(pkg, "atan", atan, 2, false), 33 r.SetEnvGoFunc(pkg, "ceil", ceil, 1, false), 34 r.SetEnvGoFunc(pkg, "cos", cos, 1, false), 35 r.SetEnvGoFunc(pkg, "deg", deg, 1, false), 36 r.SetEnvGoFunc(pkg, "exp", exp, 1, false), 37 r.SetEnvGoFunc(pkg, "floor", floor, 1, false), 38 r.SetEnvGoFunc(pkg, "fmod", fmod, 2, false), 39 r.SetEnvGoFunc(pkg, "log", log, 2, false), 40 r.SetEnvGoFunc(pkg, "max", max, 1, true), 41 r.SetEnvGoFunc(pkg, "min", min, 1, true), 42 r.SetEnvGoFunc(pkg, "modf", modf, 1, false), 43 r.SetEnvGoFunc(pkg, "rad", rad, 1, false), 44 r.SetEnvGoFunc(pkg, "random", random, 2, false), 45 r.SetEnvGoFunc(pkg, "randomseed", randomseed, 2, false), 46 r.SetEnvGoFunc(pkg, "sin", sin, 1, false), 47 r.SetEnvGoFunc(pkg, "sqrt", sqrt, 1, false), 48 r.SetEnvGoFunc(pkg, "tan", tan, 1, false), 49 r.SetEnvGoFunc(pkg, "tointeger", tointeger, 1, false), 50 r.SetEnvGoFunc(pkg, "type", typef, 1, false), 51 r.SetEnvGoFunc(pkg, "ult", ult, 2, false), 52 ) 53 54 return rt.TableValue(pkg), nil 55 } 56 57 func abs(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 58 if err := c.Check1Arg(); err != nil { 59 return nil, err 60 } 61 next := c.Next() 62 n, f, tp := rt.ToNumber(c.Arg(0)) 63 switch tp { 64 case rt.IsInt: 65 if n < 0 { 66 n = -n 67 } 68 t.Push1(next, rt.IntValue(n)) 69 case rt.IsFloat: 70 t.Push1(next, rt.FloatValue(math.Abs(f))) 71 default: 72 return nil, errors.New("#1 must be a number") 73 } 74 return next, nil 75 } 76 77 func acos(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 78 if err := c.Check1Arg(); err != nil { 79 return nil, err 80 } 81 x, err := c.FloatArg(0) 82 if err != nil { 83 return nil, err 84 } 85 y := rt.FloatValue(math.Acos(x)) 86 return c.PushingNext1(t.Runtime, y), nil 87 } 88 89 func asin(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 90 if err := c.Check1Arg(); err != nil { 91 return nil, err 92 } 93 x, err := c.FloatArg(0) 94 if err != nil { 95 return nil, err 96 } 97 y := rt.FloatValue(math.Asin(x)) 98 return c.PushingNext1(t.Runtime, y), nil 99 } 100 101 func atan(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 102 if err := c.Check1Arg(); err != nil { 103 return nil, err 104 } 105 y, err := c.FloatArg(0) 106 if err != nil { 107 return nil, err 108 } 109 var x float64 = 1 110 if c.NArgs() >= 2 { 111 x, err = c.FloatArg(1) 112 if err != nil { 113 return nil, err 114 } 115 } 116 z := rt.FloatValue(math.Atan2(y, x)) 117 return c.PushingNext1(t.Runtime, z), nil 118 } 119 120 func ceil(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 121 if err := c.Check1Arg(); err != nil { 122 return nil, err 123 } 124 next := c.Next() 125 n, f, tp := rt.ToNumber(c.Arg(0)) 126 switch tp { 127 case rt.IsInt: 128 t.Push1(next, rt.IntValue(n)) 129 case rt.IsFloat: 130 f = math.Ceil(f) 131 n = int64(f) 132 if float64(n) == f { 133 t.Push1(next, rt.IntValue(n)) 134 } else { 135 t.Push1(next, rt.FloatValue(f)) 136 } 137 default: 138 return nil, errors.New("#1 must be a number") 139 } 140 return next, nil 141 } 142 143 func cos(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 144 if err := c.Check1Arg(); err != nil { 145 return nil, err 146 } 147 x, err := c.FloatArg(0) 148 if err != nil { 149 return nil, err 150 } 151 y := rt.FloatValue(math.Cos(x)) 152 return c.PushingNext1(t.Runtime, y), nil 153 } 154 155 func deg(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 156 if err := c.Check1Arg(); err != nil { 157 return nil, err 158 } 159 x, err := c.FloatArg(0) 160 if err != nil { 161 return nil, err 162 } 163 y := rt.FloatValue(x * 180 / math.Pi) 164 return c.PushingNext1(t.Runtime, y), nil 165 } 166 167 func exp(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 168 if err := c.Check1Arg(); err != nil { 169 return nil, err 170 } 171 x, err := c.FloatArg(0) 172 if err != nil { 173 return nil, err 174 } 175 y := rt.FloatValue(math.Exp(x)) 176 return c.PushingNext1(t.Runtime, y), nil 177 } 178 179 func floor(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 180 if err := c.Check1Arg(); err != nil { 181 return nil, err 182 } 183 next := c.Next() 184 n, f, tp := rt.ToNumber(c.Arg(0)) 185 switch tp { 186 case rt.IsInt: 187 t.Push1(next, rt.IntValue(n)) 188 case rt.IsFloat: 189 f = math.Floor(f) 190 n = int64(f) 191 if float64(n) == f { 192 t.Push1(next, rt.IntValue(n)) 193 } else { 194 t.Push1(next, rt.FloatValue(f)) 195 } 196 default: 197 return nil, errors.New("#1 must be a number") 198 } 199 return next, nil 200 } 201 202 func fmod(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 203 if err := c.CheckNArgs(2); err != nil { 204 return nil, err 205 } 206 x, _ := rt.ToNumberValue(c.Arg(0)) 207 y, _ := rt.ToNumberValue(c.Arg(1)) 208 res, ok, err := rt.Mod(x, y) 209 if !ok { 210 err = errors.New("expected numeric arguments") 211 } 212 if err != nil { 213 return nil, err 214 } 215 return c.PushingNext1(t.Runtime, res), nil 216 } 217 218 func log(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 219 if err := c.Check1Arg(); err != nil { 220 return nil, err 221 } 222 x, err := c.FloatArg(0) 223 if err != nil { 224 return nil, err 225 } 226 y := math.Log(x) 227 if c.NArgs() >= 2 { 228 b, err := c.FloatArg(1) 229 if err != nil { 230 return nil, err 231 } 232 y = y / math.Log(b) 233 } 234 return c.PushingNext1(t.Runtime, rt.FloatValue(y)), nil 235 } 236 237 func max(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 238 if err := c.Check1Arg(); err != nil { 239 return nil, err 240 } 241 x := c.Arg(0) 242 for _, y := range c.Etc() { 243 lt, err := rt.Lt(t, x, y) 244 if err != nil { 245 return nil, err 246 } 247 if lt { 248 x = y 249 } 250 } 251 return c.PushingNext1(t.Runtime, x), nil 252 } 253 254 func min(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 255 if err := c.Check1Arg(); err != nil { 256 return nil, err 257 } 258 x := c.Arg(0) 259 for _, y := range c.Etc() { 260 lt, err := rt.Lt(t, y, x) 261 if err != nil { 262 return nil, err 263 } 264 if lt { 265 x = y 266 } 267 } 268 return c.PushingNext1(t.Runtime, x), nil 269 } 270 271 func modf(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 272 if err := c.Check1Arg(); err != nil { 273 return nil, err 274 } 275 next := c.Next() 276 arg := c.Arg(0) 277 if _, ok := arg.TryInt(); ok { 278 t.Push1(next, arg) 279 t.Push1(next, rt.FloatValue(0)) 280 return next, nil 281 } 282 283 x, ok := arg.TryFloat() 284 if !ok { 285 return nil, errors.New("#1 must be numeric") 286 } 287 var i, f float64 288 if math.IsInf(x, 0) { 289 i, f = x, 0 290 } else { 291 i, f = math.Modf(x) 292 } 293 t.Push1(next, rt.FloatValue(i)) 294 t.Push1(next, rt.FloatValue(f)) 295 return next, nil 296 } 297 298 func rad(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 299 if err := c.Check1Arg(); err != nil { 300 return nil, err 301 } 302 x, err := c.FloatArg(0) 303 if err != nil { 304 return nil, err 305 } 306 y := rt.FloatValue(x * math.Pi / 180) 307 return c.PushingNext1(t.Runtime, y), nil 308 } 309 310 // TODO: have a per runtime random generator 311 func random(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 312 var ( 313 err error 314 m int64 = 1 315 n int64 316 ) 317 switch c.NArgs() { 318 case 0: 319 return c.PushingNext1(t.Runtime, rt.FloatValue(rand.Float64())), nil 320 case 1: 321 n, err = c.IntArg(0) 322 // Special case, new in Lua 5.4: math.random(0) returns a uniform integer. 323 if n == 0 { 324 return c.PushingNext1(t.Runtime, rt.IntValue(int64(rand.Uint64()))), nil 325 } 326 case 2: 327 m, err = c.IntArg(0) 328 if err == nil { 329 n, err = c.IntArg(1) 330 } 331 } 332 if err != nil { 333 return nil, err 334 } 335 if m > n { 336 return nil, errors.New("#2 must be >= #1") 337 } 338 var r int64 339 if m <= 0 && m+math.MaxInt64 < n { 340 // There's >= 50% chance the loop stops at each iteration so we're OK! 341 for { 342 r = int64(rand.Uint64()) 343 if r >= m && r <= n { 344 break 345 } 346 } 347 } else if m+math.MaxInt64 == n { 348 r = rand.Int63() 349 } else { 350 r = rand.Int63n(n - m + 1) 351 } 352 return c.PushingNext1(t.Runtime, rt.IntValue(m+r)), nil 353 } 354 355 func randomseed(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 356 var ( 357 seed int64 358 err error 359 ) 360 switch c.NArgs() { 361 case 0: 362 // We need something as random as possible to make a seed. 363 readErr := binary.Read(crypto.Reader, binary.LittleEndian, &seed) 364 if readErr != nil { 365 return nil, errors.New("unable to get random seed") 366 } 367 case 1: 368 seed, err = c.IntArg(0) 369 if err != nil { 370 return nil, err 371 } 372 case 2: 373 seed, err = c.IntArg(0) 374 if err != nil { 375 return nil, err 376 } 377 seed2, err := c.IntArg(1) 378 if err != nil { 379 return nil, err 380 } 381 // In Go the seed is only 64 bits so we mangle the seeds 382 seed ^= seed2 383 } 384 rand.Seed(seed) 385 return c.PushingNext(t.Runtime, rt.IntValue(seed), rt.IntValue(0)), nil 386 } 387 388 func sin(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 389 if err := c.Check1Arg(); err != nil { 390 return nil, err 391 } 392 x, err := c.FloatArg(0) 393 if err != nil { 394 return nil, err 395 } 396 y := rt.FloatValue(math.Sin(x)) 397 return c.PushingNext1(t.Runtime, y), nil 398 } 399 400 func sqrt(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 401 if err := c.Check1Arg(); err != nil { 402 return nil, err 403 } 404 x, err := c.FloatArg(0) 405 if err != nil { 406 return nil, err 407 } 408 y := rt.FloatValue(math.Sqrt(x)) 409 return c.PushingNext1(t.Runtime, y), nil 410 } 411 412 func tan(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 413 if err := c.Check1Arg(); err != nil { 414 return nil, err 415 } 416 x, err := c.FloatArg(0) 417 if err != nil { 418 return nil, err 419 } 420 y := rt.FloatValue(math.Tan(x)) 421 return c.PushingNext1(t.Runtime, y), nil 422 } 423 424 func tointeger(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 425 if err := c.Check1Arg(); err != nil { 426 return nil, err 427 } 428 n, err := c.IntArg(0) 429 if err != nil { 430 return c.PushingNext1(t.Runtime, rt.NilValue), nil 431 } 432 return c.PushingNext1(t.Runtime, rt.IntValue(n)), nil 433 } 434 435 func typef(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 436 if err := c.Check1Arg(); err != nil { 437 return nil, err 438 } 439 var tp rt.Value 440 switch c.Arg(0).NumberType() { 441 case rt.IntType: 442 tp = rt.StringValue("integer") 443 case rt.FloatType: 444 tp = rt.StringValue("float") 445 } 446 return c.PushingNext1(t.Runtime, tp), nil 447 } 448 449 func ult(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 450 if err := c.CheckNArgs(2); err != nil { 451 return nil, err 452 } 453 x, err := c.IntArg(0) 454 var y int64 455 if err == nil { 456 y, err = c.IntArg(1) 457 } 458 if err != nil { 459 return nil, err 460 } 461 lt := rt.BoolValue(uint64(x) < uint64(y)) 462 return c.PushingNext1(t.Runtime, lt), nil 463 }