github.com/google/skylark@v0.0.0-20181101142754-a5f7082aabed/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 skylark
     6  
     7  import (
     8  	"fmt"
     9  	"math"
    10  	"math/big"
    11  
    12  	"github.com/google/skylark/syntax"
    13  )
    14  
    15  // Int is the type of a Skylark int.
    16  type Int struct{ bigint *big.Int }
    17  
    18  // MakeInt returns a Skylark int for the specified signed integer.
    19  func MakeInt(x int) Int { return MakeInt64(int64(x)) }
    20  
    21  // MakeInt64 returns a Skylark int for the specified int64.
    22  func MakeInt64(x int64) Int {
    23  	if 0 <= x && x < int64(len(smallint)) {
    24  		if !smallintok {
    25  			panic("MakeInt64 used before initialization")
    26  		}
    27  		return Int{&smallint[x]}
    28  	}
    29  	return Int{new(big.Int).SetInt64(x)}
    30  }
    31  
    32  // MakeUint returns a Skylark int for the specified unsigned integer.
    33  func MakeUint(x uint) Int { return MakeUint64(uint64(x)) }
    34  
    35  // MakeUint64 returns a Skylark int for the specified uint64.
    36  func MakeUint64(x uint64) Int {
    37  	if x < uint64(len(smallint)) {
    38  		if !smallintok {
    39  			panic("MakeUint64 used before initialization")
    40  		}
    41  		return Int{&smallint[x]}
    42  	}
    43  	return Int{new(big.Int).SetUint64(uint64(x))}
    44  }
    45  
    46  var (
    47  	smallint   [256]big.Int
    48  	smallintok bool
    49  	zero, one  Int
    50  )
    51  
    52  func init() {
    53  	for i := range smallint {
    54  		smallint[i].SetInt64(int64(i))
    55  	}
    56  	smallintok = true
    57  
    58  	zero = MakeInt64(0)
    59  	one = MakeInt64(1)
    60  }
    61  
    62  // Int64 returns the value as an int64.
    63  // If it is not exactly representable the result is undefined and ok is false.
    64  func (i Int) Int64() (_ int64, ok bool) {
    65  	x, acc := bigintToInt64(i.bigint)
    66  	if acc != big.Exact {
    67  		return // inexact
    68  	}
    69  	return x, true
    70  }
    71  
    72  // Uint64 returns the value as a uint64.
    73  // If it is not exactly representable the result is undefined and ok is false.
    74  func (i Int) Uint64() (_ uint64, ok bool) {
    75  	x, acc := bigintToUint64(i.bigint)
    76  	if acc != big.Exact {
    77  		return // inexact
    78  	}
    79  	return x, true
    80  }
    81  
    82  // The math/big API should provide this function.
    83  func bigintToInt64(i *big.Int) (int64, big.Accuracy) {
    84  	sign := i.Sign()
    85  	if sign > 0 {
    86  		if i.Cmp(maxint64) > 0 {
    87  			return math.MaxInt64, big.Below
    88  		}
    89  	} else if sign < 0 {
    90  		if i.Cmp(minint64) < 0 {
    91  			return math.MinInt64, big.Above
    92  		}
    93  	}
    94  	return i.Int64(), big.Exact
    95  }
    96  
    97  // The math/big API should provide this function.
    98  func bigintToUint64(i *big.Int) (uint64, big.Accuracy) {
    99  	sign := i.Sign()
   100  	if sign > 0 {
   101  		if i.BitLen() > 64 {
   102  			return math.MaxUint64, big.Below
   103  		}
   104  	} else if sign < 0 {
   105  		return 0, big.Above
   106  	}
   107  	return i.Uint64(), big.Exact
   108  }
   109  
   110  var (
   111  	minint64 = new(big.Int).SetInt64(math.MinInt64)
   112  	maxint64 = new(big.Int).SetInt64(math.MaxInt64)
   113  )
   114  
   115  func (i Int) String() string { return i.bigint.String() }
   116  func (i Int) Type() string   { return "int" }
   117  func (i Int) Freeze()        {} // immutable
   118  func (i Int) Truth() Bool    { return i.Sign() != 0 }
   119  func (i Int) Hash() (uint32, error) {
   120  	var lo big.Word
   121  	if i.bigint.Sign() != 0 {
   122  		lo = i.bigint.Bits()[0]
   123  	}
   124  	return 12582917 * uint32(lo+3), nil
   125  }
   126  func (x Int) CompareSameType(op syntax.Token, y Value, depth int) (bool, error) {
   127  	return threeway(op, x.bigint.Cmp(y.(Int).bigint)), nil
   128  }
   129  
   130  // Float returns the float value nearest i.
   131  func (i Int) Float() Float {
   132  	// TODO(adonovan): opt: handle common values without allocation.
   133  	f, _ := new(big.Float).SetInt(i.bigint).Float64()
   134  	return Float(f)
   135  }
   136  
   137  func (x Int) Sign() int      { return x.bigint.Sign() }
   138  func (x Int) Add(y Int) Int  { return Int{new(big.Int).Add(x.bigint, y.bigint)} }
   139  func (x Int) Sub(y Int) Int  { return Int{new(big.Int).Sub(x.bigint, y.bigint)} }
   140  func (x Int) Mul(y Int) Int  { return Int{new(big.Int).Mul(x.bigint, y.bigint)} }
   141  func (x Int) Or(y Int) Int   { return Int{new(big.Int).Or(x.bigint, y.bigint)} }
   142  func (x Int) And(y Int) Int  { return Int{new(big.Int).And(x.bigint, y.bigint)} }
   143  func (x Int) Xor(y Int) Int  { return Int{new(big.Int).Xor(x.bigint, y.bigint)} }
   144  func (x Int) Not() Int       { return Int{new(big.Int).Not(x.bigint)} }
   145  func (x Int) Lsh(y uint) Int { return Int{new(big.Int).Lsh(x.bigint, y)} }
   146  func (x Int) Rsh(y uint) Int { return Int{new(big.Int).Rsh(x.bigint, y)} }
   147  
   148  // Precondition: y is nonzero.
   149  func (x Int) Div(y Int) Int {
   150  	// http://python-history.blogspot.com/2010/08/why-pythons-integer-division-floors.html
   151  	var quo, rem big.Int
   152  	quo.QuoRem(x.bigint, y.bigint, &rem)
   153  	if (x.bigint.Sign() < 0) != (y.bigint.Sign() < 0) && rem.Sign() != 0 {
   154  		quo.Sub(&quo, one.bigint)
   155  	}
   156  	return Int{&quo}
   157  }
   158  
   159  // Precondition: y is nonzero.
   160  func (x Int) Mod(y Int) Int {
   161  	var quo, rem big.Int
   162  	quo.QuoRem(x.bigint, y.bigint, &rem)
   163  	if (x.bigint.Sign() < 0) != (y.bigint.Sign() < 0) && rem.Sign() != 0 {
   164  		rem.Add(&rem, y.bigint)
   165  	}
   166  	return Int{&rem}
   167  }
   168  
   169  func (i Int) rational() *big.Rat { return new(big.Rat).SetInt(i.bigint) }
   170  
   171  // AsInt32 returns the value of x if is representable as an int32.
   172  func AsInt32(x Value) (int, error) {
   173  	i, ok := x.(Int)
   174  	if !ok {
   175  		return 0, fmt.Errorf("got %s, want int", x.Type())
   176  	}
   177  	if i.bigint.BitLen() <= 32 {
   178  		v := i.bigint.Int64()
   179  		if v >= math.MinInt32 && v <= math.MaxInt32 {
   180  			return int(v), nil
   181  		}
   182  	}
   183  	return 0, fmt.Errorf("%s out of range", i)
   184  }
   185  
   186  // NumberToInt converts a number x to an integer value.
   187  // An int is returned unchanged, a float is truncated towards zero.
   188  // NumberToInt reports an error for all other values.
   189  func NumberToInt(x Value) (Int, error) {
   190  	switch x := x.(type) {
   191  	case Int:
   192  		return x, nil
   193  	case Float:
   194  		f := float64(x)
   195  		if math.IsInf(f, 0) {
   196  			return zero, fmt.Errorf("cannot convert float infinity to integer")
   197  		} else if math.IsNaN(f) {
   198  			return zero, fmt.Errorf("cannot convert float NaN to integer")
   199  		}
   200  		return finiteFloatToInt(x), nil
   201  
   202  	}
   203  	return zero, fmt.Errorf("cannot convert %s to int", x.Type())
   204  }
   205  
   206  // finiteFloatToInt converts f to an Int, truncating towards zero.
   207  // f must be finite.
   208  func finiteFloatToInt(f Float) Int {
   209  	var i big.Int
   210  	if math.MinInt64 <= f && f <= math.MaxInt64 {
   211  		// small values
   212  		i.SetInt64(int64(f))
   213  	} else {
   214  		rat := f.rational()
   215  		if rat == nil {
   216  			panic(f) // non-finite
   217  		}
   218  		i.Div(rat.Num(), rat.Denom())
   219  	}
   220  	return Int{&i}
   221  }