Skip to content

Commit

Permalink
internal/core/adt: move decimal logic into one place
Browse files Browse the repository at this point in the history
Or at least a start.

This prepares for the new numbering system, where
1 and 1.0 can be used interchangeably.

Change-Id: Ic311be58d59f75dc533b1bcda2ceb5bda7915056
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/7881
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
  • Loading branch information
mpvl committed Dec 4, 2020
1 parent 136e51b commit 8b13752
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 140 deletions.
12 changes: 1 addition & 11 deletions cue/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,28 +18,18 @@ import (
"cuelang.org/go/internal/core/adt"
"cuelang.org/go/internal/core/debug"
"cuelang.org/go/internal/core/eval"
"github.com/cockroachdb/apd/v2"
)

// context manages evaluation state.
type context struct {
opCtx *adt.OpContext
*apd.Context
*index
}

var baseContext apd.Context

func init() {
baseContext = apd.BaseContext
baseContext.Precision = 24
}

// newContext returns a new evaluation context.
func (idx *index) newContext() *context {
c := &context{
Context: &baseContext,
index: idx,
index: idx,
}
if idx != nil {
c.opCtx = eval.NewContext(idx.Runtime, nil)
Expand Down
88 changes: 8 additions & 80 deletions internal/core/adt/binop.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,9 @@ package adt

import (
"bytes"
"math/big"
"strings"

"github.com/cockroachdb/apd/v2"
)

var apdCtx apd.Context

func init() {
apdCtx = apd.BaseContext
apdCtx.Precision = 24
}

// BinOp handles all operations except AndOp and OrOp. This includes processing
// unary comparators such as '<4' and '=~"foo"'.
//
Expand Down Expand Up @@ -169,7 +159,7 @@ func BinOp(c *OpContext, op Op, left, right Value) Value {
case AddOp:
switch {
case leftKind&NumKind != 0 && rightKind&NumKind != 0:
return numOp(c, apdCtx.Add, left, right, AddOp)
return c.Add(c.Num(left, op), c.Num(right, op))

case leftKind == StringKind && rightKind == StringKind:
return c.NewString(c.StringValue(left) + c.StringValue(right))
Expand Down Expand Up @@ -217,13 +207,13 @@ func BinOp(c *OpContext, op Op, left, right Value) Value {
}

case SubtractOp:
return numOp(c, apdCtx.Sub, left, right, op)
return c.Sub(c.Num(left, op), c.Num(right, op))

case MultiplyOp:
switch {
// float
case leftKind&NumKind != 0 && rightKind&NumKind != 0:
return numOp(c, apdCtx.Mul, left, right, op)
return c.Mul(c.Num(left, op), c.Num(right, op))

case leftKind == StringKind && rightKind == IntKind:
const as = "string multiplication"
Expand Down Expand Up @@ -275,47 +265,27 @@ func BinOp(c *OpContext, op Op, left, right Value) Value {

case FloatQuotientOp:
if leftKind&NumKind != 0 && rightKind&NumKind != 0 {
v := numOp(c, apdCtx.Quo, left, right, op)
if n, ok := v.(*Num); ok {
n.K = FloatKind
}
return v
return c.Quo(c.Num(left, op), c.Num(right, op))
}

case IntDivideOp:
if leftKind&IntKind != 0 && rightKind&IntKind != 0 {
y := c.Num(right, op)
if y.X.IsZero() {
return c.NewErrf("division by zero")
}
return intOp(c, (*big.Int).Div, c.Num(left, op), y)
return c.IntDiv(c.Num(left, op), c.Num(right, op))
}

case IntModuloOp:
if leftKind&IntKind != 0 && rightKind&IntKind != 0 {
y := c.Num(right, op)
if y.X.IsZero() {
return c.NewErrf("division by zero")
}
return intOp(c, (*big.Int).Mod, c.Num(left, op), y)
return c.IntMod(c.Num(left, op), c.Num(right, op))
}

case IntQuotientOp:
if leftKind&IntKind != 0 && rightKind&IntKind != 0 {
y := c.Num(right, op)
if y.X.IsZero() {
return c.NewErrf("division by zero")
}
return intOp(c, (*big.Int).Quo, c.Num(left, op), y)
return c.IntQuo(c.Num(left, op), c.Num(right, op))
}

case IntRemainderOp:
if leftKind&IntKind != 0 && rightKind&IntKind != 0 {
y := c.Num(right, op)
if y.X.IsZero() {
return c.NewErrf("division by zero")
}
return intOp(c, (*big.Int).Rem, c.Num(left, op), y)
return c.IntRem(c.Num(left, op), c.Num(right, op))
}
}

Expand All @@ -341,45 +311,3 @@ func cmpTonode(c *OpContext, op Op, r int) Value {
}
return c.newBool(result)
}

type numFunc func(z, x, y *apd.Decimal) (apd.Condition, error)

func numOp(c *OpContext, fn numFunc, a, b Value, op Op) Value {
var d apd.Decimal
x := c.Num(a, op)
y := c.Num(b, op)
cond, err := fn(&d, &x.X, &y.X)
if err != nil {
return c.NewErrf("failed arithmetic: %v", err)
}
if cond.DivisionByZero() {
return c.NewErrf("division by zero")
}
k := x.Kind() & y.Kind()
if k == 0 {
k = FloatKind
}
return c.NewNum(&d, k)
}

type intFunc func(z, x, y *big.Int) *big.Int

func intOp(c *OpContext, fn intFunc, a, b *Num) Value {
var d apd.Decimal

var x, y apd.Decimal
_, _ = apdCtx.RoundToIntegralValue(&x, &a.X)
if x.Negative {
x.Coeff.Neg(&x.Coeff)
}
_, _ = apdCtx.RoundToIntegralValue(&y, &b.X)
if y.Negative {
y.Coeff.Neg(&y.Coeff)
}
fn(&d.Coeff, &x.Coeff, &y.Coeff)
if d.Coeff.Sign() < 0 {
d.Coeff.Neg(&d.Coeff)
d.Negative = true
}
return c.NewNum(&d, IntKind)
}
4 changes: 2 additions & 2 deletions internal/core/adt/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -976,9 +976,9 @@ func (c *OpContext) regexp(v Value) *regexp.Regexp {
}
}

// NewNum creates a new number of the given kind. It reports an error value
// newNum creates a new number of the given kind. It reports an error value
// instead if any error occurred.
func (c *OpContext) NewNum(d *apd.Decimal, k Kind, sources ...Node) Value {
func (c *OpContext) newNum(d *apd.Decimal, k Kind, sources ...Node) Value {
if c.HasErr() {
return c.Err()
}
Expand Down
131 changes: 131 additions & 0 deletions internal/core/adt/decimal.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// Copyright 2020 CUE Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package adt

import (
"math/big"

"github.com/cockroachdb/apd/v2"
)

var apdCtx apd.Context

func init() {
apdCtx = apd.BaseContext
apdCtx.Precision = 24
}

func (n *Num) Impl() *apd.Decimal {
return &n.X
}

func (n *Num) Negative() bool {
return n.X.Negative
}

func (a *Num) Cmp(b *Num) int {
return a.X.Cmp(&b.X)
}

func (c *OpContext) Add(a, b *Num) Value {
return numOp(c, apdCtx.Add, a, b)
}

func (c *OpContext) Sub(a, b *Num) Value {
return numOp(c, apdCtx.Sub, a, b)
}

func (c *OpContext) Mul(a, b *Num) Value {
return numOp(c, apdCtx.Mul, a, b)
}

func (c *OpContext) Quo(a, b *Num) Value {
v := numOp(c, apdCtx.Quo, a, b)
if n, ok := v.(*Num); ok {
n.K = FloatKind
}
return v
}

func (c *OpContext) Pow(a, b *Num) Value {
return numOp(c, apdCtx.Pow, a, b)
}

type numFunc func(z, x, y *apd.Decimal) (apd.Condition, error)

func numOp(c *OpContext, fn numFunc, x, y *Num) Value {
var d apd.Decimal

cond, err := fn(&d, &x.X, &y.X)

if err != nil {
return c.NewErrf("failed arithmetic: %v", err)
}

if cond.DivisionByZero() {
return c.NewErrf("division by zero")
}

k := x.Kind() & y.Kind()
if k == 0 {
k = FloatKind
}
return c.newNum(&d, k)
}

func (c *OpContext) IntDiv(a, b *Num) Value {
return intDivOp(c, (*big.Int).Div, a, b)
}

func (c *OpContext) IntMod(a, b *Num) Value {
return intDivOp(c, (*big.Int).Mod, a, b)
}

func (c *OpContext) IntQuo(a, b *Num) Value {
return intDivOp(c, (*big.Int).Quo, a, b)
}

func (c *OpContext) IntRem(a, b *Num) Value {
return intDivOp(c, (*big.Int).Rem, a, b)
}

type intFunc func(z, x, y *big.Int) *big.Int

func intDivOp(c *OpContext, fn intFunc, a, b *Num) Value {
if b.X.IsZero() {
return c.NewErrf("division by zero")
}

var x, y apd.Decimal
_, _ = apdCtx.RoundToIntegralValue(&x, &a.X)
if x.Negative {
x.Coeff.Neg(&x.Coeff)
}
_, _ = apdCtx.RoundToIntegralValue(&y, &b.X)
if y.Negative {
y.Coeff.Neg(&y.Coeff)
}

var d apd.Decimal

fn(&d.Coeff, &x.Coeff, &y.Coeff)

if d.Coeff.Sign() < 0 {
d.Coeff.Neg(&d.Coeff)
d.Negative = true
}

return c.newNum(&d, IntKind)
}
16 changes: 8 additions & 8 deletions internal/core/adt/simplify.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,17 +82,17 @@ func SimplifyBounds(ctx *OpContext, k Kind, x, y *BoundValue) Value {
// Readjust bounds for integers.
if x.Op == GreaterEqualOp {
// >=3.4 ==> >=4
_, _ = apd.BaseContext.Ceil(&lo, &a.X)
_, _ = apdCtx.Ceil(&lo, &a.X)
} else {
// >3.4 ==> >3
_, _ = apd.BaseContext.Floor(&lo, &a.X)
_, _ = apdCtx.Floor(&lo, &a.X)
}
if y.Op == LessEqualOp {
// <=2.3 ==> <= 2
_, _ = apd.BaseContext.Floor(&hi, &b.X)
_, _ = apdCtx.Floor(&hi, &b.X)
} else {
// <2.3 ==> < 3
_, _ = apd.BaseContext.Ceil(&hi, &b.X)
_, _ = apdCtx.Ceil(&hi, &b.X)
}
}

Expand Down Expand Up @@ -133,23 +133,23 @@ func SimplifyBounds(ctx *OpContext, k Kind, x, y *BoundValue) Value {
case diff == 1:
if k&FloatKind == 0 {
if x.Op == GreaterEqualOp && y.Op == LessThanOp {
return ctx.NewNum(&lo, k&NumKind, x, y)
return ctx.newNum(&lo, k&NumKind, x, y)
}
if x.Op == GreaterThanOp && y.Op == LessEqualOp {
return ctx.NewNum(&hi, k&NumKind, x, y)
return ctx.newNum(&hi, k&NumKind, x, y)
}
}

case diff == 2:
if k&FloatKind == 0 && x.Op == GreaterThanOp && y.Op == LessThanOp {
_, _ = apd.BaseContext.Add(&d, d.SetInt64(1), &lo)
return ctx.NewNum(&d, k&NumKind, x, y)
return ctx.newNum(&d, k&NumKind, x, y)

}

case diff == 0:
if x.Op == GreaterEqualOp && y.Op == LessEqualOp {
return ctx.NewNum(&lo, k&NumKind, x, y)
return ctx.newNum(&lo, k&NumKind, x, y)
}
fallthrough

Expand Down

0 comments on commit 8b13752

Please sign in to comment.