from functools import reduce
from itertools import product
from operator import mul
from ClusterShell._consortium import (
RangeSet,
RangeSetException,
RangeSetParseError,
RangeSetPaddingError,
)
try:
basestring
except NameError:
basestring = str
__all__ = ['RangeSetException',
'RangeSetParseError',
'RangeSetPaddingError',
'RangeSet',
'RangeSetND',
'AUTOSTEP_DISABLED']
AUTOSTEP_DISABLED = 1E100
def _normalized_index_bounds(length, start, stop):
if start < 0:
start = max(0, length + start)
if stop is None:
stop = length
elif stop < 0:
stop = max(0, length + stop)
return start, stop
def _set_rs_autostep_internal(rg, internal):
if internal >= AUTOSTEP_DISABLED:
rg.autostep = None
else:
rg.autostep = int(internal) + 1
class RangeSetND(object):
def __init__(self, args=None, pads=None, autostep=None, copy_rangeset=True):
self._veclist = []
self._dirty = True
self._autostep = None
self.autostep = autostep self._multivar_hint = False
if args is None:
return
for rgvec in args:
if rgvec:
if isinstance(rgvec[0], basestring):
self._veclist.append([RangeSet(rg, autostep=autostep) \
for rg in rgvec])
elif isinstance(rgvec[0], RangeSet):
if copy_rangeset:
self._veclist.append([rg.copy() for rg in rgvec])
else:
self._veclist.append(rgvec)
else:
if pads is None:
self._veclist.append( \
[RangeSet.fromone(rg, autostep=autostep) \
for rg in rgvec])
else:
self._veclist.append( \
[RangeSet.fromone(rg, pad, autostep) \
for rg, pad in zip(rgvec, pads)])
class precond_fold(object):
def __call__(self, func):
def inner(*args, **kwargs):
rgnd, fargs = args[0], args[1:]
if rgnd._dirty:
rgnd._fold()
return func(rgnd, *fargs, **kwargs)
inner.__name__ = func.__name__
inner.__doc__ = func.__doc__
inner.__dict__ = func.__dict__
inner.__module__ = func.__module__
return inner
@precond_fold()
def copy(self):
cpy = self.__class__()
cpy._veclist = [[rg.copy() for rg in rgvec] for rgvec in self._veclist]
cpy._dirty = self._dirty
return cpy
__copy__ = copy
def __eq__(self, other):
if not isinstance(other, RangeSetND):
return NotImplemented
return len(self) == len(other) and self.issubset(other)
def __bool__(self):
return bool(self._veclist)
__nonzero__ = __bool__
def __len__(self):
return sum([reduce(mul, [len(rg) for rg in rgvec]) \
for rgvec in self.veclist])
@precond_fold()
def __str__(self):
result = ""
for rgvec in self._veclist:
result += "; ".join([str(rg) for rg in rgvec])
result += "\n"
return result
@precond_fold()
def __iter__(self):
return self._iter()
def _iter(self):
for vec in self._veclist:
for ivec in product(*vec):
yield ivec
@precond_fold()
def iter_padding(self):
for vec in self._veclist:
for ivec in product(*vec):
yield ivec, [rg.padding for rg in vec]
@precond_fold()
def _get_veclist(self):
return self._veclist
def _set_veclist(self, val):
self._veclist = val
self._dirty = True
veclist = property(_get_veclist, _set_veclist)
def vectors(self):
return iter(self.veclist)
def dim(self):
try:
return len(self._veclist[0])
except IndexError:
return 0
def pads(self):
pad_veclist = ((rg.padding or 0 for rg in vec) for vec in self._veclist)
return tuple(max(pads) for pads in zip(*pad_veclist))
def get_autostep(self):
if self._autostep >= AUTOSTEP_DISABLED:
return None
else:
return self._autostep + 1
def set_autostep(self, val):
if val is None:
self._autostep = AUTOSTEP_DISABLED
else:
self._autostep = int(val) - 1
for rgvec in self._veclist:
for rg in rgvec:
_set_rs_autostep_internal(rg, self._autostep)
autostep = property(get_autostep, set_autostep)
@precond_fold()
def __getitem__(self, index):
if isinstance(index, slice):
iveclist = []
for rgvec in self._veclist:
iveclist += product(*rgvec)
assert(len(iveclist) == len(self))
rnd = RangeSetND(iveclist[index], autostep=self.autostep)
return rnd
elif isinstance(index, int):
if index < 0:
length = len(self)
if index >= -length:
index = length + index
else:
raise IndexError("%d out of range" % index)
length = 0
for rgvec in self._veclist:
cnt = reduce(mul, [len(rg) for rg in rgvec])
if length + cnt < index:
length += cnt
else:
for ivec in product(*rgvec):
if index == length:
return ivec
length += 1
raise IndexError("%d out of range" % index)
else:
raise TypeError("%s indices must be integers" %
self.__class__.__name__)
@precond_fold()
def index(self, elem, start=0, stop=None):
if isinstance(elem, basestring) or not hasattr(elem, '__iter__'):
raise TypeError("%s.index() argument must be a vector of indexes"
% self.__class__.__name__)
target = tuple("%s" % e for e in elem)
for pos, ivec in enumerate(self._iter()):
if ivec == target:
if start != 0 or stop is not None:
start, stop = _normalized_index_bounds(len(self),
start, stop)
if not start <= pos < stop:
break
return pos
raise ValueError("%s is not in RangeSetND" % (elem,))
@precond_fold()
def contiguous(self):
veclist = self._veclist
try:
dim = len(veclist[0])
except IndexError:
return
for dimidx in range(dim):
new_veclist = []
for rgvec in veclist:
for rgsli in rgvec[dimidx].contiguous():
rgvec = list(rgvec)
rgvec[dimidx] = rgsli
new_veclist.append(rgvec)
veclist = new_veclist
for rgvec in veclist:
yield RangeSetND([rgvec])
@precond_fold()
def __contains__(self, element):
if isinstance(element, RangeSetND):
rgnd_element = element
else:
rgnd_element = RangeSetND([[str(element)]])
return rgnd_element.issubset(self)
def issubset(self, other):
self._binary_sanity_check(other)
return other.issuperset(self)
@precond_fold()
def issuperset(self, other):
self._binary_sanity_check(other)
if self.dim() == 1 and other.dim() == 1:
return self._veclist[0][0].issuperset(other._veclist[0][0])
if not other._veclist:
return True
test = other.copy()
test.difference_update(self)
return not bool(test)
__le__ = issubset
__ge__ = issuperset
def __lt__(self, other):
self._binary_sanity_check(other)
return len(self) < len(other) and self.issubset(other)
def __gt__(self, other):
self._binary_sanity_check(other)
return len(self) > len(other) and self.issuperset(other)
def _binary_sanity_check(self, other):
if not isinstance(other, RangeSetND):
msg = "Binary operation only permitted between RangeSetND"
raise TypeError(msg)
def _sort(self):
def rgveckeyfunc(rgvec):
return (-reduce(mul, [len(rg) for rg in rgvec]), \
tuple((-len(rg), rg[0], rg[-1]) for rg in rgvec))
self._veclist.sort(key=rgveckeyfunc)
@precond_fold()
def fold(self):
pass
def _fold(self):
assert self._dirty
if len(self._veclist) > 1:
self._fold_univariate() or self._fold_multivariate()
else:
self._dirty = False
def _fold_univariate(self):
dim = self.dim()
vardim = dimdiff = 0
if dim > 1:
for i in range(dim):
slist = [vec[i] for vec in self._veclist]
if slist.count(slist[0]) != len(slist):
dimdiff += 1
if dimdiff > 1:
break
vardim = i
univar = (dim == 1 or dimdiff == 1)
if univar:
for vec in self._veclist[1:]:
self._veclist[0][vardim].update(vec[vardim])
del self._veclist[1:]
self._dirty = False
self._multivar_hint = not univar
return univar
def _fold_multivariate(self):
self._fold_multivariate_expand()
self._fold_multivariate_merge()
self._dirty = False
def _fold_multivariate_expand(self):
self._veclist = [[RangeSet.fromone(i, autostep=self.autostep)
for i in tvec]
for tvec in set(self._iter())]
def _fold_multivariate_merge(self):
full = False chg = True while chg:
chg = False
self._sort() index1, index2 = 0, 1
while (index1 + 1) < len(self._veclist):
item1 = self._veclist[index1]
index2 = index1 + 1
index1 += 1
while index2 < len(self._veclist):
item2 = self._veclist[index2]
index2 += 1
new_item = [None] * len(item1)
nb_diff = 0
for pos, (rg1, rg2) in enumerate(zip(item1, item2)):
if rg1 == rg2:
new_item[pos] = rg1
elif not rg1 & rg2: nb_diff += 1
if nb_diff > 1:
break
new_item[pos] = rg1 | rg2
elif (rg1 > rg2 or rg1 < rg2): nb_diff += 1
if nb_diff > 1:
break
new_item[pos] = max(rg1, rg2)
else:
nb_diff = 2
break
if nb_diff <= 1:
chg = True
item1 = self._veclist[index1 - 1] = new_item
index2 -= 1
self._veclist.pop(index2)
elif not full:
break
if not chg and not full:
chg = full = True
def __or__(self, other):
if not isinstance(other, RangeSetND):
return NotImplemented
return self.union(other)
def union(self, other):
rgnd_copy = self.copy()
rgnd_copy.update(other)
return rgnd_copy
def update(self, other):
if isinstance(other, RangeSetND):
iterable = other._veclist
else:
iterable = other
for vec in iterable:
assert isinstance(vec[0], RangeSet)
cpyvec = []
for rg in vec:
cpyrg = rg.copy()
cpyrg.autostep = self.autostep
cpyvec.append(cpyrg)
self._veclist.append(cpyvec)
self._dirty = True
if not self._multivar_hint:
self._fold_univariate()
union_update = update
def __ior__(self, other):
self._binary_sanity_check(other)
self.update(other)
return self
def __isub__(self, other):
self._binary_sanity_check(other)
self.difference_update(other)
return self
def difference_update(self, other, strict=False):
if strict and not other in self:
raise KeyError(other.difference(self)[0])
ergvx = other._veclist rgnd_new = []
index1 = 0
while index1 < len(self._veclist):
rgvec1 = self._veclist[index1]
procvx1 = [ rgvec1 ]
nextvx1 = []
index2 = 0
while index2 < len(ergvx):
rgvec2 = ergvx[index2]
while len(procvx1) > 0: rgproc1 = procvx1.pop(0)
tmpvx = []
for pos, (rg1, rg2) in enumerate(zip(rgproc1, rgvec2)):
if rg1 == rg2 or rg1 < rg2: pass
elif rg1 & rg2: tmpvec = list(rgproc1)
tmpvec[pos] = rg1.difference(rg2)
tmpvx.append(tmpvec)
else: tmpvx = [ rgproc1 ] break
if tmpvx:
nextvx1 += tmpvx
if nextvx1:
procvx1 = nextvx1
nextvx1 = []
index2 += 1
if procvx1:
rgnd_new += procvx1
index1 += 1
self.veclist = rgnd_new
def __sub__(self, other):
if not isinstance(other, RangeSetND):
return NotImplemented
return self.difference(other)
def difference(self, other):
self_copy = self.copy()
self_copy.difference_update(other)
return self_copy
def intersection(self, other):
self_copy = self.copy()
self_copy.intersection_update(other)
return self_copy
def __and__(self, other):
if not isinstance(other, RangeSetND):
return NotImplemented
return self.intersection(other)
def intersection_update(self, other):
if other is self:
return
tmp_rnd = RangeSetND()
empty_rset = RangeSet()
for rgvec in self._veclist:
for ergvec in other._veclist:
irgvec = [rg.intersection(erg) \
for rg, erg in zip(rgvec, ergvec)]
if not empty_rset in irgvec:
tmp_rnd.update([irgvec])
self.veclist = tmp_rnd.veclist
def __iand__(self, other):
self._binary_sanity_check(other)
self.intersection_update(other)
return self
def symmetric_difference(self, other):
self_copy = self.copy()
self_copy.symmetric_difference_update(other)
return self_copy
def __xor__(self, other):
if not isinstance(other, RangeSetND):
return NotImplemented
return self.symmetric_difference(other)
def symmetric_difference_update(self, other):
diff2 = other.difference(self)
self.difference_update(other)
self.update(diff2)
def __ixor__(self, other):
self._binary_sanity_check(other)
self.symmetric_difference_update(other)
return self