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  }