# Path: raccoon/sort_utils.py
# def sorted_exists(values, x):
#     """
#     For list, values, returns the insert position for item x and whether the item already exists in the list. This
#     allows one function call to return either the index to overwrite an existing value in the list, or the index to
#     insert a new item in the list and keep the list in sorted order.
# 
#     :param values: list
#     :param x: item
#     :return: (exists, index) tuple
#     """
#     i = bisect_left(values, x)
#     j = bisect_right(values, x)
#     exists = x in values[i:j]
#     return exists, i
# 
# def sorted_index(values, x):
#     """
#     For list, values, returns the index location of element x. If x does not exist will raise an error.
# 
#     :param values: list
#     :param x: item
#     :return: integer index
#     """
#     i = bisect_left(values, x)
#     j = bisect_right(values, x)
#     return values[i:j].index(x) + i
# 
# def sorted_list_indexes(list_to_sort, key=None, reverse=False):
#     """
#     Sorts a list but returns the order of the index values of the list for the sort and not the values themselves.
#     For example is the list provided is ['b', 'a', 'c'] then the result will be [2, 1, 3]
# 
#     :param list_to_sort: list to sort
#     :param key: if not None then a function of one argument that is used to extract a comparison key from each
#                 list element
#     :param reverse: if True then the list elements are sorted as if each comparison were reversed.
#     :return: list of sorted index values
#     """
#     if key is not None:
#         def key_func(i):
#             return key(list_to_sort.__getitem__(i))
#     else:
#         key_func = list_to_sort.__getitem__
#     return sorted(range(len(list_to_sort)), key=key_func, reverse=reverse)


from abc import ABC, abstractmethod
from bisect import bisect_left, bisect_right
from collections import OrderedDict
from itertools import compress
from tabulate import tabulate
from raccoon.sort_utils import sorted_exists, sorted_index, sorted_list_indexes
        self._data[i] = value

    def set_rows(self, index, values=None):
        """
        Set rows to a single value or list of values. If any of the index values are not in the current indexes
        then a new row will be created.

        :param index: list of index values or list of booleans. If a list of booleans then the list must be the same\
        length as the Series
        :param values: either a single value or a list. The list must be the same length as the index list if the index\
        list is values, or the length of the True values in the index list if the index list is booleans
        :return: nothing
        """
        if all([isinstance(i, bool) for i in index]):  # boolean list
            if not self._check_list(values):  # single value provided, not a list, so turn values into list
                values = [values for x in index if x]
            if len(index) != len(self._index):
                raise ValueError('boolean index list must be same size of existing index')
            if len(values) != index.count(True):
                raise ValueError('length of values list must equal number of True entries in index list')
            indexes = [i for i, x in enumerate(index) if x]
            for x, i in enumerate(indexes):
                self._data[i] = values[x]
        else:  # list of index
            if not self._check_list(values):  # single value provided, not a list, so turn values into list
                values = [values for _ in index]
            if len(values) != len(index):
                raise ValueError('length of values and index must be the same.')
            # insert or append indexes as needed
            if self._sort:

# Path: raccoon/sort_utils.py
# def sorted_exists(values, x):
#     """
#     For list, values, returns the insert position for item x and whether the item already exists in the list. This
#     allows one function call to return either the index to overwrite an existing value in the list, or the index to
#     insert a new item in the list and keep the list in sorted order.
# 
#     :param values: list
#     :param x: item
#     :return: (exists, index) tuple
#     """
#     i = bisect_left(values, x)
#     j = bisect_right(values, x)
#     exists = x in values[i:j]
#     return exists, i
# 
# def sorted_index(values, x):
#     """
#     For list, values, returns the index location of element x. If x does not exist will raise an error.
# 
#     :param values: list
#     :param x: item
#     :return: integer index
#     """
#     i = bisect_left(values, x)
#     j = bisect_right(values, x)
#     return values[i:j].index(x) + i
# 
# def sorted_list_indexes(list_to_sort, key=None, reverse=False):
#     """
#     Sorts a list but returns the order of the index values of the list for the sort and not the values themselves.
#     For example is the list provided is ['b', 'a', 'c'] then the result will be [2, 1, 3]
# 
#     :param list_to_sort: list to sort
#     :param key: if not None then a function of one argument that is used to extract a comparison key from each
#                 list element
#     :param reverse: if True then the list elements are sorted as if each comparison were reversed.
#     :return: list of sorted index values
#     """
#     if key is not None:
#         def key_func(i):
#             return key(list_to_sort.__getitem__(i))
#     else:
#         key_func = list_to_sort.__getitem__
#     return sorted(range(len(list_to_sort)), key=key_func, reverse=reverse)


# Path: raccoon/series.py
from abc import ABC, abstractmethod
from bisect import bisect_left, bisect_right
from collections import OrderedDict
from itertools import compress
from tabulate import tabulate
from raccoon.sort_utils import sorted_exists, sorted_index, sorted_list_indexes

        self._data[i] = value

    def set_rows(self, index, values=None):
        """
        Set rows to a single value or list of values. If any of the index values are not in the current indexes
        then a new row will be created.

        :param index: list of index values or list of booleans. If a list of booleans then the list must be the same\
        length as the Series
        :param values: either a single value or a list. The list must be the same length as the index list if the index\
        list is values, or the length of the True values in the index list if the index list is booleans
        :return: nothing
        """
        if all([isinstance(i, bool) for i in index]):  # boolean list
            if not self._check_list(values):  # single value provided, not a list, so turn values into list
                values = [values for x in index if x]
            if len(index) != len(self._index):
                raise ValueError('boolean index list must be same size of existing index')
            if len(values) != index.count(True):
                raise ValueError('length of values list must equal number of True entries in index list')
            indexes = [i for i, x in enumerate(index) if x]
            for x, i in enumerate(indexes):
                self._data[i] = values[x]
        else:  # list of index
            if not self._check_list(values):  # single value provided, not a list, so turn values into list
                values = [values for _ in index]
            if len(values) != len(index):
                raise ValueError('length of values and index must be the same.')
            # insert or append indexes as needed
            if self._sort:

                exists_tuples = list(zip(*[sorted_exists(self._index, x) for x in index]))