cuelang.org/go@v0.10.1/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/v3"
    21  
    22  	"cuelang.org/go/internal"
    23  )
    24  
    25  func roundContext(rounder apd.Rounder) internal.Context {
    26  	c := internal.BaseContext
    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 that a number like 1E10000 will need to be
    33  // expanded. Eventually it would be better to unify number types and allow
    34  // anything that results in an integer to pose as an integer type.
    35  // TODO: this is likely buggy, as we discard d.Exponent entirely.
    36  func toInt(d *internal.Decimal) *big.Int {
    37  	i := &d.Coeff
    38  	if d.Negative {
    39  		i.Neg(i)
    40  	}
    41  	return i.MathBigInt()
    42  }
    43  
    44  // Floor returns the greatest integer value less than or equal to x.
    45  //
    46  // Special cases are:
    47  //
    48  //	Floor(±0) = ±0
    49  //	Floor(±Inf) = ±Inf
    50  //	Floor(NaN) = NaN
    51  func Floor(x *internal.Decimal) (*big.Int, error) {
    52  	var d internal.Decimal
    53  	_, err := internal.BaseContext.Floor(&d, x)
    54  	_, _ = internal.BaseContext.Quantize(&d, &d, 0)
    55  	return toInt(&d), err
    56  }
    57  
    58  // Ceil returns the least integer value greater than or equal to x.
    59  //
    60  // Special cases are:
    61  //
    62  //	Ceil(±0) = ±0
    63  //	Ceil(±Inf) = ±Inf
    64  //	Ceil(NaN) = NaN
    65  func Ceil(x *internal.Decimal) (*big.Int, error) {
    66  	var d internal.Decimal
    67  	_, err := internal.BaseContext.Ceil(&d, x)
    68  	_, _ = internal.BaseContext.Quantize(&d, &d, 0)
    69  	return toInt(&d), err
    70  }
    71  
    72  var roundTruncContext = roundContext(apd.RoundDown)
    73  
    74  // Trunc returns the integer value of x.
    75  //
    76  // Special cases are:
    77  //
    78  //	Trunc(±0) = ±0
    79  //	Trunc(±Inf) = ±Inf
    80  //	Trunc(NaN) = NaN
    81  func Trunc(x *internal.Decimal) (*big.Int, error) {
    82  	var d internal.Decimal
    83  	_, err := roundTruncContext.RoundToIntegralExact(&d, x)
    84  	return toInt(&d), err
    85  }
    86  
    87  var roundUpContext = roundContext(apd.RoundHalfUp)
    88  
    89  // Round returns the nearest integer, rounding half away from zero.
    90  //
    91  // Special cases are:
    92  //
    93  //	Round(±0) = ±0
    94  //	Round(±Inf) = ±Inf
    95  //	Round(NaN) = NaN
    96  func Round(x *internal.Decimal) (*big.Int, error) {
    97  	var d internal.Decimal
    98  	_, err := roundUpContext.RoundToIntegralExact(&d, x)
    99  	return toInt(&d), err
   100  }
   101  
   102  var roundEvenContext = roundContext(apd.RoundHalfEven)
   103  
   104  // RoundToEven returns the nearest integer, rounding ties to even.
   105  //
   106  // Special cases are:
   107  //
   108  //	RoundToEven(±0) = ±0
   109  //	RoundToEven(±Inf) = ±Inf
   110  //	RoundToEven(NaN) = NaN
   111  func RoundToEven(x *internal.Decimal) (*big.Int, error) {
   112  	var d internal.Decimal
   113  	_, err := roundEvenContext.RoundToIntegralExact(&d, x)
   114  	return toInt(&d), err
   115  }
   116  
   117  // MultipleOf reports whether x is a multiple of y.
   118  func MultipleOf(x, y *internal.Decimal) (bool, error) {
   119  	var d apd.Decimal
   120  
   121  	// TODO: It would be preferable to use internal.BaseContext.Rem here, and directly
   122  	//       check the result for 0. However, this currently fails with "division impossible".
   123  	//       Fix this when https://github.com/cockroachdb/apd/issues/134 is resolved.
   124  	_, err := internal.BaseContext.Quo(&d, x, y)
   125  	if err != nil {
   126  		return false, err
   127  	}
   128  	var frac apd.Decimal
   129  	d.Modf(nil, &frac)
   130  	return frac.IsZero(), nil
   131  }