blob: 397be865dae5586c1fa29840c282018ae4146f78 [file] [log] [blame]
Alan Donovan312d1a52017-10-02 10:10:28 -04001// 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
Alan Donovane3deafe2018-10-23 11:05:09 -04005package starlark
Alan Donovan312d1a52017-10-02 10:10:28 -04006
7import (
8 "fmt"
9 "math"
10 "math/big"
Edward McFarlaned50186b2019-02-24 19:44:57 +000011 "strconv"
Alan Donovan312d1a52017-10-02 10:10:28 -040012
Alan Donovan6beab7e2018-10-31 17:53:09 -040013 "go.starlark.net/syntax"
Alan Donovan312d1a52017-10-02 10:10:28 -040014)
15
Alan Donovane3deafe2018-10-23 11:05:09 -040016// Int is the type of a Starlark int.
alandonovanc6daab62020-06-17 14:27:56 -040017//
18// The zero value is not a legal value; use MakeInt(0).
19type Int struct{ impl intImpl }
Edward McFarlaned50186b2019-02-24 19:44:57 +000020
alandonovanc6daab62020-06-17 14:27:56 -040021// --- high-level accessors ---
Alan Donovan312d1a52017-10-02 10:10:28 -040022
Alan Donovane3deafe2018-10-23 11:05:09 -040023// MakeInt returns a Starlark int for the specified signed integer.
Alan Donovan312d1a52017-10-02 10:10:28 -040024func MakeInt(x int) Int { return MakeInt64(int64(x)) }
25
Alan Donovane3deafe2018-10-23 11:05:09 -040026// MakeInt64 returns a Starlark int for the specified int64.
Alan Donovan312d1a52017-10-02 10:10:28 -040027func MakeInt64(x int64) Int {
Edward McFarlaned50186b2019-02-24 19:44:57 +000028 if math.MinInt32 <= x && x <= math.MaxInt32 {
alandonovanc6daab62020-06-17 14:27:56 -040029 return makeSmallInt(x)
Alan Donovan312d1a52017-10-02 10:10:28 -040030 }
alandonovanc6daab62020-06-17 14:27:56 -040031 return makeBigInt(big.NewInt(x))
Alan Donovan312d1a52017-10-02 10:10:28 -040032}
33
Alan Donovane3deafe2018-10-23 11:05:09 -040034// MakeUint returns a Starlark int for the specified unsigned integer.
Alan Donovan312d1a52017-10-02 10:10:28 -040035func MakeUint(x uint) Int { return MakeUint64(uint64(x)) }
36
Alan Donovane3deafe2018-10-23 11:05:09 -040037// MakeUint64 returns a Starlark int for the specified uint64.
Alan Donovan312d1a52017-10-02 10:10:28 -040038func MakeUint64(x uint64) Int {
Edward McFarlaned50186b2019-02-24 19:44:57 +000039 if x <= math.MaxInt32 {
alandonovanc6daab62020-06-17 14:27:56 -040040 return makeSmallInt(int64(x))
Alan Donovan312d1a52017-10-02 10:10:28 -040041 }
alandonovanc6daab62020-06-17 14:27:56 -040042 return makeBigInt(new(big.Int).SetUint64(x))
Edward McFarlaned50186b2019-02-24 19:44:57 +000043}
44
45// MakeBigInt returns a Starlark int for the specified big.Int.
46// The caller must not subsequently modify x.
47func MakeBigInt(x *big.Int) Int {
48 if n := x.BitLen(); n < 32 || n == 32 && x.Int64() == math.MinInt32 {
alandonovanc6daab62020-06-17 14:27:56 -040049 return makeSmallInt(x.Int64())
Edward McFarlaned50186b2019-02-24 19:44:57 +000050 }
alandonovanc6daab62020-06-17 14:27:56 -040051 return makeBigInt(x)
Alan Donovan312d1a52017-10-02 10:10:28 -040052}
53
54var (
alandonovanc6daab62020-06-17 14:27:56 -040055 zero, one = makeSmallInt(0), makeSmallInt(1)
56 oneBig = big.NewInt(1)
alandonovan58f91012019-01-03 16:32:47 -050057
58 _ HasUnary = Int{}
Alan Donovan312d1a52017-10-02 10:10:28 -040059)
60
alandonovan58f91012019-01-03 16:32:47 -050061// Unary implements the operations +int, -int, and ~int.
62func (i Int) Unary(op syntax.Token) (Value, error) {
63 switch op {
64 case syntax.MINUS:
65 return zero.Sub(i), nil
66 case syntax.PLUS:
67 return i, nil
68 case syntax.TILDE:
69 return i.Not(), nil
70 }
71 return nil, nil
72}
73
Alan Donovan312d1a52017-10-02 10:10:28 -040074// Int64 returns the value as an int64.
75// If it is not exactly representable the result is undefined and ok is false.
76func (i Int) Int64() (_ int64, ok bool) {
alandonovanc6daab62020-06-17 14:27:56 -040077 iSmall, iBig := i.get()
78 if iBig != nil {
79 x, acc := bigintToInt64(iBig)
Edward McFarlaned50186b2019-02-24 19:44:57 +000080 if acc != big.Exact {
81 return // inexact
82 }
83 return x, true
Alan Donovan312d1a52017-10-02 10:10:28 -040084 }
alandonovanc6daab62020-06-17 14:27:56 -040085 return iSmall, true
Edward McFarlaned50186b2019-02-24 19:44:57 +000086}
87
88// BigInt returns the value as a big.Int.
89// The returned variable must not be modified by the client.
90func (i Int) BigInt() *big.Int {
alandonovanc6daab62020-06-17 14:27:56 -040091 iSmall, iBig := i.get()
92 if iBig != nil {
93 return iBig
Edward McFarlaned50186b2019-02-24 19:44:57 +000094 }
alandonovanc6daab62020-06-17 14:27:56 -040095 return big.NewInt(iSmall)
Alan Donovan312d1a52017-10-02 10:10:28 -040096}
97
98// Uint64 returns the value as a uint64.
99// If it is not exactly representable the result is undefined and ok is false.
100func (i Int) Uint64() (_ uint64, ok bool) {
alandonovanc6daab62020-06-17 14:27:56 -0400101 iSmall, iBig := i.get()
102 if iBig != nil {
103 x, acc := bigintToUint64(iBig)
Edward McFarlaned50186b2019-02-24 19:44:57 +0000104 if acc != big.Exact {
105 return // inexact
106 }
107 return x, true
108 }
alandonovanc6daab62020-06-17 14:27:56 -0400109 if iSmall < 0 {
Alan Donovan312d1a52017-10-02 10:10:28 -0400110 return // inexact
111 }
alandonovanc6daab62020-06-17 14:27:56 -0400112 return uint64(iSmall), true
Alan Donovan312d1a52017-10-02 10:10:28 -0400113}
114
115// The math/big API should provide this function.
116func bigintToInt64(i *big.Int) (int64, big.Accuracy) {
117 sign := i.Sign()
118 if sign > 0 {
119 if i.Cmp(maxint64) > 0 {
120 return math.MaxInt64, big.Below
121 }
122 } else if sign < 0 {
123 if i.Cmp(minint64) < 0 {
124 return math.MinInt64, big.Above
125 }
126 }
127 return i.Int64(), big.Exact
128}
129
130// The math/big API should provide this function.
131func bigintToUint64(i *big.Int) (uint64, big.Accuracy) {
132 sign := i.Sign()
133 if sign > 0 {
134 if i.BitLen() > 64 {
135 return math.MaxUint64, big.Below
136 }
137 } else if sign < 0 {
138 return 0, big.Above
139 }
140 return i.Uint64(), big.Exact
141}
142
143var (
144 minint64 = new(big.Int).SetInt64(math.MinInt64)
145 maxint64 = new(big.Int).SetInt64(math.MaxInt64)
146)
147
Edward McFarlaned50186b2019-02-24 19:44:57 +0000148func (i Int) Format(s fmt.State, ch rune) {
alandonovanc6daab62020-06-17 14:27:56 -0400149 iSmall, iBig := i.get()
150 if iBig != nil {
151 iBig.Format(s, ch)
Edward McFarlaned50186b2019-02-24 19:44:57 +0000152 return
153 }
alandonovanc6daab62020-06-17 14:27:56 -0400154 big.NewInt(iSmall).Format(s, ch)
Edward McFarlaned50186b2019-02-24 19:44:57 +0000155}
156func (i Int) String() string {
alandonovanc6daab62020-06-17 14:27:56 -0400157 iSmall, iBig := i.get()
158 if iBig != nil {
159 return iBig.Text(10)
Edward McFarlaned50186b2019-02-24 19:44:57 +0000160 }
alandonovanc6daab62020-06-17 14:27:56 -0400161 return strconv.FormatInt(iSmall, 10)
Edward McFarlaned50186b2019-02-24 19:44:57 +0000162}
163func (i Int) Type() string { return "int" }
164func (i Int) Freeze() {} // immutable
165func (i Int) Truth() Bool { return i.Sign() != 0 }
Alan Donovan312d1a52017-10-02 10:10:28 -0400166func (i Int) Hash() (uint32, error) {
alandonovanc6daab62020-06-17 14:27:56 -0400167 iSmall, iBig := i.get()
Alan Donovan312d1a52017-10-02 10:10:28 -0400168 var lo big.Word
alandonovanc6daab62020-06-17 14:27:56 -0400169 if iBig != nil {
170 lo = iBig.Bits()[0]
Edward McFarlaned50186b2019-02-24 19:44:57 +0000171 } else {
alandonovanc6daab62020-06-17 14:27:56 -0400172 lo = big.Word(iSmall)
Alan Donovan312d1a52017-10-02 10:10:28 -0400173 }
174 return 12582917 * uint32(lo+3), nil
175}
Edward McFarlaned50186b2019-02-24 19:44:57 +0000176func (x Int) CompareSameType(op syntax.Token, v Value, depth int) (bool, error) {
177 y := v.(Int)
alandonovanc6daab62020-06-17 14:27:56 -0400178 xSmall, xBig := x.get()
179 ySmall, yBig := y.get()
180 if xBig != nil || yBig != nil {
Edward McFarlaned50186b2019-02-24 19:44:57 +0000181 return threeway(op, x.BigInt().Cmp(y.BigInt())), nil
182 }
alandonovanc6daab62020-06-17 14:27:56 -0400183 return threeway(op, signum64(xSmall-ySmall)), nil
Alan Donovan312d1a52017-10-02 10:10:28 -0400184}
185
186// Float returns the float value nearest i.
187func (i Int) Float() Float {
alandonovanc6daab62020-06-17 14:27:56 -0400188 iSmall, iBig := i.get()
189 if iBig != nil {
190 f, _ := new(big.Float).SetInt(iBig).Float64()
Edward McFarlaned50186b2019-02-24 19:44:57 +0000191 return Float(f)
192 }
alandonovanc6daab62020-06-17 14:27:56 -0400193 return Float(iSmall)
Alan Donovan312d1a52017-10-02 10:10:28 -0400194}
195
alandonovan3b7e02e2020-11-11 12:03:41 -0500196// finiteFloat returns the finite float value nearest i,
197// or an error if the magnitude is too large.
198func (i Int) finiteFloat() (Float, error) {
199 f := i.Float()
200 if math.IsInf(float64(f), 0) {
201 return 0, fmt.Errorf("int too large to convert to float")
202 }
203 return f, nil
204}
205
Edward McFarlaned50186b2019-02-24 19:44:57 +0000206func (x Int) Sign() int {
alandonovanc6daab62020-06-17 14:27:56 -0400207 xSmall, xBig := x.get()
208 if xBig != nil {
209 return xBig.Sign()
Edward McFarlaned50186b2019-02-24 19:44:57 +0000210 }
alandonovanc6daab62020-06-17 14:27:56 -0400211 return signum64(xSmall)
Edward McFarlaned50186b2019-02-24 19:44:57 +0000212}
213
214func (x Int) Add(y Int) Int {
alandonovanc6daab62020-06-17 14:27:56 -0400215 xSmall, xBig := x.get()
216 ySmall, yBig := y.get()
217 if xBig != nil || yBig != nil {
Edward McFarlaned50186b2019-02-24 19:44:57 +0000218 return MakeBigInt(new(big.Int).Add(x.BigInt(), y.BigInt()))
219 }
alandonovanc6daab62020-06-17 14:27:56 -0400220 return MakeInt64(xSmall + ySmall)
Edward McFarlaned50186b2019-02-24 19:44:57 +0000221}
222func (x Int) Sub(y Int) Int {
alandonovanc6daab62020-06-17 14:27:56 -0400223 xSmall, xBig := x.get()
224 ySmall, yBig := y.get()
225 if xBig != nil || yBig != nil {
Edward McFarlaned50186b2019-02-24 19:44:57 +0000226 return MakeBigInt(new(big.Int).Sub(x.BigInt(), y.BigInt()))
227 }
alandonovanc6daab62020-06-17 14:27:56 -0400228 return MakeInt64(xSmall - ySmall)
Edward McFarlaned50186b2019-02-24 19:44:57 +0000229}
230func (x Int) Mul(y Int) Int {
alandonovanc6daab62020-06-17 14:27:56 -0400231 xSmall, xBig := x.get()
232 ySmall, yBig := y.get()
233 if xBig != nil || yBig != nil {
Edward McFarlaned50186b2019-02-24 19:44:57 +0000234 return MakeBigInt(new(big.Int).Mul(x.BigInt(), y.BigInt()))
235 }
alandonovanc6daab62020-06-17 14:27:56 -0400236 return MakeInt64(xSmall * ySmall)
Edward McFarlaned50186b2019-02-24 19:44:57 +0000237}
238func (x Int) Or(y Int) Int {
alandonovanc6daab62020-06-17 14:27:56 -0400239 xSmall, xBig := x.get()
240 ySmall, yBig := y.get()
241 if xBig != nil || yBig != nil {
242 return MakeBigInt(new(big.Int).Or(x.BigInt(), y.BigInt()))
Edward McFarlaned50186b2019-02-24 19:44:57 +0000243 }
alandonovanc6daab62020-06-17 14:27:56 -0400244 return makeSmallInt(xSmall | ySmall)
Edward McFarlaned50186b2019-02-24 19:44:57 +0000245}
246func (x Int) And(y Int) Int {
alandonovanc6daab62020-06-17 14:27:56 -0400247 xSmall, xBig := x.get()
248 ySmall, yBig := y.get()
249 if xBig != nil || yBig != nil {
Edward McFarlaned50186b2019-02-24 19:44:57 +0000250 return MakeBigInt(new(big.Int).And(x.BigInt(), y.BigInt()))
251 }
alandonovanc6daab62020-06-17 14:27:56 -0400252 return makeSmallInt(xSmall & ySmall)
Edward McFarlaned50186b2019-02-24 19:44:57 +0000253}
254func (x Int) Xor(y Int) Int {
alandonovanc6daab62020-06-17 14:27:56 -0400255 xSmall, xBig := x.get()
256 ySmall, yBig := y.get()
257 if xBig != nil || yBig != nil {
Edward McFarlaned50186b2019-02-24 19:44:57 +0000258 return MakeBigInt(new(big.Int).Xor(x.BigInt(), y.BigInt()))
259 }
alandonovanc6daab62020-06-17 14:27:56 -0400260 return makeSmallInt(xSmall ^ ySmall)
Edward McFarlaned50186b2019-02-24 19:44:57 +0000261}
262func (x Int) Not() Int {
alandonovanc6daab62020-06-17 14:27:56 -0400263 xSmall, xBig := x.get()
264 if xBig != nil {
265 return MakeBigInt(new(big.Int).Not(xBig))
Edward McFarlaned50186b2019-02-24 19:44:57 +0000266 }
alandonovanc6daab62020-06-17 14:27:56 -0400267 return makeSmallInt(^xSmall)
Edward McFarlaned50186b2019-02-24 19:44:57 +0000268}
269func (x Int) Lsh(y uint) Int { return MakeBigInt(new(big.Int).Lsh(x.BigInt(), y)) }
270func (x Int) Rsh(y uint) Int { return MakeBigInt(new(big.Int).Rsh(x.BigInt(), y)) }
Alan Donovan312d1a52017-10-02 10:10:28 -0400271
272// Precondition: y is nonzero.
273func (x Int) Div(y Int) Int {
alandonovanc6daab62020-06-17 14:27:56 -0400274 xSmall, xBig := x.get()
275 ySmall, yBig := y.get()
Alan Donovan312d1a52017-10-02 10:10:28 -0400276 // http://python-history.blogspot.com/2010/08/why-pythons-integer-division-floors.html
alandonovanc6daab62020-06-17 14:27:56 -0400277 if xBig != nil || yBig != nil {
Edward McFarlaned50186b2019-02-24 19:44:57 +0000278 xb, yb := x.BigInt(), y.BigInt()
279
280 var quo, rem big.Int
281 quo.QuoRem(xb, yb, &rem)
282 if (xb.Sign() < 0) != (yb.Sign() < 0) && rem.Sign() != 0 {
283 quo.Sub(&quo, oneBig)
284 }
285 return MakeBigInt(&quo)
Alan Donovan312d1a52017-10-02 10:10:28 -0400286 }
alandonovanc6daab62020-06-17 14:27:56 -0400287 quo := xSmall / ySmall
288 rem := xSmall % ySmall
289 if (xSmall < 0) != (ySmall < 0) && rem != 0 {
Edward McFarlaned50186b2019-02-24 19:44:57 +0000290 quo -= 1
291 }
292 return MakeInt64(quo)
Alan Donovan312d1a52017-10-02 10:10:28 -0400293}
294
295// Precondition: y is nonzero.
296func (x Int) Mod(y Int) Int {
alandonovanc6daab62020-06-17 14:27:56 -0400297 xSmall, xBig := x.get()
298 ySmall, yBig := y.get()
299 if xBig != nil || yBig != nil {
Edward McFarlaned50186b2019-02-24 19:44:57 +0000300 xb, yb := x.BigInt(), y.BigInt()
301
302 var quo, rem big.Int
303 quo.QuoRem(xb, yb, &rem)
304 if (xb.Sign() < 0) != (yb.Sign() < 0) && rem.Sign() != 0 {
305 rem.Add(&rem, yb)
306 }
307 return MakeBigInt(&rem)
Alan Donovan312d1a52017-10-02 10:10:28 -0400308 }
alandonovanc6daab62020-06-17 14:27:56 -0400309 rem := xSmall % ySmall
310 if (xSmall < 0) != (ySmall < 0) && rem != 0 {
311 rem += ySmall
Edward McFarlaned50186b2019-02-24 19:44:57 +0000312 }
alandonovanc6daab62020-06-17 14:27:56 -0400313 return makeSmallInt(rem)
Alan Donovan312d1a52017-10-02 10:10:28 -0400314}
315
Edward McFarlaned50186b2019-02-24 19:44:57 +0000316func (i Int) rational() *big.Rat {
alandonovanc6daab62020-06-17 14:27:56 -0400317 iSmall, iBig := i.get()
318 if iBig != nil {
319 return new(big.Rat).SetInt(iBig)
Edward McFarlaned50186b2019-02-24 19:44:57 +0000320 }
alandonovanc6daab62020-06-17 14:27:56 -0400321 return new(big.Rat).SetInt64(iSmall)
Edward McFarlaned50186b2019-02-24 19:44:57 +0000322}
Alan Donovan312d1a52017-10-02 10:10:28 -0400323
324// AsInt32 returns the value of x if is representable as an int32.
325func AsInt32(x Value) (int, error) {
326 i, ok := x.(Int)
327 if !ok {
328 return 0, fmt.Errorf("got %s, want int", x.Type())
329 }
alandonovanc6daab62020-06-17 14:27:56 -0400330 iSmall, iBig := i.get()
331 if iBig != nil {
Edward McFarlaned50186b2019-02-24 19:44:57 +0000332 return 0, fmt.Errorf("%s out of range", i)
Alan Donovan312d1a52017-10-02 10:10:28 -0400333 }
alandonovanc6daab62020-06-17 14:27:56 -0400334 return int(iSmall), nil
Alan Donovan312d1a52017-10-02 10:10:28 -0400335}
336
alandonovan05f260d2017-10-18 12:43:53 -0400337// NumberToInt converts a number x to an integer value.
338// An int is returned unchanged, a float is truncated towards zero.
339// NumberToInt reports an error for all other values.
340func NumberToInt(x Value) (Int, error) {
Alan Donovan312d1a52017-10-02 10:10:28 -0400341 switch x := x.(type) {
Alan Donovan312d1a52017-10-02 10:10:28 -0400342 case Int:
343 return x, nil
344 case Float:
345 f := float64(x)
346 if math.IsInf(f, 0) {
347 return zero, fmt.Errorf("cannot convert float infinity to integer")
348 } else if math.IsNaN(f) {
349 return zero, fmt.Errorf("cannot convert float NaN to integer")
Alan Donovan312d1a52017-10-02 10:10:28 -0400350 }
Diego Siqueira6cae3352017-10-08 01:16:14 +0200351 return finiteFloatToInt(x), nil
352
Alan Donovan312d1a52017-10-02 10:10:28 -0400353 }
354 return zero, fmt.Errorf("cannot convert %s to int", x.Type())
355}
356
357// finiteFloatToInt converts f to an Int, truncating towards zero.
358// f must be finite.
359func finiteFloatToInt(f Float) Int {
Alan Donovan312d1a52017-10-02 10:10:28 -0400360 if math.MinInt64 <= f && f <= math.MaxInt64 {
361 // small values
Edward McFarlaned50186b2019-02-24 19:44:57 +0000362 return MakeInt64(int64(f))
Alan Donovan312d1a52017-10-02 10:10:28 -0400363 }
Edward McFarlaned50186b2019-02-24 19:44:57 +0000364 rat := f.rational()
365 if rat == nil {
366 panic(f) // non-finite
367 }
368 return MakeBigInt(new(big.Int).Div(rat.Num(), rat.Denom()))
Alan Donovan312d1a52017-10-02 10:10:28 -0400369}