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 }