-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSelectedRange.py
158 lines (128 loc) · 4.37 KB
/
SelectedRange.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
#!/usr/bin/env python2
__doc__ = """
Provides a simple class to parse and iterate through integral number ranges.
"""
class SelectedRange:
"""
Class containing a range of integers.
The range is the union of intervals, with bounds included.
Example of usage:
r = SelectedRange("5-8,11")
assert 3 not in r
assert 5 in r
assert 7 in r
assert 8 in r
assert 10 not in r
assert 11 in r
assert 12 not in r
for i in r: print i
"""
class Range:
def __init__(self, low, high):
self.lower, self.upper = low, high
def contains(self, v): return v >= self.lower and v <= self.upper
@staticmethod
def parse(spec):
if not spec: return None
start = 1 if spec[0] == '-' else 0
r = spec[start:].split('-', 1)
a = int(spec[:start] + r[0])
b = int(r[1]) if len(r) == 2 else a
return SelectedRange.Range(a, b)
# parse()
def begin(self): return self.lower
def end(self): return self.upper + 1
def toString(self, fmt = ''):
return ("{:" + fmt + "}").format(self.lower) if self.lower == self.upper \
else ("{:" + fmt + "}-{:" + fmt + "}").format(self.lower, self.upper)
# toString()
def __iter__(self): return iter(range(self.begin(), self.end()))
def __len__(self): return max(0, self.end() - self.begin())
def __contains__(self, v): return self.contains(v)
def __str__(self): return self.toString()
# class Range
class Iterator:
def __init__(self, r):
self.rangesIter = iter(r.ranges)
self.rangeIter = None
def __iter__(self): return self
def next(self):
if not self.rangeIter:
self.rangeIter = iter(next(self.rangesIter)) # StopIteration propagated
try: return next(self.rangeIter)
except StopIteration:
self.rangeIter = None
return next(self)
# next()
# class Iterator
def __init__(self, spec=""):
self.ranges = []
if spec: self.parse(spec)
def clear(self): self.ranges = []
def contains(self, v):
for r in self.ranges:
if r.contains(v): return True
else: return False
# contains()
def parse(self, spec):
self.ranges = []
for rspec in spec.split(","):
r = SelectedRange.Range.parse(spec=rspec.strip())
if r is not None: self.ranges.append(r)
# for
return self
# parse()
def toString(self, fmt = ''):
return ",".join(r.toString(fmt=fmt) for r in self.ranges)
def __iter__(self): return SelectedRange.Iterator(self)
def __len__(self): return sum(map(len, self.ranges))
def __contains__(self, v): return self.contains(v)
def __str__(self): return self.toString()
# class SelectedRange
if __name__ == "__main__":
import sys
import logging
testArgs = sys.argv[1:] if len(sys.argv) >= 2 else (
( '1,5', ( 1, 5, ) ),
( '1-5', range(1, 6) ),
( '1-3,5', ( 1, 2, 3, 5, ) ),
( '1-3,6, 8 - 11', ( 1, 2, 3, 6, 8, 9, 10, 11, ) ),
( '1-3,6, 5 - 11', ( 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, ) ),
( '1-3,-6,6, 5 - 11, -3 - -1', ( -6, -3, -2, -1, 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, ) ),
)
checked = 0
errors = 0
for iArg, arg in enumerate(testArgs):
spec = arg if isinstance(arg, str) else arg[0]
solution = None if isinstance(arg, str) else arg[1]
r = SelectedRange(spec=spec)
if solution is not None:
checked += 1
error = False
expected = set(solution)
content = set(r)
if expected != content:
logging.error(
"Range '{spec}' expected to contain: {expected}, got {content} instead (spurious: {{ {excess} }}; missing: {{ {deficit} }}".format(
spec=spec,
expected=("{ " + ", ".join(str(i) for i in solution) + " }"),
content=str(r),
excess=", ".join(str(i) for i in (content - expected)),
deficit=", ".join(str(i) for i in (expected - content)),
))
# if wrong content
if error: errors += 1
# if check
print "{} => {}".format(spec, r)
print " contains {} elements:".format(len(r)),
for i in range(-10, 11):
if i in r: print i,
print
print " full content:", ", ".join(map(str, r))
else: iArg += 1
if errors:
logging.critical \
("{}/{} checked ranges presented errors!".format(errors, checked))
# if errors
sys.exit(0 if errors == 0 else 1)
# main