1// Copyright 2020 CUE Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package adt 16 17import ( 18 "math/big" 19 20 "github.com/cockroachdb/apd/v2" 21) 22 23var apdCtx apd.Context 24 25func init() { 26 apdCtx = apd.BaseContext 27 apdCtx.Precision = 24 28} 29 30func (n *Num) Impl() *apd.Decimal { 31 return &n.X 32} 33 34func (n *Num) Negative() bool { 35 return n.X.Negative 36} 37 38func (a *Num) Cmp(b *Num) int { 39 return a.X.Cmp(&b.X) 40} 41 42func (c *OpContext) Add(a, b *Num) Value { 43 return numOp(c, apdCtx.Add, a, b) 44} 45 46func (c *OpContext) Sub(a, b *Num) Value { 47 return numOp(c, apdCtx.Sub, a, b) 48} 49 50func (c *OpContext) Mul(a, b *Num) Value { 51 return numOp(c, apdCtx.Mul, a, b) 52} 53 54func (c *OpContext) Quo(a, b *Num) Value { 55 v := numOp(c, apdCtx.Quo, a, b) 56 if n, ok := v.(*Num); ok { 57 n.K = FloatKind 58 } 59 return v 60} 61 62func (c *OpContext) Pow(a, b *Num) Value { 63 return numOp(c, apdCtx.Pow, a, b) 64} 65 66type numFunc func(z, x, y *apd.Decimal) (apd.Condition, error) 67 68func numOp(c *OpContext, fn numFunc, x, y *Num) Value { 69 var d apd.Decimal 70 71 cond, err := fn(&d, &x.X, &y.X) 72 73 if err != nil { 74 return c.NewErrf("failed arithmetic: %v", err) 75 } 76 77 if cond.DivisionByZero() { 78 return c.NewErrf("division by zero") 79 } 80 81 k := x.Kind() & y.Kind() 82 if k == 0 { 83 k = FloatKind 84 } 85 return c.newNum(&d, k) 86} 87 88func (c *OpContext) IntDiv(a, b *Num) Value { 89 return intDivOp(c, (*big.Int).Div, a, b) 90} 91 92func (c *OpContext) IntMod(a, b *Num) Value { 93 return intDivOp(c, (*big.Int).Mod, a, b) 94} 95 96func (c *OpContext) IntQuo(a, b *Num) Value { 97 return intDivOp(c, (*big.Int).Quo, a, b) 98} 99 100func (c *OpContext) IntRem(a, b *Num) Value { 101 return intDivOp(c, (*big.Int).Rem, a, b) 102} 103 104type intFunc func(z, x, y *big.Int) *big.Int 105 106func intDivOp(c *OpContext, fn intFunc, a, b *Num) Value { 107 if b.X.IsZero() { 108 return c.NewErrf("division by zero") 109 } 110 111 var x, y apd.Decimal 112 _, _ = apdCtx.RoundToIntegralValue(&x, &a.X) 113 if x.Negative { 114 x.Coeff.Neg(&x.Coeff) 115 } 116 _, _ = apdCtx.RoundToIntegralValue(&y, &b.X) 117 if y.Negative { 118 y.Coeff.Neg(&y.Coeff) 119 } 120 121 var d apd.Decimal 122 123 fn(&d.Coeff, &x.Coeff, &y.Coeff) 124 125 if d.Coeff.Sign() < 0 { 126 d.Coeff.Neg(&d.Coeff) 127 d.Negative = true 128 } 129 130 return c.newNum(&d, IntKind) 131} 132