pkg/math: use higher precision implementations

Most functions where it makes sense have been converted
now.

Math lib is now no longer generated form Go code.

Closes #103

Change-Id: Ib4b89d4793d8257695ca3da763291ee3944e5ce3
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/3980
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cue/builtin_test.go b/cue/builtin_test.go
index 3752689..f68db8b 100644
--- a/cue/builtin_test.go
+++ b/cue/builtin_test.go
@@ -510,6 +510,55 @@
 		test("struct", `struct.MaxFields(2) & {a: 1}`),
 		`{a: 1}`,
 	}, {
+		test("math", `math.Pow(8, 4)`), `4096`,
+	}, {
+		test("math", `math.Pow10(4)`), `10000`,
+	}, {
+		test("math", `math.Signbit(-4)`), `true`,
+	}, {
+		test("math", `math.Round(2.5)`), `3`,
+	}, {
+		test("math", `math.Round(-2.5)`), `-3`,
+	}, {
+		test("math", `math.RoundToEven(2.5)`), `2`,
+	}, {
+		test("math", `math.RoundToEven(-2.5)`), `-2`,
+	}, {
+		test("math", `math.Abs(2.5)`), `2.5`,
+	}, {
+		test("math", `math.Abs(-2.2)`), `2.2`,
+	}, {
+		test("math", `math.Cbrt(2)`), `1.25992104989487316476721`,
+	}, {
+		test("math", `math.Copysign(5, -2.2)`), `-5`,
+	}, {
+		test("math", `math.Exp(3)`), `20.0855369231876677409285`,
+	}, {
+		test("math", `math.Exp2(3.5)`), `11.3137084989847603904135`,
+	}, {
+		test("math", `math.Log(4)`), `1.38629436111989061883446`,
+	}, {
+		test("math", `math.Log10(4)`), `0.602059991327962390427478`,
+	}, {
+		test("math", `math.Log2(5)`),
+		`2.32192809488736234787032`,
+	}, {
+		test("math", `math.Dim(3, 2.5)`), `0.5`,
+	}, {
+		test("math", `math.Dim(5, 7.2)`), `0`,
+	}, {
+		test("math", `math.Ceil(2.5)`), `3`,
+	}, {
+		test("math", `math.Ceil(-2.2)`), `-2`,
+	}, {
+		test("math", `math.Floor(2.9)`), `2`,
+	}, {
+		test("math", `math.Floor(-2.2)`), `-3`,
+	}, {
+		test("math", `math.Trunc(2.5)`), `2`,
+	}, {
+		test("math", `math.Trunc(-2.9)`), `-2`,
+	}, {
 		test("math/bits", `bits.Lsh(0x8, 4)`), `128`,
 	}, {
 		test("math/bits", `bits.Rsh(0x100, 4)`), `16`,
diff --git a/cue/builtins.go b/cue/builtins.go
index b1c215e..bc72a98 100644
--- a/cue/builtins.go
+++ b/cue/builtins.go
@@ -46,8 +46,20 @@
 
 var _ io.Reader
 
+var roundTruncContext = apd.Context{Rounding: apd.RoundDown}
+
+var roundUpContext = apd.Context{Rounding: apd.RoundHalfUp}
+
+var roundEvenContext = apd.Context{Rounding: apd.RoundHalfEven}
+
 var mulContext = apd.BaseContext.WithPrecision(1)
 
+var apdContext = apd.BaseContext.WithPrecision(24)
+
+var zero = apd.New(0, 0)
+
+var two = apd.New(2, 0)
+
 var idnaProfile = idna.New(
 	idna.ValidateLabels(true),
 	idna.VerifyDNSLength(true),
@@ -1026,9 +1038,11 @@
 			Params: []kind{numKind},
 			Result: numKind,
 			Func: func(c *callCtxt) {
-				x := c.float64(0)
-				c.ret = func() interface{} {
-					return math.Floor(x)
+				x := c.decimal(0)
+				c.ret, c.err = func() (interface{}, error) {
+					var d internal.Decimal
+					_, err := apdContext.Floor(&d, x)
+					return &d, err
 				}()
 			},
 		}, {
@@ -1036,9 +1050,11 @@
 			Params: []kind{numKind},
 			Result: numKind,
 			Func: func(c *callCtxt) {
-				x := c.float64(0)
-				c.ret = func() interface{} {
-					return math.Ceil(x)
+				x := c.decimal(0)
+				c.ret, c.err = func() (interface{}, error) {
+					var d internal.Decimal
+					_, err := apdContext.Ceil(&d, x)
+					return &d, err
 				}()
 			},
 		}, {
@@ -1046,9 +1062,11 @@
 			Params: []kind{numKind},
 			Result: numKind,
 			Func: func(c *callCtxt) {
-				x := c.float64(0)
-				c.ret = func() interface{} {
-					return math.Trunc(x)
+				x := c.decimal(0)
+				c.ret, c.err = func() (interface{}, error) {
+					var d internal.Decimal
+					_, err := roundTruncContext.RoundToIntegralExact(&d, x)
+					return &d, err
 				}()
 			},
 		}, {
@@ -1056,9 +1074,11 @@
 			Params: []kind{numKind},
 			Result: numKind,
 			Func: func(c *callCtxt) {
-				x := c.float64(0)
-				c.ret = func() interface{} {
-					return math.Round(x)
+				x := c.decimal(0)
+				c.ret, c.err = func() (interface{}, error) {
+					var d internal.Decimal
+					_, err := roundUpContext.RoundToIntegralExact(&d, x)
+					return &d, err
 				}()
 			},
 		}, {
@@ -1066,9 +1086,11 @@
 			Params: []kind{numKind},
 			Result: numKind,
 			Func: func(c *callCtxt) {
-				x := c.float64(0)
-				c.ret = func() interface{} {
-					return math.RoundToEven(x)
+				x := c.decimal(0)
+				c.ret, c.err = func() (interface{}, error) {
+					var d internal.Decimal
+					_, err := roundEvenContext.RoundToIntegralExact(&d, x)
+					return &d, err
 				}()
 			},
 		}, {
@@ -1088,9 +1110,11 @@
 			Params: []kind{numKind},
 			Result: numKind,
 			Func: func(c *callCtxt) {
-				x := c.float64(0)
-				c.ret = func() interface{} {
-					return math.Abs(x)
+				x := c.decimal(0)
+				c.ret, c.err = func() (interface{}, error) {
+					var d internal.Decimal
+					_, err := apdContext.Abs(&d, x)
+					return &d, err
 				}()
 			},
 		}, {
@@ -1168,9 +1192,11 @@
 			Params: []kind{numKind},
 			Result: numKind,
 			Func: func(c *callCtxt) {
-				x := c.float64(0)
-				c.ret = func() interface{} {
-					return math.Cbrt(x)
+				x := c.decimal(0)
+				c.ret, c.err = func() (interface{}, error) {
+					var d internal.Decimal
+					_, err := apdContext.Cbrt(&d, x)
+					return &d, err
 				}()
 			},
 		}, {
@@ -1211,9 +1237,12 @@
 			Params: []kind{numKind, numKind},
 			Result: numKind,
 			Func: func(c *callCtxt) {
-				x, y := c.float64(0), c.float64(1)
+				x, y := c.decimal(0), c.decimal(1)
 				c.ret = func() interface{} {
-					return math.Copysign(x, y)
+					var d internal.Decimal
+					d.Set(x)
+					d.Negative = y.Negative
+					return &d
 				}()
 			},
 		}, {
@@ -1221,9 +1250,17 @@
 			Params: []kind{numKind, numKind},
 			Result: numKind,
 			Func: func(c *callCtxt) {
-				x, y := c.float64(0), c.float64(1)
-				c.ret = func() interface{} {
-					return math.Dim(x, y)
+				x, y := c.decimal(0), c.decimal(1)
+				c.ret, c.err = func() (interface{}, error) {
+					var d internal.Decimal
+					_, err := apdContext.Sub(&d, x, y)
+					if err != nil {
+						return nil, err
+					}
+					if d.Negative {
+						return zero, nil
+					}
+					return &d, nil
 				}()
 			},
 		}, {
@@ -1271,9 +1308,11 @@
 			Params: []kind{numKind},
 			Result: numKind,
 			Func: func(c *callCtxt) {
-				x := c.float64(0)
-				c.ret = func() interface{} {
-					return math.Exp(x)
+				x := c.decimal(0)
+				c.ret, c.err = func() (interface{}, error) {
+					var d internal.Decimal
+					_, err := apdContext.Exp(&d, x)
+					return &d, err
 				}()
 			},
 		}, {
@@ -1281,9 +1320,11 @@
 			Params: []kind{numKind},
 			Result: numKind,
 			Func: func(c *callCtxt) {
-				x := c.float64(0)
-				c.ret = func() interface{} {
-					return math.Exp2(x)
+				x := c.decimal(0)
+				c.ret, c.err = func() (interface{}, error) {
+					var d internal.Decimal
+					_, err := apdContext.Pow(&d, two, x)
+					return &d, err
 				}()
 			},
 		}, {
@@ -1391,9 +1432,11 @@
 			Params: []kind{numKind},
 			Result: numKind,
 			Func: func(c *callCtxt) {
-				x := c.float64(0)
-				c.ret = func() interface{} {
-					return math.Log(x)
+				x := c.decimal(0)
+				c.ret, c.err = func() (interface{}, error) {
+					var d internal.Decimal
+					_, err := apdContext.Ln(&d, x)
+					return &d, err
 				}()
 			},
 		}, {
@@ -1401,9 +1444,11 @@
 			Params: []kind{numKind},
 			Result: numKind,
 			Func: func(c *callCtxt) {
-				x := c.float64(0)
-				c.ret = func() interface{} {
-					return math.Log10(x)
+				x := c.decimal(0)
+				c.ret, c.err = func() (interface{}, error) {
+					var d internal.Decimal
+					_, err := apdContext.Log10(&d, x)
+					return &d, err
 				}()
 			},
 		}, {
@@ -1411,9 +1456,16 @@
 			Params: []kind{numKind},
 			Result: numKind,
 			Func: func(c *callCtxt) {
-				x := c.float64(0)
-				c.ret = func() interface{} {
-					return math.Log2(x)
+				x := c.decimal(0)
+				c.ret, c.err = func() (interface{}, error) {
+					var d, ln2 internal.Decimal
+					_, _ = apdContext.Ln(&ln2, two)
+					_, err := apdContext.Ln(&d, x)
+					if err != nil {
+						return &d, err
+					}
+					_, err = apdContext.Quo(&d, &d, &ln2)
+					return &d, nil
 				}()
 			},
 		}, {
@@ -1461,9 +1513,11 @@
 			Params: []kind{numKind, numKind},
 			Result: numKind,
 			Func: func(c *callCtxt) {
-				x, y := c.float64(0), c.float64(1)
-				c.ret = func() interface{} {
-					return math.Pow(x, y)
+				x, y := c.decimal(0), c.decimal(1)
+				c.ret, c.err = func() (interface{}, error) {
+					var d internal.Decimal
+					_, err := apdContext.Pow(&d, x, y)
+					return &d, err
 				}()
 			},
 		}, {
@@ -1471,9 +1525,9 @@
 			Params: []kind{intKind},
 			Result: numKind,
 			Func: func(c *callCtxt) {
-				n := c.int(0)
+				n := c.int32(0)
 				c.ret = func() interface{} {
-					return math.Pow10(n)
+					return apd.New(1, n)
 				}()
 			},
 		}, {
@@ -1491,9 +1545,9 @@
 			Params: []kind{numKind},
 			Result: boolKind,
 			Func: func(c *callCtxt) {
-				x := c.float64(0)
+				x := c.decimal(0)
 				c.ret = func() interface{} {
-					return math.Signbit(x)
+					return x.Negative
 				}()
 			},
 		}, {
diff --git a/pkg/math/manual.go b/pkg/math/manual.go
index 361a6c1..9c18f1c 100644
--- a/pkg/math/manual.go
+++ b/pkg/math/manual.go
@@ -15,22 +15,20 @@
 package math
 
 import (
-	"math"
-
 	"cuelang.org/go/internal"
 	"github.com/cockroachdb/apd/v2"
 )
 
-// TODO: use apd
-
 // Floor returns the greatest integer value less than or equal to x.
 //
 // Special cases are:
 //	Floor(±0) = ±0
 //	Floor(±Inf) = ±Inf
 //	Floor(NaN) = NaN
-func Floor(x float64) float64 {
-	return math.Floor(x)
+func Floor(x *internal.Decimal) (*internal.Decimal, error) {
+	var d internal.Decimal
+	_, err := apdContext.Floor(&d, x)
+	return &d, err
 }
 
 // Ceil returns the least integer value greater than or equal to x.
@@ -39,38 +37,52 @@
 //	Ceil(±0) = ±0
 //	Ceil(±Inf) = ±Inf
 //	Ceil(NaN) = NaN
-func Ceil(x float64) float64 {
-	return math.Ceil(x)
+func Ceil(x *internal.Decimal) (*internal.Decimal, error) {
+	var d internal.Decimal
+	_, err := apdContext.Ceil(&d, x)
+	return &d, err
 }
 
+var roundTruncContext = apd.Context{Rounding: apd.RoundDown}
+
 // Trunc returns the integer value of x.
 //
 // Special cases are:
 //	Trunc(±0) = ±0
 //	Trunc(±Inf) = ±Inf
 //	Trunc(NaN) = NaN
-func Trunc(x float64) float64 {
-	return math.Trunc(x)
+func Trunc(x *internal.Decimal) (*internal.Decimal, error) {
+	var d internal.Decimal
+	_, err := roundTruncContext.RoundToIntegralExact(&d, x)
+	return &d, err
 }
 
+var roundUpContext = apd.Context{Rounding: apd.RoundHalfUp}
+
 // Round returns the nearest integer, rounding half away from zero.
 //
 // Special cases are:
 //	Round(±0) = ±0
 //	Round(±Inf) = ±Inf
 //	Round(NaN) = NaN
-func Round(x float64) float64 {
-	return math.Round(x)
+func Round(x *internal.Decimal) (*internal.Decimal, error) {
+	var d internal.Decimal
+	_, err := roundUpContext.RoundToIntegralExact(&d, x)
+	return &d, err
 }
 
+var roundEvenContext = apd.Context{Rounding: apd.RoundHalfEven}
+
 // RoundToEven returns the nearest integer, rounding ties to even.
 //
 // Special cases are:
 //	RoundToEven(±0) = ±0
 //	RoundToEven(±Inf) = ±Inf
 //	RoundToEven(NaN) = NaN
-func RoundToEven(x float64) float64 {
-	return math.RoundToEven(x)
+func RoundToEven(x *internal.Decimal) (*internal.Decimal, error) {
+	var d internal.Decimal
+	_, err := roundEvenContext.RoundToIntegralExact(&d, x)
+	return &d, err
 }
 
 var mulContext = apd.BaseContext.WithPrecision(1)
diff --git a/pkg/math/math.go b/pkg/math/math.go
index a66f27a..9d55dc8 100644
--- a/pkg/math/math.go
+++ b/pkg/math/math.go
@@ -16,19 +16,24 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-//go:generate qgo -exclude=32,^Next,^Max,^Smallest,^Min,bits,Inf,NaN,Round,Trunc,Ceil,Floor$ extract math
-
 package math
 
-import "math"
+import (
+	"math"
+
+	"cuelang.org/go/internal"
+	"github.com/cockroachdb/apd/v2"
+)
+
+var apdContext = apd.BaseContext.WithPrecision(24)
 
 // Abs returns the absolute value of x.
 //
-// Special cases are:
-//	Abs(±Inf) = +Inf
-//	Abs(NaN) = NaN
-func Abs(x float64) float64 {
-	return math.Abs(x)
+// Special case: Abs(±Inf) = +Inf
+func Abs(x *internal.Decimal) (*internal.Decimal, error) {
+	var d internal.Decimal
+	_, err := apdContext.Abs(&d, x)
+	return &d, err
 }
 
 // Acosh returns the inverse hyperbolic cosine of x.
@@ -121,8 +126,10 @@
 //	Cbrt(±0) = ±0
 //	Cbrt(±Inf) = ±Inf
 //	Cbrt(NaN) = NaN
-func Cbrt(x float64) float64 {
-	return math.Cbrt(x)
+func Cbrt(x *internal.Decimal) (*internal.Decimal, error) {
+	var d internal.Decimal
+	_, err := apdContext.Cbrt(&d, x)
+	return &d, err
 }
 
 // Mathematical constants.
@@ -144,18 +151,31 @@
 
 // Copysign returns a value with the magnitude
 // of x and the sign of y.
-func Copysign(x, y float64) float64 {
-	return math.Copysign(x, y)
+func Copysign(x, y *internal.Decimal) *internal.Decimal {
+	var d internal.Decimal
+	d.Set(x)
+	d.Negative = y.Negative
+	return &d
 }
 
+var zero = apd.New(0, 0)
+
 // Dim returns the maximum of x-y or 0.
 //
 // Special cases are:
 //	Dim(+Inf, +Inf) = NaN
 //	Dim(-Inf, -Inf) = NaN
 //	Dim(x, NaN) = Dim(NaN, x) = NaN
-func Dim(x, y float64) float64 {
-	return math.Dim(x, y)
+func Dim(x, y *internal.Decimal) (*internal.Decimal, error) {
+	var d internal.Decimal
+	_, err := apdContext.Sub(&d, x, y)
+	if err != nil {
+		return nil, err
+	}
+	if d.Negative {
+		return zero, nil
+	}
+	return &d, nil
 }
 
 // Erf returns the error function of x.
@@ -207,15 +227,21 @@
 //	Exp(NaN) = NaN
 // Very large values overflow to 0 or +Inf.
 // Very small values underflow to 1.
-func Exp(x float64) float64 {
-	return math.Exp(x)
+func Exp(x *internal.Decimal) (*internal.Decimal, error) {
+	var d internal.Decimal
+	_, err := apdContext.Exp(&d, x)
+	return &d, err
 }
 
+var two = apd.New(2, 0)
+
 // Exp2 returns 2**x, the base-2 exponential of x.
 //
 // Special cases are the same as Exp.
-func Exp2(x float64) float64 {
-	return math.Exp2(x)
+func Exp2(x *internal.Decimal) (*internal.Decimal, error) {
+	var d internal.Decimal
+	_, err := apdContext.Pow(&d, two, x)
+	return &d, err
 }
 
 // Expm1 returns e**x - 1, the base-e exponential of x minus 1.
@@ -335,20 +361,31 @@
 //	Log(0) = -Inf
 //	Log(x < 0) = NaN
 //	Log(NaN) = NaN
-func Log(x float64) float64 {
-	return math.Log(x)
+func Log(x *internal.Decimal) (*internal.Decimal, error) {
+	var d internal.Decimal
+	_, err := apdContext.Ln(&d, x)
+	return &d, err
 }
 
 // Log10 returns the decimal logarithm of x.
 // The special cases are the same as for Log.
-func Log10(x float64) float64 {
-	return math.Log10(x)
+func Log10(x *internal.Decimal) (*internal.Decimal, error) {
+	var d internal.Decimal
+	_, err := apdContext.Log10(&d, x)
+	return &d, err
 }
 
 // Log2 returns the binary logarithm of x.
 // The special cases are the same as for Log.
-func Log2(x float64) float64 {
-	return math.Log2(x)
+func Log2(x *internal.Decimal) (*internal.Decimal, error) {
+	var d, ln2 internal.Decimal
+	_, _ = apdContext.Ln(&ln2, two)
+	_, err := apdContext.Ln(&d, x)
+	if err != nil {
+		return &d, err
+	}
+	_, err = apdContext.Quo(&d, &d, &ln2)
+	return &d, nil
 }
 
 // Log1p returns the natural logarithm of 1 plus its argument x.
@@ -421,17 +458,16 @@
 //	Pow(+Inf, y) = +0 for y < 0
 //	Pow(-Inf, y) = Pow(-0, -y)
 //	Pow(x, y) = NaN for finite x < 0 and finite non-integer y
-func Pow(x, y float64) float64 {
-	return math.Pow(x, y)
+func Pow(x, y *internal.Decimal) (*internal.Decimal, error) {
+	var d internal.Decimal
+	_, err := apdContext.Pow(&d, x, y)
+	return &d, err
 }
 
 // Pow10 returns 10**n, the base-10 exponential of n.
 //
-// Special cases are:
-//	Pow10(n) =    0 for n < -323
-//	Pow10(n) = +Inf for n > 308
-func Pow10(n int) float64 {
-	return math.Pow10(n)
+func Pow10(n int32) *internal.Decimal {
+	return apd.New(1, n)
 }
 
 // Remainder returns the IEEE 754 floating-point remainder of x/y.
@@ -447,8 +483,8 @@
 }
 
 // Signbit reports whether x is negative or negative zero.
-func Signbit(x float64) bool {
-	return math.Signbit(x)
+func Signbit(x *internal.Decimal) bool {
+	return x.Negative
 }
 
 // Cos returns the cosine of the radian argument x.