// Copyright 2021 The Bazel Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package math provides basic constants and mathematical functions. package math // import "go.starlark.net/lib/math" import ( "errors" "fmt" "math" "go.starlark.net/starlark" "go.starlark.net/starlarkstruct" ) // Module math is a Starlark module of math-related functions and constants. // The module defines the following functions: // // ceil(x) - Returns the ceiling of x, the smallest integer greater than or equal to x. // copysign(x, y) - Returns a value with the magnitude of x and the sign of y. // fabs(x) - Returns the absolute value of x as float. // floor(x) - Returns the floor of x, the largest integer less than or equal to x. // 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. // pow(x, y) - Returns x**y, the base-x exponential of y. // remainder(x, y) - Returns the IEEE 754 floating-point remainder of x/y. // round(x) - Returns the nearest integer, rounding half away from zero. // // exp(x) - Returns e raised to the power x, where e = 2.718281… is the base of natural logarithms. // sqrt(x) - Returns the square root of x. // // acos(x) - Returns the arc cosine of x, in radians. // asin(x) - Returns the arc sine of x, in radians. // atan(x) - Returns the arc tangent of x, in radians. // atan2(y, x) - Returns atan(y / x), in radians. // The result is between -pi and pi. // The vector in the plane from the origin to point (x, y) makes this angle with the positive X axis. // The point of atan2() is that the signs of both inputs are known to it, so it can compute the correct // quadrant for the angle. // For example, atan(1) and atan2(1, 1) are both pi/4, but atan2(-1, -1) is -3*pi/4. // cos(x) - Returns the cosine of x, in radians. // 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). // sin(x) - Returns the sine of x, in radians. // tan(x) - Returns the tangent of x, in radians. // // degrees(x) - Converts angle x from radians to degrees. // radians(x) - Converts angle x from degrees to radians. // // acosh(x) - Returns the inverse hyperbolic cosine of x. // asinh(x) - Returns the inverse hyperbolic sine of x. // atanh(x) - Returns the inverse hyperbolic tangent of x. // cosh(x) - Returns the hyperbolic cosine of x. // sinh(x) - Returns the hyperbolic sine of x. // tanh(x) - Returns the hyperbolic tangent of x. // // log(x, base) - Returns the logarithm of x in the given base, or natural logarithm by default. // // gamma(x) - Returns the Gamma function of x. // // All functions accept both int and float values as arguments. // // The module also defines approximations of the following constants: // // e - The base of natural logarithms, approximately 2.71828. // pi - The ratio of a circle's circumference to its diameter, approximately 3.14159. // var Module = &starlarkstruct.Module{ Name: "math", Members: starlark.StringDict{ "ceil": starlark.NewBuiltin("ceil", ceil), "copysign": newBinaryBuiltin("copysign", math.Copysign), "fabs": newUnaryBuiltin("fabs", math.Abs), "floor": starlark.NewBuiltin("floor", floor), "mod": newBinaryBuiltin("round", math.Mod), "pow": newBinaryBuiltin("pow", math.Pow), "remainder": newBinaryBuiltin("remainder", math.Remainder), "round": newUnaryBuiltin("round", math.Round), "exp": newUnaryBuiltin("exp", math.Exp), "sqrt": newUnaryBuiltin("sqrt", math.Sqrt), "acos": newUnaryBuiltin("acos", math.Acos), "asin": newUnaryBuiltin("asin", math.Asin), "atan": newUnaryBuiltin("atan", math.Atan), "atan2": newBinaryBuiltin("atan2", math.Atan2), "cos": newUnaryBuiltin("cos", math.Cos), "hypot": newBinaryBuiltin("hypot", math.Hypot), "sin": newUnaryBuiltin("sin", math.Sin), "tan": newUnaryBuiltin("tan", math.Tan), "degrees": newUnaryBuiltin("degrees", degrees), "radians": newUnaryBuiltin("radians", radians), "acosh": newUnaryBuiltin("acosh", math.Acosh), "asinh": newUnaryBuiltin("asinh", math.Asinh), "atanh": newUnaryBuiltin("atanh", math.Atanh), "cosh": newUnaryBuiltin("cosh", math.Cosh), "sinh": newUnaryBuiltin("sinh", math.Sinh), "tanh": newUnaryBuiltin("tanh", math.Tanh), "log": starlark.NewBuiltin("log", log), "gamma": newUnaryBuiltin("gamma", math.Gamma), "e": starlark.Float(math.E), "pi": starlark.Float(math.Pi), }, } // floatOrInt is an Unpacker that converts a Starlark int or float to Go's float64. type floatOrInt float64 func (p *floatOrInt) Unpack(v starlark.Value) error { switch v := v.(type) { case starlark.Int: *p = floatOrInt(v.Float()) return nil case starlark.Float: *p = floatOrInt(v) return nil } return fmt.Errorf("got %s, want float or int", v.Type()) } // newUnaryBuiltin wraps a unary floating-point Go function // as a Starlark built-in that accepts int or float arguments. func newUnaryBuiltin(name string, fn func(float64) float64) *starlark.Builtin { return starlark.NewBuiltin(name, func(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { var x floatOrInt if err := starlark.UnpackPositionalArgs(name, args, kwargs, 1, &x); err != nil { return nil, err } return starlark.Float(fn(float64(x))), nil }) } // newBinaryBuiltin wraps a binary floating-point Go function // as a Starlark built-in that accepts int or float arguments. func newBinaryBuiltin(name string, fn func(float64, float64) float64) *starlark.Builtin { return starlark.NewBuiltin(name, func(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { var x, y floatOrInt if err := starlark.UnpackPositionalArgs(name, args, kwargs, 2, &x, &y); err != nil { return nil, err } return starlark.Float(fn(float64(x), float64(y))), nil }) } // log wraps the Log function // as a Starlark built-in that accepts int or float arguments. func log(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { var ( x floatOrInt base floatOrInt = math.E ) if err := starlark.UnpackPositionalArgs("log", args, kwargs, 1, &x, &base); err != nil { return nil, err } if base == 1 { return nil, errors.New("division by zero") } return starlark.Float(math.Log(float64(x)) / math.Log(float64(base))), nil } func ceil(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { var x starlark.Value if err := starlark.UnpackPositionalArgs("ceil", args, kwargs, 1, &x); err != nil { return nil, err } switch t := x.(type) { case starlark.Int: return t, nil case starlark.Float: return starlark.NumberToInt(starlark.Float(math.Ceil(float64(t)))) } return nil, fmt.Errorf("got %s, want float or int", x.Type()) } func floor(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { var x starlark.Value if err := starlark.UnpackPositionalArgs("floor", args, kwargs, 1, &x); err != nil { return nil, err } switch t := x.(type) { case starlark.Int: return t, nil case starlark.Float: return starlark.NumberToInt(starlark.Float(math.Floor(float64(t)))) } return nil, fmt.Errorf("got %s, want float or int", x.Type()) } func degrees(x float64) float64 { return 360 * x / (2 * math.Pi) } func radians(x float64) float64 { return 2 * math.Pi * x / 360 }