-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlrange.py
210 lines (170 loc) · 5.6 KB
/
lrange.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
"""Define `lrange.lrange` class
`xrange`, py3k's `range` analog for large integers
See help(lrange.lrange)
>>> r = lrange(2**100, 2**101, 2**100)
>>> len(r) == 1
True
>>> for i in r:
... print i,
1267650600228229401496703205376
>>> for i in r:
... print i,
1267650600228229401496703205376
>>> 2**100 in r
True
>>> r[0], r[-1]
(1267650600228229401496703205376L, 1267650600228229401496703205376L)
>>> L = list(r)
>>> L2 = [1, 2, 3]
>>> L2[:] = r
>>> L == L2 == [2**100]
True
"""
try: long
except NameError:
long = int # Python 3.x
try: xrange
except NameError:
xrange = range # Python 3.x
try: any
except NameError: # pragma: no cover
def any(iterable): # for Python 2.4
for i in iterable:
if i:
return True
return False
import sys as _sys
if hasattr(_sys, "maxint"):
_MAXINT = _sys.maxint
else:
_MAXINT = _sys.maxsize # Python 3.x
def _toindex(arg):
"""Convert `arg` to integer type that could be used as an index.
"""
for cls in (int, long):
if isinstance(arg, cls): # allow int subclasses
return int(arg)
raise TypeError("'%s' object cannot be interpreted as an integer" % (
type(arg).__name__,))
class lrange(object):
"""lrange([start,] stop[, step]) -> lrange object
Return an iterator that generates the numbers in the range on demand.
Pure Python implementation of py3k's `range()`.
(I.e. it supports large integers)
If `xrange` and py3k `range()` differ then prefer `xrange`'s behaviour
Based on `[1]`_
.. [1] http://svn.python.org/view/python/branches/py3k/Objects/rangeobject.c?view=markup
>>> # on Python 2.6
>>> N = 10**80
>>> len(range(N, N+3))
3
>>> len(xrange(N, N+3)) #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
OverflowError: long int too large to convert to int
>>> len(lrange(N, N+3)) == 3
True
>>> xrange(N) #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
OverflowError: long int too large to convert to int
>>> lrange(N).length == N
True
"""
def __new__(cls, *args):
nargs = len(args)
if nargs == 1:
stop = _toindex(args[0])
start = 0
step = 1
elif nargs in (2, 3):
start = _toindex(args[0])
stop = _toindex(args[1])
if nargs == 3:
step = _toindex(args[2])
if step == 0:
raise ValueError("lrange() arg 3 must not be zero")
else:
step = 1
else:
raise TypeError("lrange(): wrong number of arguments," +
" got %s" % (args,))
r = super(lrange, cls).__new__(cls)
assert start is not None
assert stop is not None
assert step is not None
r._start, r._stop, r._step = start, stop, step
return r
def length(self):
"""len(self) might throw OverflowError, this method shouldn't."""
if self._step > 0:
lo, hi = self._start, self._stop
step = self._step
else:
hi, lo = self._start, self._stop
step = -self._step
assert step
if lo >= hi:
return 0
else:
return (hi - lo - 1) // step + 1
def __len__(self):
L = self.length
if L > _MAXINT:
raise OverflowError(
"cannot fit '%.200s' into an index-sized integer" % type(L).__name__)
return int(L)
def __bool__(self):
return bool(self.length)
__nonzero__ = __bool__
length = property(length)
def __getitem__(self, i):
if i < 0:
i = i + self.length
if i < 0 or i >= self.length:
raise IndexError("lrange object index out of range")
return self._start + i * self._step
def __repr__(self):
if self._step == 1:
return "%s(%r, %r)" % (
self.__class__.__name__, self._start, self._stop)
else:
return "%s(%r, %r, %r)" % (
self.__class__.__name__, self._start, self._stop, self._step)
def __contains__(self, ob):
if type(ob) not in (int, long, bool): # mimic py3k
# perform iterative search
return any(i == ob for i in self)
# if long or bool
if self._step > 0:
inrange = self._start <= ob < self._stop
else:
assert self._step
inrange = self._stop < ob <= self._start
if not inrange:
return False
else:
return ((ob - self._start) % self._step) == 0
def __iter__(self):
try:
return iter(xrange(self._start, self._stop,
self._step)) # use `xrange`'s iterator
except (NameError, OverflowError):
return self._iterator()
def _iterator(self):
len_ = self.length
i = 0
while i < len_:
yield self._start + i * self._step
i += 1
def __reversed__(self):
len_ = self.length
new_start = self._start + (len_ - 1) * self._step
new_stop = self._start
if self._step > 0:
new_stop -= 1
else:
new_stop += 1
return lrange(new_start, new_stop, -self._step)
def __getnewargs__(self):
return self._start, self._stop, self._step