Source code for Ranger.src.Range.Range

from Ranger.src.Range.Cut import Cut

[docs]class Range(object): """ Class used to represent a range along some 1-D domain. The range is represented by 2 cutpoints can can be unbounded by specifying an aboveAll or belowAll Cut. """ def __init__(self, lowerCut, upperCut): """ Instantiates a Range Parameters ---------- lowerCut : Cut object Specifies the lower cut for the range upperCut : Cut object Specifies the upper cut for the range Raises ------ ValueError If bound(s) are not Cut objects or lower > upper """ if not all(map(lambda x: isinstance(x, Cut), (lowerCut,upperCut))): raise ValueError("Bounds must be Cut objects") elif lowerCut > upperCut: raise ValueError("Lower bound cannot be greater than upper bound") self.lowerCut = lowerCut self.upperCut = upperCut def __repr__(self): try: return_str = '[' if self.isLowerBoundClosed() else '(' except TypeError: return_str = '(' return_str += (str(self.lowerCut.point) if not self.lowerCut.belowAll \ else '') return_str += ' , ' return_str += (str(self.upperCut.point) if not self.upperCut.aboveAll \ else '') try: return_str += ']' if self.isUpperBoundClosed() else ')' except TypeError: return_str += ')' return return_str def __hash__(self): return (hash(self.lowerCut)*31 + hash(self.upperCut)) def __eq__(self, other): if not isinstance(other, Range): return False else: return ((self.lowerCut == other.lowerCut) and \ (self.upperCut == other.upperCut)) def __ne__(self, other): return not self.__eq__(other)
[docs] def contains(self, val): """ Returns true if the range contains the value Parameters ---------- val : Comparable object of the appropriate type for the range Value to query whether in the range Raises ------ ValueError If the value type not compatible with cutpoint type Returns ------- True if the range contains the value """ return (self.lowerCut < val and \ self.upperCut > val)
[docs] def containsAll(self, vals): """ Returns True if the range contains all values in some iterable Parameters ---------- vals : Iterable of comparable object of appropriate type for range Values to query against the range Raises ------ ValueError If there is a value type not compatible with the cutpoint type Returns ------- True if the range contains all values """ for val in vals: if not self.contains(val): return False return True
[docs] def getDistanceFromPoint(self, val, distFunc = lambda x1, x2: abs(x1-x2)): """ Returns the minimum distance of a Range from a Point, returning 0 if there is an overlap. Note that both upper and lower bounds must be closed for this function to work Parameters ---------- val : comparable, compatible with cutpoint type The value of the point where the distance is desired distFunc : callable Function that calculates the distance between two points in the domain of the Range Raises ------ TypeError If the upper and/or lower bounds of this Range are not closed or if the distFunc not compatible with the type Returns ------- The minimum distance between the Range and the Point. Returns 0 if there is an overlap """ if not all((self.isLowerBoundClosed(), self.isUpperBoundClosed())): raise TypeError("Range is not closed") if self.contains(val): return 0. else: return min(distFunc(self.lowerCut.point, val), distFunc(self.upperCut.point, val))
[docs] def getDistanceFromRange(self, other, distFunc = lambda x1,x2: abs(x1-x2)): """ Returns the minimum distance of a Range from another Range, returning 0 if there is any overlap Note that both Ranges must be closed for this function to work Parameters ---------- other : Range, compatible with this Range's domain The Range to compare to distFunc : callable Function that calculates the distance between two points in the domain of the Range Raises ------ TypeError If the upper and/or lower bounds of this Range are not closed or if the distFunc not compatible with the type Returns ------- Minimum distance between the ranges """ if not isinstance(other, Range): raise TypeError("other is not a Range") if not all((self.isLowerBoundClosed(), self.isUpperBoundClosed(), other.isLowerBoundClosed(), other.isUpperBoundClosed())): raise TypeError("Not all Ranges closed") if self.isConnected(other): return 0. else: return min(distFunc(self.lowerCut.point, other.upperCut.point), distFunc(other.lowerCut.point, self.upperCut.point))
[docs] def hasLowerBound(self): """ Returns True if the range has a lower endpoint (not unbounded at the lower end) Returns ------- True if the range has a lower endpoint """ return (not self.lowerCut.belowAll)
[docs] def hasUpperBound(self): """ Returns True if the range has an upper endpoint (not unbounded at the upper end) Returns ------- True if the range has an upper endpoint """ return (not self.upperCut.aboveAll)
[docs] def lowerEndpoint(self): """ Returns the lower endpoint of the range if it exists. Otherwise raises a TypeError Raises ------ TypeError If the range is unbounded below Returns ------- The lower endpoint of the range """ if self.lowerCut.point is None: raise TypeError("Range unbounded below") else: return self.lowerCut.point
[docs] def upperEndpoint(self): """ Returns the upper endpoint of the range if it exists. Otherwise raises a TypeError Raises ------ TypeError If the range is unbounded above Returns ------- The upper endpoint of the range """ if self.upperCut.point is None: raise TypeError("Range unbounded above") else: return self.upperCut.point
[docs] def isLowerBoundClosed(self): """ Returns whether the lower bound is closed (if there is a lower bound) Raises ------ TypeError If the range is unbounded below Returns ------- True if the lower bound is closed """ if self.lowerCut.point is None: raise TypeError("Range unbounded below") else: return self.lowerCut.below
[docs] def isUpperBoundClosed(self): """ Returns whether the upper bound is closed (if there is an upper bound) Raises ------ TypeError If the range is unbounded above Returns ------- True if the upper bound is closed """ if self.upperCut.point is None: raise TypeError("Range unbounded above") else: return (not self.upperCut.below)
[docs] def isEmpty(self): """ Returns True if the range is of form [v, v) or (v, v] Returns ------- True if the range is of the form [v,v) or (v,v] """ return self.lowerCut == self.upperCut
[docs] def encloses(self, other): """ Returns True if the bounds of the other range do not extend outside the bounds of this range Examples: [3,6] encloses [4,5] (3,6) encloses (3,6) [3,6] encloses [4,4] (3,6] does not enclose [3,6] [4,5] does not enclose (3,6) Parameters ---------- other : A Range The range to compare to Raises ------ ValueError If object passed in is not a Range Returns ------- True if the bounds of the other range do not extend outside the bounds of this range """ if not isinstance(other, Range): raise ValueError("Range required") return ((self.lowerCut <= other.lowerCut) and \ (self.upperCut >= other.upperCut))
[docs] def isConnected(self, other): """ Returns True if there is a (possibly empty) range that is enclosed by both this range and other Examples: [2,4] and [5,7] are not connected [2,4] and [3,5] are connected [2,4] and [4,6] are connected [3,5] and (5,10) are connected Parameters ---------- other : A range The range to compare to Raises ------ ValueError If object passed in is not a Range Returns ------- True if there is a (possibly empty) range that is enclosed by both this range and other """ if not isinstance(other, Range): raise ValueError("Range required") return ((self.lowerCut <= other.upperCut) and \ (other.lowerCut <= self.upperCut))
[docs] def intersection(self, other): """ Returns the maximal range enclosed by both this range and the other range, if such a range exists Examples: Intersection of [1,5] and [3,7] is [3,5] Intersection of [1,5] and [5,7] is [5,5] Parameters ---------- other : A range The range to compare to Raises ------ ValueError If object passed in is not a Range or if there is no intersection Returns ------- The intersection range """ if not isinstance(other, Range): raise ValueError("Range required") if ((self.lowerCut >= other.lowerCut) and \ (self.upperCut <= other.upperCut)): return Range(self.lowerCut, self.upperCut) elif ((self.lowerCut <= other.lowerCut) and \ (self.upperCut >= other.upperCut)): return Range(other.lowerCut, other.upperCut) else: newLower = self.lowerCut if (self.lowerCut >= other.lowerCut) else \ other.lowerCut newUpper = self.upperCut if (self.upperCut <= other.upperCut) else \ other.upperCut return Range(newLower, newUpper)
[docs] def span(self, other): """ Returns the minimal range that encloses both this range and the other. Note that if the input ranges are not connected, the span can contain values that are not contained within either input range Examples: Span of [1,3] and [5,7] is [1,7] Parameters ---------- other : A range A range to span with Raises ------ ValueError If object passed in is not a Range or if there is no intersection Returns ------- The minimal range enclosing both with and the other range """ if ((self.lowerCut <= other.lowerCut) and \ (self.upperCut >= other.upperCut)): return Range(self.lowerCut, self.upperCut) elif ((self.lowerCut >= other.lowerCut) and \ (self.upperCut <= other.upperCut)): return Range(other.lowerCut, other.upperCut) else: newLower = self.lowerCut if (self.lowerCut <= other.lowerCut) else \ other.lowerCut newUpper = self.upperCut if (self.upperCut >= other.upperCut) else \ other.upperCut return Range(newLower, newUpper) ################## # Static methods # ##################
@staticmethod def _validate_cutpoints(*pts): if not all(map(lambda x: (hasattr(x, "__lt__") and \ hasattr(x, "__gt__")) or hasattr(x,'__cmp__'), pts)): raise ValueError("Cutpoint type(s) not comparable") if len(pts) == 2: if not (issubclass(type(pts[0]),type(pts[1])) or \ issubclass(type(pts[1]),type(pts[0]))): raise ValueError("Cutpoints are not compatible") return True @staticmethod def _get_type(*pts): if len(pts) == 1: return type(pts[0]) elif len(pts) == 2: if issubclass(type(pts[0]),type(pts[1])): return type(pts[1]) elif issubclass(type(pts[1]),type(pts[0])): return type(pts[0]) else: raise ValueError("Cutpoints are not compatible") @staticmethod
[docs] def closed(lower, upper): """ Creates a range including the endpoints (i.e. [lower, upper]) Parameters ---------- lower : comparable, of same type as or subclass of upper type The lower bound upper : comparable, of same type as or subclass of lower type The upper bound Raises ------ ValueError If type(s) are not comparable or compatible Returns ------- A Range object [lower, upper] """ # Ensure cutpoints are of compatible, appropriate types Range._validate_cutpoints(lower, upper) theType = Range._get_type(lower,upper) return Range(Cut.belowValue(lower, theType=theType), Cut.aboveValue(upper, theType=theType))
@staticmethod
[docs] def closedOpen(lower, upper): """ Creates a range including the lower endpoint (i.e. [lower, upper)) Parameters ---------- lower : comparable, of same type as or subclass of upper type The lower bound upper : comparable, of same type as or subclass of lower type The upper bound Raises ------ ValueError If type(s) are not comparable or compatible Returns ------- A Range object [lower, upper) """ # Ensure cutpoints are of compatible, appropriate types Range._validate_cutpoints(lower, upper) theType = Range._get_type(lower,upper) return Range(Cut.belowValue(lower, theType=theType), Cut.belowValue(upper, theType=theType))
@staticmethod
[docs] def openClosed(lower, upper): """ Creates a range including the upper (i.e. (lower, upper]) Parameters ---------- lower : comparable, of same type as or subclass of upper type The lower bound upper : comparable, of same type as or subclass of lower type The upper bound Raises ------ ValueError If type(s) are not comparable or compatible Returns ------- A Range object (lower, upper] """ # Ensure cutpoints are of compatible, appropriate types Range._validate_cutpoints(lower, upper) theType = Range._get_type(lower,upper) return Range(Cut.aboveValue(lower, theType=theType), Cut.aboveValue(upper, theType=theType))
@staticmethod
[docs] def open(lower, upper): """ Creates a range excluding the endpoints (i.e. (lower, upper)) Parameters ---------- lower : comparable, of same type as or subclass of upper type The lower bound upper : comparable, of same type as or subclass of lower type The upper bound Raises ------ ValueError If type(s) are not comparable or compatible or if constructing a range of type (v,v), which is invalid Returns ------- A Range object (lower, upper) """ # Ensure cutpoints are of compatible, appropriate types Range._validate_cutpoints(lower, upper) theType = Range._get_type(lower,upper) if lower == upper: raise TypeError("Range of type (v,v) is not valid") return Range(Cut.aboveValue(lower, theType=theType), Cut.belowValue(upper, theType=theType))
@staticmethod
[docs] def lessThan(val): """ Makes range including all values less than some value (i.e. (-inf, val)) Parameters ---------- val : comparable The upper bound Raises ------ ValueError If type not comparable Returns ------- A Range object (-inf, val) """ Range._validate_cutpoints(val) theType = Range._get_type(val) return Range(Cut.belowAll(theType=theType), Cut.belowValue(val, theType=theType))
@staticmethod
[docs] def atMost(val): """ Makes range including all values less than or equal to some value (i.e. (-inf, val]) Parameters ---------- val : comparable The upper bound Raises ------ ValueError If type not comparable Returns ------- A Range object (-inf, val] """ Range._validate_cutpoints(val) theType = Range._get_type(val) return Range(Cut.belowAll(theType=theType), Cut.aboveValue(val, theType=theType))
@staticmethod
[docs] def greaterThan(val): """ Makes range including all values greater than some value (i.e. (val, inf]) Parameters ---------- val : comparable The lower bound Raises ------ ValueError If type not comparable Returns ------- A Range object (val, inf) """ Range._validate_cutpoints(val) theType = Range._get_type(val) return Range(Cut.aboveValue(val,theType=theType), Cut.aboveAll(theType=theType))
@staticmethod
[docs] def atLeast(val): """ Makes range including all values greater than or equal to some value (i.e. [val, inf)) Parameters ---------- val : comparable The lower bound Raises ------ ValueError If type not comparable Returns ------- A Range object [val, inf) """ Range._validate_cutpoints(val) theType = Range._get_type(val) return Range(Cut.belowValue(val, theType=theType), Cut.aboveAll(theType=theType))