'Find values in the given range.
Parameters
lower : tuple
Lower search bound
upper : tuple
Upper search bound
bounds : tuple (x, y) of bools
Indicates whether the search should be inclusive or
exclusive with respect to the endpoints. The first
argument x corresponds to an inclusive lower bound,
and the second argument y to an inclusive upper bound.'

def range(self, lower, upper, bounds):

    lower_pos = self.find_pos(lower, 0)
    upper_pos = self.find_pos(upper, 0)
    if (lower_pos == len(self.row_index)):
        return []
    lower_bound = tuple([col[lower_pos] for col in self.cols])
    if ((not bounds[0]) and (lower_bound == lower)):
        lower_pos += 1
    if (upper_pos < len(self.row_index)):
        upper_bound = tuple([col[upper_pos] for col in self.cols])
        if ((not bounds[1]) and (upper_bound == upper)):
            upper_pos -= 1
        elif (upper_bound > upper):
            upper_pos -= 1
    return self.row_index[lower_pos:(upper_pos + 1)]
