github.com/k14s/starlark-go@v0.0.0-20200720175618-3a5c849cc368/starlark/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 starlark 6 7 import ( 8 "fmt" 9 "math" 10 "math/big" 11 "strconv" 12 13 "github.com/k14s/starlark-go/syntax" 14 ) 15 16 // Int is the type of a Starlark 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 starlark 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 Starlark int for the specified signed integer. 40 func MakeInt(x int) Int { return MakeInt64(int64(x)) } 41 42 // MakeInt64 returns a Starlark 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 Starlark int for the specified unsigned integer. 51 func MakeUint(x uint) Int { return MakeUint64(uint64(x)) } 52 53 // MakeUint64 returns a Starlark 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 Starlark 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 }