Skip to content

Commit

Permalink
add math module (google#357)
Browse files Browse the repository at this point in the history
* add math module

* math: Applied remarks

* math: Applied remarks part 2

* math: Applied remarks part 3

* math: Applied remarks part 4

* math: Add missing functions

* math: Use a dedicated function for log

* time: Inline the log function

Co-authored-by: essobedo <[email protected]>
  • Loading branch information
b5 and essobedo authored Mar 12, 2021
1 parent 6a590ae commit 74c10e2
Show file tree
Hide file tree
Showing 4 changed files with 539 additions and 0 deletions.
2 changes: 2 additions & 0 deletions cmd/starlark/starlark.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"strings"

"go.starlark.net/internal/compile"
"go.starlark.net/lib/math"
"go.starlark.net/lib/time"
"go.starlark.net/repl"
"go.starlark.net/resolve"
Expand Down Expand Up @@ -93,6 +94,7 @@ func doMain() int {
// TODO(adonovan): plumb predeclared env through to the REPL.
starlark.Universe["json"] = starlarkjson.Module
starlark.Universe["time"] = time.Module
starlark.Universe["math"] = math.Module

switch {
case flag.NArg() == 1 || *execprog != "":
Expand Down
171 changes: 171 additions & 0 deletions lib/math/math.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
// 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:
//
// abs(x) - Returns the absolute value of x.
// 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.
// 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{
"abs": newUnaryBuiltin("abs", math.Abs),
"ceil": newUnaryBuiltin("ceil", math.Ceil),
"copysign": newBinaryBuiltin("copysign", math.Copysign),
"floor": newUnaryBuiltin("floor", math.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 degrees(x float64) float64 {
return 360 * x / (2 * math.Pi)
}

func radians(x float64) float64 {
return 2 * math.Pi * x / 360
}
5 changes: 5 additions & 0 deletions starlark/eval_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"testing"

"go.starlark.net/internal/chunkedfile"
starlarkmath "go.starlark.net/lib/math"
"go.starlark.net/lib/time"
"go.starlark.net/resolve"
"go.starlark.net/starlark"
Expand Down Expand Up @@ -124,6 +125,7 @@ func TestExecFile(t *testing.T) {
"testdata/int.star",
"testdata/json.star",
"testdata/list.star",
"testdata/math.star",
"testdata/misc.star",
"testdata/set.star",
"testdata/string.star",
Expand Down Expand Up @@ -198,6 +200,9 @@ func load(thread *starlark.Thread, module string) (starlark.StringDict, error) {
if module == "time.star" {
return starlark.StringDict{"time": time.Module}, nil
}
if module == "math.star" {
return starlark.StringDict{"math": starlarkmath.Module}, nil
}

// TODO(adonovan): test load() using this execution path.
filename := filepath.Join(filepath.Dir(thread.CallFrame(0).Pos.Filename()), module)
Expand Down
Loading

0 comments on commit 74c10e2

Please sign in to comment.