github.com/joomcode/cue@v0.4.4-0.20221111115225-539fe3512047/pkg/math/manual.go (about) 1 // Copyright 2018 The 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 15 package math 16 17 import ( 18 "math/big" 19 20 "github.com/cockroachdb/apd/v2" 21 22 "github.com/joomcode/cue/internal" 23 ) 24 25 func roundContext(rounder string) *apd.Context { 26 c := *apdContext 27 c.Rounding = rounder 28 return &c 29 } 30 31 // TODO: for now we convert Decimals to int. This allows the desired type to be 32 // conveyed. This has the disadvantage tht a number like 1E10000 will need to be 33 // expanded. Eventually it would be better to to unify number types and allow 34 // anything that results in an integer to pose as an integer type. 35 func toInt(d *internal.Decimal) *big.Int { 36 i := &d.Coeff 37 if d.Negative { 38 i.Neg(i) 39 } 40 return i 41 } 42 43 // Floor returns the greatest integer value less than or equal to x. 44 // 45 // Special cases are: 46 // Floor(±0) = ±0 47 // Floor(±Inf) = ±Inf 48 // Floor(NaN) = NaN 49 func Floor(x *internal.Decimal) (*big.Int, error) { 50 var d internal.Decimal 51 _, err := apdContext.Floor(&d, x) 52 _, _ = apdContext.Quantize(&d, &d, 0) 53 return toInt(&d), err 54 } 55 56 // Ceil returns the least integer value greater than or equal to x. 57 // 58 // Special cases are: 59 // Ceil(±0) = ±0 60 // Ceil(±Inf) = ±Inf 61 // Ceil(NaN) = NaN 62 func Ceil(x *internal.Decimal) (*big.Int, error) { 63 var d internal.Decimal 64 _, err := apdContext.Ceil(&d, x) 65 _, _ = apdContext.Quantize(&d, &d, 0) 66 return toInt(&d), err 67 } 68 69 var roundTruncContext = roundContext(apd.RoundDown) 70 71 // Trunc returns the integer value of x. 72 // 73 // Special cases are: 74 // Trunc(±0) = ±0 75 // Trunc(±Inf) = ±Inf 76 // Trunc(NaN) = NaN 77 func Trunc(x *internal.Decimal) (*big.Int, error) { 78 var d internal.Decimal 79 _, err := roundTruncContext.RoundToIntegralExact(&d, x) 80 return toInt(&d), err 81 } 82 83 var roundUpContext = roundContext(apd.RoundHalfUp) 84 85 // Round returns the nearest integer, rounding half away from zero. 86 // 87 // Special cases are: 88 // Round(±0) = ±0 89 // Round(±Inf) = ±Inf 90 // Round(NaN) = NaN 91 func Round(x *internal.Decimal) (*big.Int, error) { 92 var d internal.Decimal 93 _, err := roundUpContext.RoundToIntegralExact(&d, x) 94 return toInt(&d), err 95 } 96 97 var roundEvenContext = roundContext(apd.RoundHalfEven) 98 99 // RoundToEven returns the nearest integer, rounding ties to even. 100 // 101 // Special cases are: 102 // RoundToEven(±0) = ±0 103 // RoundToEven(±Inf) = ±Inf 104 // RoundToEven(NaN) = NaN 105 func RoundToEven(x *internal.Decimal) (*big.Int, error) { 106 var d internal.Decimal 107 _, err := roundEvenContext.RoundToIntegralExact(&d, x) 108 return toInt(&d), err 109 } 110 111 var mulContext = apd.BaseContext.WithPrecision(1) 112 113 // MultipleOf reports whether x is a multiple of y. 114 func MultipleOf(x, y *internal.Decimal) (bool, error) { 115 var d apd.Decimal 116 cond, err := mulContext.Quo(&d, x, y) 117 return !cond.Inexact(), err 118 }