go.starlark.net@v0.0.0-20231101134539-556fd59b42f6/lib/math/math.go (about)

     1  // Copyright 2021 The Bazel Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package math provides basic constants and mathematical functions.
     6  package math // import "go.starlark.net/lib/math"
     7  
     8  import (
     9  	"errors"
    10  	"fmt"
    11  	"math"
    12  
    13  	"go.starlark.net/starlark"
    14  	"go.starlark.net/starlarkstruct"
    15  )
    16  
    17  // Module math is a Starlark module of math-related functions and constants.
    18  // The module defines the following functions:
    19  //
    20  //     ceil(x) - Returns the ceiling of x, the smallest integer greater than or equal to x.
    21  //     copysign(x, y) - Returns a value with the magnitude of x and the sign of y.
    22  //     fabs(x) - Returns the absolute value of x as float.
    23  //     floor(x) - Returns the floor of x, the largest integer less than or equal to x.
    24  //     mod(x, y) - Returns the floating-point remainder of x/y. The magnitude of the result is less than y and its sign agrees with that of x.
    25  //     pow(x, y) - Returns x**y, the base-x exponential of y.
    26  //     remainder(x, y) - Returns the IEEE 754 floating-point remainder of x/y.
    27  //     round(x) - Returns the nearest integer, rounding half away from zero.
    28  //
    29  //     exp(x) - Returns e raised to the power x, where e = 2.718281… is the base of natural logarithms.
    30  //     sqrt(x) - Returns the square root of x.
    31  //
    32  //     acos(x) - Returns the arc cosine of x, in radians.
    33  //     asin(x) - Returns the arc sine of x, in radians.
    34  //     atan(x) - Returns the arc tangent of x, in radians.
    35  //     atan2(y, x) - Returns atan(y / x), in radians.
    36  //                   The result is between -pi and pi.
    37  //                   The vector in the plane from the origin to point (x, y) makes this angle with the positive X axis.
    38  //                   The point of atan2() is that the signs of both inputs are known to it, so it can compute the correct
    39  //                   quadrant for the angle.
    40  //                   For example, atan(1) and atan2(1, 1) are both pi/4, but atan2(-1, -1) is -3*pi/4.
    41  //     cos(x) - Returns the cosine of x, in radians.
    42  //     hypot(x, y) - Returns the Euclidean norm, sqrt(x*x + y*y). This is the length of the vector from the origin to point (x, y).
    43  //     sin(x) - Returns the sine of x, in radians.
    44  //     tan(x) - Returns the tangent of x, in radians.
    45  //
    46  //     degrees(x) - Converts angle x from radians to degrees.
    47  //     radians(x) - Converts angle x from degrees to radians.
    48  //
    49  //     acosh(x) - Returns the inverse hyperbolic cosine of x.
    50  //     asinh(x) - Returns the inverse hyperbolic sine of x.
    51  //     atanh(x) - Returns the inverse hyperbolic tangent of x.
    52  //     cosh(x) - Returns the hyperbolic cosine of x.
    53  //     sinh(x) - Returns the hyperbolic sine of x.
    54  //     tanh(x) - Returns the hyperbolic tangent of x.
    55  //
    56  //     log(x, base) - Returns the logarithm of x in the given base, or natural logarithm by default.
    57  //
    58  //     gamma(x) - Returns the Gamma function of x.
    59  //
    60  // All functions accept both int and float values as arguments.
    61  //
    62  // The module also defines approximations of the following constants:
    63  //
    64  //     e - The base of natural logarithms, approximately 2.71828.
    65  //     pi - The ratio of a circle's circumference to its diameter, approximately 3.14159.
    66  //
    67  var Module = &starlarkstruct.Module{
    68  	Name: "math",
    69  	Members: starlark.StringDict{
    70  		"ceil":      starlark.NewBuiltin("ceil", ceil),
    71  		"copysign":  newBinaryBuiltin("copysign", math.Copysign),
    72  		"fabs":      newUnaryBuiltin("fabs", math.Abs),
    73  		"floor":     starlark.NewBuiltin("floor", floor),
    74  		"mod":       newBinaryBuiltin("mod", math.Mod),
    75  		"pow":       newBinaryBuiltin("pow", math.Pow),
    76  		"remainder": newBinaryBuiltin("remainder", math.Remainder),
    77  		"round":     newUnaryBuiltin("round", math.Round),
    78  
    79  		"exp":  newUnaryBuiltin("exp", math.Exp),
    80  		"sqrt": newUnaryBuiltin("sqrt", math.Sqrt),
    81  
    82  		"acos":  newUnaryBuiltin("acos", math.Acos),
    83  		"asin":  newUnaryBuiltin("asin", math.Asin),
    84  		"atan":  newUnaryBuiltin("atan", math.Atan),
    85  		"atan2": newBinaryBuiltin("atan2", math.Atan2),
    86  		"cos":   newUnaryBuiltin("cos", math.Cos),
    87  		"hypot": newBinaryBuiltin("hypot", math.Hypot),
    88  		"sin":   newUnaryBuiltin("sin", math.Sin),
    89  		"tan":   newUnaryBuiltin("tan", math.Tan),
    90  
    91  		"degrees": newUnaryBuiltin("degrees", degrees),
    92  		"radians": newUnaryBuiltin("radians", radians),
    93  
    94  		"acosh": newUnaryBuiltin("acosh", math.Acosh),
    95  		"asinh": newUnaryBuiltin("asinh", math.Asinh),
    96  		"atanh": newUnaryBuiltin("atanh", math.Atanh),
    97  		"cosh":  newUnaryBuiltin("cosh", math.Cosh),
    98  		"sinh":  newUnaryBuiltin("sinh", math.Sinh),
    99  		"tanh":  newUnaryBuiltin("tanh", math.Tanh),
   100  
   101  		"log": starlark.NewBuiltin("log", log),
   102  
   103  		"gamma": newUnaryBuiltin("gamma", math.Gamma),
   104  
   105  		"e":  starlark.Float(math.E),
   106  		"pi": starlark.Float(math.Pi),
   107  	},
   108  }
   109  
   110  // floatOrInt is an Unpacker that converts a Starlark int or float to Go's float64.
   111  type floatOrInt float64
   112  
   113  func (p *floatOrInt) Unpack(v starlark.Value) error {
   114  	switch v := v.(type) {
   115  	case starlark.Int:
   116  		*p = floatOrInt(v.Float())
   117  		return nil
   118  	case starlark.Float:
   119  		*p = floatOrInt(v)
   120  		return nil
   121  	}
   122  	return fmt.Errorf("got %s, want float or int", v.Type())
   123  }
   124  
   125  // newUnaryBuiltin wraps a unary floating-point Go function
   126  // as a Starlark built-in that accepts int or float arguments.
   127  func newUnaryBuiltin(name string, fn func(float64) float64) *starlark.Builtin {
   128  	return starlark.NewBuiltin(name, func(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
   129  		var x floatOrInt
   130  		if err := starlark.UnpackPositionalArgs(name, args, kwargs, 1, &x); err != nil {
   131  			return nil, err
   132  		}
   133  		return starlark.Float(fn(float64(x))), nil
   134  	})
   135  }
   136  
   137  // newBinaryBuiltin wraps a binary floating-point Go function
   138  // as a Starlark built-in that accepts int or float arguments.
   139  func newBinaryBuiltin(name string, fn func(float64, float64) float64) *starlark.Builtin {
   140  	return starlark.NewBuiltin(name, func(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
   141  		var x, y floatOrInt
   142  		if err := starlark.UnpackPositionalArgs(name, args, kwargs, 2, &x, &y); err != nil {
   143  			return nil, err
   144  		}
   145  		return starlark.Float(fn(float64(x), float64(y))), nil
   146  	})
   147  }
   148  
   149  //  log wraps the Log function
   150  // as a Starlark built-in that accepts int or float arguments.
   151  func log(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
   152  	var (
   153  		x    floatOrInt
   154  		base floatOrInt = math.E
   155  	)
   156  	if err := starlark.UnpackPositionalArgs("log", args, kwargs, 1, &x, &base); err != nil {
   157  		return nil, err
   158  	}
   159  	if base == 1 {
   160  		return nil, errors.New("division by zero")
   161  	}
   162  	return starlark.Float(math.Log(float64(x)) / math.Log(float64(base))), nil
   163  }
   164  
   165  func ceil(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
   166  	var x starlark.Value
   167  
   168  	if err := starlark.UnpackPositionalArgs("ceil", args, kwargs, 1, &x); err != nil {
   169  		return nil, err
   170  	}
   171  
   172  	switch t := x.(type) {
   173  	case starlark.Int:
   174  		return t, nil
   175  	case starlark.Float:
   176  		return starlark.NumberToInt(starlark.Float(math.Ceil(float64(t))))
   177  	}
   178  
   179  	return nil, fmt.Errorf("got %s, want float or int", x.Type())
   180  }
   181  
   182  func floor(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
   183  	var x starlark.Value
   184  
   185  	if err := starlark.UnpackPositionalArgs("floor", args, kwargs, 1, &x); err != nil {
   186  		return nil, err
   187  	}
   188  
   189  	switch t := x.(type) {
   190  	case starlark.Int:
   191  		return t, nil
   192  	case starlark.Float:
   193  		return starlark.NumberToInt(starlark.Float(math.Floor(float64(t))))
   194  	}
   195  
   196  	return nil, fmt.Errorf("got %s, want float or int", x.Type())
   197  }
   198  
   199  func degrees(x float64) float64 {
   200  	return 360 * x / (2 * math.Pi)
   201  }
   202  
   203  func radians(x float64) float64 {
   204  	return 2 * math.Pi * x / 360
   205  }