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  }