[3] | 1 | # -*- coding: iso-8859-15 -*- |
---|
| 2 | """Immutable integer set type. |
---|
| 3 | |
---|
| 4 | Integer set class. |
---|
| 5 | |
---|
| 6 | Copyright (C) 2006, Heiko Wundram. |
---|
| 7 | Released under the MIT license. |
---|
| 8 | """ |
---|
| 9 | |
---|
| 10 | # Version information |
---|
| 11 | # ------------------- |
---|
| 12 | |
---|
| 13 | __author__ = "Heiko Wundram <me@modelnine.org>" |
---|
| 14 | __version__ = "0.2" |
---|
| 15 | __revision__ = "6" |
---|
| 16 | __date__ = "2006-01-20" |
---|
| 17 | |
---|
| 18 | |
---|
| 19 | # Utility classes |
---|
| 20 | # --------------- |
---|
| 21 | |
---|
| 22 | class _Infinity(object): |
---|
| 23 | """Internal type used to represent infinity values.""" |
---|
| 24 | |
---|
| 25 | __slots__ = ["_neg"] |
---|
| 26 | |
---|
| 27 | def __init__(self,neg): |
---|
| 28 | self._neg = neg |
---|
| 29 | |
---|
| 30 | def __lt__(self,value): |
---|
| 31 | if not isinstance(value,(int,long,_Infinity)): |
---|
| 32 | return NotImplemented |
---|
| 33 | return ( self._neg and |
---|
| 34 | not ( isinstance(value,_Infinity) and value._neg ) ) |
---|
| 35 | |
---|
| 36 | def __le__(self,value): |
---|
| 37 | if not isinstance(value,(int,long,_Infinity)): |
---|
| 38 | return NotImplemented |
---|
| 39 | return self._neg |
---|
| 40 | |
---|
| 41 | def __gt__(self,value): |
---|
| 42 | if not isinstance(value,(int,long,_Infinity)): |
---|
| 43 | return NotImplemented |
---|
| 44 | return not ( self._neg or |
---|
| 45 | ( isinstance(value,_Infinity) and not value._neg ) ) |
---|
| 46 | |
---|
| 47 | def __ge__(self,value): |
---|
| 48 | if not isinstance(value,(int,long,_Infinity)): |
---|
| 49 | return NotImplemented |
---|
| 50 | return not self._neg |
---|
| 51 | |
---|
| 52 | def __eq__(self,value): |
---|
| 53 | if not isinstance(value,(int,long,_Infinity)): |
---|
| 54 | return NotImplemented |
---|
| 55 | return isinstance(value,_Infinity) and self._neg == value._neg |
---|
| 56 | |
---|
| 57 | def __ne__(self,value): |
---|
| 58 | if not isinstance(value,(int,long,_Infinity)): |
---|
| 59 | return NotImplemented |
---|
| 60 | return not isinstance(value,_Infinity) or self._neg <> value._neg |
---|
| 61 | |
---|
| 62 | def __repr__(self): |
---|
| 63 | return "None" |
---|
| 64 | |
---|
| 65 | |
---|
| 66 | # Constants |
---|
| 67 | # --------- |
---|
| 68 | |
---|
| 69 | _MININF = _Infinity(True) |
---|
| 70 | _MAXINF = _Infinity(False) |
---|
| 71 | |
---|
| 72 | |
---|
| 73 | # Integer set class |
---|
| 74 | # ----------------- |
---|
| 75 | |
---|
| 76 | class IntSet(object): |
---|
| 77 | """Integer set class with efficient storage in a RLE format of ranges. |
---|
| 78 | Supports minus and plus infinity in the range.""" |
---|
| 79 | |
---|
| 80 | __slots__ = ["_ranges","_min","_max","_hash"] |
---|
| 81 | |
---|
| 82 | def __init__(self,*args,**kwargs): |
---|
| 83 | """Initialize an integer set. The constructor accepts an unlimited |
---|
| 84 | number of arguments that may either be tuples in the form of |
---|
| 85 | (start,stop) where either start or stop may be a number or None to |
---|
| 86 | represent maximum/minimum in that direction. The range specified by |
---|
| 87 | (start,stop) is always inclusive (differing from the builtin range |
---|
| 88 | operator). |
---|
| 89 | |
---|
| 90 | Keyword arguments that can be passed to an integer set are min and |
---|
| 91 | max, which specify the minimum and maximum number in the set, |
---|
| 92 | respectively. You can also pass None here to represent minus or plus |
---|
| 93 | infinity, which is also the default. |
---|
| 94 | """ |
---|
| 95 | |
---|
| 96 | # Special case copy constructor. |
---|
| 97 | if len(args) == 1 and isinstance(args[0],IntSet): |
---|
| 98 | if kwargs: |
---|
| 99 | raise ValueError("No keyword arguments for copy constructor.") |
---|
| 100 | self._min = args[0]._min |
---|
| 101 | self._max = args[0]._max |
---|
| 102 | self._ranges = args[0]._ranges |
---|
| 103 | self._hash = args[0]._hash |
---|
| 104 | return |
---|
| 105 | |
---|
| 106 | # Initialize set. |
---|
| 107 | self._ranges = [] |
---|
| 108 | |
---|
| 109 | # Process keyword arguments. |
---|
| 110 | self._min = kwargs.pop("min",_MININF) |
---|
| 111 | self._max = kwargs.pop("max",_MAXINF) |
---|
| 112 | if self._min is None: |
---|
| 113 | self._min = _MININF |
---|
| 114 | if self._max is None: |
---|
| 115 | self._max = _MAXINF |
---|
| 116 | |
---|
| 117 | # Check keyword arguments. |
---|
| 118 | if kwargs: |
---|
| 119 | raise ValueError("Invalid keyword argument.") |
---|
| 120 | if not ( isinstance(self._min,(int,long)) or self._min is _MININF ): |
---|
| 121 | raise TypeError("Invalid type of min argument.") |
---|
| 122 | if not ( isinstance(self._max,(int,long)) or self._max is _MAXINF ): |
---|
| 123 | raise TypeError("Invalid type of max argument.") |
---|
| 124 | if ( self._min is not _MININF and self._max is not _MAXINF and |
---|
| 125 | self._min > self._max ): |
---|
| 126 | raise ValueError("Minimum is not smaller than maximum.") |
---|
| 127 | if isinstance(self._max,(int,long)): |
---|
| 128 | self._max += 1 |
---|
| 129 | |
---|
| 130 | # Process arguments. |
---|
| 131 | for arg in args: |
---|
| 132 | if isinstance(arg,(int,long)): |
---|
| 133 | start, stop = arg, arg+1 |
---|
| 134 | elif isinstance(arg,tuple): |
---|
| 135 | if len(arg) <> 2: |
---|
| 136 | raise ValueError("Invalid tuple, must be (start,stop).") |
---|
| 137 | |
---|
| 138 | # Process argument. |
---|
| 139 | start, stop = arg |
---|
| 140 | if start is None: |
---|
| 141 | start = self._min |
---|
| 142 | if stop is None: |
---|
| 143 | stop = self._max |
---|
| 144 | |
---|
| 145 | # Check arguments. |
---|
| 146 | if not ( isinstance(start,(int,long)) or start is _MININF ): |
---|
| 147 | raise TypeError("Invalid type of tuple start.") |
---|
| 148 | if not ( isinstance(stop,(int,long)) or stop is _MAXINF ): |
---|
| 149 | raise TypeError("Invalid type of tuple stop.") |
---|
| 150 | if ( start is not _MININF and stop is not _MAXINF and |
---|
| 151 | start > stop ): |
---|
| 152 | continue |
---|
| 153 | if isinstance(stop,(int,long)): |
---|
| 154 | stop += 1 |
---|
| 155 | else: |
---|
| 156 | raise TypeError("Invalid argument.") |
---|
| 157 | |
---|
| 158 | if start > self._max: |
---|
| 159 | continue |
---|
| 160 | elif start < self._min: |
---|
| 161 | start = self._min |
---|
| 162 | if stop < self._min: |
---|
| 163 | continue |
---|
| 164 | elif stop > self._max: |
---|
| 165 | stop = self._max |
---|
| 166 | self._ranges.append((start,stop)) |
---|
| 167 | |
---|
| 168 | # Normalize set. |
---|
| 169 | self._normalize() |
---|
| 170 | |
---|
| 171 | # Utility functions for set operations |
---|
| 172 | # ------------------------------------ |
---|
| 173 | |
---|
| 174 | def _iterranges(self,r1,r2,minval=_MININF,maxval=_MAXINF): |
---|
| 175 | curval = minval |
---|
| 176 | curstates = {"r1":False,"r2":False} |
---|
| 177 | imax, jmax = 2*len(r1), 2*len(r2) |
---|
| 178 | i, j = 0, 0 |
---|
| 179 | while i < imax or j < jmax: |
---|
| 180 | if i < imax and ( ( j < jmax and |
---|
| 181 | r1[i>>1][i&1] < r2[j>>1][j&1] ) or |
---|
| 182 | j == jmax ): |
---|
| 183 | cur_r, newname, newstate = r1[i>>1][i&1], "r1", not (i&1) |
---|
| 184 | i += 1 |
---|
| 185 | else: |
---|
| 186 | cur_r, newname, newstate = r2[j>>1][j&1], "r2", not (j&1) |
---|
| 187 | j += 1 |
---|
| 188 | if curval < cur_r: |
---|
| 189 | if cur_r > maxval: |
---|
| 190 | break |
---|
| 191 | yield curstates, (curval,cur_r) |
---|
| 192 | curval = cur_r |
---|
| 193 | curstates[newname] = newstate |
---|
| 194 | if curval < maxval: |
---|
| 195 | yield curstates, (curval,maxval) |
---|
| 196 | |
---|
| 197 | def _normalize(self): |
---|
| 198 | self._ranges.sort() |
---|
| 199 | i = 1 |
---|
| 200 | while i < len(self._ranges): |
---|
| 201 | if self._ranges[i][0] < self._ranges[i-1][1]: |
---|
| 202 | self._ranges[i-1] = (self._ranges[i-1][0], |
---|
| 203 | max(self._ranges[i-1][1], |
---|
| 204 | self._ranges[i][1])) |
---|
| 205 | del self._ranges[i] |
---|
| 206 | else: |
---|
| 207 | i += 1 |
---|
| 208 | self._ranges = tuple(self._ranges) |
---|
| 209 | self._hash = hash(self._ranges) |
---|
| 210 | |
---|
| 211 | def __coerce__(self,other): |
---|
| 212 | if isinstance(other,IntSet): |
---|
| 213 | return self, other |
---|
| 214 | elif isinstance(other,(int,long,tuple)): |
---|
| 215 | try: |
---|
| 216 | return self, self.__class__(other) |
---|
| 217 | except TypeError: |
---|
| 218 | # Catch a type error, in that case the structure specified by |
---|
| 219 | # other is something we can't coerce, return NotImplemented. |
---|
| 220 | # ValueErrors are not caught, they signal that the data was |
---|
| 221 | # invalid for the constructor. This is appropriate to signal |
---|
| 222 | # as a ValueError to the caller. |
---|
| 223 | return NotImplemented |
---|
| 224 | elif isinstance(other,list): |
---|
| 225 | try: |
---|
| 226 | return self, self.__class__(*other) |
---|
| 227 | except TypeError: |
---|
| 228 | # See above. |
---|
| 229 | return NotImplemented |
---|
| 230 | return NotImplemented |
---|
| 231 | |
---|
| 232 | # Set function definitions |
---|
| 233 | # ------------------------ |
---|
| 234 | |
---|
| 235 | def _make_function(name,type,doc,pall,pany=None): |
---|
| 236 | """Makes a function to match two ranges. Accepts two types: either |
---|
| 237 | 'set', which defines a function which returns a set with all ranges |
---|
| 238 | matching pall (pany is ignored), or 'bool', which returns True if pall |
---|
| 239 | matches for all ranges and pany matches for any one range. doc is the |
---|
| 240 | dostring to give this function. pany may be none to ignore the any |
---|
| 241 | match. |
---|
| 242 | |
---|
| 243 | The predicates get a dict with two keys, 'r1', 'r2', which denote |
---|
| 244 | whether the current range is present in range1 (self) and/or range2 |
---|
| 245 | (other) or none of the two, respectively.""" |
---|
| 246 | |
---|
| 247 | if type == "set": |
---|
| 248 | def f(self,other): |
---|
| 249 | coerced = self.__coerce__(other) |
---|
| 250 | if coerced is NotImplemented: |
---|
| 251 | return NotImplemented |
---|
| 252 | other = coerced[1] |
---|
| 253 | newset = self.__class__.__new__(self.__class__) |
---|
| 254 | newset._min = min(self._min,other._min) |
---|
| 255 | newset._max = max(self._max,other._max) |
---|
| 256 | newset._ranges = [] |
---|
| 257 | for states, (start,stop) in \ |
---|
| 258 | self._iterranges(self._ranges,other._ranges, |
---|
| 259 | newset._min,newset._max): |
---|
| 260 | if pall(states): |
---|
| 261 | if newset._ranges and newset._ranges[-1][1] == start: |
---|
| 262 | newset._ranges[-1] = (newset._ranges[-1][0],stop) |
---|
| 263 | else: |
---|
| 264 | newset._ranges.append((start,stop)) |
---|
| 265 | newset._ranges = tuple(newset._ranges) |
---|
| 266 | newset._hash = hash(self._ranges) |
---|
| 267 | return newset |
---|
| 268 | elif type == "bool": |
---|
| 269 | def f(self,other): |
---|
| 270 | coerced = self.__coerce__(other) |
---|
| 271 | if coerced is NotImplemented: |
---|
| 272 | return NotImplemented |
---|
| 273 | other = coerced[1] |
---|
| 274 | _min = min(self._min,other._min) |
---|
| 275 | _max = max(self._max,other._max) |
---|
| 276 | found = not pany |
---|
| 277 | for states, (start,stop) in \ |
---|
| 278 | self._iterranges(self._ranges,other._ranges,_min,_max): |
---|
| 279 | if not pall(states): |
---|
| 280 | return False |
---|
| 281 | found = found or pany(states) |
---|
| 282 | return found |
---|
| 283 | else: |
---|
| 284 | raise ValueError("Invalid type of function to create.") |
---|
| 285 | try: |
---|
| 286 | f.func_name = name |
---|
| 287 | except TypeError: |
---|
| 288 | pass |
---|
| 289 | f.func_doc = doc |
---|
| 290 | return f |
---|
| 291 | |
---|
| 292 | # Intersection. |
---|
| 293 | __and__ = _make_function("__and__","set", |
---|
| 294 | "Intersection of two sets as a new set.", |
---|
| 295 | lambda s: s["r1"] and s["r2"]) |
---|
| 296 | __rand__ = _make_function("__rand__","set", |
---|
| 297 | "Intersection of two sets as a new set.", |
---|
| 298 | lambda s: s["r1"] and s["r2"]) |
---|
| 299 | intersection = _make_function("intersection","set", |
---|
| 300 | "Intersection of two sets as a new set.", |
---|
| 301 | lambda s: s["r1"] and s["r2"]) |
---|
| 302 | |
---|
| 303 | # Union. |
---|
| 304 | __or__ = _make_function("__or__","set", |
---|
| 305 | "Union of two sets as a new set.", |
---|
| 306 | lambda s: s["r1"] or s["r2"]) |
---|
| 307 | __ror__ = _make_function("__ror__","set", |
---|
| 308 | "Union of two sets as a new set.", |
---|
| 309 | lambda s: s["r1"] or s["r2"]) |
---|
| 310 | union = _make_function("union","set", |
---|
| 311 | "Union of two sets as a new set.", |
---|
| 312 | lambda s: s["r1"] or s["r2"]) |
---|
| 313 | |
---|
| 314 | # Difference. |
---|
| 315 | __sub__ = _make_function("__sub__","set", |
---|
| 316 | "Difference of two sets as a new set.", |
---|
| 317 | lambda s: s["r1"] and not s["r2"]) |
---|
| 318 | __rsub__ = _make_function("__rsub__","set", |
---|
| 319 | "Difference of two sets as a new set.", |
---|
| 320 | lambda s: s["r2"] and not s["r1"]) |
---|
| 321 | difference = _make_function("difference","set", |
---|
| 322 | "Difference of two sets as a new set.", |
---|
| 323 | lambda s: s["r1"] and not s["r2"]) |
---|
| 324 | |
---|
| 325 | # Symmetric difference. |
---|
| 326 | __xor__ = _make_function("__xor__","set", |
---|
| 327 | "Symmetric difference of two sets as a new set.", |
---|
| 328 | lambda s: s["r1"] ^ s["r2"]) |
---|
| 329 | __rxor__ = _make_function("__rxor__","set", |
---|
| 330 | "Symmetric difference of two sets as a new set.", |
---|
| 331 | lambda s: s["r1"] ^ s["r2"]) |
---|
| 332 | symmetric_difference = _make_function("symmetric_difference","set", |
---|
| 333 | "Symmetric difference of two sets as a new set.", |
---|
| 334 | lambda s: s["r1"] ^ s["r2"]) |
---|
| 335 | |
---|
| 336 | # Containership testing. |
---|
| 337 | __contains__ = _make_function("__contains__","bool", |
---|
| 338 | "Returns true if self is superset of other.", |
---|
| 339 | lambda s: s["r1"] or not s["r2"]) |
---|
| 340 | issubset = _make_function("issubset","bool", |
---|
| 341 | "Returns true if self is subset of other.", |
---|
| 342 | lambda s: s["r2"] or not s["r1"]) |
---|
| 343 | istruesubset = _make_function("istruesubset","bool", |
---|
| 344 | "Returns true if self is true subset of other.", |
---|
| 345 | lambda s: s["r2"] or not s["r1"], |
---|
| 346 | lambda s: s["r2"] and not s["r1"]) |
---|
| 347 | issuperset = _make_function("issuperset","bool", |
---|
| 348 | "Returns true if self is superset of other.", |
---|
| 349 | lambda s: s["r1"] or not s["r2"]) |
---|
| 350 | istruesuperset = _make_function("istruesuperset","bool", |
---|
| 351 | "Returns true if self is true superset of other.", |
---|
| 352 | lambda s: s["r1"] or not s["r2"], |
---|
| 353 | lambda s: s["r1"] and not s["r2"]) |
---|
| 354 | overlaps = _make_function("overlaps","bool", |
---|
| 355 | "Returns true if self overlaps with other.", |
---|
| 356 | lambda s: True, |
---|
| 357 | lambda s: s["r1"] and s["r2"]) |
---|
| 358 | |
---|
| 359 | # Comparison. |
---|
| 360 | __eq__ = _make_function("__eq__","bool", |
---|
| 361 | "Returns true if self is equal to other.", |
---|
| 362 | lambda s: not ( s["r1"] ^ s["r2"] )) |
---|
| 363 | __ne__ = _make_function("__ne__","bool", |
---|
| 364 | "Returns true if self is different to other.", |
---|
| 365 | lambda s: True, |
---|
| 366 | lambda s: s["r1"] ^ s["r2"]) |
---|
| 367 | |
---|
| 368 | # Clean up namespace. |
---|
| 369 | del _make_function |
---|
| 370 | |
---|
| 371 | # Define other functions. |
---|
| 372 | def inverse(self): |
---|
| 373 | """Inverse of set as a new set.""" |
---|
| 374 | |
---|
| 375 | newset = self.__class__.__new__(self.__class__) |
---|
| 376 | newset._min = self._min |
---|
| 377 | newset._max = self._max |
---|
| 378 | newset._ranges = [] |
---|
| 379 | laststop = self._min |
---|
| 380 | for r in self._ranges: |
---|
| 381 | if laststop < r[0]: |
---|
| 382 | newset._ranges.append((laststop,r[0])) |
---|
| 383 | laststop = r[1] |
---|
| 384 | if laststop < self._max: |
---|
| 385 | newset._ranges.append((laststop,self._max)) |
---|
| 386 | return newset |
---|
| 387 | |
---|
| 388 | __invert__ = inverse |
---|
| 389 | |
---|
| 390 | # Hashing |
---|
| 391 | # ------- |
---|
| 392 | |
---|
| 393 | def __hash__(self): |
---|
| 394 | """Returns a hash value representing this integer set. As the set is |
---|
| 395 | always stored normalized, the hash value is guaranteed to match for |
---|
| 396 | matching ranges.""" |
---|
| 397 | |
---|
| 398 | return self._hash |
---|
| 399 | |
---|
| 400 | # Iterating |
---|
| 401 | # --------- |
---|
| 402 | |
---|
| 403 | def __len__(self): |
---|
| 404 | """Get length of this integer set. In case the length is larger than |
---|
| 405 | 2**31 (including infinitely sized integer sets), it raises an |
---|
| 406 | OverflowError. This is due to len() restricting the size to |
---|
| 407 | 0 <= len < 2**31.""" |
---|
| 408 | |
---|
| 409 | if not self._ranges: |
---|
| 410 | return 0 |
---|
| 411 | if self._ranges[0][0] is _MININF or self._ranges[-1][1] is _MAXINF: |
---|
| 412 | raise OverflowError("Infinitely sized integer set.") |
---|
| 413 | rlen = 0 |
---|
| 414 | for r in self._ranges: |
---|
| 415 | rlen += r[1]-r[0] |
---|
| 416 | if rlen >= 2**31: |
---|
| 417 | raise OverflowError("Integer set bigger than 2**31.") |
---|
| 418 | return rlen |
---|
| 419 | |
---|
| 420 | def len(self): |
---|
| 421 | """Returns the length of this integer set as an integer. In case the |
---|
| 422 | length is infinite, returns -1. This function exists because of a |
---|
| 423 | limitation of the builtin len() function which expects values in |
---|
| 424 | the range 0 <= len < 2**31. Use this function in case your integer |
---|
| 425 | set might be larger.""" |
---|
| 426 | |
---|
| 427 | if not self._ranges: |
---|
| 428 | return 0 |
---|
| 429 | if self._ranges[0][0] is _MININF or self._ranges[-1][1] is _MAXINF: |
---|
| 430 | return -1 |
---|
| 431 | rlen = 0 |
---|
| 432 | for r in self._ranges: |
---|
| 433 | rlen += r[1]-r[0] |
---|
| 434 | return rlen |
---|
| 435 | |
---|
| 436 | def __nonzero__(self): |
---|
| 437 | """Returns true if this integer set contains at least one item.""" |
---|
| 438 | |
---|
| 439 | return bool(self._ranges) |
---|
| 440 | |
---|
| 441 | def __iter__(self): |
---|
| 442 | """Iterate over all values in this integer set. Iteration always starts |
---|
| 443 | by iterating from lowest to highest over the ranges that are bounded. |
---|
| 444 | After processing these, all ranges that are unbounded (maximum 2) are |
---|
| 445 | yielded intermixed.""" |
---|
| 446 | |
---|
| 447 | ubranges = [] |
---|
| 448 | for r in self._ranges: |
---|
| 449 | if r[0] is _MININF: |
---|
| 450 | if r[1] is _MAXINF: |
---|
| 451 | ubranges.extend(([0,1],[-1,-1])) |
---|
| 452 | else: |
---|
| 453 | ubranges.append([r[1]-1,-1]) |
---|
| 454 | elif r[1] is _MAXINF: |
---|
| 455 | ubranges.append([r[0],1]) |
---|
| 456 | else: |
---|
| 457 | for val in xrange(r[0],r[1]): |
---|
| 458 | yield val |
---|
| 459 | if ubranges: |
---|
| 460 | while True: |
---|
| 461 | for ubrange in ubranges: |
---|
| 462 | yield ubrange[0] |
---|
| 463 | ubrange[0] += ubrange[1] |
---|
| 464 | |
---|
| 465 | # Printing |
---|
| 466 | # -------- |
---|
| 467 | |
---|
| 468 | def __repr__(self): |
---|
| 469 | """Return a representation of this integer set. The representation is |
---|
| 470 | executable to get an equal integer set.""" |
---|
| 471 | |
---|
| 472 | rv = [] |
---|
| 473 | for start, stop in self._ranges: |
---|
| 474 | if ( isinstance(start,(int,long)) and isinstance(stop,(int,long)) |
---|
| 475 | and stop-start == 1 ): |
---|
| 476 | rv.append("%r" % start) |
---|
| 477 | elif isinstance(stop,(int,long)): |
---|
| 478 | rv.append("(%r,%r)" % (start,stop-1)) |
---|
| 479 | else: |
---|
| 480 | rv.append("(%r,%r)" % (start,stop)) |
---|
| 481 | if self._min is not _MININF: |
---|
| 482 | rv.append("min=%r" % self._min) |
---|
| 483 | if self._max is not _MAXINF: |
---|
| 484 | rv.append("max=%r" % self._max) |
---|
| 485 | return "%s(%s)" % (self.__class__.__name__,",".join(rv)) |
---|
| 486 | |
---|
| 487 | if __name__ == "__main__": |
---|
| 488 | # Little test script demonstrating functionality. |
---|
| 489 | x = IntSet((10,20),30) |
---|
| 490 | y = IntSet((10,20)) |
---|
| 491 | z = IntSet((10,20),30,(15,19),min=0,max=40) |
---|
| 492 | print x |
---|
| 493 | print x&110 |
---|
| 494 | print x|110 |
---|
| 495 | print x^(15,25) |
---|
| 496 | print x-12 |
---|
| 497 | print 12 in x |
---|
| 498 | print x.issubset(x) |
---|
| 499 | print y.issubset(x) |
---|
| 500 | print x.istruesubset(x) |
---|
| 501 | print y.istruesubset(x) |
---|
| 502 | for val in x: |
---|
| 503 | print val |
---|
| 504 | print x.inverse() |
---|
| 505 | print x == z |
---|
| 506 | print x == y |
---|
| 507 | print x <> y |
---|
| 508 | print hash(x) |
---|
| 509 | print hash(z) |
---|
| 510 | print len(x) |
---|
| 511 | print x.len() |
---|