github.com/lab47/exprcore@v0.0.0-20210525052339-fb7d6bd9331e/exprcore/int.go (about)

     1  // Copyright 2017 The Bazel Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package exprcore
     6  
     7  import (
     8  	"fmt"
     9  	"math"
    10  	"math/big"
    11  	"strconv"
    12  
    13  	"github.com/lab47/exprcore/syntax"
    14  )
    15  
    16  // Int is the type of a exprcore int.
    17  type Int struct {
    18  	// We use only the signed 32 bit range of small to ensure
    19  	// that small+small and small*small do not overflow.
    20  
    21  	small int64    // minint32 <= small <= maxint32
    22  	big   *big.Int // big != nil <=> value is not representable as int32
    23  }
    24  
    25  // newBig allocates a new big.Int.
    26  func newBig(x int64) *big.Int {
    27  	if 0 <= x && int64(big.Word(x)) == x {
    28  		// x is guaranteed to fit into a single big.Word.
    29  		// Most exprcore ints are small,
    30  		// but math/big assumes that since you've chosen to use math/big,
    31  		// your big.Ints will probably grow, so it over-allocates.
    32  		// Avoid that over-allocation by manually constructing a single-word slice.
    33  		// See https://golang.org/cl/150999, which will hopefully land in Go 1.13.
    34  		return new(big.Int).SetBits([]big.Word{big.Word(x)})
    35  	}
    36  	return big.NewInt(x)
    37  }
    38  
    39  // MakeInt returns a exprcore int for the specified signed integer.
    40  func MakeInt(x int) Int { return MakeInt64(int64(x)) }
    41  
    42  // MakeInt64 returns a exprcore int for the specified int64.
    43  func MakeInt64(x int64) Int {
    44  	if math.MinInt32 <= x && x <= math.MaxInt32 {
    45  		return Int{small: x}
    46  	}
    47  	return Int{big: newBig(x)}
    48  }
    49  
    50  // MakeUint returns a exprcore int for the specified unsigned integer.
    51  func MakeUint(x uint) Int { return MakeUint64(uint64(x)) }
    52  
    53  // MakeUint64 returns a exprcore int for the specified uint64.
    54  func MakeUint64(x uint64) Int {
    55  	if x <= math.MaxInt32 {
    56  		return Int{small: int64(x)}
    57  	}
    58  	if uint64(big.Word(x)) == x {
    59  		// See comment in newBig for an explanation of this optimization.
    60  		return Int{big: new(big.Int).SetBits([]big.Word{big.Word(x)})}
    61  	}
    62  	return Int{big: new(big.Int).SetUint64(x)}
    63  }
    64  
    65  // MakeBigInt returns a exprcore int for the specified big.Int.
    66  // The caller must not subsequently modify x.
    67  func MakeBigInt(x *big.Int) Int {
    68  	if n := x.BitLen(); n < 32 || n == 32 && x.Int64() == math.MinInt32 {
    69  		return Int{small: x.Int64()}
    70  	}
    71  	return Int{big: x}
    72  }
    73  
    74  var (
    75  	zero, one = Int{small: 0}, Int{small: 1}
    76  	oneBig    = newBig(1)
    77  
    78  	_ HasUnary = Int{}
    79  )
    80  
    81  // Unary implements the operations +int, -int, and ~int.
    82  func (i Int) Unary(op syntax.Token) (Value, error) {
    83  	switch op {
    84  	case syntax.MINUS:
    85  		return zero.Sub(i), nil
    86  	case syntax.PLUS:
    87  		return i, nil
    88  	case syntax.TILDE:
    89  		return i.Not(), nil
    90  	}
    91  	return nil, nil
    92  }
    93  
    94  // Int64 returns the value as an int64.
    95  // If it is not exactly representable the result is undefined and ok is false.
    96  func (i Int) Int64() (_ int64, ok bool) {
    97  	if i.big != nil {
    98  		x, acc := bigintToInt64(i.big)
    99  		if acc != big.Exact {
   100  			return // inexact
   101  		}
   102  		return x, true
   103  	}
   104  	return i.small, true
   105  }
   106  
   107  // BigInt returns the value as a big.Int.
   108  // The returned variable must not be modified by the client.
   109  func (i Int) BigInt() *big.Int {
   110  	if i.big != nil {
   111  		return i.big
   112  	}
   113  	return newBig(i.small)
   114  }
   115  
   116  // Uint64 returns the value as a uint64.
   117  // If it is not exactly representable the result is undefined and ok is false.
   118  func (i Int) Uint64() (_ uint64, ok bool) {
   119  	if i.big != nil {
   120  		x, acc := bigintToUint64(i.big)
   121  		if acc != big.Exact {
   122  			return // inexact
   123  		}
   124  		return x, true
   125  	}
   126  	if i.small < 0 {
   127  		return // inexact
   128  	}
   129  	return uint64(i.small), true
   130  }
   131  
   132  // The math/big API should provide this function.
   133  func bigintToInt64(i *big.Int) (int64, big.Accuracy) {
   134  	sign := i.Sign()
   135  	if sign > 0 {
   136  		if i.Cmp(maxint64) > 0 {
   137  			return math.MaxInt64, big.Below
   138  		}
   139  	} else if sign < 0 {
   140  		if i.Cmp(minint64) < 0 {
   141  			return math.MinInt64, big.Above
   142  		}
   143  	}
   144  	return i.Int64(), big.Exact
   145  }
   146  
   147  // The math/big API should provide this function.
   148  func bigintToUint64(i *big.Int) (uint64, big.Accuracy) {
   149  	sign := i.Sign()
   150  	if sign > 0 {
   151  		if i.BitLen() > 64 {
   152  			return math.MaxUint64, big.Below
   153  		}
   154  	} else if sign < 0 {
   155  		return 0, big.Above
   156  	}
   157  	return i.Uint64(), big.Exact
   158  }
   159  
   160  var (
   161  	minint64 = new(big.Int).SetInt64(math.MinInt64)
   162  	maxint64 = new(big.Int).SetInt64(math.MaxInt64)
   163  )
   164  
   165  func (i Int) Format(s fmt.State, ch rune) {
   166  	if i.big != nil {
   167  		i.big.Format(s, ch)
   168  		return
   169  	}
   170  	newBig(i.small).Format(s, ch)
   171  }
   172  func (i Int) String() string {
   173  	if i.big != nil {
   174  		return i.big.Text(10)
   175  	}
   176  	return strconv.FormatInt(i.small, 10)
   177  }
   178  func (i Int) Type() string { return "int" }
   179  func (i Int) Freeze()      {} // immutable
   180  func (i Int) Truth() Bool  { return i.Sign() != 0 }
   181  func (i Int) Hash() (uint32, error) {
   182  	var lo big.Word
   183  	if i.big != nil {
   184  		lo = i.big.Bits()[0]
   185  	} else {
   186  		lo = big.Word(i.small)
   187  	}
   188  	return 12582917 * uint32(lo+3), nil
   189  }
   190  func (x Int) CompareSameType(op syntax.Token, v Value, depth int) (bool, error) {
   191  	y := v.(Int)
   192  	if x.big != nil || y.big != nil {
   193  		return threeway(op, x.BigInt().Cmp(y.BigInt())), nil
   194  	}
   195  	return threeway(op, signum64(x.small-y.small)), nil
   196  }
   197  
   198  // Float returns the float value nearest i.
   199  func (i Int) Float() Float {
   200  	if i.big != nil {
   201  		f, _ := new(big.Float).SetInt(i.big).Float64()
   202  		return Float(f)
   203  	}
   204  	return Float(i.small)
   205  }
   206  
   207  func (x Int) Sign() int {
   208  	if x.big != nil {
   209  		return x.big.Sign()
   210  	}
   211  	return signum64(x.small)
   212  }
   213  
   214  func (x Int) Add(y Int) Int {
   215  	if x.big != nil || y.big != nil {
   216  		return MakeBigInt(new(big.Int).Add(x.BigInt(), y.BigInt()))
   217  	}
   218  	return MakeInt64(x.small + y.small)
   219  }
   220  func (x Int) Sub(y Int) Int {
   221  	if x.big != nil || y.big != nil {
   222  		return MakeBigInt(new(big.Int).Sub(x.BigInt(), y.BigInt()))
   223  	}
   224  	return MakeInt64(x.small - y.small)
   225  }
   226  func (x Int) Mul(y Int) Int {
   227  	if x.big != nil || y.big != nil {
   228  		return MakeBigInt(new(big.Int).Mul(x.BigInt(), y.BigInt()))
   229  	}
   230  	return MakeInt64(x.small * y.small)
   231  }
   232  func (x Int) Or(y Int) Int {
   233  	if x.big != nil || y.big != nil {
   234  		return Int{big: new(big.Int).Or(x.BigInt(), y.BigInt())}
   235  	}
   236  	return Int{small: x.small | y.small}
   237  }
   238  func (x Int) And(y Int) Int {
   239  	if x.big != nil || y.big != nil {
   240  		return MakeBigInt(new(big.Int).And(x.BigInt(), y.BigInt()))
   241  	}
   242  	return Int{small: x.small & y.small}
   243  }
   244  func (x Int) Xor(y Int) Int {
   245  	if x.big != nil || y.big != nil {
   246  		return MakeBigInt(new(big.Int).Xor(x.BigInt(), y.BigInt()))
   247  	}
   248  	return Int{small: x.small ^ y.small}
   249  }
   250  func (x Int) Not() Int {
   251  	if x.big != nil {
   252  		return MakeBigInt(new(big.Int).Not(x.big))
   253  	}
   254  	return Int{small: ^x.small}
   255  }
   256  func (x Int) Lsh(y uint) Int { return MakeBigInt(new(big.Int).Lsh(x.BigInt(), y)) }
   257  func (x Int) Rsh(y uint) Int { return MakeBigInt(new(big.Int).Rsh(x.BigInt(), y)) }
   258  
   259  // Precondition: y is nonzero.
   260  func (x Int) Div(y Int) Int {
   261  	// http://python-history.blogspot.com/2010/08/why-pythons-integer-division-floors.html
   262  	if x.big != nil || y.big != nil {
   263  		xb, yb := x.BigInt(), y.BigInt()
   264  
   265  		var quo, rem big.Int
   266  		quo.QuoRem(xb, yb, &rem)
   267  		if (xb.Sign() < 0) != (yb.Sign() < 0) && rem.Sign() != 0 {
   268  			quo.Sub(&quo, oneBig)
   269  		}
   270  		return MakeBigInt(&quo)
   271  	}
   272  	quo := x.small / y.small
   273  	rem := x.small % y.small
   274  	if (x.small < 0) != (y.small < 0) && rem != 0 {
   275  		quo -= 1
   276  	}
   277  	return MakeInt64(quo)
   278  }
   279  
   280  // Precondition: y is nonzero.
   281  func (x Int) Mod(y Int) Int {
   282  	if x.big != nil || y.big != nil {
   283  		xb, yb := x.BigInt(), y.BigInt()
   284  
   285  		var quo, rem big.Int
   286  		quo.QuoRem(xb, yb, &rem)
   287  		if (xb.Sign() < 0) != (yb.Sign() < 0) && rem.Sign() != 0 {
   288  			rem.Add(&rem, yb)
   289  		}
   290  		return MakeBigInt(&rem)
   291  	}
   292  	rem := x.small % y.small
   293  	if (x.small < 0) != (y.small < 0) && rem != 0 {
   294  		rem += y.small
   295  	}
   296  	return Int{small: rem}
   297  }
   298  
   299  func (i Int) rational() *big.Rat {
   300  	if i.big != nil {
   301  		return new(big.Rat).SetInt(i.big)
   302  	}
   303  	return new(big.Rat).SetInt64(i.small)
   304  }
   305  
   306  // AsInt32 returns the value of x if is representable as an int32.
   307  func AsInt32(x Value) (int, error) {
   308  	i, ok := x.(Int)
   309  	if !ok {
   310  		return 0, fmt.Errorf("got %s, want int", x.Type())
   311  	}
   312  	if i.big != nil {
   313  		return 0, fmt.Errorf("%s out of range", i)
   314  	}
   315  	return int(i.small), nil
   316  }
   317  
   318  // NumberToInt converts a number x to an integer value.
   319  // An int is returned unchanged, a float is truncated towards zero.
   320  // NumberToInt reports an error for all other values.
   321  func NumberToInt(x Value) (Int, error) {
   322  	switch x := x.(type) {
   323  	case Int:
   324  		return x, nil
   325  	case Float:
   326  		f := float64(x)
   327  		if math.IsInf(f, 0) {
   328  			return zero, fmt.Errorf("cannot convert float infinity to integer")
   329  		} else if math.IsNaN(f) {
   330  			return zero, fmt.Errorf("cannot convert float NaN to integer")
   331  		}
   332  		return finiteFloatToInt(x), nil
   333  
   334  	}
   335  	return zero, fmt.Errorf("cannot convert %s to int", x.Type())
   336  }
   337  
   338  // finiteFloatToInt converts f to an Int, truncating towards zero.
   339  // f must be finite.
   340  func finiteFloatToInt(f Float) Int {
   341  	if math.MinInt64 <= f && f <= math.MaxInt64 {
   342  		// small values
   343  		return MakeInt64(int64(f))
   344  	}
   345  	rat := f.rational()
   346  	if rat == nil {
   347  		panic(f) // non-finite
   348  	}
   349  	return MakeBigInt(new(big.Int).Div(rat.Num(), rat.Denom()))
   350  }