github.com/arnodel/golua@v0.0.0-20230215163904-e0b5347eaaa1/runtime/arith.go (about)

     1  package runtime
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"math"
     7  )
     8  
     9  // Unm returns (z, true) where z is the value representing -x if x is a number,
    10  // else (NilValue, false).
    11  func Unm(x Value) (Value, bool) {
    12  	switch x.iface.(type) {
    13  	case int64:
    14  		return IntValue(-x.AsInt()), true
    15  	case float64:
    16  		return FloatValue(-x.AsFloat()), true
    17  	default:
    18  		return NilValue, false
    19  	}
    20  }
    21  
    22  // Add returns (z, true) where z is the value representing x+y if x and y are
    23  // numbers, else (NilValue, false).
    24  func Add(x, y Value) (Value, bool) {
    25  	switch x.iface.(type) {
    26  	case int64:
    27  		switch y.iface.(type) {
    28  		case int64:
    29  			return IntValue(x.AsInt() + y.AsInt()), true
    30  		case float64:
    31  			return FloatValue(float64(x.AsInt()) + y.AsFloat()), true
    32  		}
    33  	case float64:
    34  		switch y.iface.(type) {
    35  		case int64:
    36  			return FloatValue(x.AsFloat() + float64(y.AsInt())), true
    37  		case float64:
    38  			return FloatValue(x.AsFloat() + y.AsFloat()), true
    39  		}
    40  	}
    41  	return NilValue, false
    42  }
    43  
    44  // Sub returns (z, true) where z is the value representing x-y if x and y are
    45  // numbers, else (NilValue, false).
    46  func Sub(x, y Value) (Value, bool) {
    47  	switch x.iface.(type) {
    48  	case int64:
    49  		switch y.iface.(type) {
    50  		case int64:
    51  			return IntValue(x.AsInt() - y.AsInt()), true
    52  		case float64:
    53  			return FloatValue(float64(x.AsInt()) - y.AsFloat()), true
    54  		}
    55  	case float64:
    56  		switch y.iface.(type) {
    57  		case int64:
    58  			return FloatValue(x.AsFloat() - float64(y.AsInt())), true
    59  		case float64:
    60  			return FloatValue(x.AsFloat() - y.AsFloat()), true
    61  		}
    62  	}
    63  	return NilValue, false
    64  }
    65  
    66  // Mul returns (z, true) where z is the value representing x*y if x and y are
    67  // numbers, else (NilValue, false).
    68  func Mul(x, y Value) (Value, bool) {
    69  	switch x.iface.(type) {
    70  	case int64:
    71  		switch y.iface.(type) {
    72  		case int64:
    73  			return IntValue(x.AsInt() * y.AsInt()), true
    74  		case float64:
    75  			return FloatValue(float64(x.AsInt()) * y.AsFloat()), true
    76  		}
    77  	case float64:
    78  		switch y.iface.(type) {
    79  		case int64:
    80  			return FloatValue(x.AsFloat() * float64(y.AsInt())), true
    81  		case float64:
    82  			return FloatValue(x.AsFloat() * y.AsFloat()), true
    83  		}
    84  	}
    85  	return NilValue, false
    86  }
    87  
    88  // Div returns (z, true) where z is the (float) value representing x/y if x and
    89  // y are numbers, else (NilValue, false).
    90  func Div(x, y Value) (Value, bool) {
    91  	switch x.iface.(type) {
    92  	case int64:
    93  		switch y.iface.(type) {
    94  		case int64:
    95  			return FloatValue(float64(x.AsInt()) / float64(y.AsInt())), true
    96  		case float64:
    97  			return FloatValue(float64(x.AsInt()) / y.AsFloat()), true
    98  		}
    99  	case float64:
   100  		switch y.iface.(type) {
   101  		case int64:
   102  			return FloatValue(x.AsFloat() / float64(y.AsInt())), true
   103  		case float64:
   104  			return FloatValue(x.AsFloat() / y.AsFloat()), true
   105  		}
   106  	}
   107  	return NilValue, false
   108  }
   109  
   110  func floordivInt(x, y int64) int64 {
   111  	r := x % y
   112  	q := x / y
   113  	if r != 0 && (r < 0) != (y < 0) {
   114  		q--
   115  	}
   116  	return q
   117  }
   118  
   119  func floordivFloat(x, y float64) float64 {
   120  	return math.Floor(x / y)
   121  }
   122  
   123  // Div returns (z, true, nil) where z is the (integer) value representing x//y
   124  // if x and y are numbers and y != 0, if y == 0 it returns (NilValue, true,
   125  // div_by_zero_error), else (NilValue, false, nil) if x or y is not a number.
   126  func Idiv(x Value, y Value) (Value, bool, error) {
   127  	switch x.iface.(type) {
   128  	case int64:
   129  		switch y.iface.(type) {
   130  		case int64:
   131  			ny := y.AsInt()
   132  			if ny == 0 {
   133  				return NilValue, true, errors.New("attempt to divide by zero")
   134  			}
   135  			return IntValue(floordivInt(x.AsInt(), ny)), true, nil
   136  		case float64:
   137  			return FloatValue(floordivFloat(float64(x.AsInt()), y.AsFloat())), true, nil
   138  		}
   139  	case float64:
   140  		switch y.iface.(type) {
   141  		case int64:
   142  			return FloatValue(floordivFloat(x.AsFloat(), float64(y.AsInt()))), true, nil
   143  		case float64:
   144  			return FloatValue(floordivFloat(x.AsFloat(), y.AsFloat())), true, nil
   145  		}
   146  	}
   147  	return NilValue, false, nil
   148  }
   149  
   150  func modInt(x, y int64) int64 {
   151  	r := x % y
   152  	if r != 0 && (r < 0) != (y < 0) {
   153  		r += y
   154  	}
   155  	return r
   156  }
   157  
   158  func modFloat(x, y float64) float64 {
   159  	r := math.Mod(x, y)
   160  	if r != 0 && (r < 0) != (y < 0) {
   161  		r += y
   162  	}
   163  	return r
   164  }
   165  
   166  // Mod returns (z, true, nil) where z is the (integer or float) value
   167  // representing x%y if x and y are numbers and y != 0, if y == 0 it returns
   168  // (NilValue, true, mod_by_zero_error), else (NilValue, false, nil) if x or y is
   169  // not a number.
   170  func Mod(x Value, y Value) (Value, bool, error) {
   171  	switch x.iface.(type) {
   172  	case int64:
   173  		switch y.iface.(type) {
   174  		case int64:
   175  			ny := y.AsInt()
   176  			if ny == 0 {
   177  				return NilValue, true, errors.New("attempt to perform 'n%0'")
   178  			}
   179  			return IntValue(modInt(x.AsInt(), ny)), true, nil
   180  		case float64:
   181  			return FloatValue(modFloat(float64(x.AsInt()), y.AsFloat())), true, nil
   182  		}
   183  	case float64:
   184  		switch y.iface.(type) {
   185  		case int64:
   186  			return FloatValue(modFloat(x.AsFloat(), float64(y.AsInt()))), true, nil
   187  		case float64:
   188  			return FloatValue(modFloat(x.AsFloat(), y.AsFloat())), true, nil
   189  		}
   190  	}
   191  	return NilValue, false, nil
   192  }
   193  
   194  func powFloat(x, y float64) float64 {
   195  	return math.Pow(x, y)
   196  }
   197  
   198  // Pow returns (z, true) where z is the (float) value representing x^y if x and
   199  // y are numbers, else (NilValue, false).
   200  func Pow(x, y Value) (Value, bool) {
   201  	var fx, fy float64
   202  	switch x.iface.(type) {
   203  	case int64:
   204  		fx = float64(x.AsInt())
   205  	case float64:
   206  		fx = x.AsFloat()
   207  	default:
   208  		return NilValue, false
   209  	}
   210  	switch y.iface.(type) {
   211  	case int64:
   212  		fy = float64(y.AsInt())
   213  	case float64:
   214  		fy = y.AsFloat()
   215  	default:
   216  		return NilValue, false
   217  	}
   218  	return FloatValue(powFloat(fx, fy)), true
   219  }
   220  
   221  func binaryArithFallback(t *Thread, op string, x, y Value) (Value, error) {
   222  	res, err, ok := metabin(t, op, x, y)
   223  	if ok {
   224  		return res, err
   225  	}
   226  	return NilValue, BinaryArithmeticError(op[2:], x, y)
   227  }
   228  
   229  // BinaryArithmeticError returns an error describing the problem with trying to
   230  // perform x op y.
   231  func BinaryArithmeticError(op string, x, y Value) error {
   232  	var wrongVal Value
   233  	switch {
   234  	case numberType(y) != NaN:
   235  		wrongVal = x
   236  	case numberType(x) != NaN:
   237  		wrongVal = y
   238  	default:
   239  		return fmt.Errorf("attempt to %s a '%s' with a '%s'", op, x.CustomTypeName(), y.CustomTypeName())
   240  	}
   241  	return fmt.Errorf("attempt to perform arithmetic on a %s value", wrongVal.CustomTypeName())
   242  }
   243  
   244  func unaryArithFallback(t *Thread, op string, x Value) (Value, error) {
   245  	res, err, ok := metaun(t, op, x)
   246  	if ok {
   247  		return res, err
   248  	}
   249  	return NilValue, UnaryArithmeticError(op[2:], x)
   250  }
   251  
   252  // UnaryArithmeticError returns an error describing the problem with trying to
   253  // perform the unary operation op(x).
   254  func UnaryArithmeticError(op string, x Value) error {
   255  	return fmt.Errorf("attempt to %s a '%s'", op, x.CustomTypeName())
   256  }