@cython.internal
cdef class __TreeSeries_uint8_t(__BaseTreeSeries):

    cdef interpolate_uint8_t     __interpolate
    cdef itermode_search_uint8_t __get_item
    cdef arithmetic_type_uint8_t __arithmetic

    cdef public   str  name
    cdef public   str  arithmetic
    cdef readonly str  interpolate_type
    cdef public   uint8_t extrapolate


    def __init__(self):
        pass


    def __cinit__( self, object index=None, object values=None, str name="Untitled", 
                   str interpolate="floor", uint8_t extrapolate=0, str arithmetic="left" ):

        self.set_interpolation(interpolate)
        self.extrapolate = extrapolate
        self.name = name
        self.arithmetic = arithmetic

        self.__get_item = self.__get_item_tree
        self.set_arithmetic(arithmetic)

        if index is None:
            
            if values is not None:
                raise IndexError("Index cannot be None while values are not.")

        else:
            if values is not None:
                self.__init_tree(index, values)

            else:
                self.__init_tree_index(index)


    #--------------------------------------------------------------------------------------------
    # Public methods
    #--------------------------------------------------------------------------------------------

    cpdef void insert(self, key, uint8_t value) except*:

        __insert_node_uint8_t(self.__index, key, value)


    cpdef void insert_range(self, list pairs) except*:

        cdef tuple pair

        for pair in pairs:
            __insert_node_uint8_t(self.__index, pair[0], pair[1])


    cpdef void erase(self, key) except*:

        __erase_node_uint8_t(self.__index, key)


    cpdef void erase_range(self, list keys) except*:

        cdef Py_ssize_t it

        for it in range( len(keys) ):
            __erase_node_uint8_t(self.__index, keys[it])


    cpdef list values(self):

        cdef rb_node_valued* it
        cdef list result = []

        for it in deref(self.__index):
            result.append( __deref_value_ptr_uint8_t(it) )

        return result


    cpdef list items(self):

        cdef node_ptr it
        cdef list result = []

        for it in deref(self.__index):
            result.append( ( self.__get_key_from_ptr(it), 
                             __deref_value_ptr_uint8_t(it) ) )

        return result


    def iteritems(self):

        cdef node_ptr it

        for it in deref(self.__index):
            yield ( self.__get_key_from_ptr(it), __deref_value_ptr_uint8_t(it) )


    cpdef tuple floor(self, key):

        cdef pair[node_ptr, node_ptr] bounds
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)
        bounds = self.__index.tree_search(c_key)

        if bounds.first == self.link():
            return (None, None)

        return ( self.__get_key_from_ptr(bounds.first),
                 __deref_value_ptr_uint8_t(bounds.first) )


    cpdef tuple ceil(self, key):

        cdef pair[node_ptr, node_ptr] bounds
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)  
        bounds = self.__index.tree_search(c_key)

        if bounds.second == self.link():
            return (None, None)

        return ( self.__get_key_from_ptr(bounds.second),
                 __deref_value_ptr_uint8_t(bounds.second) )


    cpdef __TreeSeries_uint8_t truncate(self, start, stop):

        cdef iterator begin
        cdef iterator end
        cdef pair[node_ptr, node_ptr] bounds_start 
        cdef pair[node_ptr, node_ptr] bounds_end
        cdef __TreeSeries_uint8_t result
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_start = c_pyobject(<PyObject*>start)
        cdef c_pyobject c_stop = c_pyobject(<PyObject*>stop)

        result = __TreeSeries_uint8_t.__new__( __TreeSeries_uint8_t, 
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        bounds_start = self.__index.tree_search(c_start)
        bounds_end = self.__index.tree_search(c_stop)
        begin = deref(bounds_start.second).it_position
        end = deref(bounds_end.first).it_position

        if( bounds_start.second == self.link() ) \
            or ( bounds_end.first == self.link() ):

            return result

        if self.__get_key_from_iter(begin) > self.__get_key_from_iter(end):
            return result

        while True:
            result.insert( self.__get_key_from_iter(begin), 
                           __deref_value_ptr_uint8_t( deref(begin) ) )

            if begin == end:
                break

            inc(begin)

        return result


    cpdef __TreeSeries_uint8_t periodic(self, start, stop, step):

        cdef __TreeSeries_uint8_t result
        cdef object it = start
        result = __TreeSeries_uint8_t.__new__( __TreeSeries_uint8_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        self.on_itermode()

        while it < stop:
            result.insert( it, self.__getitem__(it) )
            it = it + step

        self.off_itermode()

        return result


    cpdef __TreeSeries_uint8_t map( self, method, tuple args=(), 
                                  dict kwargs={} ):

        cdef __TreeSeries_uint8_t result
        cdef tuple it

        result = __TreeSeries_uint8_t.__new__( __TreeSeries_uint8_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        for it in self.iteritems():
            result.insert( it[0], method(it[1], *args, **kwargs) )

        return result


    cpdef void map_inplace( self, method, tuple args=(), 
                            dict kwargs={} ) except*:

        cdef node_ptr it

        for it in deref(self.__index):
            __set_value_uint8_t( it, method( __deref_value_ptr_uint8_t(it),
                                             *args, **kwargs ) )


    cpdef void set_interpolation(self, str interpolate):

        cdef string c_interpolate = bytearray(interpolate, 'utf8')
        self.interpolate_type = interpolate
        self.__interpolate = __INTERPOLATE_uint8_t[c_interpolate]


    cpdef void set_arithmetic(self, str arithmetic):

        if arithmetic == "union":
            self.__arithmetic = self.__arithmetic_union

        if arithmetic == "left":
            self.__arithmetic = self.__arithmetic_left


    cpdef void on_itermode(self):

        __BaseTreeSeries.on_itermode(self)
        self.__get_item = self.__get_item_linear


    cpdef void off_itermode(self):

        __BaseTreeSeries.off_itermode(self)
        self.__get_item = self.__get_item_tree


    cpdef void copy_data(self, other, str to_type="numerical") except*:

        cdef tuple it
        cdef uint8_t num
        self.clear()

        if to_type == "numerical":

            for it in other.iteritems():
                num = <uint8_t>float(it[1])
                self.insert( it[0], num )

        elif to_type == "str":
            for it in other.iteritems():
                self.insert( it[0], str(it[1]) )

        else:
            for it in other.iteritems():
                self.insert(it[0], it[1])


    cpdef void clear(self) except*:

        for key in self.keys():
            self.erase(key)

        self.__index.clear()

    #--------------------------------------------------------------------------------------------
    # Special methods
    #--------------------------------------------------------------------------------------------
    def __dealloc__(self):

        del self.__index


    def __str__(self):

        cdef str out_format = '{key}: {value}\n'
        cdef str result = 'Series object ' + self.name + '\n'
        cdef tuple it
        cdef list items = self.items()

        if len(items) <= 50: 
            for it in items:
                result += out_format.format(key=it[0], value=it[1])

        else:
            for it in items[:25]:
                result += out_format.format(key=it[0], value=it[1])

            result += '...\n'

            for it in items[-25:]:
                result += out_format.format(key=it[0], value=it[1])

        return result


    def __repr__(self):

        return self.__str__()

    
    def __iter__(self):

        cdef node_ptr it

        for it in deref(self.__index):
            yield __deref_value_ptr_uint8_t(it)


    def __getitem__(self, key):

        cdef int32_t error = TYPE_ERROR
        cdef uint8_t result

        if self.__index.size() == 0:
            return self.extrapolate

        if isinstance(key, slice):
            return self.__get_item_slice(key)

        result = self.__get_item(self, key, &error)

        if error == 0:
            return result

        if error == INT_KEY_ERROR:
            raise KeyError( str(key) )

        if error == TYPE_ERROR:
            raise TypeError("Inconsistent key type: " + key.__class__.__name__)


    def __setitem__(self, key, uint8_t value):

        cdef c_pyobject key_holder = to_c_pyobject(key)
        cdef node_ptr node = self.__index.insert_search(key_holder)

        if self.__index.size() == 0:
            __insert_node_by_ptr_uint8_t(self.__index, node, key, value)

        else:
            if self.__get_key_from_ptr(node) == key:
                __set_value_uint8_t(node, value)

            else:
                __insert_node_by_ptr_uint8_t(self.__index, node, key, value)


    #--------------------------------------------------------------------------------------------
    # Emulating numeric types
    #--------------------------------------------------------------------------------------------
    cpdef __TreeSeries_uint8_t add(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __add_uint8_t)


    cpdef __TreeSeries_uint8_t sub(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __sub_uint8_t)


    cpdef __TreeSeries_uint8_t mul(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __mul_uint8_t)


    cpdef __TreeSeries_uint8_t div(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __div_uint8_t)


    cpdef __TreeSeries_uint8_t lshift(self, shift_param):

        if isinstance(shift_param, int):
            return self.__lshift_int(shift_param)


    cpdef __TreeSeries_uint8_t rshift(self, shift_param):

        if isinstance(shift_param, int):
            return self.__rshift_int(shift_param)


    #--------------------------------------------------------------------------------------------
    # Private methods
    #--------------------------------------------------------------------------------------------
    cdef void __init_tree(self, index, values) except*:

        cdef tuple it

        if len(index) != len(values):
            raise IndexError("Index must have the same length as values.")

        for it in zip(index, values):
            self.insert(it[0], it[1])


    cdef void __init_tree_index(self, index) except*:

        for it in index:
            self.insert(it, 0)


    cdef uint8_t __get_item_linear(self, key, int32_t* error):

        #bug? cython 0.28.3 translate c_key to const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key) 
        cdef pair[node_ptr, node_ptr] bounds
        
        bounds = self.__index.linear_search_from(self.__last_call, c_key)
        error[0] = 0

        if bounds.first != self.link():
            self.__last_call = bounds.first

        else:
            self.__last_call = deref( self.__index.begin() )

        return self.__interpolate( bounds.first, bounds.second, key,
                                   self.link(), self.extrapolate,
                                   error )

    
    cdef uint8_t __get_item_tree(self, key, int32_t* error):

        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)
        cdef pair[node_ptr, node_ptr] bounds

        bounds = self.__index.tree_search(c_key)
        error[0] = 0

        return self.__interpolate( bounds.first, bounds.second, key,
                                   self.link(), self.extrapolate, 
                                   error )

    
    cdef __TreeSeries_uint8_t __get_item_slice(self, key):

        cdef c_pyobject c_key
        cdef node_ptr begin = deref( self.__index.begin() ) 
        cdef node_ptr end = deref( self.__index.back() )
        cdef __TreeSeries_uint8_t result
        result = __TreeSeries_uint8_t.__new__( __TreeSeries_uint8_t,
                                             interpolate=self.interpolate_type,
                                             arithmetic=self.arithmetic )

        if self.__len__() == 0:
            return result

        if key.start is not None and key.stop is not None:

            if key.start < self.begin() and key.stop < self.begin():
                return result

            if key.start > self.end() and key.stop < self.end():
                return result

        if key.start is not None:
            c_key = c_pyobject(<PyObject*>key.start)
            begin = self.__index.tree_search(c_key).second

        if key.stop is not None:
            c_key = c_pyobject(<PyObject*>key.stop)
            end = self.__index.tree_search(c_key).first

        if compare_func(end.key, begin.key) \
            and ( not equal(end.key, begin.key) ):

            return result

        if begin == self.link() or end == self.link():
            return result 

        if key.step is None:
            return self.truncate( self.__get_key_from_ptr(begin),
                                  self.__get_key_from_ptr(end) )

        else: 
            return self.periodic( self.__get_key_from_ptr(begin), 
                                  self.__get_key_from_ptr(end), 
                                  key.step )


    cdef __TreeSeries_uint8_t __lshift_int(self, Py_ssize_t shift_param):

        cdef __TreeSeries_uint8_t result
        cdef list delay_holder = []
        cdef tuple pair
        cdef Py_ssize_t count = 0
        cdef bool append_status = False

        result = __TreeSeries_uint8_t.__new__( __TreeSeries_uint8_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        if shift_param <= 0:
            raise ValueError("Shift parameter must be more than zero.")

        for pair in self.iteritems():
            
            if append_status:
                result.insert(delay_holder[0], pair[1])
                delay_holder.pop(0)

            if count == shift_param - 1:
                append_status = True

            delay_holder.append(pair[0])
            count += 1

        return result


    cdef __TreeSeries_uint8_t __rshift_int(self, Py_ssize_t shift_param):

        cdef __TreeSeries_uint8_t result
        cdef list delay_holder = []
        cdef tuple pair
        cdef Py_ssize_t count = 0
        cdef bool append_status = False

        result = __TreeSeries_uint8_t.__new__( __TreeSeries_uint8_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        if isinstance(shift_param, int):

            if shift_param <= 0:
                raise ValueError("Shift parameter must be more than zero.")

            for pair in self.iteritems():
                
                if append_status:
                    result.insert(pair[0], delay_holder[0])
                    delay_holder.pop(0)

                if count == shift_param - 1:
                    append_status = True

                delay_holder.append(pair[1])
                count += 1

            return result


    cdef __TreeSeries_uint8_t __arithmetic_left( self, __BaseTreeSeries other, 
                                                 arithmetic_uint8_t action ):

        cdef uint8_t current
        cdef node_ptr node
        cdef object key
        cdef __TreeSeries_uint8_t result
        result = __TreeSeries_uint8_t.__new__( __TreeSeries_uint8_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        other.on_itermode()

        for node in deref(self.__index):

            key = self.__get_key_from_ptr(node)
            current = action(__deref_value_ptr_uint8_t(node), other[key])
            __insert_node_uint8_t(result.__index, key, current)

        other.off_itermode()

        return result


    cdef __TreeSeries_uint8_t __arithmetic_union( self, __BaseTreeSeries other, 
                                                  arithmetic_uint8_t action ):

        cdef uint8_t current
        cdef object key
        cdef trees_iterator[rb_tree, rb_node_valued] iterator
        cdef vector[rb_tree_ptr] trees = vector[rb_tree_ptr](2)
        cdef __TreeSeries_uint8_t result
        result = __TreeSeries_uint8_t.__new__( __TreeSeries_uint8_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        
        trees[0] = self.__index
        trees[1] = other.get_tree()
        iterator.set_equal(equal_pair)
        iterator.set_compare(comp_pair)
        iterator.set_iterator(trees, "forward")

        self.on_itermode()
        other.on_itermode()

        while not iterator.empty():
            key = <object>deref(iterator).key.data
            current = action(self.__getitem__(key), other[key])
            __insert_node_uint8_t(result.__index, key, current)
            pinc(iterator)

        self.off_itermode()
        other.off_itermode()

        return result
@cython.internal
cdef class __TreeSeries_uint16_t(__BaseTreeSeries):

    cdef interpolate_uint16_t     __interpolate
    cdef itermode_search_uint16_t __get_item
    cdef arithmetic_type_uint16_t __arithmetic

    cdef public   str  name
    cdef public   str  arithmetic
    cdef readonly str  interpolate_type
    cdef public   uint16_t extrapolate


    def __init__(self):
        pass


    def __cinit__( self, object index=None, object values=None, str name="Untitled", 
                   str interpolate="floor", uint16_t extrapolate=0, str arithmetic="left" ):

        self.set_interpolation(interpolate)
        self.extrapolate = extrapolate
        self.name = name
        self.arithmetic = arithmetic

        self.__get_item = self.__get_item_tree
        self.set_arithmetic(arithmetic)

        if index is None:
            
            if values is not None:
                raise IndexError("Index cannot be None while values are not.")

        else:
            if values is not None:
                self.__init_tree(index, values)

            else:
                self.__init_tree_index(index)


    #--------------------------------------------------------------------------------------------
    # Public methods
    #--------------------------------------------------------------------------------------------

    cpdef void insert(self, key, uint16_t value) except*:

        __insert_node_uint16_t(self.__index, key, value)


    cpdef void insert_range(self, list pairs) except*:

        cdef tuple pair

        for pair in pairs:
            __insert_node_uint16_t(self.__index, pair[0], pair[1])


    cpdef void erase(self, key) except*:

        __erase_node_uint16_t(self.__index, key)


    cpdef void erase_range(self, list keys) except*:

        cdef Py_ssize_t it

        for it in range( len(keys) ):
            __erase_node_uint16_t(self.__index, keys[it])


    cpdef list values(self):

        cdef rb_node_valued* it
        cdef list result = []

        for it in deref(self.__index):
            result.append( __deref_value_ptr_uint16_t(it) )

        return result


    cpdef list items(self):

        cdef node_ptr it
        cdef list result = []

        for it in deref(self.__index):
            result.append( ( self.__get_key_from_ptr(it), 
                             __deref_value_ptr_uint16_t(it) ) )

        return result


    def iteritems(self):

        cdef node_ptr it

        for it in deref(self.__index):
            yield ( self.__get_key_from_ptr(it), __deref_value_ptr_uint16_t(it) )


    cpdef tuple floor(self, key):

        cdef pair[node_ptr, node_ptr] bounds
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)
        bounds = self.__index.tree_search(c_key)

        if bounds.first == self.link():
            return (None, None)

        return ( self.__get_key_from_ptr(bounds.first),
                 __deref_value_ptr_uint16_t(bounds.first) )


    cpdef tuple ceil(self, key):

        cdef pair[node_ptr, node_ptr] bounds
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)  
        bounds = self.__index.tree_search(c_key)

        if bounds.second == self.link():
            return (None, None)

        return ( self.__get_key_from_ptr(bounds.second),
                 __deref_value_ptr_uint16_t(bounds.second) )


    cpdef __TreeSeries_uint16_t truncate(self, start, stop):

        cdef iterator begin
        cdef iterator end
        cdef pair[node_ptr, node_ptr] bounds_start 
        cdef pair[node_ptr, node_ptr] bounds_end
        cdef __TreeSeries_uint16_t result
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_start = c_pyobject(<PyObject*>start)
        cdef c_pyobject c_stop = c_pyobject(<PyObject*>stop)

        result = __TreeSeries_uint16_t.__new__( __TreeSeries_uint16_t, 
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        bounds_start = self.__index.tree_search(c_start)
        bounds_end = self.__index.tree_search(c_stop)
        begin = deref(bounds_start.second).it_position
        end = deref(bounds_end.first).it_position

        if( bounds_start.second == self.link() ) \
            or ( bounds_end.first == self.link() ):

            return result

        if self.__get_key_from_iter(begin) > self.__get_key_from_iter(end):
            return result

        while True:
            result.insert( self.__get_key_from_iter(begin), 
                           __deref_value_ptr_uint16_t( deref(begin) ) )

            if begin == end:
                break

            inc(begin)

        return result


    cpdef __TreeSeries_uint16_t periodic(self, start, stop, step):

        cdef __TreeSeries_uint16_t result
        cdef object it = start
        result = __TreeSeries_uint16_t.__new__( __TreeSeries_uint16_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        self.on_itermode()

        while it < stop:
            result.insert( it, self.__getitem__(it) )
            it = it + step

        self.off_itermode()

        return result


    cpdef __TreeSeries_uint16_t map( self, method, tuple args=(), 
                                  dict kwargs={} ):

        cdef __TreeSeries_uint16_t result
        cdef tuple it

        result = __TreeSeries_uint16_t.__new__( __TreeSeries_uint16_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        for it in self.iteritems():
            result.insert( it[0], method(it[1], *args, **kwargs) )

        return result


    cpdef void map_inplace( self, method, tuple args=(), 
                            dict kwargs={} ) except*:

        cdef node_ptr it

        for it in deref(self.__index):
            __set_value_uint16_t( it, method( __deref_value_ptr_uint16_t(it),
                                             *args, **kwargs ) )


    cpdef void set_interpolation(self, str interpolate):

        cdef string c_interpolate = bytearray(interpolate, 'utf8')
        self.interpolate_type = interpolate
        self.__interpolate = __INTERPOLATE_uint16_t[c_interpolate]


    cpdef void set_arithmetic(self, str arithmetic):

        if arithmetic == "union":
            self.__arithmetic = self.__arithmetic_union

        if arithmetic == "left":
            self.__arithmetic = self.__arithmetic_left


    cpdef void on_itermode(self):

        __BaseTreeSeries.on_itermode(self)
        self.__get_item = self.__get_item_linear


    cpdef void off_itermode(self):

        __BaseTreeSeries.off_itermode(self)
        self.__get_item = self.__get_item_tree


    cpdef void copy_data(self, other, str to_type="numerical") except*:

        cdef tuple it
        cdef uint16_t num
        self.clear()

        if to_type == "numerical":

            for it in other.iteritems():
                num = <uint16_t>float(it[1])
                self.insert( it[0], num )

        elif to_type == "str":
            for it in other.iteritems():
                self.insert( it[0], str(it[1]) )

        else:
            for it in other.iteritems():
                self.insert(it[0], it[1])


    cpdef void clear(self) except*:

        for key in self.keys():
            self.erase(key)

        self.__index.clear()

    #--------------------------------------------------------------------------------------------
    # Special methods
    #--------------------------------------------------------------------------------------------
    def __dealloc__(self):

        del self.__index


    def __str__(self):

        cdef str out_format = '{key}: {value}\n'
        cdef str result = 'Series object ' + self.name + '\n'
        cdef tuple it
        cdef list items = self.items()

        if len(items) <= 50: 
            for it in items:
                result += out_format.format(key=it[0], value=it[1])

        else:
            for it in items[:25]:
                result += out_format.format(key=it[0], value=it[1])

            result += '...\n'

            for it in items[-25:]:
                result += out_format.format(key=it[0], value=it[1])

        return result


    def __repr__(self):

        return self.__str__()

    
    def __iter__(self):

        cdef node_ptr it

        for it in deref(self.__index):
            yield __deref_value_ptr_uint16_t(it)


    def __getitem__(self, key):

        cdef int32_t error = TYPE_ERROR
        cdef uint16_t result

        if self.__index.size() == 0:
            return self.extrapolate

        if isinstance(key, slice):
            return self.__get_item_slice(key)

        result = self.__get_item(self, key, &error)

        if error == 0:
            return result

        if error == INT_KEY_ERROR:
            raise KeyError( str(key) )

        if error == TYPE_ERROR:
            raise TypeError("Inconsistent key type: " + key.__class__.__name__)


    def __setitem__(self, key, uint16_t value):

        cdef c_pyobject key_holder = to_c_pyobject(key)
        cdef node_ptr node = self.__index.insert_search(key_holder)

        if self.__index.size() == 0:
            __insert_node_by_ptr_uint16_t(self.__index, node, key, value)

        else:
            if self.__get_key_from_ptr(node) == key:
                __set_value_uint16_t(node, value)

            else:
                __insert_node_by_ptr_uint16_t(self.__index, node, key, value)


    #--------------------------------------------------------------------------------------------
    # Emulating numeric types
    #--------------------------------------------------------------------------------------------
    cpdef __TreeSeries_uint16_t add(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __add_uint16_t)


    cpdef __TreeSeries_uint16_t sub(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __sub_uint16_t)


    cpdef __TreeSeries_uint16_t mul(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __mul_uint16_t)


    cpdef __TreeSeries_uint16_t div(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __div_uint16_t)


    cpdef __TreeSeries_uint16_t lshift(self, shift_param):

        if isinstance(shift_param, int):
            return self.__lshift_int(shift_param)


    cpdef __TreeSeries_uint16_t rshift(self, shift_param):

        if isinstance(shift_param, int):
            return self.__rshift_int(shift_param)


    #--------------------------------------------------------------------------------------------
    # Private methods
    #--------------------------------------------------------------------------------------------
    cdef void __init_tree(self, index, values) except*:

        cdef tuple it

        if len(index) != len(values):
            raise IndexError("Index must have the same length as values.")

        for it in zip(index, values):
            self.insert(it[0], it[1])


    cdef void __init_tree_index(self, index) except*:

        for it in index:
            self.insert(it, 0)


    cdef uint16_t __get_item_linear(self, key, int32_t* error):

        #bug? cython 0.28.3 translate c_key to const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key) 
        cdef pair[node_ptr, node_ptr] bounds
        
        bounds = self.__index.linear_search_from(self.__last_call, c_key)
        error[0] = 0

        if bounds.first != self.link():
            self.__last_call = bounds.first

        else:
            self.__last_call = deref( self.__index.begin() )

        return self.__interpolate( bounds.first, bounds.second, key,
                                   self.link(), self.extrapolate,
                                   error )

    
    cdef uint16_t __get_item_tree(self, key, int32_t* error):

        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)
        cdef pair[node_ptr, node_ptr] bounds

        bounds = self.__index.tree_search(c_key)
        error[0] = 0

        return self.__interpolate( bounds.first, bounds.second, key,
                                   self.link(), self.extrapolate, 
                                   error )

    
    cdef __TreeSeries_uint16_t __get_item_slice(self, key):

        cdef c_pyobject c_key
        cdef node_ptr begin = deref( self.__index.begin() ) 
        cdef node_ptr end = deref( self.__index.back() )
        cdef __TreeSeries_uint16_t result
        result = __TreeSeries_uint16_t.__new__( __TreeSeries_uint16_t,
                                             interpolate=self.interpolate_type,
                                             arithmetic=self.arithmetic )

        if self.__len__() == 0:
            return result

        if key.start is not None and key.stop is not None:

            if key.start < self.begin() and key.stop < self.begin():
                return result

            if key.start > self.end() and key.stop < self.end():
                return result

        if key.start is not None:
            c_key = c_pyobject(<PyObject*>key.start)
            begin = self.__index.tree_search(c_key).second

        if key.stop is not None:
            c_key = c_pyobject(<PyObject*>key.stop)
            end = self.__index.tree_search(c_key).first

        if compare_func(end.key, begin.key) \
            and ( not equal(end.key, begin.key) ):

            return result

        if begin == self.link() or end == self.link():
            return result 

        if key.step is None:
            return self.truncate( self.__get_key_from_ptr(begin),
                                  self.__get_key_from_ptr(end) )

        else: 
            return self.periodic( self.__get_key_from_ptr(begin), 
                                  self.__get_key_from_ptr(end), 
                                  key.step )


    cdef __TreeSeries_uint16_t __lshift_int(self, Py_ssize_t shift_param):

        cdef __TreeSeries_uint16_t result
        cdef list delay_holder = []
        cdef tuple pair
        cdef Py_ssize_t count = 0
        cdef bool append_status = False

        result = __TreeSeries_uint16_t.__new__( __TreeSeries_uint16_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        if shift_param <= 0:
            raise ValueError("Shift parameter must be more than zero.")

        for pair in self.iteritems():
            
            if append_status:
                result.insert(delay_holder[0], pair[1])
                delay_holder.pop(0)

            if count == shift_param - 1:
                append_status = True

            delay_holder.append(pair[0])
            count += 1

        return result


    cdef __TreeSeries_uint16_t __rshift_int(self, Py_ssize_t shift_param):

        cdef __TreeSeries_uint16_t result
        cdef list delay_holder = []
        cdef tuple pair
        cdef Py_ssize_t count = 0
        cdef bool append_status = False

        result = __TreeSeries_uint16_t.__new__( __TreeSeries_uint16_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        if isinstance(shift_param, int):

            if shift_param <= 0:
                raise ValueError("Shift parameter must be more than zero.")

            for pair in self.iteritems():
                
                if append_status:
                    result.insert(pair[0], delay_holder[0])
                    delay_holder.pop(0)

                if count == shift_param - 1:
                    append_status = True

                delay_holder.append(pair[1])
                count += 1

            return result


    cdef __TreeSeries_uint16_t __arithmetic_left( self, __BaseTreeSeries other, 
                                                 arithmetic_uint16_t action ):

        cdef uint16_t current
        cdef node_ptr node
        cdef object key
        cdef __TreeSeries_uint16_t result
        result = __TreeSeries_uint16_t.__new__( __TreeSeries_uint16_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        other.on_itermode()

        for node in deref(self.__index):

            key = self.__get_key_from_ptr(node)
            current = action(__deref_value_ptr_uint16_t(node), other[key])
            __insert_node_uint16_t(result.__index, key, current)

        other.off_itermode()

        return result


    cdef __TreeSeries_uint16_t __arithmetic_union( self, __BaseTreeSeries other, 
                                                  arithmetic_uint16_t action ):

        cdef uint16_t current
        cdef object key
        cdef trees_iterator[rb_tree, rb_node_valued] iterator
        cdef vector[rb_tree_ptr] trees = vector[rb_tree_ptr](2)
        cdef __TreeSeries_uint16_t result
        result = __TreeSeries_uint16_t.__new__( __TreeSeries_uint16_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        
        trees[0] = self.__index
        trees[1] = other.get_tree()
        iterator.set_equal(equal_pair)
        iterator.set_compare(comp_pair)
        iterator.set_iterator(trees, "forward")

        self.on_itermode()
        other.on_itermode()

        while not iterator.empty():
            key = <object>deref(iterator).key.data
            current = action(self.__getitem__(key), other[key])
            __insert_node_uint16_t(result.__index, key, current)
            pinc(iterator)

        self.off_itermode()
        other.off_itermode()

        return result
@cython.internal
cdef class __TreeSeries_uint32_t(__BaseTreeSeries):

    cdef interpolate_uint32_t     __interpolate
    cdef itermode_search_uint32_t __get_item
    cdef arithmetic_type_uint32_t __arithmetic

    cdef public   str  name
    cdef public   str  arithmetic
    cdef readonly str  interpolate_type
    cdef public   uint32_t extrapolate


    def __init__(self):
        pass


    def __cinit__( self, object index=None, object values=None, str name="Untitled", 
                   str interpolate="floor", uint32_t extrapolate=0, str arithmetic="left" ):

        self.set_interpolation(interpolate)
        self.extrapolate = extrapolate
        self.name = name
        self.arithmetic = arithmetic

        self.__get_item = self.__get_item_tree
        self.set_arithmetic(arithmetic)

        if index is None:
            
            if values is not None:
                raise IndexError("Index cannot be None while values are not.")

        else:
            if values is not None:
                self.__init_tree(index, values)

            else:
                self.__init_tree_index(index)


    #--------------------------------------------------------------------------------------------
    # Public methods
    #--------------------------------------------------------------------------------------------

    cpdef void insert(self, key, uint32_t value) except*:

        __insert_node_uint32_t(self.__index, key, value)


    cpdef void insert_range(self, list pairs) except*:

        cdef tuple pair

        for pair in pairs:
            __insert_node_uint32_t(self.__index, pair[0], pair[1])


    cpdef void erase(self, key) except*:

        __erase_node_uint32_t(self.__index, key)


    cpdef void erase_range(self, list keys) except*:

        cdef Py_ssize_t it

        for it in range( len(keys) ):
            __erase_node_uint32_t(self.__index, keys[it])


    cpdef list values(self):

        cdef rb_node_valued* it
        cdef list result = []

        for it in deref(self.__index):
            result.append( __deref_value_ptr_uint32_t(it) )

        return result


    cpdef list items(self):

        cdef node_ptr it
        cdef list result = []

        for it in deref(self.__index):
            result.append( ( self.__get_key_from_ptr(it), 
                             __deref_value_ptr_uint32_t(it) ) )

        return result


    def iteritems(self):

        cdef node_ptr it

        for it in deref(self.__index):
            yield ( self.__get_key_from_ptr(it), __deref_value_ptr_uint32_t(it) )


    cpdef tuple floor(self, key):

        cdef pair[node_ptr, node_ptr] bounds
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)
        bounds = self.__index.tree_search(c_key)

        if bounds.first == self.link():
            return (None, None)

        return ( self.__get_key_from_ptr(bounds.first),
                 __deref_value_ptr_uint32_t(bounds.first) )


    cpdef tuple ceil(self, key):

        cdef pair[node_ptr, node_ptr] bounds
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)  
        bounds = self.__index.tree_search(c_key)

        if bounds.second == self.link():
            return (None, None)

        return ( self.__get_key_from_ptr(bounds.second),
                 __deref_value_ptr_uint32_t(bounds.second) )


    cpdef __TreeSeries_uint32_t truncate(self, start, stop):

        cdef iterator begin
        cdef iterator end
        cdef pair[node_ptr, node_ptr] bounds_start 
        cdef pair[node_ptr, node_ptr] bounds_end
        cdef __TreeSeries_uint32_t result
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_start = c_pyobject(<PyObject*>start)
        cdef c_pyobject c_stop = c_pyobject(<PyObject*>stop)

        result = __TreeSeries_uint32_t.__new__( __TreeSeries_uint32_t, 
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        bounds_start = self.__index.tree_search(c_start)
        bounds_end = self.__index.tree_search(c_stop)
        begin = deref(bounds_start.second).it_position
        end = deref(bounds_end.first).it_position

        if( bounds_start.second == self.link() ) \
            or ( bounds_end.first == self.link() ):

            return result

        if self.__get_key_from_iter(begin) > self.__get_key_from_iter(end):
            return result

        while True:
            result.insert( self.__get_key_from_iter(begin), 
                           __deref_value_ptr_uint32_t( deref(begin) ) )

            if begin == end:
                break

            inc(begin)

        return result


    cpdef __TreeSeries_uint32_t periodic(self, start, stop, step):

        cdef __TreeSeries_uint32_t result
        cdef object it = start
        result = __TreeSeries_uint32_t.__new__( __TreeSeries_uint32_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        self.on_itermode()

        while it < stop:
            result.insert( it, self.__getitem__(it) )
            it = it + step

        self.off_itermode()

        return result


    cpdef __TreeSeries_uint32_t map( self, method, tuple args=(), 
                                  dict kwargs={} ):

        cdef __TreeSeries_uint32_t result
        cdef tuple it

        result = __TreeSeries_uint32_t.__new__( __TreeSeries_uint32_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        for it in self.iteritems():
            result.insert( it[0], method(it[1], *args, **kwargs) )

        return result


    cpdef void map_inplace( self, method, tuple args=(), 
                            dict kwargs={} ) except*:

        cdef node_ptr it

        for it in deref(self.__index):
            __set_value_uint32_t( it, method( __deref_value_ptr_uint32_t(it),
                                             *args, **kwargs ) )


    cpdef void set_interpolation(self, str interpolate):

        cdef string c_interpolate = bytearray(interpolate, 'utf8')
        self.interpolate_type = interpolate
        self.__interpolate = __INTERPOLATE_uint32_t[c_interpolate]


    cpdef void set_arithmetic(self, str arithmetic):

        if arithmetic == "union":
            self.__arithmetic = self.__arithmetic_union

        if arithmetic == "left":
            self.__arithmetic = self.__arithmetic_left


    cpdef void on_itermode(self):

        __BaseTreeSeries.on_itermode(self)
        self.__get_item = self.__get_item_linear


    cpdef void off_itermode(self):

        __BaseTreeSeries.off_itermode(self)
        self.__get_item = self.__get_item_tree


    cpdef void copy_data(self, other, str to_type="numerical") except*:

        cdef tuple it
        cdef uint32_t num
        self.clear()

        if to_type == "numerical":

            for it in other.iteritems():
                num = <uint32_t>float(it[1])
                self.insert( it[0], num )

        elif to_type == "str":
            for it in other.iteritems():
                self.insert( it[0], str(it[1]) )

        else:
            for it in other.iteritems():
                self.insert(it[0], it[1])


    cpdef void clear(self) except*:

        for key in self.keys():
            self.erase(key)

        self.__index.clear()

    #--------------------------------------------------------------------------------------------
    # Special methods
    #--------------------------------------------------------------------------------------------
    def __dealloc__(self):

        del self.__index


    def __str__(self):

        cdef str out_format = '{key}: {value}\n'
        cdef str result = 'Series object ' + self.name + '\n'
        cdef tuple it
        cdef list items = self.items()

        if len(items) <= 50: 
            for it in items:
                result += out_format.format(key=it[0], value=it[1])

        else:
            for it in items[:25]:
                result += out_format.format(key=it[0], value=it[1])

            result += '...\n'

            for it in items[-25:]:
                result += out_format.format(key=it[0], value=it[1])

        return result


    def __repr__(self):

        return self.__str__()

    
    def __iter__(self):

        cdef node_ptr it

        for it in deref(self.__index):
            yield __deref_value_ptr_uint32_t(it)


    def __getitem__(self, key):

        cdef int32_t error = TYPE_ERROR
        cdef uint32_t result

        if self.__index.size() == 0:
            return self.extrapolate

        if isinstance(key, slice):
            return self.__get_item_slice(key)

        result = self.__get_item(self, key, &error)

        if error == 0:
            return result

        if error == INT_KEY_ERROR:
            raise KeyError( str(key) )

        if error == TYPE_ERROR:
            raise TypeError("Inconsistent key type: " + key.__class__.__name__)


    def __setitem__(self, key, uint32_t value):

        cdef c_pyobject key_holder = to_c_pyobject(key)
        cdef node_ptr node = self.__index.insert_search(key_holder)

        if self.__index.size() == 0:
            __insert_node_by_ptr_uint32_t(self.__index, node, key, value)

        else:
            if self.__get_key_from_ptr(node) == key:
                __set_value_uint32_t(node, value)

            else:
                __insert_node_by_ptr_uint32_t(self.__index, node, key, value)


    #--------------------------------------------------------------------------------------------
    # Emulating numeric types
    #--------------------------------------------------------------------------------------------
    cpdef __TreeSeries_uint32_t add(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __add_uint32_t)


    cpdef __TreeSeries_uint32_t sub(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __sub_uint32_t)


    cpdef __TreeSeries_uint32_t mul(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __mul_uint32_t)


    cpdef __TreeSeries_uint32_t div(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __div_uint32_t)


    cpdef __TreeSeries_uint32_t lshift(self, shift_param):

        if isinstance(shift_param, int):
            return self.__lshift_int(shift_param)


    cpdef __TreeSeries_uint32_t rshift(self, shift_param):

        if isinstance(shift_param, int):
            return self.__rshift_int(shift_param)


    #--------------------------------------------------------------------------------------------
    # Private methods
    #--------------------------------------------------------------------------------------------
    cdef void __init_tree(self, index, values) except*:

        cdef tuple it

        if len(index) != len(values):
            raise IndexError("Index must have the same length as values.")

        for it in zip(index, values):
            self.insert(it[0], it[1])


    cdef void __init_tree_index(self, index) except*:

        for it in index:
            self.insert(it, 0)


    cdef uint32_t __get_item_linear(self, key, int32_t* error):

        #bug? cython 0.28.3 translate c_key to const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key) 
        cdef pair[node_ptr, node_ptr] bounds
        
        bounds = self.__index.linear_search_from(self.__last_call, c_key)
        error[0] = 0

        if bounds.first != self.link():
            self.__last_call = bounds.first

        else:
            self.__last_call = deref( self.__index.begin() )

        return self.__interpolate( bounds.first, bounds.second, key,
                                   self.link(), self.extrapolate,
                                   error )

    
    cdef uint32_t __get_item_tree(self, key, int32_t* error):

        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)
        cdef pair[node_ptr, node_ptr] bounds

        bounds = self.__index.tree_search(c_key)
        error[0] = 0

        return self.__interpolate( bounds.first, bounds.second, key,
                                   self.link(), self.extrapolate, 
                                   error )

    
    cdef __TreeSeries_uint32_t __get_item_slice(self, key):

        cdef c_pyobject c_key
        cdef node_ptr begin = deref( self.__index.begin() ) 
        cdef node_ptr end = deref( self.__index.back() )
        cdef __TreeSeries_uint32_t result
        result = __TreeSeries_uint32_t.__new__( __TreeSeries_uint32_t,
                                             interpolate=self.interpolate_type,
                                             arithmetic=self.arithmetic )

        if self.__len__() == 0:
            return result

        if key.start is not None and key.stop is not None:

            if key.start < self.begin() and key.stop < self.begin():
                return result

            if key.start > self.end() and key.stop < self.end():
                return result

        if key.start is not None:
            c_key = c_pyobject(<PyObject*>key.start)
            begin = self.__index.tree_search(c_key).second

        if key.stop is not None:
            c_key = c_pyobject(<PyObject*>key.stop)
            end = self.__index.tree_search(c_key).first

        if compare_func(end.key, begin.key) \
            and ( not equal(end.key, begin.key) ):

            return result

        if begin == self.link() or end == self.link():
            return result 

        if key.step is None:
            return self.truncate( self.__get_key_from_ptr(begin),
                                  self.__get_key_from_ptr(end) )

        else: 
            return self.periodic( self.__get_key_from_ptr(begin), 
                                  self.__get_key_from_ptr(end), 
                                  key.step )


    cdef __TreeSeries_uint32_t __lshift_int(self, Py_ssize_t shift_param):

        cdef __TreeSeries_uint32_t result
        cdef list delay_holder = []
        cdef tuple pair
        cdef Py_ssize_t count = 0
        cdef bool append_status = False

        result = __TreeSeries_uint32_t.__new__( __TreeSeries_uint32_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        if shift_param <= 0:
            raise ValueError("Shift parameter must be more than zero.")

        for pair in self.iteritems():
            
            if append_status:
                result.insert(delay_holder[0], pair[1])
                delay_holder.pop(0)

            if count == shift_param - 1:
                append_status = True

            delay_holder.append(pair[0])
            count += 1

        return result


    cdef __TreeSeries_uint32_t __rshift_int(self, Py_ssize_t shift_param):

        cdef __TreeSeries_uint32_t result
        cdef list delay_holder = []
        cdef tuple pair
        cdef Py_ssize_t count = 0
        cdef bool append_status = False

        result = __TreeSeries_uint32_t.__new__( __TreeSeries_uint32_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        if isinstance(shift_param, int):

            if shift_param <= 0:
                raise ValueError("Shift parameter must be more than zero.")

            for pair in self.iteritems():
                
                if append_status:
                    result.insert(pair[0], delay_holder[0])
                    delay_holder.pop(0)

                if count == shift_param - 1:
                    append_status = True

                delay_holder.append(pair[1])
                count += 1

            return result


    cdef __TreeSeries_uint32_t __arithmetic_left( self, __BaseTreeSeries other, 
                                                 arithmetic_uint32_t action ):

        cdef uint32_t current
        cdef node_ptr node
        cdef object key
        cdef __TreeSeries_uint32_t result
        result = __TreeSeries_uint32_t.__new__( __TreeSeries_uint32_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        other.on_itermode()

        for node in deref(self.__index):

            key = self.__get_key_from_ptr(node)
            current = action(__deref_value_ptr_uint32_t(node), other[key])
            __insert_node_uint32_t(result.__index, key, current)

        other.off_itermode()

        return result


    cdef __TreeSeries_uint32_t __arithmetic_union( self, __BaseTreeSeries other, 
                                                  arithmetic_uint32_t action ):

        cdef uint32_t current
        cdef object key
        cdef trees_iterator[rb_tree, rb_node_valued] iterator
        cdef vector[rb_tree_ptr] trees = vector[rb_tree_ptr](2)
        cdef __TreeSeries_uint32_t result
        result = __TreeSeries_uint32_t.__new__( __TreeSeries_uint32_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        
        trees[0] = self.__index
        trees[1] = other.get_tree()
        iterator.set_equal(equal_pair)
        iterator.set_compare(comp_pair)
        iterator.set_iterator(trees, "forward")

        self.on_itermode()
        other.on_itermode()

        while not iterator.empty():
            key = <object>deref(iterator).key.data
            current = action(self.__getitem__(key), other[key])
            __insert_node_uint32_t(result.__index, key, current)
            pinc(iterator)

        self.off_itermode()
        other.off_itermode()

        return result
@cython.internal
cdef class __TreeSeries_uint64_t(__BaseTreeSeries):

    cdef interpolate_uint64_t     __interpolate
    cdef itermode_search_uint64_t __get_item
    cdef arithmetic_type_uint64_t __arithmetic

    cdef public   str  name
    cdef public   str  arithmetic
    cdef readonly str  interpolate_type
    cdef public   uint64_t extrapolate


    def __init__(self):
        pass


    def __cinit__( self, object index=None, object values=None, str name="Untitled", 
                   str interpolate="floor", uint64_t extrapolate=0, str arithmetic="left" ):

        self.set_interpolation(interpolate)
        self.extrapolate = extrapolate
        self.name = name
        self.arithmetic = arithmetic

        self.__get_item = self.__get_item_tree
        self.set_arithmetic(arithmetic)

        if index is None:
            
            if values is not None:
                raise IndexError("Index cannot be None while values are not.")

        else:
            if values is not None:
                self.__init_tree(index, values)

            else:
                self.__init_tree_index(index)


    #--------------------------------------------------------------------------------------------
    # Public methods
    #--------------------------------------------------------------------------------------------

    cpdef void insert(self, key, uint64_t value) except*:

        __insert_node_uint64_t(self.__index, key, value)


    cpdef void insert_range(self, list pairs) except*:

        cdef tuple pair

        for pair in pairs:
            __insert_node_uint64_t(self.__index, pair[0], pair[1])


    cpdef void erase(self, key) except*:

        __erase_node_uint64_t(self.__index, key)


    cpdef void erase_range(self, list keys) except*:

        cdef Py_ssize_t it

        for it in range( len(keys) ):
            __erase_node_uint64_t(self.__index, keys[it])


    cpdef list values(self):

        cdef rb_node_valued* it
        cdef list result = []

        for it in deref(self.__index):
            result.append( __deref_value_ptr_uint64_t(it) )

        return result


    cpdef list items(self):

        cdef node_ptr it
        cdef list result = []

        for it in deref(self.__index):
            result.append( ( self.__get_key_from_ptr(it), 
                             __deref_value_ptr_uint64_t(it) ) )

        return result


    def iteritems(self):

        cdef node_ptr it

        for it in deref(self.__index):
            yield ( self.__get_key_from_ptr(it), __deref_value_ptr_uint64_t(it) )


    cpdef tuple floor(self, key):

        cdef pair[node_ptr, node_ptr] bounds
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)
        bounds = self.__index.tree_search(c_key)

        if bounds.first == self.link():
            return (None, None)

        return ( self.__get_key_from_ptr(bounds.first),
                 __deref_value_ptr_uint64_t(bounds.first) )


    cpdef tuple ceil(self, key):

        cdef pair[node_ptr, node_ptr] bounds
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)  
        bounds = self.__index.tree_search(c_key)

        if bounds.second == self.link():
            return (None, None)

        return ( self.__get_key_from_ptr(bounds.second),
                 __deref_value_ptr_uint64_t(bounds.second) )


    cpdef __TreeSeries_uint64_t truncate(self, start, stop):

        cdef iterator begin
        cdef iterator end
        cdef pair[node_ptr, node_ptr] bounds_start 
        cdef pair[node_ptr, node_ptr] bounds_end
        cdef __TreeSeries_uint64_t result
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_start = c_pyobject(<PyObject*>start)
        cdef c_pyobject c_stop = c_pyobject(<PyObject*>stop)

        result = __TreeSeries_uint64_t.__new__( __TreeSeries_uint64_t, 
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        bounds_start = self.__index.tree_search(c_start)
        bounds_end = self.__index.tree_search(c_stop)
        begin = deref(bounds_start.second).it_position
        end = deref(bounds_end.first).it_position

        if( bounds_start.second == self.link() ) \
            or ( bounds_end.first == self.link() ):

            return result

        if self.__get_key_from_iter(begin) > self.__get_key_from_iter(end):
            return result

        while True:
            result.insert( self.__get_key_from_iter(begin), 
                           __deref_value_ptr_uint64_t( deref(begin) ) )

            if begin == end:
                break

            inc(begin)

        return result


    cpdef __TreeSeries_uint64_t periodic(self, start, stop, step):

        cdef __TreeSeries_uint64_t result
        cdef object it = start
        result = __TreeSeries_uint64_t.__new__( __TreeSeries_uint64_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        self.on_itermode()

        while it < stop:
            result.insert( it, self.__getitem__(it) )
            it = it + step

        self.off_itermode()

        return result


    cpdef __TreeSeries_uint64_t map( self, method, tuple args=(), 
                                  dict kwargs={} ):

        cdef __TreeSeries_uint64_t result
        cdef tuple it

        result = __TreeSeries_uint64_t.__new__( __TreeSeries_uint64_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        for it in self.iteritems():
            result.insert( it[0], method(it[1], *args, **kwargs) )

        return result


    cpdef void map_inplace( self, method, tuple args=(), 
                            dict kwargs={} ) except*:

        cdef node_ptr it

        for it in deref(self.__index):
            __set_value_uint64_t( it, method( __deref_value_ptr_uint64_t(it),
                                             *args, **kwargs ) )


    cpdef void set_interpolation(self, str interpolate):

        cdef string c_interpolate = bytearray(interpolate, 'utf8')
        self.interpolate_type = interpolate
        self.__interpolate = __INTERPOLATE_uint64_t[c_interpolate]


    cpdef void set_arithmetic(self, str arithmetic):

        if arithmetic == "union":
            self.__arithmetic = self.__arithmetic_union

        if arithmetic == "left":
            self.__arithmetic = self.__arithmetic_left


    cpdef void on_itermode(self):

        __BaseTreeSeries.on_itermode(self)
        self.__get_item = self.__get_item_linear


    cpdef void off_itermode(self):

        __BaseTreeSeries.off_itermode(self)
        self.__get_item = self.__get_item_tree


    cpdef void copy_data(self, other, str to_type="numerical") except*:

        cdef tuple it
        cdef uint64_t num
        self.clear()

        if to_type == "numerical":

            for it in other.iteritems():
                num = <uint64_t>float(it[1])
                self.insert( it[0], num )

        elif to_type == "str":
            for it in other.iteritems():
                self.insert( it[0], str(it[1]) )

        else:
            for it in other.iteritems():
                self.insert(it[0], it[1])


    cpdef void clear(self) except*:

        for key in self.keys():
            self.erase(key)

        self.__index.clear()

    #--------------------------------------------------------------------------------------------
    # Special methods
    #--------------------------------------------------------------------------------------------
    def __dealloc__(self):

        del self.__index


    def __str__(self):

        cdef str out_format = '{key}: {value}\n'
        cdef str result = 'Series object ' + self.name + '\n'
        cdef tuple it
        cdef list items = self.items()

        if len(items) <= 50: 
            for it in items:
                result += out_format.format(key=it[0], value=it[1])

        else:
            for it in items[:25]:
                result += out_format.format(key=it[0], value=it[1])

            result += '...\n'

            for it in items[-25:]:
                result += out_format.format(key=it[0], value=it[1])

        return result


    def __repr__(self):

        return self.__str__()

    
    def __iter__(self):

        cdef node_ptr it

        for it in deref(self.__index):
            yield __deref_value_ptr_uint64_t(it)


    def __getitem__(self, key):

        cdef int32_t error = TYPE_ERROR
        cdef uint64_t result

        if self.__index.size() == 0:
            return self.extrapolate

        if isinstance(key, slice):
            return self.__get_item_slice(key)

        result = self.__get_item(self, key, &error)

        if error == 0:
            return result

        if error == INT_KEY_ERROR:
            raise KeyError( str(key) )

        if error == TYPE_ERROR:
            raise TypeError("Inconsistent key type: " + key.__class__.__name__)


    def __setitem__(self, key, uint64_t value):

        cdef c_pyobject key_holder = to_c_pyobject(key)
        cdef node_ptr node = self.__index.insert_search(key_holder)

        if self.__index.size() == 0:
            __insert_node_by_ptr_uint64_t(self.__index, node, key, value)

        else:
            if self.__get_key_from_ptr(node) == key:
                __set_value_uint64_t(node, value)

            else:
                __insert_node_by_ptr_uint64_t(self.__index, node, key, value)


    #--------------------------------------------------------------------------------------------
    # Emulating numeric types
    #--------------------------------------------------------------------------------------------
    cpdef __TreeSeries_uint64_t add(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __add_uint64_t)


    cpdef __TreeSeries_uint64_t sub(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __sub_uint64_t)


    cpdef __TreeSeries_uint64_t mul(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __mul_uint64_t)


    cpdef __TreeSeries_uint64_t div(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __div_uint64_t)


    cpdef __TreeSeries_uint64_t lshift(self, shift_param):

        if isinstance(shift_param, int):
            return self.__lshift_int(shift_param)


    cpdef __TreeSeries_uint64_t rshift(self, shift_param):

        if isinstance(shift_param, int):
            return self.__rshift_int(shift_param)


    #--------------------------------------------------------------------------------------------
    # Private methods
    #--------------------------------------------------------------------------------------------
    cdef void __init_tree(self, index, values) except*:

        cdef tuple it

        if len(index) != len(values):
            raise IndexError("Index must have the same length as values.")

        for it in zip(index, values):
            self.insert(it[0], it[1])


    cdef void __init_tree_index(self, index) except*:

        for it in index:
            self.insert(it, 0)


    cdef uint64_t __get_item_linear(self, key, int32_t* error):

        #bug? cython 0.28.3 translate c_key to const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key) 
        cdef pair[node_ptr, node_ptr] bounds
        
        bounds = self.__index.linear_search_from(self.__last_call, c_key)
        error[0] = 0

        if bounds.first != self.link():
            self.__last_call = bounds.first

        else:
            self.__last_call = deref( self.__index.begin() )

        return self.__interpolate( bounds.first, bounds.second, key,
                                   self.link(), self.extrapolate,
                                   error )

    
    cdef uint64_t __get_item_tree(self, key, int32_t* error):

        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)
        cdef pair[node_ptr, node_ptr] bounds

        bounds = self.__index.tree_search(c_key)
        error[0] = 0

        return self.__interpolate( bounds.first, bounds.second, key,
                                   self.link(), self.extrapolate, 
                                   error )

    
    cdef __TreeSeries_uint64_t __get_item_slice(self, key):

        cdef c_pyobject c_key
        cdef node_ptr begin = deref( self.__index.begin() ) 
        cdef node_ptr end = deref( self.__index.back() )
        cdef __TreeSeries_uint64_t result
        result = __TreeSeries_uint64_t.__new__( __TreeSeries_uint64_t,
                                             interpolate=self.interpolate_type,
                                             arithmetic=self.arithmetic )

        if self.__len__() == 0:
            return result

        if key.start is not None and key.stop is not None:

            if key.start < self.begin() and key.stop < self.begin():
                return result

            if key.start > self.end() and key.stop < self.end():
                return result

        if key.start is not None:
            c_key = c_pyobject(<PyObject*>key.start)
            begin = self.__index.tree_search(c_key).second

        if key.stop is not None:
            c_key = c_pyobject(<PyObject*>key.stop)
            end = self.__index.tree_search(c_key).first

        if compare_func(end.key, begin.key) \
            and ( not equal(end.key, begin.key) ):

            return result

        if begin == self.link() or end == self.link():
            return result 

        if key.step is None:
            return self.truncate( self.__get_key_from_ptr(begin),
                                  self.__get_key_from_ptr(end) )

        else: 
            return self.periodic( self.__get_key_from_ptr(begin), 
                                  self.__get_key_from_ptr(end), 
                                  key.step )


    cdef __TreeSeries_uint64_t __lshift_int(self, Py_ssize_t shift_param):

        cdef __TreeSeries_uint64_t result
        cdef list delay_holder = []
        cdef tuple pair
        cdef Py_ssize_t count = 0
        cdef bool append_status = False

        result = __TreeSeries_uint64_t.__new__( __TreeSeries_uint64_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        if shift_param <= 0:
            raise ValueError("Shift parameter must be more than zero.")

        for pair in self.iteritems():
            
            if append_status:
                result.insert(delay_holder[0], pair[1])
                delay_holder.pop(0)

            if count == shift_param - 1:
                append_status = True

            delay_holder.append(pair[0])
            count += 1

        return result


    cdef __TreeSeries_uint64_t __rshift_int(self, Py_ssize_t shift_param):

        cdef __TreeSeries_uint64_t result
        cdef list delay_holder = []
        cdef tuple pair
        cdef Py_ssize_t count = 0
        cdef bool append_status = False

        result = __TreeSeries_uint64_t.__new__( __TreeSeries_uint64_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        if isinstance(shift_param, int):

            if shift_param <= 0:
                raise ValueError("Shift parameter must be more than zero.")

            for pair in self.iteritems():
                
                if append_status:
                    result.insert(pair[0], delay_holder[0])
                    delay_holder.pop(0)

                if count == shift_param - 1:
                    append_status = True

                delay_holder.append(pair[1])
                count += 1

            return result


    cdef __TreeSeries_uint64_t __arithmetic_left( self, __BaseTreeSeries other, 
                                                 arithmetic_uint64_t action ):

        cdef uint64_t current
        cdef node_ptr node
        cdef object key
        cdef __TreeSeries_uint64_t result
        result = __TreeSeries_uint64_t.__new__( __TreeSeries_uint64_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        other.on_itermode()

        for node in deref(self.__index):

            key = self.__get_key_from_ptr(node)
            current = action(__deref_value_ptr_uint64_t(node), other[key])
            __insert_node_uint64_t(result.__index, key, current)

        other.off_itermode()

        return result


    cdef __TreeSeries_uint64_t __arithmetic_union( self, __BaseTreeSeries other, 
                                                  arithmetic_uint64_t action ):

        cdef uint64_t current
        cdef object key
        cdef trees_iterator[rb_tree, rb_node_valued] iterator
        cdef vector[rb_tree_ptr] trees = vector[rb_tree_ptr](2)
        cdef __TreeSeries_uint64_t result
        result = __TreeSeries_uint64_t.__new__( __TreeSeries_uint64_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        
        trees[0] = self.__index
        trees[1] = other.get_tree()
        iterator.set_equal(equal_pair)
        iterator.set_compare(comp_pair)
        iterator.set_iterator(trees, "forward")

        self.on_itermode()
        other.on_itermode()

        while not iterator.empty():
            key = <object>deref(iterator).key.data
            current = action(self.__getitem__(key), other[key])
            __insert_node_uint64_t(result.__index, key, current)
            pinc(iterator)

        self.off_itermode()
        other.off_itermode()

        return result
@cython.internal
cdef class __TreeSeries_uint96_t(__BaseTreeSeries):

    cdef interpolate_uint96_t     __interpolate
    cdef itermode_search_uint96_t __get_item
    cdef arithmetic_type_uint96_t __arithmetic

    cdef public   str  name
    cdef public   str  arithmetic
    cdef readonly str  interpolate_type
    cdef public   uint96_t extrapolate


    def __init__(self):
        pass


    def __cinit__( self, object index=None, object values=None, str name="Untitled", 
                   str interpolate="floor", uint96_t extrapolate=0, str arithmetic="left" ):

        self.set_interpolation(interpolate)
        self.extrapolate = extrapolate
        self.name = name
        self.arithmetic = arithmetic

        self.__get_item = self.__get_item_tree
        self.set_arithmetic(arithmetic)

        if index is None:
            
            if values is not None:
                raise IndexError("Index cannot be None while values are not.")

        else:
            if values is not None:
                self.__init_tree(index, values)

            else:
                self.__init_tree_index(index)


    #--------------------------------------------------------------------------------------------
    # Public methods
    #--------------------------------------------------------------------------------------------

    cpdef void insert(self, key, uint96_t value) except*:

        __insert_node_uint96_t(self.__index, key, value)


    cpdef void insert_range(self, list pairs) except*:

        cdef tuple pair

        for pair in pairs:
            __insert_node_uint96_t(self.__index, pair[0], pair[1])


    cpdef void erase(self, key) except*:

        __erase_node_uint96_t(self.__index, key)


    cpdef void erase_range(self, list keys) except*:

        cdef Py_ssize_t it

        for it in range( len(keys) ):
            __erase_node_uint96_t(self.__index, keys[it])


    cpdef list values(self):

        cdef rb_node_valued* it
        cdef list result = []

        for it in deref(self.__index):
            result.append( __deref_value_ptr_uint96_t(it) )

        return result


    cpdef list items(self):

        cdef node_ptr it
        cdef list result = []

        for it in deref(self.__index):
            result.append( ( self.__get_key_from_ptr(it), 
                             __deref_value_ptr_uint96_t(it) ) )

        return result


    def iteritems(self):

        cdef node_ptr it

        for it in deref(self.__index):
            yield ( self.__get_key_from_ptr(it), __deref_value_ptr_uint96_t(it) )


    cpdef tuple floor(self, key):

        cdef pair[node_ptr, node_ptr] bounds
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)
        bounds = self.__index.tree_search(c_key)

        if bounds.first == self.link():
            return (None, None)

        return ( self.__get_key_from_ptr(bounds.first),
                 __deref_value_ptr_uint96_t(bounds.first) )


    cpdef tuple ceil(self, key):

        cdef pair[node_ptr, node_ptr] bounds
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)  
        bounds = self.__index.tree_search(c_key)

        if bounds.second == self.link():
            return (None, None)

        return ( self.__get_key_from_ptr(bounds.second),
                 __deref_value_ptr_uint96_t(bounds.second) )


    cpdef __TreeSeries_uint96_t truncate(self, start, stop):

        cdef iterator begin
        cdef iterator end
        cdef pair[node_ptr, node_ptr] bounds_start 
        cdef pair[node_ptr, node_ptr] bounds_end
        cdef __TreeSeries_uint96_t result
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_start = c_pyobject(<PyObject*>start)
        cdef c_pyobject c_stop = c_pyobject(<PyObject*>stop)

        result = __TreeSeries_uint96_t.__new__( __TreeSeries_uint96_t, 
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        bounds_start = self.__index.tree_search(c_start)
        bounds_end = self.__index.tree_search(c_stop)
        begin = deref(bounds_start.second).it_position
        end = deref(bounds_end.first).it_position

        if( bounds_start.second == self.link() ) \
            or ( bounds_end.first == self.link() ):

            return result

        if self.__get_key_from_iter(begin) > self.__get_key_from_iter(end):
            return result

        while True:
            result.insert( self.__get_key_from_iter(begin), 
                           __deref_value_ptr_uint96_t( deref(begin) ) )

            if begin == end:
                break

            inc(begin)

        return result


    cpdef __TreeSeries_uint96_t periodic(self, start, stop, step):

        cdef __TreeSeries_uint96_t result
        cdef object it = start
        result = __TreeSeries_uint96_t.__new__( __TreeSeries_uint96_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        self.on_itermode()

        while it < stop:
            result.insert( it, self.__getitem__(it) )
            it = it + step

        self.off_itermode()

        return result


    cpdef __TreeSeries_uint96_t map( self, method, tuple args=(), 
                                  dict kwargs={} ):

        cdef __TreeSeries_uint96_t result
        cdef tuple it

        result = __TreeSeries_uint96_t.__new__( __TreeSeries_uint96_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        for it in self.iteritems():
            result.insert( it[0], method(it[1], *args, **kwargs) )

        return result


    cpdef void map_inplace( self, method, tuple args=(), 
                            dict kwargs={} ) except*:

        cdef node_ptr it

        for it in deref(self.__index):
            __set_value_uint96_t( it, method( __deref_value_ptr_uint96_t(it),
                                             *args, **kwargs ) )


    cpdef void set_interpolation(self, str interpolate):

        cdef string c_interpolate = bytearray(interpolate, 'utf8')
        self.interpolate_type = interpolate
        self.__interpolate = __INTERPOLATE_uint96_t[c_interpolate]


    cpdef void set_arithmetic(self, str arithmetic):

        if arithmetic == "union":
            self.__arithmetic = self.__arithmetic_union

        if arithmetic == "left":
            self.__arithmetic = self.__arithmetic_left


    cpdef void on_itermode(self):

        __BaseTreeSeries.on_itermode(self)
        self.__get_item = self.__get_item_linear


    cpdef void off_itermode(self):

        __BaseTreeSeries.off_itermode(self)
        self.__get_item = self.__get_item_tree


    cpdef void copy_data(self, other, str to_type="numerical") except*:

        cdef tuple it
        cdef uint96_t num
        self.clear()

        if to_type == "numerical":

            for it in other.iteritems():
                num = <uint96_t>float(it[1])
                self.insert( it[0], num )

        elif to_type == "str":
            for it in other.iteritems():
                self.insert( it[0], str(it[1]) )

        else:
            for it in other.iteritems():
                self.insert(it[0], it[1])


    cpdef void clear(self) except*:

        for key in self.keys():
            self.erase(key)

        self.__index.clear()

    #--------------------------------------------------------------------------------------------
    # Special methods
    #--------------------------------------------------------------------------------------------
    def __dealloc__(self):

        del self.__index


    def __str__(self):

        cdef str out_format = '{key}: {value}\n'
        cdef str result = 'Series object ' + self.name + '\n'
        cdef tuple it
        cdef list items = self.items()

        if len(items) <= 50: 
            for it in items:
                result += out_format.format(key=it[0], value=it[1])

        else:
            for it in items[:25]:
                result += out_format.format(key=it[0], value=it[1])

            result += '...\n'

            for it in items[-25:]:
                result += out_format.format(key=it[0], value=it[1])

        return result


    def __repr__(self):

        return self.__str__()

    
    def __iter__(self):

        cdef node_ptr it

        for it in deref(self.__index):
            yield __deref_value_ptr_uint96_t(it)


    def __getitem__(self, key):

        cdef int32_t error = TYPE_ERROR
        cdef uint96_t result

        if self.__index.size() == 0:
            return self.extrapolate

        if isinstance(key, slice):
            return self.__get_item_slice(key)

        result = self.__get_item(self, key, &error)

        if error == 0:
            return result

        if error == INT_KEY_ERROR:
            raise KeyError( str(key) )

        if error == TYPE_ERROR:
            raise TypeError("Inconsistent key type: " + key.__class__.__name__)


    def __setitem__(self, key, uint96_t value):

        cdef c_pyobject key_holder = to_c_pyobject(key)
        cdef node_ptr node = self.__index.insert_search(key_holder)

        if self.__index.size() == 0:
            __insert_node_by_ptr_uint96_t(self.__index, node, key, value)

        else:
            if self.__get_key_from_ptr(node) == key:
                __set_value_uint96_t(node, value)

            else:
                __insert_node_by_ptr_uint96_t(self.__index, node, key, value)


    #--------------------------------------------------------------------------------------------
    # Emulating numeric types
    #--------------------------------------------------------------------------------------------
    cpdef __TreeSeries_uint96_t add(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __add_uint96_t)


    cpdef __TreeSeries_uint96_t sub(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __sub_uint96_t)


    cpdef __TreeSeries_uint96_t mul(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __mul_uint96_t)


    cpdef __TreeSeries_uint96_t div(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __div_uint96_t)


    cpdef __TreeSeries_uint96_t lshift(self, shift_param):

        if isinstance(shift_param, int):
            return self.__lshift_int(shift_param)


    cpdef __TreeSeries_uint96_t rshift(self, shift_param):

        if isinstance(shift_param, int):
            return self.__rshift_int(shift_param)


    #--------------------------------------------------------------------------------------------
    # Private methods
    #--------------------------------------------------------------------------------------------
    cdef void __init_tree(self, index, values) except*:

        cdef tuple it

        if len(index) != len(values):
            raise IndexError("Index must have the same length as values.")

        for it in zip(index, values):
            self.insert(it[0], it[1])


    cdef void __init_tree_index(self, index) except*:

        for it in index:
            self.insert(it, 0)


    cdef uint96_t __get_item_linear(self, key, int32_t* error):

        #bug? cython 0.28.3 translate c_key to const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key) 
        cdef pair[node_ptr, node_ptr] bounds
        
        bounds = self.__index.linear_search_from(self.__last_call, c_key)
        error[0] = 0

        if bounds.first != self.link():
            self.__last_call = bounds.first

        else:
            self.__last_call = deref( self.__index.begin() )

        return self.__interpolate( bounds.first, bounds.second, key,
                                   self.link(), self.extrapolate,
                                   error )

    
    cdef uint96_t __get_item_tree(self, key, int32_t* error):

        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)
        cdef pair[node_ptr, node_ptr] bounds

        bounds = self.__index.tree_search(c_key)
        error[0] = 0

        return self.__interpolate( bounds.first, bounds.second, key,
                                   self.link(), self.extrapolate, 
                                   error )

    
    cdef __TreeSeries_uint96_t __get_item_slice(self, key):

        cdef c_pyobject c_key
        cdef node_ptr begin = deref( self.__index.begin() ) 
        cdef node_ptr end = deref( self.__index.back() )
        cdef __TreeSeries_uint96_t result
        result = __TreeSeries_uint96_t.__new__( __TreeSeries_uint96_t,
                                             interpolate=self.interpolate_type,
                                             arithmetic=self.arithmetic )

        if self.__len__() == 0:
            return result

        if key.start is not None and key.stop is not None:

            if key.start < self.begin() and key.stop < self.begin():
                return result

            if key.start > self.end() and key.stop < self.end():
                return result

        if key.start is not None:
            c_key = c_pyobject(<PyObject*>key.start)
            begin = self.__index.tree_search(c_key).second

        if key.stop is not None:
            c_key = c_pyobject(<PyObject*>key.stop)
            end = self.__index.tree_search(c_key).first

        if compare_func(end.key, begin.key) \
            and ( not equal(end.key, begin.key) ):

            return result

        if begin == self.link() or end == self.link():
            return result 

        if key.step is None:
            return self.truncate( self.__get_key_from_ptr(begin),
                                  self.__get_key_from_ptr(end) )

        else: 
            return self.periodic( self.__get_key_from_ptr(begin), 
                                  self.__get_key_from_ptr(end), 
                                  key.step )


    cdef __TreeSeries_uint96_t __lshift_int(self, Py_ssize_t shift_param):

        cdef __TreeSeries_uint96_t result
        cdef list delay_holder = []
        cdef tuple pair
        cdef Py_ssize_t count = 0
        cdef bool append_status = False

        result = __TreeSeries_uint96_t.__new__( __TreeSeries_uint96_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        if shift_param <= 0:
            raise ValueError("Shift parameter must be more than zero.")

        for pair in self.iteritems():
            
            if append_status:
                result.insert(delay_holder[0], pair[1])
                delay_holder.pop(0)

            if count == shift_param - 1:
                append_status = True

            delay_holder.append(pair[0])
            count += 1

        return result


    cdef __TreeSeries_uint96_t __rshift_int(self, Py_ssize_t shift_param):

        cdef __TreeSeries_uint96_t result
        cdef list delay_holder = []
        cdef tuple pair
        cdef Py_ssize_t count = 0
        cdef bool append_status = False

        result = __TreeSeries_uint96_t.__new__( __TreeSeries_uint96_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        if isinstance(shift_param, int):

            if shift_param <= 0:
                raise ValueError("Shift parameter must be more than zero.")

            for pair in self.iteritems():
                
                if append_status:
                    result.insert(pair[0], delay_holder[0])
                    delay_holder.pop(0)

                if count == shift_param - 1:
                    append_status = True

                delay_holder.append(pair[1])
                count += 1

            return result


    cdef __TreeSeries_uint96_t __arithmetic_left( self, __BaseTreeSeries other, 
                                                 arithmetic_uint96_t action ):

        cdef uint96_t current
        cdef node_ptr node
        cdef object key
        cdef __TreeSeries_uint96_t result
        result = __TreeSeries_uint96_t.__new__( __TreeSeries_uint96_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        other.on_itermode()

        for node in deref(self.__index):

            key = self.__get_key_from_ptr(node)
            current = action(__deref_value_ptr_uint96_t(node), other[key])
            __insert_node_uint96_t(result.__index, key, current)

        other.off_itermode()

        return result


    cdef __TreeSeries_uint96_t __arithmetic_union( self, __BaseTreeSeries other, 
                                                  arithmetic_uint96_t action ):

        cdef uint96_t current
        cdef object key
        cdef trees_iterator[rb_tree, rb_node_valued] iterator
        cdef vector[rb_tree_ptr] trees = vector[rb_tree_ptr](2)
        cdef __TreeSeries_uint96_t result
        result = __TreeSeries_uint96_t.__new__( __TreeSeries_uint96_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        
        trees[0] = self.__index
        trees[1] = other.get_tree()
        iterator.set_equal(equal_pair)
        iterator.set_compare(comp_pair)
        iterator.set_iterator(trees, "forward")

        self.on_itermode()
        other.on_itermode()

        while not iterator.empty():
            key = <object>deref(iterator).key.data
            current = action(self.__getitem__(key), other[key])
            __insert_node_uint96_t(result.__index, key, current)
            pinc(iterator)

        self.off_itermode()
        other.off_itermode()

        return result
@cython.internal
cdef class __TreeSeries_uint128_t(__BaseTreeSeries):

    cdef interpolate_uint128_t     __interpolate
    cdef itermode_search_uint128_t __get_item
    cdef arithmetic_type_uint128_t __arithmetic

    cdef public   str  name
    cdef public   str  arithmetic
    cdef readonly str  interpolate_type
    cdef public   uint128_t extrapolate


    def __init__(self):
        pass


    def __cinit__( self, object index=None, object values=None, str name="Untitled", 
                   str interpolate="floor", uint128_t extrapolate=0, str arithmetic="left" ):

        self.set_interpolation(interpolate)
        self.extrapolate = extrapolate
        self.name = name
        self.arithmetic = arithmetic

        self.__get_item = self.__get_item_tree
        self.set_arithmetic(arithmetic)

        if index is None:
            
            if values is not None:
                raise IndexError("Index cannot be None while values are not.")

        else:
            if values is not None:
                self.__init_tree(index, values)

            else:
                self.__init_tree_index(index)


    #--------------------------------------------------------------------------------------------
    # Public methods
    #--------------------------------------------------------------------------------------------

    cpdef void insert(self, key, uint128_t value) except*:

        __insert_node_uint128_t(self.__index, key, value)


    cpdef void insert_range(self, list pairs) except*:

        cdef tuple pair

        for pair in pairs:
            __insert_node_uint128_t(self.__index, pair[0], pair[1])


    cpdef void erase(self, key) except*:

        __erase_node_uint128_t(self.__index, key)


    cpdef void erase_range(self, list keys) except*:

        cdef Py_ssize_t it

        for it in range( len(keys) ):
            __erase_node_uint128_t(self.__index, keys[it])


    cpdef list values(self):

        cdef rb_node_valued* it
        cdef list result = []

        for it in deref(self.__index):
            result.append( __deref_value_ptr_uint128_t(it) )

        return result


    cpdef list items(self):

        cdef node_ptr it
        cdef list result = []

        for it in deref(self.__index):
            result.append( ( self.__get_key_from_ptr(it), 
                             __deref_value_ptr_uint128_t(it) ) )

        return result


    def iteritems(self):

        cdef node_ptr it

        for it in deref(self.__index):
            yield ( self.__get_key_from_ptr(it), __deref_value_ptr_uint128_t(it) )


    cpdef tuple floor(self, key):

        cdef pair[node_ptr, node_ptr] bounds
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)
        bounds = self.__index.tree_search(c_key)

        if bounds.first == self.link():
            return (None, None)

        return ( self.__get_key_from_ptr(bounds.first),
                 __deref_value_ptr_uint128_t(bounds.first) )


    cpdef tuple ceil(self, key):

        cdef pair[node_ptr, node_ptr] bounds
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)  
        bounds = self.__index.tree_search(c_key)

        if bounds.second == self.link():
            return (None, None)

        return ( self.__get_key_from_ptr(bounds.second),
                 __deref_value_ptr_uint128_t(bounds.second) )


    cpdef __TreeSeries_uint128_t truncate(self, start, stop):

        cdef iterator begin
        cdef iterator end
        cdef pair[node_ptr, node_ptr] bounds_start 
        cdef pair[node_ptr, node_ptr] bounds_end
        cdef __TreeSeries_uint128_t result
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_start = c_pyobject(<PyObject*>start)
        cdef c_pyobject c_stop = c_pyobject(<PyObject*>stop)

        result = __TreeSeries_uint128_t.__new__( __TreeSeries_uint128_t, 
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        bounds_start = self.__index.tree_search(c_start)
        bounds_end = self.__index.tree_search(c_stop)
        begin = deref(bounds_start.second).it_position
        end = deref(bounds_end.first).it_position

        if( bounds_start.second == self.link() ) \
            or ( bounds_end.first == self.link() ):

            return result

        if self.__get_key_from_iter(begin) > self.__get_key_from_iter(end):
            return result

        while True:
            result.insert( self.__get_key_from_iter(begin), 
                           __deref_value_ptr_uint128_t( deref(begin) ) )

            if begin == end:
                break

            inc(begin)

        return result


    cpdef __TreeSeries_uint128_t periodic(self, start, stop, step):

        cdef __TreeSeries_uint128_t result
        cdef object it = start
        result = __TreeSeries_uint128_t.__new__( __TreeSeries_uint128_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        self.on_itermode()

        while it < stop:
            result.insert( it, self.__getitem__(it) )
            it = it + step

        self.off_itermode()

        return result


    cpdef __TreeSeries_uint128_t map( self, method, tuple args=(), 
                                  dict kwargs={} ):

        cdef __TreeSeries_uint128_t result
        cdef tuple it

        result = __TreeSeries_uint128_t.__new__( __TreeSeries_uint128_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        for it in self.iteritems():
            result.insert( it[0], method(it[1], *args, **kwargs) )

        return result


    cpdef void map_inplace( self, method, tuple args=(), 
                            dict kwargs={} ) except*:

        cdef node_ptr it

        for it in deref(self.__index):
            __set_value_uint128_t( it, method( __deref_value_ptr_uint128_t(it),
                                             *args, **kwargs ) )


    cpdef void set_interpolation(self, str interpolate):

        cdef string c_interpolate = bytearray(interpolate, 'utf8')
        self.interpolate_type = interpolate
        self.__interpolate = __INTERPOLATE_uint128_t[c_interpolate]


    cpdef void set_arithmetic(self, str arithmetic):

        if arithmetic == "union":
            self.__arithmetic = self.__arithmetic_union

        if arithmetic == "left":
            self.__arithmetic = self.__arithmetic_left


    cpdef void on_itermode(self):

        __BaseTreeSeries.on_itermode(self)
        self.__get_item = self.__get_item_linear


    cpdef void off_itermode(self):

        __BaseTreeSeries.off_itermode(self)
        self.__get_item = self.__get_item_tree


    cpdef void copy_data(self, other, str to_type="numerical") except*:

        cdef tuple it
        cdef uint128_t num
        self.clear()

        if to_type == "numerical":

            for it in other.iteritems():
                num = <uint128_t>float(it[1])
                self.insert( it[0], num )

        elif to_type == "str":
            for it in other.iteritems():
                self.insert( it[0], str(it[1]) )

        else:
            for it in other.iteritems():
                self.insert(it[0], it[1])


    cpdef void clear(self) except*:

        for key in self.keys():
            self.erase(key)

        self.__index.clear()

    #--------------------------------------------------------------------------------------------
    # Special methods
    #--------------------------------------------------------------------------------------------
    def __dealloc__(self):

        del self.__index


    def __str__(self):

        cdef str out_format = '{key}: {value}\n'
        cdef str result = 'Series object ' + self.name + '\n'
        cdef tuple it
        cdef list items = self.items()

        if len(items) <= 50: 
            for it in items:
                result += out_format.format(key=it[0], value=it[1])

        else:
            for it in items[:25]:
                result += out_format.format(key=it[0], value=it[1])

            result += '...\n'

            for it in items[-25:]:
                result += out_format.format(key=it[0], value=it[1])

        return result


    def __repr__(self):

        return self.__str__()

    
    def __iter__(self):

        cdef node_ptr it

        for it in deref(self.__index):
            yield __deref_value_ptr_uint128_t(it)


    def __getitem__(self, key):

        cdef int32_t error = TYPE_ERROR
        cdef uint128_t result

        if self.__index.size() == 0:
            return self.extrapolate

        if isinstance(key, slice):
            return self.__get_item_slice(key)

        result = self.__get_item(self, key, &error)

        if error == 0:
            return result

        if error == INT_KEY_ERROR:
            raise KeyError( str(key) )

        if error == TYPE_ERROR:
            raise TypeError("Inconsistent key type: " + key.__class__.__name__)


    def __setitem__(self, key, uint128_t value):

        cdef c_pyobject key_holder = to_c_pyobject(key)
        cdef node_ptr node = self.__index.insert_search(key_holder)

        if self.__index.size() == 0:
            __insert_node_by_ptr_uint128_t(self.__index, node, key, value)

        else:
            if self.__get_key_from_ptr(node) == key:
                __set_value_uint128_t(node, value)

            else:
                __insert_node_by_ptr_uint128_t(self.__index, node, key, value)


    #--------------------------------------------------------------------------------------------
    # Emulating numeric types
    #--------------------------------------------------------------------------------------------
    cpdef __TreeSeries_uint128_t add(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __add_uint128_t)


    cpdef __TreeSeries_uint128_t sub(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __sub_uint128_t)


    cpdef __TreeSeries_uint128_t mul(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __mul_uint128_t)


    cpdef __TreeSeries_uint128_t div(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __div_uint128_t)


    cpdef __TreeSeries_uint128_t lshift(self, shift_param):

        if isinstance(shift_param, int):
            return self.__lshift_int(shift_param)


    cpdef __TreeSeries_uint128_t rshift(self, shift_param):

        if isinstance(shift_param, int):
            return self.__rshift_int(shift_param)


    #--------------------------------------------------------------------------------------------
    # Private methods
    #--------------------------------------------------------------------------------------------
    cdef void __init_tree(self, index, values) except*:

        cdef tuple it

        if len(index) != len(values):
            raise IndexError("Index must have the same length as values.")

        for it in zip(index, values):
            self.insert(it[0], it[1])


    cdef void __init_tree_index(self, index) except*:

        for it in index:
            self.insert(it, 0)


    cdef uint128_t __get_item_linear(self, key, int32_t* error):

        #bug? cython 0.28.3 translate c_key to const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key) 
        cdef pair[node_ptr, node_ptr] bounds
        
        bounds = self.__index.linear_search_from(self.__last_call, c_key)
        error[0] = 0

        if bounds.first != self.link():
            self.__last_call = bounds.first

        else:
            self.__last_call = deref( self.__index.begin() )

        return self.__interpolate( bounds.first, bounds.second, key,
                                   self.link(), self.extrapolate,
                                   error )

    
    cdef uint128_t __get_item_tree(self, key, int32_t* error):

        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)
        cdef pair[node_ptr, node_ptr] bounds

        bounds = self.__index.tree_search(c_key)
        error[0] = 0

        return self.__interpolate( bounds.first, bounds.second, key,
                                   self.link(), self.extrapolate, 
                                   error )

    
    cdef __TreeSeries_uint128_t __get_item_slice(self, key):

        cdef c_pyobject c_key
        cdef node_ptr begin = deref( self.__index.begin() ) 
        cdef node_ptr end = deref( self.__index.back() )
        cdef __TreeSeries_uint128_t result
        result = __TreeSeries_uint128_t.__new__( __TreeSeries_uint128_t,
                                             interpolate=self.interpolate_type,
                                             arithmetic=self.arithmetic )

        if self.__len__() == 0:
            return result

        if key.start is not None and key.stop is not None:

            if key.start < self.begin() and key.stop < self.begin():
                return result

            if key.start > self.end() and key.stop < self.end():
                return result

        if key.start is not None:
            c_key = c_pyobject(<PyObject*>key.start)
            begin = self.__index.tree_search(c_key).second

        if key.stop is not None:
            c_key = c_pyobject(<PyObject*>key.stop)
            end = self.__index.tree_search(c_key).first

        if compare_func(end.key, begin.key) \
            and ( not equal(end.key, begin.key) ):

            return result

        if begin == self.link() or end == self.link():
            return result 

        if key.step is None:
            return self.truncate( self.__get_key_from_ptr(begin),
                                  self.__get_key_from_ptr(end) )

        else: 
            return self.periodic( self.__get_key_from_ptr(begin), 
                                  self.__get_key_from_ptr(end), 
                                  key.step )


    cdef __TreeSeries_uint128_t __lshift_int(self, Py_ssize_t shift_param):

        cdef __TreeSeries_uint128_t result
        cdef list delay_holder = []
        cdef tuple pair
        cdef Py_ssize_t count = 0
        cdef bool append_status = False

        result = __TreeSeries_uint128_t.__new__( __TreeSeries_uint128_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        if shift_param <= 0:
            raise ValueError("Shift parameter must be more than zero.")

        for pair in self.iteritems():
            
            if append_status:
                result.insert(delay_holder[0], pair[1])
                delay_holder.pop(0)

            if count == shift_param - 1:
                append_status = True

            delay_holder.append(pair[0])
            count += 1

        return result


    cdef __TreeSeries_uint128_t __rshift_int(self, Py_ssize_t shift_param):

        cdef __TreeSeries_uint128_t result
        cdef list delay_holder = []
        cdef tuple pair
        cdef Py_ssize_t count = 0
        cdef bool append_status = False

        result = __TreeSeries_uint128_t.__new__( __TreeSeries_uint128_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        if isinstance(shift_param, int):

            if shift_param <= 0:
                raise ValueError("Shift parameter must be more than zero.")

            for pair in self.iteritems():
                
                if append_status:
                    result.insert(pair[0], delay_holder[0])
                    delay_holder.pop(0)

                if count == shift_param - 1:
                    append_status = True

                delay_holder.append(pair[1])
                count += 1

            return result


    cdef __TreeSeries_uint128_t __arithmetic_left( self, __BaseTreeSeries other, 
                                                 arithmetic_uint128_t action ):

        cdef uint128_t current
        cdef node_ptr node
        cdef object key
        cdef __TreeSeries_uint128_t result
        result = __TreeSeries_uint128_t.__new__( __TreeSeries_uint128_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        other.on_itermode()

        for node in deref(self.__index):

            key = self.__get_key_from_ptr(node)
            current = action(__deref_value_ptr_uint128_t(node), other[key])
            __insert_node_uint128_t(result.__index, key, current)

        other.off_itermode()

        return result


    cdef __TreeSeries_uint128_t __arithmetic_union( self, __BaseTreeSeries other, 
                                                  arithmetic_uint128_t action ):

        cdef uint128_t current
        cdef object key
        cdef trees_iterator[rb_tree, rb_node_valued] iterator
        cdef vector[rb_tree_ptr] trees = vector[rb_tree_ptr](2)
        cdef __TreeSeries_uint128_t result
        result = __TreeSeries_uint128_t.__new__( __TreeSeries_uint128_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        
        trees[0] = self.__index
        trees[1] = other.get_tree()
        iterator.set_equal(equal_pair)
        iterator.set_compare(comp_pair)
        iterator.set_iterator(trees, "forward")

        self.on_itermode()
        other.on_itermode()

        while not iterator.empty():
            key = <object>deref(iterator).key.data
            current = action(self.__getitem__(key), other[key])
            __insert_node_uint128_t(result.__index, key, current)
            pinc(iterator)

        self.off_itermode()
        other.off_itermode()

        return result
@cython.internal
cdef class __TreeSeries_int8_t(__BaseTreeSeries):

    cdef interpolate_int8_t     __interpolate
    cdef itermode_search_int8_t __get_item
    cdef arithmetic_type_int8_t __arithmetic

    cdef public   str  name
    cdef public   str  arithmetic
    cdef readonly str  interpolate_type
    cdef public   int8_t extrapolate


    def __init__(self):
        pass


    def __cinit__( self, object index=None, object values=None, str name="Untitled", 
                   str interpolate="floor", int8_t extrapolate=0, str arithmetic="left" ):

        self.set_interpolation(interpolate)
        self.extrapolate = extrapolate
        self.name = name
        self.arithmetic = arithmetic

        self.__get_item = self.__get_item_tree
        self.set_arithmetic(arithmetic)

        if index is None:
            
            if values is not None:
                raise IndexError("Index cannot be None while values are not.")

        else:
            if values is not None:
                self.__init_tree(index, values)

            else:
                self.__init_tree_index(index)


    #--------------------------------------------------------------------------------------------
    # Public methods
    #--------------------------------------------------------------------------------------------

    cpdef void insert(self, key, int8_t value) except*:

        __insert_node_int8_t(self.__index, key, value)


    cpdef void insert_range(self, list pairs) except*:

        cdef tuple pair

        for pair in pairs:
            __insert_node_int8_t(self.__index, pair[0], pair[1])


    cpdef void erase(self, key) except*:

        __erase_node_int8_t(self.__index, key)


    cpdef void erase_range(self, list keys) except*:

        cdef Py_ssize_t it

        for it in range( len(keys) ):
            __erase_node_int8_t(self.__index, keys[it])


    cpdef list values(self):

        cdef rb_node_valued* it
        cdef list result = []

        for it in deref(self.__index):
            result.append( __deref_value_ptr_int8_t(it) )

        return result


    cpdef list items(self):

        cdef node_ptr it
        cdef list result = []

        for it in deref(self.__index):
            result.append( ( self.__get_key_from_ptr(it), 
                             __deref_value_ptr_int8_t(it) ) )

        return result


    def iteritems(self):

        cdef node_ptr it

        for it in deref(self.__index):
            yield ( self.__get_key_from_ptr(it), __deref_value_ptr_int8_t(it) )


    cpdef tuple floor(self, key):

        cdef pair[node_ptr, node_ptr] bounds
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)
        bounds = self.__index.tree_search(c_key)

        if bounds.first == self.link():
            return (None, None)

        return ( self.__get_key_from_ptr(bounds.first),
                 __deref_value_ptr_int8_t(bounds.first) )


    cpdef tuple ceil(self, key):

        cdef pair[node_ptr, node_ptr] bounds
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)  
        bounds = self.__index.tree_search(c_key)

        if bounds.second == self.link():
            return (None, None)

        return ( self.__get_key_from_ptr(bounds.second),
                 __deref_value_ptr_int8_t(bounds.second) )


    cpdef __TreeSeries_int8_t truncate(self, start, stop):

        cdef iterator begin
        cdef iterator end
        cdef pair[node_ptr, node_ptr] bounds_start 
        cdef pair[node_ptr, node_ptr] bounds_end
        cdef __TreeSeries_int8_t result
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_start = c_pyobject(<PyObject*>start)
        cdef c_pyobject c_stop = c_pyobject(<PyObject*>stop)

        result = __TreeSeries_int8_t.__new__( __TreeSeries_int8_t, 
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        bounds_start = self.__index.tree_search(c_start)
        bounds_end = self.__index.tree_search(c_stop)
        begin = deref(bounds_start.second).it_position
        end = deref(bounds_end.first).it_position

        if( bounds_start.second == self.link() ) \
            or ( bounds_end.first == self.link() ):

            return result

        if self.__get_key_from_iter(begin) > self.__get_key_from_iter(end):
            return result

        while True:
            result.insert( self.__get_key_from_iter(begin), 
                           __deref_value_ptr_int8_t( deref(begin) ) )

            if begin == end:
                break

            inc(begin)

        return result


    cpdef __TreeSeries_int8_t periodic(self, start, stop, step):

        cdef __TreeSeries_int8_t result
        cdef object it = start
        result = __TreeSeries_int8_t.__new__( __TreeSeries_int8_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        self.on_itermode()

        while it < stop:
            result.insert( it, self.__getitem__(it) )
            it = it + step

        self.off_itermode()

        return result


    cpdef __TreeSeries_int8_t map( self, method, tuple args=(), 
                                  dict kwargs={} ):

        cdef __TreeSeries_int8_t result
        cdef tuple it

        result = __TreeSeries_int8_t.__new__( __TreeSeries_int8_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        for it in self.iteritems():
            result.insert( it[0], method(it[1], *args, **kwargs) )

        return result


    cpdef void map_inplace( self, method, tuple args=(), 
                            dict kwargs={} ) except*:

        cdef node_ptr it

        for it in deref(self.__index):
            __set_value_int8_t( it, method( __deref_value_ptr_int8_t(it),
                                             *args, **kwargs ) )


    cpdef void set_interpolation(self, str interpolate):

        cdef string c_interpolate = bytearray(interpolate, 'utf8')
        self.interpolate_type = interpolate
        self.__interpolate = __INTERPOLATE_int8_t[c_interpolate]


    cpdef void set_arithmetic(self, str arithmetic):

        if arithmetic == "union":
            self.__arithmetic = self.__arithmetic_union

        if arithmetic == "left":
            self.__arithmetic = self.__arithmetic_left


    cpdef void on_itermode(self):

        __BaseTreeSeries.on_itermode(self)
        self.__get_item = self.__get_item_linear


    cpdef void off_itermode(self):

        __BaseTreeSeries.off_itermode(self)
        self.__get_item = self.__get_item_tree


    cpdef void copy_data(self, other, str to_type="numerical") except*:

        cdef tuple it
        cdef int8_t num
        self.clear()

        if to_type == "numerical":

            for it in other.iteritems():
                num = <int8_t>float(it[1])
                self.insert( it[0], num )

        elif to_type == "str":
            for it in other.iteritems():
                self.insert( it[0], str(it[1]) )

        else:
            for it in other.iteritems():
                self.insert(it[0], it[1])


    cpdef void clear(self) except*:

        for key in self.keys():
            self.erase(key)

        self.__index.clear()

    #--------------------------------------------------------------------------------------------
    # Special methods
    #--------------------------------------------------------------------------------------------
    def __dealloc__(self):

        del self.__index


    def __str__(self):

        cdef str out_format = '{key}: {value}\n'
        cdef str result = 'Series object ' + self.name + '\n'
        cdef tuple it
        cdef list items = self.items()

        if len(items) <= 50: 
            for it in items:
                result += out_format.format(key=it[0], value=it[1])

        else:
            for it in items[:25]:
                result += out_format.format(key=it[0], value=it[1])

            result += '...\n'

            for it in items[-25:]:
                result += out_format.format(key=it[0], value=it[1])

        return result


    def __repr__(self):

        return self.__str__()

    
    def __iter__(self):

        cdef node_ptr it

        for it in deref(self.__index):
            yield __deref_value_ptr_int8_t(it)


    def __getitem__(self, key):

        cdef int32_t error = TYPE_ERROR
        cdef int8_t result

        if self.__index.size() == 0:
            return self.extrapolate

        if isinstance(key, slice):
            return self.__get_item_slice(key)

        result = self.__get_item(self, key, &error)

        if error == 0:
            return result

        if error == INT_KEY_ERROR:
            raise KeyError( str(key) )

        if error == TYPE_ERROR:
            raise TypeError("Inconsistent key type: " + key.__class__.__name__)


    def __setitem__(self, key, int8_t value):

        cdef c_pyobject key_holder = to_c_pyobject(key)
        cdef node_ptr node = self.__index.insert_search(key_holder)

        if self.__index.size() == 0:
            __insert_node_by_ptr_int8_t(self.__index, node, key, value)

        else:
            if self.__get_key_from_ptr(node) == key:
                __set_value_int8_t(node, value)

            else:
                __insert_node_by_ptr_int8_t(self.__index, node, key, value)


    #--------------------------------------------------------------------------------------------
    # Emulating numeric types
    #--------------------------------------------------------------------------------------------
    cpdef __TreeSeries_int8_t add(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __add_int8_t)


    cpdef __TreeSeries_int8_t sub(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __sub_int8_t)


    cpdef __TreeSeries_int8_t mul(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __mul_int8_t)


    cpdef __TreeSeries_int8_t div(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __div_int8_t)


    cpdef __TreeSeries_int8_t lshift(self, shift_param):

        if isinstance(shift_param, int):
            return self.__lshift_int(shift_param)


    cpdef __TreeSeries_int8_t rshift(self, shift_param):

        if isinstance(shift_param, int):
            return self.__rshift_int(shift_param)


    #--------------------------------------------------------------------------------------------
    # Private methods
    #--------------------------------------------------------------------------------------------
    cdef void __init_tree(self, index, values) except*:

        cdef tuple it

        if len(index) != len(values):
            raise IndexError("Index must have the same length as values.")

        for it in zip(index, values):
            self.insert(it[0], it[1])


    cdef void __init_tree_index(self, index) except*:

        for it in index:
            self.insert(it, 0)


    cdef int8_t __get_item_linear(self, key, int32_t* error):

        #bug? cython 0.28.3 translate c_key to const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key) 
        cdef pair[node_ptr, node_ptr] bounds
        
        bounds = self.__index.linear_search_from(self.__last_call, c_key)
        error[0] = 0

        if bounds.first != self.link():
            self.__last_call = bounds.first

        else:
            self.__last_call = deref( self.__index.begin() )

        return self.__interpolate( bounds.first, bounds.second, key,
                                   self.link(), self.extrapolate,
                                   error )

    
    cdef int8_t __get_item_tree(self, key, int32_t* error):

        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)
        cdef pair[node_ptr, node_ptr] bounds

        bounds = self.__index.tree_search(c_key)
        error[0] = 0

        return self.__interpolate( bounds.first, bounds.second, key,
                                   self.link(), self.extrapolate, 
                                   error )

    
    cdef __TreeSeries_int8_t __get_item_slice(self, key):

        cdef c_pyobject c_key
        cdef node_ptr begin = deref( self.__index.begin() ) 
        cdef node_ptr end = deref( self.__index.back() )
        cdef __TreeSeries_int8_t result
        result = __TreeSeries_int8_t.__new__( __TreeSeries_int8_t,
                                             interpolate=self.interpolate_type,
                                             arithmetic=self.arithmetic )

        if self.__len__() == 0:
            return result

        if key.start is not None and key.stop is not None:

            if key.start < self.begin() and key.stop < self.begin():
                return result

            if key.start > self.end() and key.stop < self.end():
                return result

        if key.start is not None:
            c_key = c_pyobject(<PyObject*>key.start)
            begin = self.__index.tree_search(c_key).second

        if key.stop is not None:
            c_key = c_pyobject(<PyObject*>key.stop)
            end = self.__index.tree_search(c_key).first

        if compare_func(end.key, begin.key) \
            and ( not equal(end.key, begin.key) ):

            return result

        if begin == self.link() or end == self.link():
            return result 

        if key.step is None:
            return self.truncate( self.__get_key_from_ptr(begin),
                                  self.__get_key_from_ptr(end) )

        else: 
            return self.periodic( self.__get_key_from_ptr(begin), 
                                  self.__get_key_from_ptr(end), 
                                  key.step )


    cdef __TreeSeries_int8_t __lshift_int(self, Py_ssize_t shift_param):

        cdef __TreeSeries_int8_t result
        cdef list delay_holder = []
        cdef tuple pair
        cdef Py_ssize_t count = 0
        cdef bool append_status = False

        result = __TreeSeries_int8_t.__new__( __TreeSeries_int8_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        if shift_param <= 0:
            raise ValueError("Shift parameter must be more than zero.")

        for pair in self.iteritems():
            
            if append_status:
                result.insert(delay_holder[0], pair[1])
                delay_holder.pop(0)

            if count == shift_param - 1:
                append_status = True

            delay_holder.append(pair[0])
            count += 1

        return result


    cdef __TreeSeries_int8_t __rshift_int(self, Py_ssize_t shift_param):

        cdef __TreeSeries_int8_t result
        cdef list delay_holder = []
        cdef tuple pair
        cdef Py_ssize_t count = 0
        cdef bool append_status = False

        result = __TreeSeries_int8_t.__new__( __TreeSeries_int8_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        if isinstance(shift_param, int):

            if shift_param <= 0:
                raise ValueError("Shift parameter must be more than zero.")

            for pair in self.iteritems():
                
                if append_status:
                    result.insert(pair[0], delay_holder[0])
                    delay_holder.pop(0)

                if count == shift_param - 1:
                    append_status = True

                delay_holder.append(pair[1])
                count += 1

            return result


    cdef __TreeSeries_int8_t __arithmetic_left( self, __BaseTreeSeries other, 
                                                 arithmetic_int8_t action ):

        cdef int8_t current
        cdef node_ptr node
        cdef object key
        cdef __TreeSeries_int8_t result
        result = __TreeSeries_int8_t.__new__( __TreeSeries_int8_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        other.on_itermode()

        for node in deref(self.__index):

            key = self.__get_key_from_ptr(node)
            current = action(__deref_value_ptr_int8_t(node), other[key])
            __insert_node_int8_t(result.__index, key, current)

        other.off_itermode()

        return result


    cdef __TreeSeries_int8_t __arithmetic_union( self, __BaseTreeSeries other, 
                                                  arithmetic_int8_t action ):

        cdef int8_t current
        cdef object key
        cdef trees_iterator[rb_tree, rb_node_valued] iterator
        cdef vector[rb_tree_ptr] trees = vector[rb_tree_ptr](2)
        cdef __TreeSeries_int8_t result
        result = __TreeSeries_int8_t.__new__( __TreeSeries_int8_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        
        trees[0] = self.__index
        trees[1] = other.get_tree()
        iterator.set_equal(equal_pair)
        iterator.set_compare(comp_pair)
        iterator.set_iterator(trees, "forward")

        self.on_itermode()
        other.on_itermode()

        while not iterator.empty():
            key = <object>deref(iterator).key.data
            current = action(self.__getitem__(key), other[key])
            __insert_node_int8_t(result.__index, key, current)
            pinc(iterator)

        self.off_itermode()
        other.off_itermode()

        return result
@cython.internal
cdef class __TreeSeries_int16_t(__BaseTreeSeries):

    cdef interpolate_int16_t     __interpolate
    cdef itermode_search_int16_t __get_item
    cdef arithmetic_type_int16_t __arithmetic

    cdef public   str  name
    cdef public   str  arithmetic
    cdef readonly str  interpolate_type
    cdef public   int16_t extrapolate


    def __init__(self):
        pass


    def __cinit__( self, object index=None, object values=None, str name="Untitled", 
                   str interpolate="floor", int16_t extrapolate=0, str arithmetic="left" ):

        self.set_interpolation(interpolate)
        self.extrapolate = extrapolate
        self.name = name
        self.arithmetic = arithmetic

        self.__get_item = self.__get_item_tree
        self.set_arithmetic(arithmetic)

        if index is None:
            
            if values is not None:
                raise IndexError("Index cannot be None while values are not.")

        else:
            if values is not None:
                self.__init_tree(index, values)

            else:
                self.__init_tree_index(index)


    #--------------------------------------------------------------------------------------------
    # Public methods
    #--------------------------------------------------------------------------------------------

    cpdef void insert(self, key, int16_t value) except*:

        __insert_node_int16_t(self.__index, key, value)


    cpdef void insert_range(self, list pairs) except*:

        cdef tuple pair

        for pair in pairs:
            __insert_node_int16_t(self.__index, pair[0], pair[1])


    cpdef void erase(self, key) except*:

        __erase_node_int16_t(self.__index, key)


    cpdef void erase_range(self, list keys) except*:

        cdef Py_ssize_t it

        for it in range( len(keys) ):
            __erase_node_int16_t(self.__index, keys[it])


    cpdef list values(self):

        cdef rb_node_valued* it
        cdef list result = []

        for it in deref(self.__index):
            result.append( __deref_value_ptr_int16_t(it) )

        return result


    cpdef list items(self):

        cdef node_ptr it
        cdef list result = []

        for it in deref(self.__index):
            result.append( ( self.__get_key_from_ptr(it), 
                             __deref_value_ptr_int16_t(it) ) )

        return result


    def iteritems(self):

        cdef node_ptr it

        for it in deref(self.__index):
            yield ( self.__get_key_from_ptr(it), __deref_value_ptr_int16_t(it) )


    cpdef tuple floor(self, key):

        cdef pair[node_ptr, node_ptr] bounds
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)
        bounds = self.__index.tree_search(c_key)

        if bounds.first == self.link():
            return (None, None)

        return ( self.__get_key_from_ptr(bounds.first),
                 __deref_value_ptr_int16_t(bounds.first) )


    cpdef tuple ceil(self, key):

        cdef pair[node_ptr, node_ptr] bounds
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)  
        bounds = self.__index.tree_search(c_key)

        if bounds.second == self.link():
            return (None, None)

        return ( self.__get_key_from_ptr(bounds.second),
                 __deref_value_ptr_int16_t(bounds.second) )


    cpdef __TreeSeries_int16_t truncate(self, start, stop):

        cdef iterator begin
        cdef iterator end
        cdef pair[node_ptr, node_ptr] bounds_start 
        cdef pair[node_ptr, node_ptr] bounds_end
        cdef __TreeSeries_int16_t result
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_start = c_pyobject(<PyObject*>start)
        cdef c_pyobject c_stop = c_pyobject(<PyObject*>stop)

        result = __TreeSeries_int16_t.__new__( __TreeSeries_int16_t, 
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        bounds_start = self.__index.tree_search(c_start)
        bounds_end = self.__index.tree_search(c_stop)
        begin = deref(bounds_start.second).it_position
        end = deref(bounds_end.first).it_position

        if( bounds_start.second == self.link() ) \
            or ( bounds_end.first == self.link() ):

            return result

        if self.__get_key_from_iter(begin) > self.__get_key_from_iter(end):
            return result

        while True:
            result.insert( self.__get_key_from_iter(begin), 
                           __deref_value_ptr_int16_t( deref(begin) ) )

            if begin == end:
                break

            inc(begin)

        return result


    cpdef __TreeSeries_int16_t periodic(self, start, stop, step):

        cdef __TreeSeries_int16_t result
        cdef object it = start
        result = __TreeSeries_int16_t.__new__( __TreeSeries_int16_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        self.on_itermode()

        while it < stop:
            result.insert( it, self.__getitem__(it) )
            it = it + step

        self.off_itermode()

        return result


    cpdef __TreeSeries_int16_t map( self, method, tuple args=(), 
                                  dict kwargs={} ):

        cdef __TreeSeries_int16_t result
        cdef tuple it

        result = __TreeSeries_int16_t.__new__( __TreeSeries_int16_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        for it in self.iteritems():
            result.insert( it[0], method(it[1], *args, **kwargs) )

        return result


    cpdef void map_inplace( self, method, tuple args=(), 
                            dict kwargs={} ) except*:

        cdef node_ptr it

        for it in deref(self.__index):
            __set_value_int16_t( it, method( __deref_value_ptr_int16_t(it),
                                             *args, **kwargs ) )


    cpdef void set_interpolation(self, str interpolate):

        cdef string c_interpolate = bytearray(interpolate, 'utf8')
        self.interpolate_type = interpolate
        self.__interpolate = __INTERPOLATE_int16_t[c_interpolate]


    cpdef void set_arithmetic(self, str arithmetic):

        if arithmetic == "union":
            self.__arithmetic = self.__arithmetic_union

        if arithmetic == "left":
            self.__arithmetic = self.__arithmetic_left


    cpdef void on_itermode(self):

        __BaseTreeSeries.on_itermode(self)
        self.__get_item = self.__get_item_linear


    cpdef void off_itermode(self):

        __BaseTreeSeries.off_itermode(self)
        self.__get_item = self.__get_item_tree


    cpdef void copy_data(self, other, str to_type="numerical") except*:

        cdef tuple it
        cdef int16_t num
        self.clear()

        if to_type == "numerical":

            for it in other.iteritems():
                num = <int16_t>float(it[1])
                self.insert( it[0], num )

        elif to_type == "str":
            for it in other.iteritems():
                self.insert( it[0], str(it[1]) )

        else:
            for it in other.iteritems():
                self.insert(it[0], it[1])


    cpdef void clear(self) except*:

        for key in self.keys():
            self.erase(key)

        self.__index.clear()

    #--------------------------------------------------------------------------------------------
    # Special methods
    #--------------------------------------------------------------------------------------------
    def __dealloc__(self):

        del self.__index


    def __str__(self):

        cdef str out_format = '{key}: {value}\n'
        cdef str result = 'Series object ' + self.name + '\n'
        cdef tuple it
        cdef list items = self.items()

        if len(items) <= 50: 
            for it in items:
                result += out_format.format(key=it[0], value=it[1])

        else:
            for it in items[:25]:
                result += out_format.format(key=it[0], value=it[1])

            result += '...\n'

            for it in items[-25:]:
                result += out_format.format(key=it[0], value=it[1])

        return result


    def __repr__(self):

        return self.__str__()

    
    def __iter__(self):

        cdef node_ptr it

        for it in deref(self.__index):
            yield __deref_value_ptr_int16_t(it)


    def __getitem__(self, key):

        cdef int32_t error = TYPE_ERROR
        cdef int16_t result

        if self.__index.size() == 0:
            return self.extrapolate

        if isinstance(key, slice):
            return self.__get_item_slice(key)

        result = self.__get_item(self, key, &error)

        if error == 0:
            return result

        if error == INT_KEY_ERROR:
            raise KeyError( str(key) )

        if error == TYPE_ERROR:
            raise TypeError("Inconsistent key type: " + key.__class__.__name__)


    def __setitem__(self, key, int16_t value):

        cdef c_pyobject key_holder = to_c_pyobject(key)
        cdef node_ptr node = self.__index.insert_search(key_holder)

        if self.__index.size() == 0:
            __insert_node_by_ptr_int16_t(self.__index, node, key, value)

        else:
            if self.__get_key_from_ptr(node) == key:
                __set_value_int16_t(node, value)

            else:
                __insert_node_by_ptr_int16_t(self.__index, node, key, value)


    #--------------------------------------------------------------------------------------------
    # Emulating numeric types
    #--------------------------------------------------------------------------------------------
    cpdef __TreeSeries_int16_t add(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __add_int16_t)


    cpdef __TreeSeries_int16_t sub(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __sub_int16_t)


    cpdef __TreeSeries_int16_t mul(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __mul_int16_t)


    cpdef __TreeSeries_int16_t div(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __div_int16_t)


    cpdef __TreeSeries_int16_t lshift(self, shift_param):

        if isinstance(shift_param, int):
            return self.__lshift_int(shift_param)


    cpdef __TreeSeries_int16_t rshift(self, shift_param):

        if isinstance(shift_param, int):
            return self.__rshift_int(shift_param)


    #--------------------------------------------------------------------------------------------
    # Private methods
    #--------------------------------------------------------------------------------------------
    cdef void __init_tree(self, index, values) except*:

        cdef tuple it

        if len(index) != len(values):
            raise IndexError("Index must have the same length as values.")

        for it in zip(index, values):
            self.insert(it[0], it[1])


    cdef void __init_tree_index(self, index) except*:

        for it in index:
            self.insert(it, 0)


    cdef int16_t __get_item_linear(self, key, int32_t* error):

        #bug? cython 0.28.3 translate c_key to const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key) 
        cdef pair[node_ptr, node_ptr] bounds
        
        bounds = self.__index.linear_search_from(self.__last_call, c_key)
        error[0] = 0

        if bounds.first != self.link():
            self.__last_call = bounds.first

        else:
            self.__last_call = deref( self.__index.begin() )

        return self.__interpolate( bounds.first, bounds.second, key,
                                   self.link(), self.extrapolate,
                                   error )

    
    cdef int16_t __get_item_tree(self, key, int32_t* error):

        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)
        cdef pair[node_ptr, node_ptr] bounds

        bounds = self.__index.tree_search(c_key)
        error[0] = 0

        return self.__interpolate( bounds.first, bounds.second, key,
                                   self.link(), self.extrapolate, 
                                   error )

    
    cdef __TreeSeries_int16_t __get_item_slice(self, key):

        cdef c_pyobject c_key
        cdef node_ptr begin = deref( self.__index.begin() ) 
        cdef node_ptr end = deref( self.__index.back() )
        cdef __TreeSeries_int16_t result
        result = __TreeSeries_int16_t.__new__( __TreeSeries_int16_t,
                                             interpolate=self.interpolate_type,
                                             arithmetic=self.arithmetic )

        if self.__len__() == 0:
            return result

        if key.start is not None and key.stop is not None:

            if key.start < self.begin() and key.stop < self.begin():
                return result

            if key.start > self.end() and key.stop < self.end():
                return result

        if key.start is not None:
            c_key = c_pyobject(<PyObject*>key.start)
            begin = self.__index.tree_search(c_key).second

        if key.stop is not None:
            c_key = c_pyobject(<PyObject*>key.stop)
            end = self.__index.tree_search(c_key).first

        if compare_func(end.key, begin.key) \
            and ( not equal(end.key, begin.key) ):

            return result

        if begin == self.link() or end == self.link():
            return result 

        if key.step is None:
            return self.truncate( self.__get_key_from_ptr(begin),
                                  self.__get_key_from_ptr(end) )

        else: 
            return self.periodic( self.__get_key_from_ptr(begin), 
                                  self.__get_key_from_ptr(end), 
                                  key.step )


    cdef __TreeSeries_int16_t __lshift_int(self, Py_ssize_t shift_param):

        cdef __TreeSeries_int16_t result
        cdef list delay_holder = []
        cdef tuple pair
        cdef Py_ssize_t count = 0
        cdef bool append_status = False

        result = __TreeSeries_int16_t.__new__( __TreeSeries_int16_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        if shift_param <= 0:
            raise ValueError("Shift parameter must be more than zero.")

        for pair in self.iteritems():
            
            if append_status:
                result.insert(delay_holder[0], pair[1])
                delay_holder.pop(0)

            if count == shift_param - 1:
                append_status = True

            delay_holder.append(pair[0])
            count += 1

        return result


    cdef __TreeSeries_int16_t __rshift_int(self, Py_ssize_t shift_param):

        cdef __TreeSeries_int16_t result
        cdef list delay_holder = []
        cdef tuple pair
        cdef Py_ssize_t count = 0
        cdef bool append_status = False

        result = __TreeSeries_int16_t.__new__( __TreeSeries_int16_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        if isinstance(shift_param, int):

            if shift_param <= 0:
                raise ValueError("Shift parameter must be more than zero.")

            for pair in self.iteritems():
                
                if append_status:
                    result.insert(pair[0], delay_holder[0])
                    delay_holder.pop(0)

                if count == shift_param - 1:
                    append_status = True

                delay_holder.append(pair[1])
                count += 1

            return result


    cdef __TreeSeries_int16_t __arithmetic_left( self, __BaseTreeSeries other, 
                                                 arithmetic_int16_t action ):

        cdef int16_t current
        cdef node_ptr node
        cdef object key
        cdef __TreeSeries_int16_t result
        result = __TreeSeries_int16_t.__new__( __TreeSeries_int16_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        other.on_itermode()

        for node in deref(self.__index):

            key = self.__get_key_from_ptr(node)
            current = action(__deref_value_ptr_int16_t(node), other[key])
            __insert_node_int16_t(result.__index, key, current)

        other.off_itermode()

        return result


    cdef __TreeSeries_int16_t __arithmetic_union( self, __BaseTreeSeries other, 
                                                  arithmetic_int16_t action ):

        cdef int16_t current
        cdef object key
        cdef trees_iterator[rb_tree, rb_node_valued] iterator
        cdef vector[rb_tree_ptr] trees = vector[rb_tree_ptr](2)
        cdef __TreeSeries_int16_t result
        result = __TreeSeries_int16_t.__new__( __TreeSeries_int16_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        
        trees[0] = self.__index
        trees[1] = other.get_tree()
        iterator.set_equal(equal_pair)
        iterator.set_compare(comp_pair)
        iterator.set_iterator(trees, "forward")

        self.on_itermode()
        other.on_itermode()

        while not iterator.empty():
            key = <object>deref(iterator).key.data
            current = action(self.__getitem__(key), other[key])
            __insert_node_int16_t(result.__index, key, current)
            pinc(iterator)

        self.off_itermode()
        other.off_itermode()

        return result
@cython.internal
cdef class __TreeSeries_int32_t(__BaseTreeSeries):

    cdef interpolate_int32_t     __interpolate
    cdef itermode_search_int32_t __get_item
    cdef arithmetic_type_int32_t __arithmetic

    cdef public   str  name
    cdef public   str  arithmetic
    cdef readonly str  interpolate_type
    cdef public   int32_t extrapolate


    def __init__(self):
        pass


    def __cinit__( self, object index=None, object values=None, str name="Untitled", 
                   str interpolate="floor", int32_t extrapolate=0, str arithmetic="left" ):

        self.set_interpolation(interpolate)
        self.extrapolate = extrapolate
        self.name = name
        self.arithmetic = arithmetic

        self.__get_item = self.__get_item_tree
        self.set_arithmetic(arithmetic)

        if index is None:
            
            if values is not None:
                raise IndexError("Index cannot be None while values are not.")

        else:
            if values is not None:
                self.__init_tree(index, values)

            else:
                self.__init_tree_index(index)


    #--------------------------------------------------------------------------------------------
    # Public methods
    #--------------------------------------------------------------------------------------------

    cpdef void insert(self, key, int32_t value) except*:

        __insert_node_int32_t(self.__index, key, value)


    cpdef void insert_range(self, list pairs) except*:

        cdef tuple pair

        for pair in pairs:
            __insert_node_int32_t(self.__index, pair[0], pair[1])


    cpdef void erase(self, key) except*:

        __erase_node_int32_t(self.__index, key)


    cpdef void erase_range(self, list keys) except*:

        cdef Py_ssize_t it

        for it in range( len(keys) ):
            __erase_node_int32_t(self.__index, keys[it])


    cpdef list values(self):

        cdef rb_node_valued* it
        cdef list result = []

        for it in deref(self.__index):
            result.append( __deref_value_ptr_int32_t(it) )

        return result


    cpdef list items(self):

        cdef node_ptr it
        cdef list result = []

        for it in deref(self.__index):
            result.append( ( self.__get_key_from_ptr(it), 
                             __deref_value_ptr_int32_t(it) ) )

        return result


    def iteritems(self):

        cdef node_ptr it

        for it in deref(self.__index):
            yield ( self.__get_key_from_ptr(it), __deref_value_ptr_int32_t(it) )


    cpdef tuple floor(self, key):

        cdef pair[node_ptr, node_ptr] bounds
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)
        bounds = self.__index.tree_search(c_key)

        if bounds.first == self.link():
            return (None, None)

        return ( self.__get_key_from_ptr(bounds.first),
                 __deref_value_ptr_int32_t(bounds.first) )


    cpdef tuple ceil(self, key):

        cdef pair[node_ptr, node_ptr] bounds
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)  
        bounds = self.__index.tree_search(c_key)

        if bounds.second == self.link():
            return (None, None)

        return ( self.__get_key_from_ptr(bounds.second),
                 __deref_value_ptr_int32_t(bounds.second) )


    cpdef __TreeSeries_int32_t truncate(self, start, stop):

        cdef iterator begin
        cdef iterator end
        cdef pair[node_ptr, node_ptr] bounds_start 
        cdef pair[node_ptr, node_ptr] bounds_end
        cdef __TreeSeries_int32_t result
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_start = c_pyobject(<PyObject*>start)
        cdef c_pyobject c_stop = c_pyobject(<PyObject*>stop)

        result = __TreeSeries_int32_t.__new__( __TreeSeries_int32_t, 
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        bounds_start = self.__index.tree_search(c_start)
        bounds_end = self.__index.tree_search(c_stop)
        begin = deref(bounds_start.second).it_position
        end = deref(bounds_end.first).it_position

        if( bounds_start.second == self.link() ) \
            or ( bounds_end.first == self.link() ):

            return result

        if self.__get_key_from_iter(begin) > self.__get_key_from_iter(end):
            return result

        while True:
            result.insert( self.__get_key_from_iter(begin), 
                           __deref_value_ptr_int32_t( deref(begin) ) )

            if begin == end:
                break

            inc(begin)

        return result


    cpdef __TreeSeries_int32_t periodic(self, start, stop, step):

        cdef __TreeSeries_int32_t result
        cdef object it = start
        result = __TreeSeries_int32_t.__new__( __TreeSeries_int32_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        self.on_itermode()

        while it < stop:
            result.insert( it, self.__getitem__(it) )
            it = it + step

        self.off_itermode()

        return result


    cpdef __TreeSeries_int32_t map( self, method, tuple args=(), 
                                  dict kwargs={} ):

        cdef __TreeSeries_int32_t result
        cdef tuple it

        result = __TreeSeries_int32_t.__new__( __TreeSeries_int32_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        for it in self.iteritems():
            result.insert( it[0], method(it[1], *args, **kwargs) )

        return result


    cpdef void map_inplace( self, method, tuple args=(), 
                            dict kwargs={} ) except*:

        cdef node_ptr it

        for it in deref(self.__index):
            __set_value_int32_t( it, method( __deref_value_ptr_int32_t(it),
                                             *args, **kwargs ) )


    cpdef void set_interpolation(self, str interpolate):

        cdef string c_interpolate = bytearray(interpolate, 'utf8')
        self.interpolate_type = interpolate
        self.__interpolate = __INTERPOLATE_int32_t[c_interpolate]


    cpdef void set_arithmetic(self, str arithmetic):

        if arithmetic == "union":
            self.__arithmetic = self.__arithmetic_union

        if arithmetic == "left":
            self.__arithmetic = self.__arithmetic_left


    cpdef void on_itermode(self):

        __BaseTreeSeries.on_itermode(self)
        self.__get_item = self.__get_item_linear


    cpdef void off_itermode(self):

        __BaseTreeSeries.off_itermode(self)
        self.__get_item = self.__get_item_tree


    cpdef void copy_data(self, other, str to_type="numerical") except*:

        cdef tuple it
        cdef int32_t num
        self.clear()

        if to_type == "numerical":

            for it in other.iteritems():
                num = <int32_t>float(it[1])
                self.insert( it[0], num )

        elif to_type == "str":
            for it in other.iteritems():
                self.insert( it[0], str(it[1]) )

        else:
            for it in other.iteritems():
                self.insert(it[0], it[1])


    cpdef void clear(self) except*:

        for key in self.keys():
            self.erase(key)

        self.__index.clear()

    #--------------------------------------------------------------------------------------------
    # Special methods
    #--------------------------------------------------------------------------------------------
    def __dealloc__(self):

        del self.__index


    def __str__(self):

        cdef str out_format = '{key}: {value}\n'
        cdef str result = 'Series object ' + self.name + '\n'
        cdef tuple it
        cdef list items = self.items()

        if len(items) <= 50: 
            for it in items:
                result += out_format.format(key=it[0], value=it[1])

        else:
            for it in items[:25]:
                result += out_format.format(key=it[0], value=it[1])

            result += '...\n'

            for it in items[-25:]:
                result += out_format.format(key=it[0], value=it[1])

        return result


    def __repr__(self):

        return self.__str__()

    
    def __iter__(self):

        cdef node_ptr it

        for it in deref(self.__index):
            yield __deref_value_ptr_int32_t(it)


    def __getitem__(self, key):

        cdef int32_t error = TYPE_ERROR
        cdef int32_t result

        if self.__index.size() == 0:
            return self.extrapolate

        if isinstance(key, slice):
            return self.__get_item_slice(key)

        result = self.__get_item(self, key, &error)

        if error == 0:
            return result

        if error == INT_KEY_ERROR:
            raise KeyError( str(key) )

        if error == TYPE_ERROR:
            raise TypeError("Inconsistent key type: " + key.__class__.__name__)


    def __setitem__(self, key, int32_t value):

        cdef c_pyobject key_holder = to_c_pyobject(key)
        cdef node_ptr node = self.__index.insert_search(key_holder)

        if self.__index.size() == 0:
            __insert_node_by_ptr_int32_t(self.__index, node, key, value)

        else:
            if self.__get_key_from_ptr(node) == key:
                __set_value_int32_t(node, value)

            else:
                __insert_node_by_ptr_int32_t(self.__index, node, key, value)


    #--------------------------------------------------------------------------------------------
    # Emulating numeric types
    #--------------------------------------------------------------------------------------------
    cpdef __TreeSeries_int32_t add(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __add_int32_t)


    cpdef __TreeSeries_int32_t sub(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __sub_int32_t)


    cpdef __TreeSeries_int32_t mul(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __mul_int32_t)


    cpdef __TreeSeries_int32_t div(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __div_int32_t)


    cpdef __TreeSeries_int32_t lshift(self, shift_param):

        if isinstance(shift_param, int):
            return self.__lshift_int(shift_param)


    cpdef __TreeSeries_int32_t rshift(self, shift_param):

        if isinstance(shift_param, int):
            return self.__rshift_int(shift_param)


    #--------------------------------------------------------------------------------------------
    # Private methods
    #--------------------------------------------------------------------------------------------
    cdef void __init_tree(self, index, values) except*:

        cdef tuple it

        if len(index) != len(values):
            raise IndexError("Index must have the same length as values.")

        for it in zip(index, values):
            self.insert(it[0], it[1])


    cdef void __init_tree_index(self, index) except*:

        for it in index:
            self.insert(it, 0)


    cdef int32_t __get_item_linear(self, key, int32_t* error):

        #bug? cython 0.28.3 translate c_key to const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key) 
        cdef pair[node_ptr, node_ptr] bounds
        
        bounds = self.__index.linear_search_from(self.__last_call, c_key)
        error[0] = 0

        if bounds.first != self.link():
            self.__last_call = bounds.first

        else:
            self.__last_call = deref( self.__index.begin() )

        return self.__interpolate( bounds.first, bounds.second, key,
                                   self.link(), self.extrapolate,
                                   error )

    
    cdef int32_t __get_item_tree(self, key, int32_t* error):

        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)
        cdef pair[node_ptr, node_ptr] bounds

        bounds = self.__index.tree_search(c_key)
        error[0] = 0

        return self.__interpolate( bounds.first, bounds.second, key,
                                   self.link(), self.extrapolate, 
                                   error )

    
    cdef __TreeSeries_int32_t __get_item_slice(self, key):

        cdef c_pyobject c_key
        cdef node_ptr begin = deref( self.__index.begin() ) 
        cdef node_ptr end = deref( self.__index.back() )
        cdef __TreeSeries_int32_t result
        result = __TreeSeries_int32_t.__new__( __TreeSeries_int32_t,
                                             interpolate=self.interpolate_type,
                                             arithmetic=self.arithmetic )

        if self.__len__() == 0:
            return result

        if key.start is not None and key.stop is not None:

            if key.start < self.begin() and key.stop < self.begin():
                return result

            if key.start > self.end() and key.stop < self.end():
                return result

        if key.start is not None:
            c_key = c_pyobject(<PyObject*>key.start)
            begin = self.__index.tree_search(c_key).second

        if key.stop is not None:
            c_key = c_pyobject(<PyObject*>key.stop)
            end = self.__index.tree_search(c_key).first

        if compare_func(end.key, begin.key) \
            and ( not equal(end.key, begin.key) ):

            return result

        if begin == self.link() or end == self.link():
            return result 

        if key.step is None:
            return self.truncate( self.__get_key_from_ptr(begin),
                                  self.__get_key_from_ptr(end) )

        else: 
            return self.periodic( self.__get_key_from_ptr(begin), 
                                  self.__get_key_from_ptr(end), 
                                  key.step )


    cdef __TreeSeries_int32_t __lshift_int(self, Py_ssize_t shift_param):

        cdef __TreeSeries_int32_t result
        cdef list delay_holder = []
        cdef tuple pair
        cdef Py_ssize_t count = 0
        cdef bool append_status = False

        result = __TreeSeries_int32_t.__new__( __TreeSeries_int32_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        if shift_param <= 0:
            raise ValueError("Shift parameter must be more than zero.")

        for pair in self.iteritems():
            
            if append_status:
                result.insert(delay_holder[0], pair[1])
                delay_holder.pop(0)

            if count == shift_param - 1:
                append_status = True

            delay_holder.append(pair[0])
            count += 1

        return result


    cdef __TreeSeries_int32_t __rshift_int(self, Py_ssize_t shift_param):

        cdef __TreeSeries_int32_t result
        cdef list delay_holder = []
        cdef tuple pair
        cdef Py_ssize_t count = 0
        cdef bool append_status = False

        result = __TreeSeries_int32_t.__new__( __TreeSeries_int32_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        if isinstance(shift_param, int):

            if shift_param <= 0:
                raise ValueError("Shift parameter must be more than zero.")

            for pair in self.iteritems():
                
                if append_status:
                    result.insert(pair[0], delay_holder[0])
                    delay_holder.pop(0)

                if count == shift_param - 1:
                    append_status = True

                delay_holder.append(pair[1])
                count += 1

            return result


    cdef __TreeSeries_int32_t __arithmetic_left( self, __BaseTreeSeries other, 
                                                 arithmetic_int32_t action ):

        cdef int32_t current
        cdef node_ptr node
        cdef object key
        cdef __TreeSeries_int32_t result
        result = __TreeSeries_int32_t.__new__( __TreeSeries_int32_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        other.on_itermode()

        for node in deref(self.__index):

            key = self.__get_key_from_ptr(node)
            current = action(__deref_value_ptr_int32_t(node), other[key])
            __insert_node_int32_t(result.__index, key, current)

        other.off_itermode()

        return result


    cdef __TreeSeries_int32_t __arithmetic_union( self, __BaseTreeSeries other, 
                                                  arithmetic_int32_t action ):

        cdef int32_t current
        cdef object key
        cdef trees_iterator[rb_tree, rb_node_valued] iterator
        cdef vector[rb_tree_ptr] trees = vector[rb_tree_ptr](2)
        cdef __TreeSeries_int32_t result
        result = __TreeSeries_int32_t.__new__( __TreeSeries_int32_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        
        trees[0] = self.__index
        trees[1] = other.get_tree()
        iterator.set_equal(equal_pair)
        iterator.set_compare(comp_pair)
        iterator.set_iterator(trees, "forward")

        self.on_itermode()
        other.on_itermode()

        while not iterator.empty():
            key = <object>deref(iterator).key.data
            current = action(self.__getitem__(key), other[key])
            __insert_node_int32_t(result.__index, key, current)
            pinc(iterator)

        self.off_itermode()
        other.off_itermode()

        return result
@cython.internal
cdef class __TreeSeries_int64_t(__BaseTreeSeries):

    cdef interpolate_int64_t     __interpolate
    cdef itermode_search_int64_t __get_item
    cdef arithmetic_type_int64_t __arithmetic

    cdef public   str  name
    cdef public   str  arithmetic
    cdef readonly str  interpolate_type
    cdef public   int64_t extrapolate


    def __init__(self):
        pass


    def __cinit__( self, object index=None, object values=None, str name="Untitled", 
                   str interpolate="floor", int64_t extrapolate=0, str arithmetic="left" ):

        self.set_interpolation(interpolate)
        self.extrapolate = extrapolate
        self.name = name
        self.arithmetic = arithmetic

        self.__get_item = self.__get_item_tree
        self.set_arithmetic(arithmetic)

        if index is None:
            
            if values is not None:
                raise IndexError("Index cannot be None while values are not.")

        else:
            if values is not None:
                self.__init_tree(index, values)

            else:
                self.__init_tree_index(index)


    #--------------------------------------------------------------------------------------------
    # Public methods
    #--------------------------------------------------------------------------------------------

    cpdef void insert(self, key, int64_t value) except*:

        __insert_node_int64_t(self.__index, key, value)


    cpdef void insert_range(self, list pairs) except*:

        cdef tuple pair

        for pair in pairs:
            __insert_node_int64_t(self.__index, pair[0], pair[1])


    cpdef void erase(self, key) except*:

        __erase_node_int64_t(self.__index, key)


    cpdef void erase_range(self, list keys) except*:

        cdef Py_ssize_t it

        for it in range( len(keys) ):
            __erase_node_int64_t(self.__index, keys[it])


    cpdef list values(self):

        cdef rb_node_valued* it
        cdef list result = []

        for it in deref(self.__index):
            result.append( __deref_value_ptr_int64_t(it) )

        return result


    cpdef list items(self):

        cdef node_ptr it
        cdef list result = []

        for it in deref(self.__index):
            result.append( ( self.__get_key_from_ptr(it), 
                             __deref_value_ptr_int64_t(it) ) )

        return result


    def iteritems(self):

        cdef node_ptr it

        for it in deref(self.__index):
            yield ( self.__get_key_from_ptr(it), __deref_value_ptr_int64_t(it) )


    cpdef tuple floor(self, key):

        cdef pair[node_ptr, node_ptr] bounds
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)
        bounds = self.__index.tree_search(c_key)

        if bounds.first == self.link():
            return (None, None)

        return ( self.__get_key_from_ptr(bounds.first),
                 __deref_value_ptr_int64_t(bounds.first) )


    cpdef tuple ceil(self, key):

        cdef pair[node_ptr, node_ptr] bounds
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)  
        bounds = self.__index.tree_search(c_key)

        if bounds.second == self.link():
            return (None, None)

        return ( self.__get_key_from_ptr(bounds.second),
                 __deref_value_ptr_int64_t(bounds.second) )


    cpdef __TreeSeries_int64_t truncate(self, start, stop):

        cdef iterator begin
        cdef iterator end
        cdef pair[node_ptr, node_ptr] bounds_start 
        cdef pair[node_ptr, node_ptr] bounds_end
        cdef __TreeSeries_int64_t result
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_start = c_pyobject(<PyObject*>start)
        cdef c_pyobject c_stop = c_pyobject(<PyObject*>stop)

        result = __TreeSeries_int64_t.__new__( __TreeSeries_int64_t, 
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        bounds_start = self.__index.tree_search(c_start)
        bounds_end = self.__index.tree_search(c_stop)
        begin = deref(bounds_start.second).it_position
        end = deref(bounds_end.first).it_position

        if( bounds_start.second == self.link() ) \
            or ( bounds_end.first == self.link() ):

            return result

        if self.__get_key_from_iter(begin) > self.__get_key_from_iter(end):
            return result

        while True:
            result.insert( self.__get_key_from_iter(begin), 
                           __deref_value_ptr_int64_t( deref(begin) ) )

            if begin == end:
                break

            inc(begin)

        return result


    cpdef __TreeSeries_int64_t periodic(self, start, stop, step):

        cdef __TreeSeries_int64_t result
        cdef object it = start
        result = __TreeSeries_int64_t.__new__( __TreeSeries_int64_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        self.on_itermode()

        while it < stop:
            result.insert( it, self.__getitem__(it) )
            it = it + step

        self.off_itermode()

        return result


    cpdef __TreeSeries_int64_t map( self, method, tuple args=(), 
                                  dict kwargs={} ):

        cdef __TreeSeries_int64_t result
        cdef tuple it

        result = __TreeSeries_int64_t.__new__( __TreeSeries_int64_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        for it in self.iteritems():
            result.insert( it[0], method(it[1], *args, **kwargs) )

        return result


    cpdef void map_inplace( self, method, tuple args=(), 
                            dict kwargs={} ) except*:

        cdef node_ptr it

        for it in deref(self.__index):
            __set_value_int64_t( it, method( __deref_value_ptr_int64_t(it),
                                             *args, **kwargs ) )


    cpdef void set_interpolation(self, str interpolate):

        cdef string c_interpolate = bytearray(interpolate, 'utf8')
        self.interpolate_type = interpolate
        self.__interpolate = __INTERPOLATE_int64_t[c_interpolate]


    cpdef void set_arithmetic(self, str arithmetic):

        if arithmetic == "union":
            self.__arithmetic = self.__arithmetic_union

        if arithmetic == "left":
            self.__arithmetic = self.__arithmetic_left


    cpdef void on_itermode(self):

        __BaseTreeSeries.on_itermode(self)
        self.__get_item = self.__get_item_linear


    cpdef void off_itermode(self):

        __BaseTreeSeries.off_itermode(self)
        self.__get_item = self.__get_item_tree


    cpdef void copy_data(self, other, str to_type="numerical") except*:

        cdef tuple it
        cdef int64_t num
        self.clear()

        if to_type == "numerical":

            for it in other.iteritems():
                num = <int64_t>float(it[1])
                self.insert( it[0], num )

        elif to_type == "str":
            for it in other.iteritems():
                self.insert( it[0], str(it[1]) )

        else:
            for it in other.iteritems():
                self.insert(it[0], it[1])


    cpdef void clear(self) except*:

        for key in self.keys():
            self.erase(key)

        self.__index.clear()

    #--------------------------------------------------------------------------------------------
    # Special methods
    #--------------------------------------------------------------------------------------------
    def __dealloc__(self):

        del self.__index


    def __str__(self):

        cdef str out_format = '{key}: {value}\n'
        cdef str result = 'Series object ' + self.name + '\n'
        cdef tuple it
        cdef list items = self.items()

        if len(items) <= 50: 
            for it in items:
                result += out_format.format(key=it[0], value=it[1])

        else:
            for it in items[:25]:
                result += out_format.format(key=it[0], value=it[1])

            result += '...\n'

            for it in items[-25:]:
                result += out_format.format(key=it[0], value=it[1])

        return result


    def __repr__(self):

        return self.__str__()

    
    def __iter__(self):

        cdef node_ptr it

        for it in deref(self.__index):
            yield __deref_value_ptr_int64_t(it)


    def __getitem__(self, key):

        cdef int32_t error = TYPE_ERROR
        cdef int64_t result

        if self.__index.size() == 0:
            return self.extrapolate

        if isinstance(key, slice):
            return self.__get_item_slice(key)

        result = self.__get_item(self, key, &error)

        if error == 0:
            return result

        if error == INT_KEY_ERROR:
            raise KeyError( str(key) )

        if error == TYPE_ERROR:
            raise TypeError("Inconsistent key type: " + key.__class__.__name__)


    def __setitem__(self, key, int64_t value):

        cdef c_pyobject key_holder = to_c_pyobject(key)
        cdef node_ptr node = self.__index.insert_search(key_holder)

        if self.__index.size() == 0:
            __insert_node_by_ptr_int64_t(self.__index, node, key, value)

        else:
            if self.__get_key_from_ptr(node) == key:
                __set_value_int64_t(node, value)

            else:
                __insert_node_by_ptr_int64_t(self.__index, node, key, value)


    #--------------------------------------------------------------------------------------------
    # Emulating numeric types
    #--------------------------------------------------------------------------------------------
    cpdef __TreeSeries_int64_t add(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __add_int64_t)


    cpdef __TreeSeries_int64_t sub(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __sub_int64_t)


    cpdef __TreeSeries_int64_t mul(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __mul_int64_t)


    cpdef __TreeSeries_int64_t div(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __div_int64_t)


    cpdef __TreeSeries_int64_t lshift(self, shift_param):

        if isinstance(shift_param, int):
            return self.__lshift_int(shift_param)


    cpdef __TreeSeries_int64_t rshift(self, shift_param):

        if isinstance(shift_param, int):
            return self.__rshift_int(shift_param)


    #--------------------------------------------------------------------------------------------
    # Private methods
    #--------------------------------------------------------------------------------------------
    cdef void __init_tree(self, index, values) except*:

        cdef tuple it

        if len(index) != len(values):
            raise IndexError("Index must have the same length as values.")

        for it in zip(index, values):
            self.insert(it[0], it[1])


    cdef void __init_tree_index(self, index) except*:

        for it in index:
            self.insert(it, 0)


    cdef int64_t __get_item_linear(self, key, int32_t* error):

        #bug? cython 0.28.3 translate c_key to const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key) 
        cdef pair[node_ptr, node_ptr] bounds
        
        bounds = self.__index.linear_search_from(self.__last_call, c_key)
        error[0] = 0

        if bounds.first != self.link():
            self.__last_call = bounds.first

        else:
            self.__last_call = deref( self.__index.begin() )

        return self.__interpolate( bounds.first, bounds.second, key,
                                   self.link(), self.extrapolate,
                                   error )

    
    cdef int64_t __get_item_tree(self, key, int32_t* error):

        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)
        cdef pair[node_ptr, node_ptr] bounds

        bounds = self.__index.tree_search(c_key)
        error[0] = 0

        return self.__interpolate( bounds.first, bounds.second, key,
                                   self.link(), self.extrapolate, 
                                   error )

    
    cdef __TreeSeries_int64_t __get_item_slice(self, key):

        cdef c_pyobject c_key
        cdef node_ptr begin = deref( self.__index.begin() ) 
        cdef node_ptr end = deref( self.__index.back() )
        cdef __TreeSeries_int64_t result
        result = __TreeSeries_int64_t.__new__( __TreeSeries_int64_t,
                                             interpolate=self.interpolate_type,
                                             arithmetic=self.arithmetic )

        if self.__len__() == 0:
            return result

        if key.start is not None and key.stop is not None:

            if key.start < self.begin() and key.stop < self.begin():
                return result

            if key.start > self.end() and key.stop < self.end():
                return result

        if key.start is not None:
            c_key = c_pyobject(<PyObject*>key.start)
            begin = self.__index.tree_search(c_key).second

        if key.stop is not None:
            c_key = c_pyobject(<PyObject*>key.stop)
            end = self.__index.tree_search(c_key).first

        if compare_func(end.key, begin.key) \
            and ( not equal(end.key, begin.key) ):

            return result

        if begin == self.link() or end == self.link():
            return result 

        if key.step is None:
            return self.truncate( self.__get_key_from_ptr(begin),
                                  self.__get_key_from_ptr(end) )

        else: 
            return self.periodic( self.__get_key_from_ptr(begin), 
                                  self.__get_key_from_ptr(end), 
                                  key.step )


    cdef __TreeSeries_int64_t __lshift_int(self, Py_ssize_t shift_param):

        cdef __TreeSeries_int64_t result
        cdef list delay_holder = []
        cdef tuple pair
        cdef Py_ssize_t count = 0
        cdef bool append_status = False

        result = __TreeSeries_int64_t.__new__( __TreeSeries_int64_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        if shift_param <= 0:
            raise ValueError("Shift parameter must be more than zero.")

        for pair in self.iteritems():
            
            if append_status:
                result.insert(delay_holder[0], pair[1])
                delay_holder.pop(0)

            if count == shift_param - 1:
                append_status = True

            delay_holder.append(pair[0])
            count += 1

        return result


    cdef __TreeSeries_int64_t __rshift_int(self, Py_ssize_t shift_param):

        cdef __TreeSeries_int64_t result
        cdef list delay_holder = []
        cdef tuple pair
        cdef Py_ssize_t count = 0
        cdef bool append_status = False

        result = __TreeSeries_int64_t.__new__( __TreeSeries_int64_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        if isinstance(shift_param, int):

            if shift_param <= 0:
                raise ValueError("Shift parameter must be more than zero.")

            for pair in self.iteritems():
                
                if append_status:
                    result.insert(pair[0], delay_holder[0])
                    delay_holder.pop(0)

                if count == shift_param - 1:
                    append_status = True

                delay_holder.append(pair[1])
                count += 1

            return result


    cdef __TreeSeries_int64_t __arithmetic_left( self, __BaseTreeSeries other, 
                                                 arithmetic_int64_t action ):

        cdef int64_t current
        cdef node_ptr node
        cdef object key
        cdef __TreeSeries_int64_t result
        result = __TreeSeries_int64_t.__new__( __TreeSeries_int64_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        other.on_itermode()

        for node in deref(self.__index):

            key = self.__get_key_from_ptr(node)
            current = action(__deref_value_ptr_int64_t(node), other[key])
            __insert_node_int64_t(result.__index, key, current)

        other.off_itermode()

        return result


    cdef __TreeSeries_int64_t __arithmetic_union( self, __BaseTreeSeries other, 
                                                  arithmetic_int64_t action ):

        cdef int64_t current
        cdef object key
        cdef trees_iterator[rb_tree, rb_node_valued] iterator
        cdef vector[rb_tree_ptr] trees = vector[rb_tree_ptr](2)
        cdef __TreeSeries_int64_t result
        result = __TreeSeries_int64_t.__new__( __TreeSeries_int64_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        
        trees[0] = self.__index
        trees[1] = other.get_tree()
        iterator.set_equal(equal_pair)
        iterator.set_compare(comp_pair)
        iterator.set_iterator(trees, "forward")

        self.on_itermode()
        other.on_itermode()

        while not iterator.empty():
            key = <object>deref(iterator).key.data
            current = action(self.__getitem__(key), other[key])
            __insert_node_int64_t(result.__index, key, current)
            pinc(iterator)

        self.off_itermode()
        other.off_itermode()

        return result
@cython.internal
cdef class __TreeSeries_int96_t(__BaseTreeSeries):

    cdef interpolate_int96_t     __interpolate
    cdef itermode_search_int96_t __get_item
    cdef arithmetic_type_int96_t __arithmetic

    cdef public   str  name
    cdef public   str  arithmetic
    cdef readonly str  interpolate_type
    cdef public   int96_t extrapolate


    def __init__(self):
        pass


    def __cinit__( self, object index=None, object values=None, str name="Untitled", 
                   str interpolate="floor", int96_t extrapolate=0, str arithmetic="left" ):

        self.set_interpolation(interpolate)
        self.extrapolate = extrapolate
        self.name = name
        self.arithmetic = arithmetic

        self.__get_item = self.__get_item_tree
        self.set_arithmetic(arithmetic)

        if index is None:
            
            if values is not None:
                raise IndexError("Index cannot be None while values are not.")

        else:
            if values is not None:
                self.__init_tree(index, values)

            else:
                self.__init_tree_index(index)


    #--------------------------------------------------------------------------------------------
    # Public methods
    #--------------------------------------------------------------------------------------------

    cpdef void insert(self, key, int96_t value) except*:

        __insert_node_int96_t(self.__index, key, value)


    cpdef void insert_range(self, list pairs) except*:

        cdef tuple pair

        for pair in pairs:
            __insert_node_int96_t(self.__index, pair[0], pair[1])


    cpdef void erase(self, key) except*:

        __erase_node_int96_t(self.__index, key)


    cpdef void erase_range(self, list keys) except*:

        cdef Py_ssize_t it

        for it in range( len(keys) ):
            __erase_node_int96_t(self.__index, keys[it])


    cpdef list values(self):

        cdef rb_node_valued* it
        cdef list result = []

        for it in deref(self.__index):
            result.append( __deref_value_ptr_int96_t(it) )

        return result


    cpdef list items(self):

        cdef node_ptr it
        cdef list result = []

        for it in deref(self.__index):
            result.append( ( self.__get_key_from_ptr(it), 
                             __deref_value_ptr_int96_t(it) ) )

        return result


    def iteritems(self):

        cdef node_ptr it

        for it in deref(self.__index):
            yield ( self.__get_key_from_ptr(it), __deref_value_ptr_int96_t(it) )


    cpdef tuple floor(self, key):

        cdef pair[node_ptr, node_ptr] bounds
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)
        bounds = self.__index.tree_search(c_key)

        if bounds.first == self.link():
            return (None, None)

        return ( self.__get_key_from_ptr(bounds.first),
                 __deref_value_ptr_int96_t(bounds.first) )


    cpdef tuple ceil(self, key):

        cdef pair[node_ptr, node_ptr] bounds
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)  
        bounds = self.__index.tree_search(c_key)

        if bounds.second == self.link():
            return (None, None)

        return ( self.__get_key_from_ptr(bounds.second),
                 __deref_value_ptr_int96_t(bounds.second) )


    cpdef __TreeSeries_int96_t truncate(self, start, stop):

        cdef iterator begin
        cdef iterator end
        cdef pair[node_ptr, node_ptr] bounds_start 
        cdef pair[node_ptr, node_ptr] bounds_end
        cdef __TreeSeries_int96_t result
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_start = c_pyobject(<PyObject*>start)
        cdef c_pyobject c_stop = c_pyobject(<PyObject*>stop)

        result = __TreeSeries_int96_t.__new__( __TreeSeries_int96_t, 
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        bounds_start = self.__index.tree_search(c_start)
        bounds_end = self.__index.tree_search(c_stop)
        begin = deref(bounds_start.second).it_position
        end = deref(bounds_end.first).it_position

        if( bounds_start.second == self.link() ) \
            or ( bounds_end.first == self.link() ):

            return result

        if self.__get_key_from_iter(begin) > self.__get_key_from_iter(end):
            return result

        while True:
            result.insert( self.__get_key_from_iter(begin), 
                           __deref_value_ptr_int96_t( deref(begin) ) )

            if begin == end:
                break

            inc(begin)

        return result


    cpdef __TreeSeries_int96_t periodic(self, start, stop, step):

        cdef __TreeSeries_int96_t result
        cdef object it = start
        result = __TreeSeries_int96_t.__new__( __TreeSeries_int96_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        self.on_itermode()

        while it < stop:
            result.insert( it, self.__getitem__(it) )
            it = it + step

        self.off_itermode()

        return result


    cpdef __TreeSeries_int96_t map( self, method, tuple args=(), 
                                  dict kwargs={} ):

        cdef __TreeSeries_int96_t result
        cdef tuple it

        result = __TreeSeries_int96_t.__new__( __TreeSeries_int96_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        for it in self.iteritems():
            result.insert( it[0], method(it[1], *args, **kwargs) )

        return result


    cpdef void map_inplace( self, method, tuple args=(), 
                            dict kwargs={} ) except*:

        cdef node_ptr it

        for it in deref(self.__index):
            __set_value_int96_t( it, method( __deref_value_ptr_int96_t(it),
                                             *args, **kwargs ) )


    cpdef void set_interpolation(self, str interpolate):

        cdef string c_interpolate = bytearray(interpolate, 'utf8')
        self.interpolate_type = interpolate
        self.__interpolate = __INTERPOLATE_int96_t[c_interpolate]


    cpdef void set_arithmetic(self, str arithmetic):

        if arithmetic == "union":
            self.__arithmetic = self.__arithmetic_union

        if arithmetic == "left":
            self.__arithmetic = self.__arithmetic_left


    cpdef void on_itermode(self):

        __BaseTreeSeries.on_itermode(self)
        self.__get_item = self.__get_item_linear


    cpdef void off_itermode(self):

        __BaseTreeSeries.off_itermode(self)
        self.__get_item = self.__get_item_tree


    cpdef void copy_data(self, other, str to_type="numerical") except*:

        cdef tuple it
        cdef int96_t num
        self.clear()

        if to_type == "numerical":

            for it in other.iteritems():
                num = <int96_t>float(it[1])
                self.insert( it[0], num )

        elif to_type == "str":
            for it in other.iteritems():
                self.insert( it[0], str(it[1]) )

        else:
            for it in other.iteritems():
                self.insert(it[0], it[1])


    cpdef void clear(self) except*:

        for key in self.keys():
            self.erase(key)

        self.__index.clear()

    #--------------------------------------------------------------------------------------------
    # Special methods
    #--------------------------------------------------------------------------------------------
    def __dealloc__(self):

        del self.__index


    def __str__(self):

        cdef str out_format = '{key}: {value}\n'
        cdef str result = 'Series object ' + self.name + '\n'
        cdef tuple it
        cdef list items = self.items()

        if len(items) <= 50: 
            for it in items:
                result += out_format.format(key=it[0], value=it[1])

        else:
            for it in items[:25]:
                result += out_format.format(key=it[0], value=it[1])

            result += '...\n'

            for it in items[-25:]:
                result += out_format.format(key=it[0], value=it[1])

        return result


    def __repr__(self):

        return self.__str__()

    
    def __iter__(self):

        cdef node_ptr it

        for it in deref(self.__index):
            yield __deref_value_ptr_int96_t(it)


    def __getitem__(self, key):

        cdef int32_t error = TYPE_ERROR
        cdef int96_t result

        if self.__index.size() == 0:
            return self.extrapolate

        if isinstance(key, slice):
            return self.__get_item_slice(key)

        result = self.__get_item(self, key, &error)

        if error == 0:
            return result

        if error == INT_KEY_ERROR:
            raise KeyError( str(key) )

        if error == TYPE_ERROR:
            raise TypeError("Inconsistent key type: " + key.__class__.__name__)


    def __setitem__(self, key, int96_t value):

        cdef c_pyobject key_holder = to_c_pyobject(key)
        cdef node_ptr node = self.__index.insert_search(key_holder)

        if self.__index.size() == 0:
            __insert_node_by_ptr_int96_t(self.__index, node, key, value)

        else:
            if self.__get_key_from_ptr(node) == key:
                __set_value_int96_t(node, value)

            else:
                __insert_node_by_ptr_int96_t(self.__index, node, key, value)


    #--------------------------------------------------------------------------------------------
    # Emulating numeric types
    #--------------------------------------------------------------------------------------------
    cpdef __TreeSeries_int96_t add(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __add_int96_t)


    cpdef __TreeSeries_int96_t sub(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __sub_int96_t)


    cpdef __TreeSeries_int96_t mul(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __mul_int96_t)


    cpdef __TreeSeries_int96_t div(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __div_int96_t)


    cpdef __TreeSeries_int96_t lshift(self, shift_param):

        if isinstance(shift_param, int):
            return self.__lshift_int(shift_param)


    cpdef __TreeSeries_int96_t rshift(self, shift_param):

        if isinstance(shift_param, int):
            return self.__rshift_int(shift_param)


    #--------------------------------------------------------------------------------------------
    # Private methods
    #--------------------------------------------------------------------------------------------
    cdef void __init_tree(self, index, values) except*:

        cdef tuple it

        if len(index) != len(values):
            raise IndexError("Index must have the same length as values.")

        for it in zip(index, values):
            self.insert(it[0], it[1])


    cdef void __init_tree_index(self, index) except*:

        for it in index:
            self.insert(it, 0)


    cdef int96_t __get_item_linear(self, key, int32_t* error):

        #bug? cython 0.28.3 translate c_key to const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key) 
        cdef pair[node_ptr, node_ptr] bounds
        
        bounds = self.__index.linear_search_from(self.__last_call, c_key)
        error[0] = 0

        if bounds.first != self.link():
            self.__last_call = bounds.first

        else:
            self.__last_call = deref( self.__index.begin() )

        return self.__interpolate( bounds.first, bounds.second, key,
                                   self.link(), self.extrapolate,
                                   error )

    
    cdef int96_t __get_item_tree(self, key, int32_t* error):

        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)
        cdef pair[node_ptr, node_ptr] bounds

        bounds = self.__index.tree_search(c_key)
        error[0] = 0

        return self.__interpolate( bounds.first, bounds.second, key,
                                   self.link(), self.extrapolate, 
                                   error )

    
    cdef __TreeSeries_int96_t __get_item_slice(self, key):

        cdef c_pyobject c_key
        cdef node_ptr begin = deref( self.__index.begin() ) 
        cdef node_ptr end = deref( self.__index.back() )
        cdef __TreeSeries_int96_t result
        result = __TreeSeries_int96_t.__new__( __TreeSeries_int96_t,
                                             interpolate=self.interpolate_type,
                                             arithmetic=self.arithmetic )

        if self.__len__() == 0:
            return result

        if key.start is not None and key.stop is not None:

            if key.start < self.begin() and key.stop < self.begin():
                return result

            if key.start > self.end() and key.stop < self.end():
                return result

        if key.start is not None:
            c_key = c_pyobject(<PyObject*>key.start)
            begin = self.__index.tree_search(c_key).second

        if key.stop is not None:
            c_key = c_pyobject(<PyObject*>key.stop)
            end = self.__index.tree_search(c_key).first

        if compare_func(end.key, begin.key) \
            and ( not equal(end.key, begin.key) ):

            return result

        if begin == self.link() or end == self.link():
            return result 

        if key.step is None:
            return self.truncate( self.__get_key_from_ptr(begin),
                                  self.__get_key_from_ptr(end) )

        else: 
            return self.periodic( self.__get_key_from_ptr(begin), 
                                  self.__get_key_from_ptr(end), 
                                  key.step )


    cdef __TreeSeries_int96_t __lshift_int(self, Py_ssize_t shift_param):

        cdef __TreeSeries_int96_t result
        cdef list delay_holder = []
        cdef tuple pair
        cdef Py_ssize_t count = 0
        cdef bool append_status = False

        result = __TreeSeries_int96_t.__new__( __TreeSeries_int96_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        if shift_param <= 0:
            raise ValueError("Shift parameter must be more than zero.")

        for pair in self.iteritems():
            
            if append_status:
                result.insert(delay_holder[0], pair[1])
                delay_holder.pop(0)

            if count == shift_param - 1:
                append_status = True

            delay_holder.append(pair[0])
            count += 1

        return result


    cdef __TreeSeries_int96_t __rshift_int(self, Py_ssize_t shift_param):

        cdef __TreeSeries_int96_t result
        cdef list delay_holder = []
        cdef tuple pair
        cdef Py_ssize_t count = 0
        cdef bool append_status = False

        result = __TreeSeries_int96_t.__new__( __TreeSeries_int96_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        if isinstance(shift_param, int):

            if shift_param <= 0:
                raise ValueError("Shift parameter must be more than zero.")

            for pair in self.iteritems():
                
                if append_status:
                    result.insert(pair[0], delay_holder[0])
                    delay_holder.pop(0)

                if count == shift_param - 1:
                    append_status = True

                delay_holder.append(pair[1])
                count += 1

            return result


    cdef __TreeSeries_int96_t __arithmetic_left( self, __BaseTreeSeries other, 
                                                 arithmetic_int96_t action ):

        cdef int96_t current
        cdef node_ptr node
        cdef object key
        cdef __TreeSeries_int96_t result
        result = __TreeSeries_int96_t.__new__( __TreeSeries_int96_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        other.on_itermode()

        for node in deref(self.__index):

            key = self.__get_key_from_ptr(node)
            current = action(__deref_value_ptr_int96_t(node), other[key])
            __insert_node_int96_t(result.__index, key, current)

        other.off_itermode()

        return result


    cdef __TreeSeries_int96_t __arithmetic_union( self, __BaseTreeSeries other, 
                                                  arithmetic_int96_t action ):

        cdef int96_t current
        cdef object key
        cdef trees_iterator[rb_tree, rb_node_valued] iterator
        cdef vector[rb_tree_ptr] trees = vector[rb_tree_ptr](2)
        cdef __TreeSeries_int96_t result
        result = __TreeSeries_int96_t.__new__( __TreeSeries_int96_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        
        trees[0] = self.__index
        trees[1] = other.get_tree()
        iterator.set_equal(equal_pair)
        iterator.set_compare(comp_pair)
        iterator.set_iterator(trees, "forward")

        self.on_itermode()
        other.on_itermode()

        while not iterator.empty():
            key = <object>deref(iterator).key.data
            current = action(self.__getitem__(key), other[key])
            __insert_node_int96_t(result.__index, key, current)
            pinc(iterator)

        self.off_itermode()
        other.off_itermode()

        return result
@cython.internal
cdef class __TreeSeries_int128_t(__BaseTreeSeries):

    cdef interpolate_int128_t     __interpolate
    cdef itermode_search_int128_t __get_item
    cdef arithmetic_type_int128_t __arithmetic

    cdef public   str  name
    cdef public   str  arithmetic
    cdef readonly str  interpolate_type
    cdef public   int128_t extrapolate


    def __init__(self):
        pass


    def __cinit__( self, object index=None, object values=None, str name="Untitled", 
                   str interpolate="floor", int128_t extrapolate=0, str arithmetic="left" ):

        self.set_interpolation(interpolate)
        self.extrapolate = extrapolate
        self.name = name
        self.arithmetic = arithmetic

        self.__get_item = self.__get_item_tree
        self.set_arithmetic(arithmetic)

        if index is None:
            
            if values is not None:
                raise IndexError("Index cannot be None while values are not.")

        else:
            if values is not None:
                self.__init_tree(index, values)

            else:
                self.__init_tree_index(index)


    #--------------------------------------------------------------------------------------------
    # Public methods
    #--------------------------------------------------------------------------------------------

    cpdef void insert(self, key, int128_t value) except*:

        __insert_node_int128_t(self.__index, key, value)


    cpdef void insert_range(self, list pairs) except*:

        cdef tuple pair

        for pair in pairs:
            __insert_node_int128_t(self.__index, pair[0], pair[1])


    cpdef void erase(self, key) except*:

        __erase_node_int128_t(self.__index, key)


    cpdef void erase_range(self, list keys) except*:

        cdef Py_ssize_t it

        for it in range( len(keys) ):
            __erase_node_int128_t(self.__index, keys[it])


    cpdef list values(self):

        cdef rb_node_valued* it
        cdef list result = []

        for it in deref(self.__index):
            result.append( __deref_value_ptr_int128_t(it) )

        return result


    cpdef list items(self):

        cdef node_ptr it
        cdef list result = []

        for it in deref(self.__index):
            result.append( ( self.__get_key_from_ptr(it), 
                             __deref_value_ptr_int128_t(it) ) )

        return result


    def iteritems(self):

        cdef node_ptr it

        for it in deref(self.__index):
            yield ( self.__get_key_from_ptr(it), __deref_value_ptr_int128_t(it) )


    cpdef tuple floor(self, key):

        cdef pair[node_ptr, node_ptr] bounds
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)
        bounds = self.__index.tree_search(c_key)

        if bounds.first == self.link():
            return (None, None)

        return ( self.__get_key_from_ptr(bounds.first),
                 __deref_value_ptr_int128_t(bounds.first) )


    cpdef tuple ceil(self, key):

        cdef pair[node_ptr, node_ptr] bounds
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)  
        bounds = self.__index.tree_search(c_key)

        if bounds.second == self.link():
            return (None, None)

        return ( self.__get_key_from_ptr(bounds.second),
                 __deref_value_ptr_int128_t(bounds.second) )


    cpdef __TreeSeries_int128_t truncate(self, start, stop):

        cdef iterator begin
        cdef iterator end
        cdef pair[node_ptr, node_ptr] bounds_start 
        cdef pair[node_ptr, node_ptr] bounds_end
        cdef __TreeSeries_int128_t result
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_start = c_pyobject(<PyObject*>start)
        cdef c_pyobject c_stop = c_pyobject(<PyObject*>stop)

        result = __TreeSeries_int128_t.__new__( __TreeSeries_int128_t, 
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        bounds_start = self.__index.tree_search(c_start)
        bounds_end = self.__index.tree_search(c_stop)
        begin = deref(bounds_start.second).it_position
        end = deref(bounds_end.first).it_position

        if( bounds_start.second == self.link() ) \
            or ( bounds_end.first == self.link() ):

            return result

        if self.__get_key_from_iter(begin) > self.__get_key_from_iter(end):
            return result

        while True:
            result.insert( self.__get_key_from_iter(begin), 
                           __deref_value_ptr_int128_t( deref(begin) ) )

            if begin == end:
                break

            inc(begin)

        return result


    cpdef __TreeSeries_int128_t periodic(self, start, stop, step):

        cdef __TreeSeries_int128_t result
        cdef object it = start
        result = __TreeSeries_int128_t.__new__( __TreeSeries_int128_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        self.on_itermode()

        while it < stop:
            result.insert( it, self.__getitem__(it) )
            it = it + step

        self.off_itermode()

        return result


    cpdef __TreeSeries_int128_t map( self, method, tuple args=(), 
                                  dict kwargs={} ):

        cdef __TreeSeries_int128_t result
        cdef tuple it

        result = __TreeSeries_int128_t.__new__( __TreeSeries_int128_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        for it in self.iteritems():
            result.insert( it[0], method(it[1], *args, **kwargs) )

        return result


    cpdef void map_inplace( self, method, tuple args=(), 
                            dict kwargs={} ) except*:

        cdef node_ptr it

        for it in deref(self.__index):
            __set_value_int128_t( it, method( __deref_value_ptr_int128_t(it),
                                             *args, **kwargs ) )


    cpdef void set_interpolation(self, str interpolate):

        cdef string c_interpolate = bytearray(interpolate, 'utf8')
        self.interpolate_type = interpolate
        self.__interpolate = __INTERPOLATE_int128_t[c_interpolate]


    cpdef void set_arithmetic(self, str arithmetic):

        if arithmetic == "union":
            self.__arithmetic = self.__arithmetic_union

        if arithmetic == "left":
            self.__arithmetic = self.__arithmetic_left


    cpdef void on_itermode(self):

        __BaseTreeSeries.on_itermode(self)
        self.__get_item = self.__get_item_linear


    cpdef void off_itermode(self):

        __BaseTreeSeries.off_itermode(self)
        self.__get_item = self.__get_item_tree


    cpdef void copy_data(self, other, str to_type="numerical") except*:

        cdef tuple it
        cdef int128_t num
        self.clear()

        if to_type == "numerical":

            for it in other.iteritems():
                num = <int128_t>float(it[1])
                self.insert( it[0], num )

        elif to_type == "str":
            for it in other.iteritems():
                self.insert( it[0], str(it[1]) )

        else:
            for it in other.iteritems():
                self.insert(it[0], it[1])


    cpdef void clear(self) except*:

        for key in self.keys():
            self.erase(key)

        self.__index.clear()

    #--------------------------------------------------------------------------------------------
    # Special methods
    #--------------------------------------------------------------------------------------------
    def __dealloc__(self):

        del self.__index


    def __str__(self):

        cdef str out_format = '{key}: {value}\n'
        cdef str result = 'Series object ' + self.name + '\n'
        cdef tuple it
        cdef list items = self.items()

        if len(items) <= 50: 
            for it in items:
                result += out_format.format(key=it[0], value=it[1])

        else:
            for it in items[:25]:
                result += out_format.format(key=it[0], value=it[1])

            result += '...\n'

            for it in items[-25:]:
                result += out_format.format(key=it[0], value=it[1])

        return result


    def __repr__(self):

        return self.__str__()

    
    def __iter__(self):

        cdef node_ptr it

        for it in deref(self.__index):
            yield __deref_value_ptr_int128_t(it)


    def __getitem__(self, key):

        cdef int32_t error = TYPE_ERROR
        cdef int128_t result

        if self.__index.size() == 0:
            return self.extrapolate

        if isinstance(key, slice):
            return self.__get_item_slice(key)

        result = self.__get_item(self, key, &error)

        if error == 0:
            return result

        if error == INT_KEY_ERROR:
            raise KeyError( str(key) )

        if error == TYPE_ERROR:
            raise TypeError("Inconsistent key type: " + key.__class__.__name__)


    def __setitem__(self, key, int128_t value):

        cdef c_pyobject key_holder = to_c_pyobject(key)
        cdef node_ptr node = self.__index.insert_search(key_holder)

        if self.__index.size() == 0:
            __insert_node_by_ptr_int128_t(self.__index, node, key, value)

        else:
            if self.__get_key_from_ptr(node) == key:
                __set_value_int128_t(node, value)

            else:
                __insert_node_by_ptr_int128_t(self.__index, node, key, value)


    #--------------------------------------------------------------------------------------------
    # Emulating numeric types
    #--------------------------------------------------------------------------------------------
    cpdef __TreeSeries_int128_t add(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __add_int128_t)


    cpdef __TreeSeries_int128_t sub(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __sub_int128_t)


    cpdef __TreeSeries_int128_t mul(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __mul_int128_t)


    cpdef __TreeSeries_int128_t div(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __div_int128_t)


    cpdef __TreeSeries_int128_t lshift(self, shift_param):

        if isinstance(shift_param, int):
            return self.__lshift_int(shift_param)


    cpdef __TreeSeries_int128_t rshift(self, shift_param):

        if isinstance(shift_param, int):
            return self.__rshift_int(shift_param)


    #--------------------------------------------------------------------------------------------
    # Private methods
    #--------------------------------------------------------------------------------------------
    cdef void __init_tree(self, index, values) except*:

        cdef tuple it

        if len(index) != len(values):
            raise IndexError("Index must have the same length as values.")

        for it in zip(index, values):
            self.insert(it[0], it[1])


    cdef void __init_tree_index(self, index) except*:

        for it in index:
            self.insert(it, 0)


    cdef int128_t __get_item_linear(self, key, int32_t* error):

        #bug? cython 0.28.3 translate c_key to const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key) 
        cdef pair[node_ptr, node_ptr] bounds
        
        bounds = self.__index.linear_search_from(self.__last_call, c_key)
        error[0] = 0

        if bounds.first != self.link():
            self.__last_call = bounds.first

        else:
            self.__last_call = deref( self.__index.begin() )

        return self.__interpolate( bounds.first, bounds.second, key,
                                   self.link(), self.extrapolate,
                                   error )

    
    cdef int128_t __get_item_tree(self, key, int32_t* error):

        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)
        cdef pair[node_ptr, node_ptr] bounds

        bounds = self.__index.tree_search(c_key)
        error[0] = 0

        return self.__interpolate( bounds.first, bounds.second, key,
                                   self.link(), self.extrapolate, 
                                   error )

    
    cdef __TreeSeries_int128_t __get_item_slice(self, key):

        cdef c_pyobject c_key
        cdef node_ptr begin = deref( self.__index.begin() ) 
        cdef node_ptr end = deref( self.__index.back() )
        cdef __TreeSeries_int128_t result
        result = __TreeSeries_int128_t.__new__( __TreeSeries_int128_t,
                                             interpolate=self.interpolate_type,
                                             arithmetic=self.arithmetic )

        if self.__len__() == 0:
            return result

        if key.start is not None and key.stop is not None:

            if key.start < self.begin() and key.stop < self.begin():
                return result

            if key.start > self.end() and key.stop < self.end():
                return result

        if key.start is not None:
            c_key = c_pyobject(<PyObject*>key.start)
            begin = self.__index.tree_search(c_key).second

        if key.stop is not None:
            c_key = c_pyobject(<PyObject*>key.stop)
            end = self.__index.tree_search(c_key).first

        if compare_func(end.key, begin.key) \
            and ( not equal(end.key, begin.key) ):

            return result

        if begin == self.link() or end == self.link():
            return result 

        if key.step is None:
            return self.truncate( self.__get_key_from_ptr(begin),
                                  self.__get_key_from_ptr(end) )

        else: 
            return self.periodic( self.__get_key_from_ptr(begin), 
                                  self.__get_key_from_ptr(end), 
                                  key.step )


    cdef __TreeSeries_int128_t __lshift_int(self, Py_ssize_t shift_param):

        cdef __TreeSeries_int128_t result
        cdef list delay_holder = []
        cdef tuple pair
        cdef Py_ssize_t count = 0
        cdef bool append_status = False

        result = __TreeSeries_int128_t.__new__( __TreeSeries_int128_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        if shift_param <= 0:
            raise ValueError("Shift parameter must be more than zero.")

        for pair in self.iteritems():
            
            if append_status:
                result.insert(delay_holder[0], pair[1])
                delay_holder.pop(0)

            if count == shift_param - 1:
                append_status = True

            delay_holder.append(pair[0])
            count += 1

        return result


    cdef __TreeSeries_int128_t __rshift_int(self, Py_ssize_t shift_param):

        cdef __TreeSeries_int128_t result
        cdef list delay_holder = []
        cdef tuple pair
        cdef Py_ssize_t count = 0
        cdef bool append_status = False

        result = __TreeSeries_int128_t.__new__( __TreeSeries_int128_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        if isinstance(shift_param, int):

            if shift_param <= 0:
                raise ValueError("Shift parameter must be more than zero.")

            for pair in self.iteritems():
                
                if append_status:
                    result.insert(pair[0], delay_holder[0])
                    delay_holder.pop(0)

                if count == shift_param - 1:
                    append_status = True

                delay_holder.append(pair[1])
                count += 1

            return result


    cdef __TreeSeries_int128_t __arithmetic_left( self, __BaseTreeSeries other, 
                                                 arithmetic_int128_t action ):

        cdef int128_t current
        cdef node_ptr node
        cdef object key
        cdef __TreeSeries_int128_t result
        result = __TreeSeries_int128_t.__new__( __TreeSeries_int128_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        other.on_itermode()

        for node in deref(self.__index):

            key = self.__get_key_from_ptr(node)
            current = action(__deref_value_ptr_int128_t(node), other[key])
            __insert_node_int128_t(result.__index, key, current)

        other.off_itermode()

        return result


    cdef __TreeSeries_int128_t __arithmetic_union( self, __BaseTreeSeries other, 
                                                  arithmetic_int128_t action ):

        cdef int128_t current
        cdef object key
        cdef trees_iterator[rb_tree, rb_node_valued] iterator
        cdef vector[rb_tree_ptr] trees = vector[rb_tree_ptr](2)
        cdef __TreeSeries_int128_t result
        result = __TreeSeries_int128_t.__new__( __TreeSeries_int128_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        
        trees[0] = self.__index
        trees[1] = other.get_tree()
        iterator.set_equal(equal_pair)
        iterator.set_compare(comp_pair)
        iterator.set_iterator(trees, "forward")

        self.on_itermode()
        other.on_itermode()

        while not iterator.empty():
            key = <object>deref(iterator).key.data
            current = action(self.__getitem__(key), other[key])
            __insert_node_int128_t(result.__index, key, current)
            pinc(iterator)

        self.off_itermode()
        other.off_itermode()

        return result
@cython.internal
cdef class __TreeSeries_float32_t(__BaseTreeSeries):

    cdef interpolate_float32_t     __interpolate
    cdef itermode_search_float32_t __get_item
    cdef arithmetic_type_float32_t __arithmetic

    cdef public   str  name
    cdef public   str  arithmetic
    cdef readonly str  interpolate_type
    cdef public   float32_t extrapolate


    def __init__(self):
        pass


    def __cinit__( self, object index=None, object values=None, str name="Untitled", 
                   str interpolate="floor", float32_t extrapolate=0, str arithmetic="left" ):

        self.set_interpolation(interpolate)
        self.extrapolate = extrapolate
        self.name = name
        self.arithmetic = arithmetic

        self.__get_item = self.__get_item_tree
        self.set_arithmetic(arithmetic)

        if index is None:
            
            if values is not None:
                raise IndexError("Index cannot be None while values are not.")

        else:
            if values is not None:
                self.__init_tree(index, values)

            else:
                self.__init_tree_index(index)


    #--------------------------------------------------------------------------------------------
    # Public methods
    #--------------------------------------------------------------------------------------------

    cpdef void insert(self, key, float32_t value) except*:

        __insert_node_float32_t(self.__index, key, value)


    cpdef void insert_range(self, list pairs) except*:

        cdef tuple pair

        for pair in pairs:
            __insert_node_float32_t(self.__index, pair[0], pair[1])


    cpdef void erase(self, key) except*:

        __erase_node_float32_t(self.__index, key)


    cpdef void erase_range(self, list keys) except*:

        cdef Py_ssize_t it

        for it in range( len(keys) ):
            __erase_node_float32_t(self.__index, keys[it])


    cpdef list values(self):

        cdef rb_node_valued* it
        cdef list result = []

        for it in deref(self.__index):
            result.append( __deref_value_ptr_float32_t(it) )

        return result


    cpdef list items(self):

        cdef node_ptr it
        cdef list result = []

        for it in deref(self.__index):
            result.append( ( self.__get_key_from_ptr(it), 
                             __deref_value_ptr_float32_t(it) ) )

        return result


    def iteritems(self):

        cdef node_ptr it

        for it in deref(self.__index):
            yield ( self.__get_key_from_ptr(it), __deref_value_ptr_float32_t(it) )


    cpdef tuple floor(self, key):

        cdef pair[node_ptr, node_ptr] bounds
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)
        bounds = self.__index.tree_search(c_key)

        if bounds.first == self.link():
            return (None, None)

        return ( self.__get_key_from_ptr(bounds.first),
                 __deref_value_ptr_float32_t(bounds.first) )


    cpdef tuple ceil(self, key):

        cdef pair[node_ptr, node_ptr] bounds
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)  
        bounds = self.__index.tree_search(c_key)

        if bounds.second == self.link():
            return (None, None)

        return ( self.__get_key_from_ptr(bounds.second),
                 __deref_value_ptr_float32_t(bounds.second) )


    cpdef __TreeSeries_float32_t truncate(self, start, stop):

        cdef iterator begin
        cdef iterator end
        cdef pair[node_ptr, node_ptr] bounds_start 
        cdef pair[node_ptr, node_ptr] bounds_end
        cdef __TreeSeries_float32_t result
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_start = c_pyobject(<PyObject*>start)
        cdef c_pyobject c_stop = c_pyobject(<PyObject*>stop)

        result = __TreeSeries_float32_t.__new__( __TreeSeries_float32_t, 
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        bounds_start = self.__index.tree_search(c_start)
        bounds_end = self.__index.tree_search(c_stop)
        begin = deref(bounds_start.second).it_position
        end = deref(bounds_end.first).it_position

        if( bounds_start.second == self.link() ) \
            or ( bounds_end.first == self.link() ):

            return result

        if self.__get_key_from_iter(begin) > self.__get_key_from_iter(end):
            return result

        while True:
            result.insert( self.__get_key_from_iter(begin), 
                           __deref_value_ptr_float32_t( deref(begin) ) )

            if begin == end:
                break

            inc(begin)

        return result


    cpdef __TreeSeries_float32_t periodic(self, start, stop, step):

        cdef __TreeSeries_float32_t result
        cdef object it = start
        result = __TreeSeries_float32_t.__new__( __TreeSeries_float32_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        self.on_itermode()

        while it < stop:
            result.insert( it, self.__getitem__(it) )
            it = it + step

        self.off_itermode()

        return result


    cpdef __TreeSeries_float32_t map( self, method, tuple args=(), 
                                  dict kwargs={} ):

        cdef __TreeSeries_float32_t result
        cdef tuple it

        result = __TreeSeries_float32_t.__new__( __TreeSeries_float32_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        for it in self.iteritems():
            result.insert( it[0], method(it[1], *args, **kwargs) )

        return result


    cpdef void map_inplace( self, method, tuple args=(), 
                            dict kwargs={} ) except*:

        cdef node_ptr it

        for it in deref(self.__index):
            __set_value_float32_t( it, method( __deref_value_ptr_float32_t(it),
                                             *args, **kwargs ) )


    cpdef void set_interpolation(self, str interpolate):

        cdef string c_interpolate = bytearray(interpolate, 'utf8')
        self.interpolate_type = interpolate
        self.__interpolate = __INTERPOLATE_float32_t[c_interpolate]


    cpdef void set_arithmetic(self, str arithmetic):

        if arithmetic == "union":
            self.__arithmetic = self.__arithmetic_union

        if arithmetic == "left":
            self.__arithmetic = self.__arithmetic_left


    cpdef void on_itermode(self):

        __BaseTreeSeries.on_itermode(self)
        self.__get_item = self.__get_item_linear


    cpdef void off_itermode(self):

        __BaseTreeSeries.off_itermode(self)
        self.__get_item = self.__get_item_tree


    cpdef void copy_data(self, other, str to_type="numerical") except*:

        cdef tuple it
        cdef float32_t num
        self.clear()

        if to_type == "numerical":

            for it in other.iteritems():
                num = <float32_t>float(it[1])
                self.insert( it[0], num )

        elif to_type == "str":
            for it in other.iteritems():
                self.insert( it[0], str(it[1]) )

        else:
            for it in other.iteritems():
                self.insert(it[0], it[1])


    cpdef void clear(self) except*:

        for key in self.keys():
            self.erase(key)

        self.__index.clear()

    #--------------------------------------------------------------------------------------------
    # Special methods
    #--------------------------------------------------------------------------------------------
    def __dealloc__(self):

        del self.__index


    def __str__(self):

        cdef str out_format = '{key}: {value}\n'
        cdef str result = 'Series object ' + self.name + '\n'
        cdef tuple it
        cdef list items = self.items()

        if len(items) <= 50: 
            for it in items:
                result += out_format.format(key=it[0], value=it[1])

        else:
            for it in items[:25]:
                result += out_format.format(key=it[0], value=it[1])

            result += '...\n'

            for it in items[-25:]:
                result += out_format.format(key=it[0], value=it[1])

        return result


    def __repr__(self):

        return self.__str__()

    
    def __iter__(self):

        cdef node_ptr it

        for it in deref(self.__index):
            yield __deref_value_ptr_float32_t(it)


    def __getitem__(self, key):

        cdef int32_t error = TYPE_ERROR
        cdef float32_t result

        if self.__index.size() == 0:
            return self.extrapolate

        if isinstance(key, slice):
            return self.__get_item_slice(key)

        result = self.__get_item(self, key, &error)

        if error == 0:
            return result

        if error == INT_KEY_ERROR:
            raise KeyError( str(key) )

        if error == TYPE_ERROR:
            raise TypeError("Inconsistent key type: " + key.__class__.__name__)


    def __setitem__(self, key, float32_t value):

        cdef c_pyobject key_holder = to_c_pyobject(key)
        cdef node_ptr node = self.__index.insert_search(key_holder)

        if self.__index.size() == 0:
            __insert_node_by_ptr_float32_t(self.__index, node, key, value)

        else:
            if self.__get_key_from_ptr(node) == key:
                __set_value_float32_t(node, value)

            else:
                __insert_node_by_ptr_float32_t(self.__index, node, key, value)


    #--------------------------------------------------------------------------------------------
    # Emulating numeric types
    #--------------------------------------------------------------------------------------------
    cpdef __TreeSeries_float32_t add(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __add_float32_t)


    cpdef __TreeSeries_float32_t sub(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __sub_float32_t)


    cpdef __TreeSeries_float32_t mul(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __mul_float32_t)


    cpdef __TreeSeries_float32_t div(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __div_float32_t)


    cpdef __TreeSeries_float32_t lshift(self, shift_param):

        if isinstance(shift_param, int):
            return self.__lshift_int(shift_param)


    cpdef __TreeSeries_float32_t rshift(self, shift_param):

        if isinstance(shift_param, int):
            return self.__rshift_int(shift_param)


    #--------------------------------------------------------------------------------------------
    # Private methods
    #--------------------------------------------------------------------------------------------
    cdef void __init_tree(self, index, values) except*:

        cdef tuple it

        if len(index) != len(values):
            raise IndexError("Index must have the same length as values.")

        for it in zip(index, values):
            self.insert(it[0], it[1])


    cdef void __init_tree_index(self, index) except*:

        for it in index:
            self.insert(it, 0)


    cdef float32_t __get_item_linear(self, key, int32_t* error):

        #bug? cython 0.28.3 translate c_key to const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key) 
        cdef pair[node_ptr, node_ptr] bounds
        
        bounds = self.__index.linear_search_from(self.__last_call, c_key)
        error[0] = 0

        if bounds.first != self.link():
            self.__last_call = bounds.first

        else:
            self.__last_call = deref( self.__index.begin() )

        return self.__interpolate( bounds.first, bounds.second, key,
                                   self.link(), self.extrapolate,
                                   error )

    
    cdef float32_t __get_item_tree(self, key, int32_t* error):

        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)
        cdef pair[node_ptr, node_ptr] bounds

        bounds = self.__index.tree_search(c_key)
        error[0] = 0

        return self.__interpolate( bounds.first, bounds.second, key,
                                   self.link(), self.extrapolate, 
                                   error )

    
    cdef __TreeSeries_float32_t __get_item_slice(self, key):

        cdef c_pyobject c_key
        cdef node_ptr begin = deref( self.__index.begin() ) 
        cdef node_ptr end = deref( self.__index.back() )
        cdef __TreeSeries_float32_t result
        result = __TreeSeries_float32_t.__new__( __TreeSeries_float32_t,
                                             interpolate=self.interpolate_type,
                                             arithmetic=self.arithmetic )

        if self.__len__() == 0:
            return result

        if key.start is not None and key.stop is not None:

            if key.start < self.begin() and key.stop < self.begin():
                return result

            if key.start > self.end() and key.stop < self.end():
                return result

        if key.start is not None:
            c_key = c_pyobject(<PyObject*>key.start)
            begin = self.__index.tree_search(c_key).second

        if key.stop is not None:
            c_key = c_pyobject(<PyObject*>key.stop)
            end = self.__index.tree_search(c_key).first

        if compare_func(end.key, begin.key) \
            and ( not equal(end.key, begin.key) ):

            return result

        if begin == self.link() or end == self.link():
            return result 

        if key.step is None:
            return self.truncate( self.__get_key_from_ptr(begin),
                                  self.__get_key_from_ptr(end) )

        else: 
            return self.periodic( self.__get_key_from_ptr(begin), 
                                  self.__get_key_from_ptr(end), 
                                  key.step )


    cdef __TreeSeries_float32_t __lshift_int(self, Py_ssize_t shift_param):

        cdef __TreeSeries_float32_t result
        cdef list delay_holder = []
        cdef tuple pair
        cdef Py_ssize_t count = 0
        cdef bool append_status = False

        result = __TreeSeries_float32_t.__new__( __TreeSeries_float32_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        if shift_param <= 0:
            raise ValueError("Shift parameter must be more than zero.")

        for pair in self.iteritems():
            
            if append_status:
                result.insert(delay_holder[0], pair[1])
                delay_holder.pop(0)

            if count == shift_param - 1:
                append_status = True

            delay_holder.append(pair[0])
            count += 1

        return result


    cdef __TreeSeries_float32_t __rshift_int(self, Py_ssize_t shift_param):

        cdef __TreeSeries_float32_t result
        cdef list delay_holder = []
        cdef tuple pair
        cdef Py_ssize_t count = 0
        cdef bool append_status = False

        result = __TreeSeries_float32_t.__new__( __TreeSeries_float32_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        if isinstance(shift_param, int):

            if shift_param <= 0:
                raise ValueError("Shift parameter must be more than zero.")

            for pair in self.iteritems():
                
                if append_status:
                    result.insert(pair[0], delay_holder[0])
                    delay_holder.pop(0)

                if count == shift_param - 1:
                    append_status = True

                delay_holder.append(pair[1])
                count += 1

            return result


    cdef __TreeSeries_float32_t __arithmetic_left( self, __BaseTreeSeries other, 
                                                 arithmetic_float32_t action ):

        cdef float32_t current
        cdef node_ptr node
        cdef object key
        cdef __TreeSeries_float32_t result
        result = __TreeSeries_float32_t.__new__( __TreeSeries_float32_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        other.on_itermode()

        for node in deref(self.__index):

            key = self.__get_key_from_ptr(node)
            current = action(__deref_value_ptr_float32_t(node), other[key])
            __insert_node_float32_t(result.__index, key, current)

        other.off_itermode()

        return result


    cdef __TreeSeries_float32_t __arithmetic_union( self, __BaseTreeSeries other, 
                                                  arithmetic_float32_t action ):

        cdef float32_t current
        cdef object key
        cdef trees_iterator[rb_tree, rb_node_valued] iterator
        cdef vector[rb_tree_ptr] trees = vector[rb_tree_ptr](2)
        cdef __TreeSeries_float32_t result
        result = __TreeSeries_float32_t.__new__( __TreeSeries_float32_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        
        trees[0] = self.__index
        trees[1] = other.get_tree()
        iterator.set_equal(equal_pair)
        iterator.set_compare(comp_pair)
        iterator.set_iterator(trees, "forward")

        self.on_itermode()
        other.on_itermode()

        while not iterator.empty():
            key = <object>deref(iterator).key.data
            current = action(self.__getitem__(key), other[key])
            __insert_node_float32_t(result.__index, key, current)
            pinc(iterator)

        self.off_itermode()
        other.off_itermode()

        return result
@cython.internal
cdef class __TreeSeries_float64_t(__BaseTreeSeries):

    cdef interpolate_float64_t     __interpolate
    cdef itermode_search_float64_t __get_item
    cdef arithmetic_type_float64_t __arithmetic

    cdef public   str  name
    cdef public   str  arithmetic
    cdef readonly str  interpolate_type
    cdef public   float64_t extrapolate


    def __init__(self):
        pass


    def __cinit__( self, object index=None, object values=None, str name="Untitled", 
                   str interpolate="floor", float64_t extrapolate=0, str arithmetic="left" ):

        self.set_interpolation(interpolate)
        self.extrapolate = extrapolate
        self.name = name
        self.arithmetic = arithmetic

        self.__get_item = self.__get_item_tree
        self.set_arithmetic(arithmetic)

        if index is None:
            
            if values is not None:
                raise IndexError("Index cannot be None while values are not.")

        else:
            if values is not None:
                self.__init_tree(index, values)

            else:
                self.__init_tree_index(index)


    #--------------------------------------------------------------------------------------------
    # Public methods
    #--------------------------------------------------------------------------------------------

    cpdef void insert(self, key, float64_t value) except*:

        __insert_node_float64_t(self.__index, key, value)


    cpdef void insert_range(self, list pairs) except*:

        cdef tuple pair

        for pair in pairs:
            __insert_node_float64_t(self.__index, pair[0], pair[1])


    cpdef void erase(self, key) except*:

        __erase_node_float64_t(self.__index, key)


    cpdef void erase_range(self, list keys) except*:

        cdef Py_ssize_t it

        for it in range( len(keys) ):
            __erase_node_float64_t(self.__index, keys[it])


    cpdef list values(self):

        cdef rb_node_valued* it
        cdef list result = []

        for it in deref(self.__index):
            result.append( __deref_value_ptr_float64_t(it) )

        return result


    cpdef list items(self):

        cdef node_ptr it
        cdef list result = []

        for it in deref(self.__index):
            result.append( ( self.__get_key_from_ptr(it), 
                             __deref_value_ptr_float64_t(it) ) )

        return result


    def iteritems(self):

        cdef node_ptr it

        for it in deref(self.__index):
            yield ( self.__get_key_from_ptr(it), __deref_value_ptr_float64_t(it) )


    cpdef tuple floor(self, key):

        cdef pair[node_ptr, node_ptr] bounds
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)
        bounds = self.__index.tree_search(c_key)

        if bounds.first == self.link():
            return (None, None)

        return ( self.__get_key_from_ptr(bounds.first),
                 __deref_value_ptr_float64_t(bounds.first) )


    cpdef tuple ceil(self, key):

        cdef pair[node_ptr, node_ptr] bounds
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)  
        bounds = self.__index.tree_search(c_key)

        if bounds.second == self.link():
            return (None, None)

        return ( self.__get_key_from_ptr(bounds.second),
                 __deref_value_ptr_float64_t(bounds.second) )


    cpdef __TreeSeries_float64_t truncate(self, start, stop):

        cdef iterator begin
        cdef iterator end
        cdef pair[node_ptr, node_ptr] bounds_start 
        cdef pair[node_ptr, node_ptr] bounds_end
        cdef __TreeSeries_float64_t result
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_start = c_pyobject(<PyObject*>start)
        cdef c_pyobject c_stop = c_pyobject(<PyObject*>stop)

        result = __TreeSeries_float64_t.__new__( __TreeSeries_float64_t, 
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        bounds_start = self.__index.tree_search(c_start)
        bounds_end = self.__index.tree_search(c_stop)
        begin = deref(bounds_start.second).it_position
        end = deref(bounds_end.first).it_position

        if( bounds_start.second == self.link() ) \
            or ( bounds_end.first == self.link() ):

            return result

        if self.__get_key_from_iter(begin) > self.__get_key_from_iter(end):
            return result

        while True:
            result.insert( self.__get_key_from_iter(begin), 
                           __deref_value_ptr_float64_t( deref(begin) ) )

            if begin == end:
                break

            inc(begin)

        return result


    cpdef __TreeSeries_float64_t periodic(self, start, stop, step):

        cdef __TreeSeries_float64_t result
        cdef object it = start
        result = __TreeSeries_float64_t.__new__( __TreeSeries_float64_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        self.on_itermode()

        while it < stop:
            result.insert( it, self.__getitem__(it) )
            it = it + step

        self.off_itermode()

        return result


    cpdef __TreeSeries_float64_t map( self, method, tuple args=(), 
                                  dict kwargs={} ):

        cdef __TreeSeries_float64_t result
        cdef tuple it

        result = __TreeSeries_float64_t.__new__( __TreeSeries_float64_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        for it in self.iteritems():
            result.insert( it[0], method(it[1], *args, **kwargs) )

        return result


    cpdef void map_inplace( self, method, tuple args=(), 
                            dict kwargs={} ) except*:

        cdef node_ptr it

        for it in deref(self.__index):
            __set_value_float64_t( it, method( __deref_value_ptr_float64_t(it),
                                             *args, **kwargs ) )


    cpdef void set_interpolation(self, str interpolate):

        cdef string c_interpolate = bytearray(interpolate, 'utf8')
        self.interpolate_type = interpolate
        self.__interpolate = __INTERPOLATE_float64_t[c_interpolate]


    cpdef void set_arithmetic(self, str arithmetic):

        if arithmetic == "union":
            self.__arithmetic = self.__arithmetic_union

        if arithmetic == "left":
            self.__arithmetic = self.__arithmetic_left


    cpdef void on_itermode(self):

        __BaseTreeSeries.on_itermode(self)
        self.__get_item = self.__get_item_linear


    cpdef void off_itermode(self):

        __BaseTreeSeries.off_itermode(self)
        self.__get_item = self.__get_item_tree


    cpdef void copy_data(self, other, str to_type="numerical") except*:

        cdef tuple it
        cdef float64_t num
        self.clear()

        if to_type == "numerical":

            for it in other.iteritems():
                num = <float64_t>float(it[1])
                self.insert( it[0], num )

        elif to_type == "str":
            for it in other.iteritems():
                self.insert( it[0], str(it[1]) )

        else:
            for it in other.iteritems():
                self.insert(it[0], it[1])


    cpdef void clear(self) except*:

        for key in self.keys():
            self.erase(key)

        self.__index.clear()

    #--------------------------------------------------------------------------------------------
    # Special methods
    #--------------------------------------------------------------------------------------------
    def __dealloc__(self):

        del self.__index


    def __str__(self):

        cdef str out_format = '{key}: {value}\n'
        cdef str result = 'Series object ' + self.name + '\n'
        cdef tuple it
        cdef list items = self.items()

        if len(items) <= 50: 
            for it in items:
                result += out_format.format(key=it[0], value=it[1])

        else:
            for it in items[:25]:
                result += out_format.format(key=it[0], value=it[1])

            result += '...\n'

            for it in items[-25:]:
                result += out_format.format(key=it[0], value=it[1])

        return result


    def __repr__(self):

        return self.__str__()

    
    def __iter__(self):

        cdef node_ptr it

        for it in deref(self.__index):
            yield __deref_value_ptr_float64_t(it)


    def __getitem__(self, key):

        cdef int32_t error = TYPE_ERROR
        cdef float64_t result

        if self.__index.size() == 0:
            return self.extrapolate

        if isinstance(key, slice):
            return self.__get_item_slice(key)

        result = self.__get_item(self, key, &error)

        if error == 0:
            return result

        if error == INT_KEY_ERROR:
            raise KeyError( str(key) )

        if error == TYPE_ERROR:
            raise TypeError("Inconsistent key type: " + key.__class__.__name__)


    def __setitem__(self, key, float64_t value):

        cdef c_pyobject key_holder = to_c_pyobject(key)
        cdef node_ptr node = self.__index.insert_search(key_holder)

        if self.__index.size() == 0:
            __insert_node_by_ptr_float64_t(self.__index, node, key, value)

        else:
            if self.__get_key_from_ptr(node) == key:
                __set_value_float64_t(node, value)

            else:
                __insert_node_by_ptr_float64_t(self.__index, node, key, value)


    #--------------------------------------------------------------------------------------------
    # Emulating numeric types
    #--------------------------------------------------------------------------------------------
    cpdef __TreeSeries_float64_t add(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __add_float64_t)


    cpdef __TreeSeries_float64_t sub(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __sub_float64_t)


    cpdef __TreeSeries_float64_t mul(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __mul_float64_t)


    cpdef __TreeSeries_float64_t div(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __div_float64_t)


    cpdef __TreeSeries_float64_t lshift(self, shift_param):

        if isinstance(shift_param, int):
            return self.__lshift_int(shift_param)


    cpdef __TreeSeries_float64_t rshift(self, shift_param):

        if isinstance(shift_param, int):
            return self.__rshift_int(shift_param)


    #--------------------------------------------------------------------------------------------
    # Private methods
    #--------------------------------------------------------------------------------------------
    cdef void __init_tree(self, index, values) except*:

        cdef tuple it

        if len(index) != len(values):
            raise IndexError("Index must have the same length as values.")

        for it in zip(index, values):
            self.insert(it[0], it[1])


    cdef void __init_tree_index(self, index) except*:

        for it in index:
            self.insert(it, 0)


    cdef float64_t __get_item_linear(self, key, int32_t* error):

        #bug? cython 0.28.3 translate c_key to const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key) 
        cdef pair[node_ptr, node_ptr] bounds
        
        bounds = self.__index.linear_search_from(self.__last_call, c_key)
        error[0] = 0

        if bounds.first != self.link():
            self.__last_call = bounds.first

        else:
            self.__last_call = deref( self.__index.begin() )

        return self.__interpolate( bounds.first, bounds.second, key,
                                   self.link(), self.extrapolate,
                                   error )

    
    cdef float64_t __get_item_tree(self, key, int32_t* error):

        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)
        cdef pair[node_ptr, node_ptr] bounds

        bounds = self.__index.tree_search(c_key)
        error[0] = 0

        return self.__interpolate( bounds.first, bounds.second, key,
                                   self.link(), self.extrapolate, 
                                   error )

    
    cdef __TreeSeries_float64_t __get_item_slice(self, key):

        cdef c_pyobject c_key
        cdef node_ptr begin = deref( self.__index.begin() ) 
        cdef node_ptr end = deref( self.__index.back() )
        cdef __TreeSeries_float64_t result
        result = __TreeSeries_float64_t.__new__( __TreeSeries_float64_t,
                                             interpolate=self.interpolate_type,
                                             arithmetic=self.arithmetic )

        if self.__len__() == 0:
            return result

        if key.start is not None and key.stop is not None:

            if key.start < self.begin() and key.stop < self.begin():
                return result

            if key.start > self.end() and key.stop < self.end():
                return result

        if key.start is not None:
            c_key = c_pyobject(<PyObject*>key.start)
            begin = self.__index.tree_search(c_key).second

        if key.stop is not None:
            c_key = c_pyobject(<PyObject*>key.stop)
            end = self.__index.tree_search(c_key).first

        if compare_func(end.key, begin.key) \
            and ( not equal(end.key, begin.key) ):

            return result

        if begin == self.link() or end == self.link():
            return result 

        if key.step is None:
            return self.truncate( self.__get_key_from_ptr(begin),
                                  self.__get_key_from_ptr(end) )

        else: 
            return self.periodic( self.__get_key_from_ptr(begin), 
                                  self.__get_key_from_ptr(end), 
                                  key.step )


    cdef __TreeSeries_float64_t __lshift_int(self, Py_ssize_t shift_param):

        cdef __TreeSeries_float64_t result
        cdef list delay_holder = []
        cdef tuple pair
        cdef Py_ssize_t count = 0
        cdef bool append_status = False

        result = __TreeSeries_float64_t.__new__( __TreeSeries_float64_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        if shift_param <= 0:
            raise ValueError("Shift parameter must be more than zero.")

        for pair in self.iteritems():
            
            if append_status:
                result.insert(delay_holder[0], pair[1])
                delay_holder.pop(0)

            if count == shift_param - 1:
                append_status = True

            delay_holder.append(pair[0])
            count += 1

        return result


    cdef __TreeSeries_float64_t __rshift_int(self, Py_ssize_t shift_param):

        cdef __TreeSeries_float64_t result
        cdef list delay_holder = []
        cdef tuple pair
        cdef Py_ssize_t count = 0
        cdef bool append_status = False

        result = __TreeSeries_float64_t.__new__( __TreeSeries_float64_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        if isinstance(shift_param, int):

            if shift_param <= 0:
                raise ValueError("Shift parameter must be more than zero.")

            for pair in self.iteritems():
                
                if append_status:
                    result.insert(pair[0], delay_holder[0])
                    delay_holder.pop(0)

                if count == shift_param - 1:
                    append_status = True

                delay_holder.append(pair[1])
                count += 1

            return result


    cdef __TreeSeries_float64_t __arithmetic_left( self, __BaseTreeSeries other, 
                                                 arithmetic_float64_t action ):

        cdef float64_t current
        cdef node_ptr node
        cdef object key
        cdef __TreeSeries_float64_t result
        result = __TreeSeries_float64_t.__new__( __TreeSeries_float64_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        other.on_itermode()

        for node in deref(self.__index):

            key = self.__get_key_from_ptr(node)
            current = action(__deref_value_ptr_float64_t(node), other[key])
            __insert_node_float64_t(result.__index, key, current)

        other.off_itermode()

        return result


    cdef __TreeSeries_float64_t __arithmetic_union( self, __BaseTreeSeries other, 
                                                  arithmetic_float64_t action ):

        cdef float64_t current
        cdef object key
        cdef trees_iterator[rb_tree, rb_node_valued] iterator
        cdef vector[rb_tree_ptr] trees = vector[rb_tree_ptr](2)
        cdef __TreeSeries_float64_t result
        result = __TreeSeries_float64_t.__new__( __TreeSeries_float64_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        
        trees[0] = self.__index
        trees[1] = other.get_tree()
        iterator.set_equal(equal_pair)
        iterator.set_compare(comp_pair)
        iterator.set_iterator(trees, "forward")

        self.on_itermode()
        other.on_itermode()

        while not iterator.empty():
            key = <object>deref(iterator).key.data
            current = action(self.__getitem__(key), other[key])
            __insert_node_float64_t(result.__index, key, current)
            pinc(iterator)

        self.off_itermode()
        other.off_itermode()

        return result
@cython.internal
cdef class __TreeSeries_float80_t(__BaseTreeSeries):

    cdef interpolate_float80_t     __interpolate
    cdef itermode_search_float80_t __get_item
    cdef arithmetic_type_float80_t __arithmetic

    cdef public   str  name
    cdef public   str  arithmetic
    cdef readonly str  interpolate_type
    cdef public   float80_t extrapolate


    def __init__(self):
        pass


    def __cinit__( self, object index=None, object values=None, str name="Untitled", 
                   str interpolate="floor", float80_t extrapolate=0, str arithmetic="left" ):

        self.set_interpolation(interpolate)
        self.extrapolate = extrapolate
        self.name = name
        self.arithmetic = arithmetic

        self.__get_item = self.__get_item_tree
        self.set_arithmetic(arithmetic)

        if index is None:
            
            if values is not None:
                raise IndexError("Index cannot be None while values are not.")

        else:
            if values is not None:
                self.__init_tree(index, values)

            else:
                self.__init_tree_index(index)


    #--------------------------------------------------------------------------------------------
    # Public methods
    #--------------------------------------------------------------------------------------------

    cpdef void insert(self, key, float80_t value) except*:

        __insert_node_float80_t(self.__index, key, value)


    cpdef void insert_range(self, list pairs) except*:

        cdef tuple pair

        for pair in pairs:
            __insert_node_float80_t(self.__index, pair[0], pair[1])


    cpdef void erase(self, key) except*:

        __erase_node_float80_t(self.__index, key)


    cpdef void erase_range(self, list keys) except*:

        cdef Py_ssize_t it

        for it in range( len(keys) ):
            __erase_node_float80_t(self.__index, keys[it])


    cpdef list values(self):

        cdef rb_node_valued* it
        cdef list result = []

        for it in deref(self.__index):
            result.append( __deref_value_ptr_float80_t(it) )

        return result


    cpdef list items(self):

        cdef node_ptr it
        cdef list result = []

        for it in deref(self.__index):
            result.append( ( self.__get_key_from_ptr(it), 
                             __deref_value_ptr_float80_t(it) ) )

        return result


    def iteritems(self):

        cdef node_ptr it

        for it in deref(self.__index):
            yield ( self.__get_key_from_ptr(it), __deref_value_ptr_float80_t(it) )


    cpdef tuple floor(self, key):

        cdef pair[node_ptr, node_ptr] bounds
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)
        bounds = self.__index.tree_search(c_key)

        if bounds.first == self.link():
            return (None, None)

        return ( self.__get_key_from_ptr(bounds.first),
                 __deref_value_ptr_float80_t(bounds.first) )


    cpdef tuple ceil(self, key):

        cdef pair[node_ptr, node_ptr] bounds
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)  
        bounds = self.__index.tree_search(c_key)

        if bounds.second == self.link():
            return (None, None)

        return ( self.__get_key_from_ptr(bounds.second),
                 __deref_value_ptr_float80_t(bounds.second) )


    cpdef __TreeSeries_float80_t truncate(self, start, stop):

        cdef iterator begin
        cdef iterator end
        cdef pair[node_ptr, node_ptr] bounds_start 
        cdef pair[node_ptr, node_ptr] bounds_end
        cdef __TreeSeries_float80_t result
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_start = c_pyobject(<PyObject*>start)
        cdef c_pyobject c_stop = c_pyobject(<PyObject*>stop)

        result = __TreeSeries_float80_t.__new__( __TreeSeries_float80_t, 
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        bounds_start = self.__index.tree_search(c_start)
        bounds_end = self.__index.tree_search(c_stop)
        begin = deref(bounds_start.second).it_position
        end = deref(bounds_end.first).it_position

        if( bounds_start.second == self.link() ) \
            or ( bounds_end.first == self.link() ):

            return result

        if self.__get_key_from_iter(begin) > self.__get_key_from_iter(end):
            return result

        while True:
            result.insert( self.__get_key_from_iter(begin), 
                           __deref_value_ptr_float80_t( deref(begin) ) )

            if begin == end:
                break

            inc(begin)

        return result


    cpdef __TreeSeries_float80_t periodic(self, start, stop, step):

        cdef __TreeSeries_float80_t result
        cdef object it = start
        result = __TreeSeries_float80_t.__new__( __TreeSeries_float80_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        self.on_itermode()

        while it < stop:
            result.insert( it, self.__getitem__(it) )
            it = it + step

        self.off_itermode()

        return result


    cpdef __TreeSeries_float80_t map( self, method, tuple args=(), 
                                  dict kwargs={} ):

        cdef __TreeSeries_float80_t result
        cdef tuple it

        result = __TreeSeries_float80_t.__new__( __TreeSeries_float80_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        for it in self.iteritems():
            result.insert( it[0], method(it[1], *args, **kwargs) )

        return result


    cpdef void map_inplace( self, method, tuple args=(), 
                            dict kwargs={} ) except*:

        cdef node_ptr it

        for it in deref(self.__index):
            __set_value_float80_t( it, method( __deref_value_ptr_float80_t(it),
                                             *args, **kwargs ) )


    cpdef void set_interpolation(self, str interpolate):

        cdef string c_interpolate = bytearray(interpolate, 'utf8')
        self.interpolate_type = interpolate
        self.__interpolate = __INTERPOLATE_float80_t[c_interpolate]


    cpdef void set_arithmetic(self, str arithmetic):

        if arithmetic == "union":
            self.__arithmetic = self.__arithmetic_union

        if arithmetic == "left":
            self.__arithmetic = self.__arithmetic_left


    cpdef void on_itermode(self):

        __BaseTreeSeries.on_itermode(self)
        self.__get_item = self.__get_item_linear


    cpdef void off_itermode(self):

        __BaseTreeSeries.off_itermode(self)
        self.__get_item = self.__get_item_tree


    cpdef void copy_data(self, other, str to_type="numerical") except*:

        cdef tuple it
        cdef float80_t num
        self.clear()

        if to_type == "numerical":

            for it in other.iteritems():
                num = <float80_t>float(it[1])
                self.insert( it[0], num )

        elif to_type == "str":
            for it in other.iteritems():
                self.insert( it[0], str(it[1]) )

        else:
            for it in other.iteritems():
                self.insert(it[0], it[1])


    cpdef void clear(self) except*:

        for key in self.keys():
            self.erase(key)

        self.__index.clear()

    #--------------------------------------------------------------------------------------------
    # Special methods
    #--------------------------------------------------------------------------------------------
    def __dealloc__(self):

        del self.__index


    def __str__(self):

        cdef str out_format = '{key}: {value}\n'
        cdef str result = 'Series object ' + self.name + '\n'
        cdef tuple it
        cdef list items = self.items()

        if len(items) <= 50: 
            for it in items:
                result += out_format.format(key=it[0], value=it[1])

        else:
            for it in items[:25]:
                result += out_format.format(key=it[0], value=it[1])

            result += '...\n'

            for it in items[-25:]:
                result += out_format.format(key=it[0], value=it[1])

        return result


    def __repr__(self):

        return self.__str__()

    
    def __iter__(self):

        cdef node_ptr it

        for it in deref(self.__index):
            yield __deref_value_ptr_float80_t(it)


    def __getitem__(self, key):

        cdef int32_t error = TYPE_ERROR
        cdef float80_t result

        if self.__index.size() == 0:
            return self.extrapolate

        if isinstance(key, slice):
            return self.__get_item_slice(key)

        result = self.__get_item(self, key, &error)

        if error == 0:
            return result

        if error == INT_KEY_ERROR:
            raise KeyError( str(key) )

        if error == TYPE_ERROR:
            raise TypeError("Inconsistent key type: " + key.__class__.__name__)


    def __setitem__(self, key, float80_t value):

        cdef c_pyobject key_holder = to_c_pyobject(key)
        cdef node_ptr node = self.__index.insert_search(key_holder)

        if self.__index.size() == 0:
            __insert_node_by_ptr_float80_t(self.__index, node, key, value)

        else:
            if self.__get_key_from_ptr(node) == key:
                __set_value_float80_t(node, value)

            else:
                __insert_node_by_ptr_float80_t(self.__index, node, key, value)


    #--------------------------------------------------------------------------------------------
    # Emulating numeric types
    #--------------------------------------------------------------------------------------------
    cpdef __TreeSeries_float80_t add(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __add_float80_t)


    cpdef __TreeSeries_float80_t sub(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __sub_float80_t)


    cpdef __TreeSeries_float80_t mul(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __mul_float80_t)


    cpdef __TreeSeries_float80_t div(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __div_float80_t)


    cpdef __TreeSeries_float80_t lshift(self, shift_param):

        if isinstance(shift_param, int):
            return self.__lshift_int(shift_param)


    cpdef __TreeSeries_float80_t rshift(self, shift_param):

        if isinstance(shift_param, int):
            return self.__rshift_int(shift_param)


    #--------------------------------------------------------------------------------------------
    # Private methods
    #--------------------------------------------------------------------------------------------
    cdef void __init_tree(self, index, values) except*:

        cdef tuple it

        if len(index) != len(values):
            raise IndexError("Index must have the same length as values.")

        for it in zip(index, values):
            self.insert(it[0], it[1])


    cdef void __init_tree_index(self, index) except*:

        for it in index:
            self.insert(it, 0)


    cdef float80_t __get_item_linear(self, key, int32_t* error):

        #bug? cython 0.28.3 translate c_key to const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key) 
        cdef pair[node_ptr, node_ptr] bounds
        
        bounds = self.__index.linear_search_from(self.__last_call, c_key)
        error[0] = 0

        if bounds.first != self.link():
            self.__last_call = bounds.first

        else:
            self.__last_call = deref( self.__index.begin() )

        return self.__interpolate( bounds.first, bounds.second, key,
                                   self.link(), self.extrapolate,
                                   error )

    
    cdef float80_t __get_item_tree(self, key, int32_t* error):

        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)
        cdef pair[node_ptr, node_ptr] bounds

        bounds = self.__index.tree_search(c_key)
        error[0] = 0

        return self.__interpolate( bounds.first, bounds.second, key,
                                   self.link(), self.extrapolate, 
                                   error )

    
    cdef __TreeSeries_float80_t __get_item_slice(self, key):

        cdef c_pyobject c_key
        cdef node_ptr begin = deref( self.__index.begin() ) 
        cdef node_ptr end = deref( self.__index.back() )
        cdef __TreeSeries_float80_t result
        result = __TreeSeries_float80_t.__new__( __TreeSeries_float80_t,
                                             interpolate=self.interpolate_type,
                                             arithmetic=self.arithmetic )

        if self.__len__() == 0:
            return result

        if key.start is not None and key.stop is not None:

            if key.start < self.begin() and key.stop < self.begin():
                return result

            if key.start > self.end() and key.stop < self.end():
                return result

        if key.start is not None:
            c_key = c_pyobject(<PyObject*>key.start)
            begin = self.__index.tree_search(c_key).second

        if key.stop is not None:
            c_key = c_pyobject(<PyObject*>key.stop)
            end = self.__index.tree_search(c_key).first

        if compare_func(end.key, begin.key) \
            and ( not equal(end.key, begin.key) ):

            return result

        if begin == self.link() or end == self.link():
            return result 

        if key.step is None:
            return self.truncate( self.__get_key_from_ptr(begin),
                                  self.__get_key_from_ptr(end) )

        else: 
            return self.periodic( self.__get_key_from_ptr(begin), 
                                  self.__get_key_from_ptr(end), 
                                  key.step )


    cdef __TreeSeries_float80_t __lshift_int(self, Py_ssize_t shift_param):

        cdef __TreeSeries_float80_t result
        cdef list delay_holder = []
        cdef tuple pair
        cdef Py_ssize_t count = 0
        cdef bool append_status = False

        result = __TreeSeries_float80_t.__new__( __TreeSeries_float80_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        if shift_param <= 0:
            raise ValueError("Shift parameter must be more than zero.")

        for pair in self.iteritems():
            
            if append_status:
                result.insert(delay_holder[0], pair[1])
                delay_holder.pop(0)

            if count == shift_param - 1:
                append_status = True

            delay_holder.append(pair[0])
            count += 1

        return result


    cdef __TreeSeries_float80_t __rshift_int(self, Py_ssize_t shift_param):

        cdef __TreeSeries_float80_t result
        cdef list delay_holder = []
        cdef tuple pair
        cdef Py_ssize_t count = 0
        cdef bool append_status = False

        result = __TreeSeries_float80_t.__new__( __TreeSeries_float80_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        if isinstance(shift_param, int):

            if shift_param <= 0:
                raise ValueError("Shift parameter must be more than zero.")

            for pair in self.iteritems():
                
                if append_status:
                    result.insert(pair[0], delay_holder[0])
                    delay_holder.pop(0)

                if count == shift_param - 1:
                    append_status = True

                delay_holder.append(pair[1])
                count += 1

            return result


    cdef __TreeSeries_float80_t __arithmetic_left( self, __BaseTreeSeries other, 
                                                 arithmetic_float80_t action ):

        cdef float80_t current
        cdef node_ptr node
        cdef object key
        cdef __TreeSeries_float80_t result
        result = __TreeSeries_float80_t.__new__( __TreeSeries_float80_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        other.on_itermode()

        for node in deref(self.__index):

            key = self.__get_key_from_ptr(node)
            current = action(__deref_value_ptr_float80_t(node), other[key])
            __insert_node_float80_t(result.__index, key, current)

        other.off_itermode()

        return result


    cdef __TreeSeries_float80_t __arithmetic_union( self, __BaseTreeSeries other, 
                                                  arithmetic_float80_t action ):

        cdef float80_t current
        cdef object key
        cdef trees_iterator[rb_tree, rb_node_valued] iterator
        cdef vector[rb_tree_ptr] trees = vector[rb_tree_ptr](2)
        cdef __TreeSeries_float80_t result
        result = __TreeSeries_float80_t.__new__( __TreeSeries_float80_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        
        trees[0] = self.__index
        trees[1] = other.get_tree()
        iterator.set_equal(equal_pair)
        iterator.set_compare(comp_pair)
        iterator.set_iterator(trees, "forward")

        self.on_itermode()
        other.on_itermode()

        while not iterator.empty():
            key = <object>deref(iterator).key.data
            current = action(self.__getitem__(key), other[key])
            __insert_node_float80_t(result.__index, key, current)
            pinc(iterator)

        self.off_itermode()
        other.off_itermode()

        return result
@cython.internal
cdef class __TreeSeries_float96_t(__BaseTreeSeries):

    cdef interpolate_float96_t     __interpolate
    cdef itermode_search_float96_t __get_item
    cdef arithmetic_type_float96_t __arithmetic

    cdef public   str  name
    cdef public   str  arithmetic
    cdef readonly str  interpolate_type
    cdef public   float96_t extrapolate


    def __init__(self):
        pass


    def __cinit__( self, object index=None, object values=None, str name="Untitled", 
                   str interpolate="floor", float96_t extrapolate=0, str arithmetic="left" ):

        self.set_interpolation(interpolate)
        self.extrapolate = extrapolate
        self.name = name
        self.arithmetic = arithmetic

        self.__get_item = self.__get_item_tree
        self.set_arithmetic(arithmetic)

        if index is None:
            
            if values is not None:
                raise IndexError("Index cannot be None while values are not.")

        else:
            if values is not None:
                self.__init_tree(index, values)

            else:
                self.__init_tree_index(index)


    #--------------------------------------------------------------------------------------------
    # Public methods
    #--------------------------------------------------------------------------------------------

    cpdef void insert(self, key, float96_t value) except*:

        __insert_node_float96_t(self.__index, key, value)


    cpdef void insert_range(self, list pairs) except*:

        cdef tuple pair

        for pair in pairs:
            __insert_node_float96_t(self.__index, pair[0], pair[1])


    cpdef void erase(self, key) except*:

        __erase_node_float96_t(self.__index, key)


    cpdef void erase_range(self, list keys) except*:

        cdef Py_ssize_t it

        for it in range( len(keys) ):
            __erase_node_float96_t(self.__index, keys[it])


    cpdef list values(self):

        cdef rb_node_valued* it
        cdef list result = []

        for it in deref(self.__index):
            result.append( __deref_value_ptr_float96_t(it) )

        return result


    cpdef list items(self):

        cdef node_ptr it
        cdef list result = []

        for it in deref(self.__index):
            result.append( ( self.__get_key_from_ptr(it), 
                             __deref_value_ptr_float96_t(it) ) )

        return result


    def iteritems(self):

        cdef node_ptr it

        for it in deref(self.__index):
            yield ( self.__get_key_from_ptr(it), __deref_value_ptr_float96_t(it) )


    cpdef tuple floor(self, key):

        cdef pair[node_ptr, node_ptr] bounds
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)
        bounds = self.__index.tree_search(c_key)

        if bounds.first == self.link():
            return (None, None)

        return ( self.__get_key_from_ptr(bounds.first),
                 __deref_value_ptr_float96_t(bounds.first) )


    cpdef tuple ceil(self, key):

        cdef pair[node_ptr, node_ptr] bounds
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)  
        bounds = self.__index.tree_search(c_key)

        if bounds.second == self.link():
            return (None, None)

        return ( self.__get_key_from_ptr(bounds.second),
                 __deref_value_ptr_float96_t(bounds.second) )


    cpdef __TreeSeries_float96_t truncate(self, start, stop):

        cdef iterator begin
        cdef iterator end
        cdef pair[node_ptr, node_ptr] bounds_start 
        cdef pair[node_ptr, node_ptr] bounds_end
        cdef __TreeSeries_float96_t result
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_start = c_pyobject(<PyObject*>start)
        cdef c_pyobject c_stop = c_pyobject(<PyObject*>stop)

        result = __TreeSeries_float96_t.__new__( __TreeSeries_float96_t, 
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        bounds_start = self.__index.tree_search(c_start)
        bounds_end = self.__index.tree_search(c_stop)
        begin = deref(bounds_start.second).it_position
        end = deref(bounds_end.first).it_position

        if( bounds_start.second == self.link() ) \
            or ( bounds_end.first == self.link() ):

            return result

        if self.__get_key_from_iter(begin) > self.__get_key_from_iter(end):
            return result

        while True:
            result.insert( self.__get_key_from_iter(begin), 
                           __deref_value_ptr_float96_t( deref(begin) ) )

            if begin == end:
                break

            inc(begin)

        return result


    cpdef __TreeSeries_float96_t periodic(self, start, stop, step):

        cdef __TreeSeries_float96_t result
        cdef object it = start
        result = __TreeSeries_float96_t.__new__( __TreeSeries_float96_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        self.on_itermode()

        while it < stop:
            result.insert( it, self.__getitem__(it) )
            it = it + step

        self.off_itermode()

        return result


    cpdef __TreeSeries_float96_t map( self, method, tuple args=(), 
                                  dict kwargs={} ):

        cdef __TreeSeries_float96_t result
        cdef tuple it

        result = __TreeSeries_float96_t.__new__( __TreeSeries_float96_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        for it in self.iteritems():
            result.insert( it[0], method(it[1], *args, **kwargs) )

        return result


    cpdef void map_inplace( self, method, tuple args=(), 
                            dict kwargs={} ) except*:

        cdef node_ptr it

        for it in deref(self.__index):
            __set_value_float96_t( it, method( __deref_value_ptr_float96_t(it),
                                             *args, **kwargs ) )


    cpdef void set_interpolation(self, str interpolate):

        cdef string c_interpolate = bytearray(interpolate, 'utf8')
        self.interpolate_type = interpolate
        self.__interpolate = __INTERPOLATE_float96_t[c_interpolate]


    cpdef void set_arithmetic(self, str arithmetic):

        if arithmetic == "union":
            self.__arithmetic = self.__arithmetic_union

        if arithmetic == "left":
            self.__arithmetic = self.__arithmetic_left


    cpdef void on_itermode(self):

        __BaseTreeSeries.on_itermode(self)
        self.__get_item = self.__get_item_linear


    cpdef void off_itermode(self):

        __BaseTreeSeries.off_itermode(self)
        self.__get_item = self.__get_item_tree


    cpdef void copy_data(self, other, str to_type="numerical") except*:

        cdef tuple it
        cdef float96_t num
        self.clear()

        if to_type == "numerical":

            for it in other.iteritems():
                num = <float96_t>float(it[1])
                self.insert( it[0], num )

        elif to_type == "str":
            for it in other.iteritems():
                self.insert( it[0], str(it[1]) )

        else:
            for it in other.iteritems():
                self.insert(it[0], it[1])


    cpdef void clear(self) except*:

        for key in self.keys():
            self.erase(key)

        self.__index.clear()

    #--------------------------------------------------------------------------------------------
    # Special methods
    #--------------------------------------------------------------------------------------------
    def __dealloc__(self):

        del self.__index


    def __str__(self):

        cdef str out_format = '{key}: {value}\n'
        cdef str result = 'Series object ' + self.name + '\n'
        cdef tuple it
        cdef list items = self.items()

        if len(items) <= 50: 
            for it in items:
                result += out_format.format(key=it[0], value=it[1])

        else:
            for it in items[:25]:
                result += out_format.format(key=it[0], value=it[1])

            result += '...\n'

            for it in items[-25:]:
                result += out_format.format(key=it[0], value=it[1])

        return result


    def __repr__(self):

        return self.__str__()

    
    def __iter__(self):

        cdef node_ptr it

        for it in deref(self.__index):
            yield __deref_value_ptr_float96_t(it)


    def __getitem__(self, key):

        cdef int32_t error = TYPE_ERROR
        cdef float96_t result

        if self.__index.size() == 0:
            return self.extrapolate

        if isinstance(key, slice):
            return self.__get_item_slice(key)

        result = self.__get_item(self, key, &error)

        if error == 0:
            return result

        if error == INT_KEY_ERROR:
            raise KeyError( str(key) )

        if error == TYPE_ERROR:
            raise TypeError("Inconsistent key type: " + key.__class__.__name__)


    def __setitem__(self, key, float96_t value):

        cdef c_pyobject key_holder = to_c_pyobject(key)
        cdef node_ptr node = self.__index.insert_search(key_holder)

        if self.__index.size() == 0:
            __insert_node_by_ptr_float96_t(self.__index, node, key, value)

        else:
            if self.__get_key_from_ptr(node) == key:
                __set_value_float96_t(node, value)

            else:
                __insert_node_by_ptr_float96_t(self.__index, node, key, value)


    #--------------------------------------------------------------------------------------------
    # Emulating numeric types
    #--------------------------------------------------------------------------------------------
    cpdef __TreeSeries_float96_t add(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __add_float96_t)


    cpdef __TreeSeries_float96_t sub(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __sub_float96_t)


    cpdef __TreeSeries_float96_t mul(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __mul_float96_t)


    cpdef __TreeSeries_float96_t div(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __div_float96_t)


    cpdef __TreeSeries_float96_t lshift(self, shift_param):

        if isinstance(shift_param, int):
            return self.__lshift_int(shift_param)


    cpdef __TreeSeries_float96_t rshift(self, shift_param):

        if isinstance(shift_param, int):
            return self.__rshift_int(shift_param)


    #--------------------------------------------------------------------------------------------
    # Private methods
    #--------------------------------------------------------------------------------------------
    cdef void __init_tree(self, index, values) except*:

        cdef tuple it

        if len(index) != len(values):
            raise IndexError("Index must have the same length as values.")

        for it in zip(index, values):
            self.insert(it[0], it[1])


    cdef void __init_tree_index(self, index) except*:

        for it in index:
            self.insert(it, 0)


    cdef float96_t __get_item_linear(self, key, int32_t* error):

        #bug? cython 0.28.3 translate c_key to const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key) 
        cdef pair[node_ptr, node_ptr] bounds
        
        bounds = self.__index.linear_search_from(self.__last_call, c_key)
        error[0] = 0

        if bounds.first != self.link():
            self.__last_call = bounds.first

        else:
            self.__last_call = deref( self.__index.begin() )

        return self.__interpolate( bounds.first, bounds.second, key,
                                   self.link(), self.extrapolate,
                                   error )

    
    cdef float96_t __get_item_tree(self, key, int32_t* error):

        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)
        cdef pair[node_ptr, node_ptr] bounds

        bounds = self.__index.tree_search(c_key)
        error[0] = 0

        return self.__interpolate( bounds.first, bounds.second, key,
                                   self.link(), self.extrapolate, 
                                   error )

    
    cdef __TreeSeries_float96_t __get_item_slice(self, key):

        cdef c_pyobject c_key
        cdef node_ptr begin = deref( self.__index.begin() ) 
        cdef node_ptr end = deref( self.__index.back() )
        cdef __TreeSeries_float96_t result
        result = __TreeSeries_float96_t.__new__( __TreeSeries_float96_t,
                                             interpolate=self.interpolate_type,
                                             arithmetic=self.arithmetic )

        if self.__len__() == 0:
            return result

        if key.start is not None and key.stop is not None:

            if key.start < self.begin() and key.stop < self.begin():
                return result

            if key.start > self.end() and key.stop < self.end():
                return result

        if key.start is not None:
            c_key = c_pyobject(<PyObject*>key.start)
            begin = self.__index.tree_search(c_key).second

        if key.stop is not None:
            c_key = c_pyobject(<PyObject*>key.stop)
            end = self.__index.tree_search(c_key).first

        if compare_func(end.key, begin.key) \
            and ( not equal(end.key, begin.key) ):

            return result

        if begin == self.link() or end == self.link():
            return result 

        if key.step is None:
            return self.truncate( self.__get_key_from_ptr(begin),
                                  self.__get_key_from_ptr(end) )

        else: 
            return self.periodic( self.__get_key_from_ptr(begin), 
                                  self.__get_key_from_ptr(end), 
                                  key.step )


    cdef __TreeSeries_float96_t __lshift_int(self, Py_ssize_t shift_param):

        cdef __TreeSeries_float96_t result
        cdef list delay_holder = []
        cdef tuple pair
        cdef Py_ssize_t count = 0
        cdef bool append_status = False

        result = __TreeSeries_float96_t.__new__( __TreeSeries_float96_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        if shift_param <= 0:
            raise ValueError("Shift parameter must be more than zero.")

        for pair in self.iteritems():
            
            if append_status:
                result.insert(delay_holder[0], pair[1])
                delay_holder.pop(0)

            if count == shift_param - 1:
                append_status = True

            delay_holder.append(pair[0])
            count += 1

        return result


    cdef __TreeSeries_float96_t __rshift_int(self, Py_ssize_t shift_param):

        cdef __TreeSeries_float96_t result
        cdef list delay_holder = []
        cdef tuple pair
        cdef Py_ssize_t count = 0
        cdef bool append_status = False

        result = __TreeSeries_float96_t.__new__( __TreeSeries_float96_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        if isinstance(shift_param, int):

            if shift_param <= 0:
                raise ValueError("Shift parameter must be more than zero.")

            for pair in self.iteritems():
                
                if append_status:
                    result.insert(pair[0], delay_holder[0])
                    delay_holder.pop(0)

                if count == shift_param - 1:
                    append_status = True

                delay_holder.append(pair[1])
                count += 1

            return result


    cdef __TreeSeries_float96_t __arithmetic_left( self, __BaseTreeSeries other, 
                                                 arithmetic_float96_t action ):

        cdef float96_t current
        cdef node_ptr node
        cdef object key
        cdef __TreeSeries_float96_t result
        result = __TreeSeries_float96_t.__new__( __TreeSeries_float96_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        other.on_itermode()

        for node in deref(self.__index):

            key = self.__get_key_from_ptr(node)
            current = action(__deref_value_ptr_float96_t(node), other[key])
            __insert_node_float96_t(result.__index, key, current)

        other.off_itermode()

        return result


    cdef __TreeSeries_float96_t __arithmetic_union( self, __BaseTreeSeries other, 
                                                  arithmetic_float96_t action ):

        cdef float96_t current
        cdef object key
        cdef trees_iterator[rb_tree, rb_node_valued] iterator
        cdef vector[rb_tree_ptr] trees = vector[rb_tree_ptr](2)
        cdef __TreeSeries_float96_t result
        result = __TreeSeries_float96_t.__new__( __TreeSeries_float96_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        
        trees[0] = self.__index
        trees[1] = other.get_tree()
        iterator.set_equal(equal_pair)
        iterator.set_compare(comp_pair)
        iterator.set_iterator(trees, "forward")

        self.on_itermode()
        other.on_itermode()

        while not iterator.empty():
            key = <object>deref(iterator).key.data
            current = action(self.__getitem__(key), other[key])
            __insert_node_float96_t(result.__index, key, current)
            pinc(iterator)

        self.off_itermode()
        other.off_itermode()

        return result
@cython.internal
cdef class __TreeSeries_float128_t(__BaseTreeSeries):

    cdef interpolate_float128_t     __interpolate
    cdef itermode_search_float128_t __get_item
    cdef arithmetic_type_float128_t __arithmetic

    cdef public   str  name
    cdef public   str  arithmetic
    cdef readonly str  interpolate_type
    cdef public   float128_t extrapolate


    def __init__(self):
        pass


    def __cinit__( self, object index=None, object values=None, str name="Untitled", 
                   str interpolate="floor", float128_t extrapolate=0, str arithmetic="left" ):

        self.set_interpolation(interpolate)
        self.extrapolate = extrapolate
        self.name = name
        self.arithmetic = arithmetic

        self.__get_item = self.__get_item_tree
        self.set_arithmetic(arithmetic)

        if index is None:
            
            if values is not None:
                raise IndexError("Index cannot be None while values are not.")

        else:
            if values is not None:
                self.__init_tree(index, values)

            else:
                self.__init_tree_index(index)


    #--------------------------------------------------------------------------------------------
    # Public methods
    #--------------------------------------------------------------------------------------------

    cpdef void insert(self, key, float128_t value) except*:

        __insert_node_float128_t(self.__index, key, value)


    cpdef void insert_range(self, list pairs) except*:

        cdef tuple pair

        for pair in pairs:
            __insert_node_float128_t(self.__index, pair[0], pair[1])


    cpdef void erase(self, key) except*:

        __erase_node_float128_t(self.__index, key)


    cpdef void erase_range(self, list keys) except*:

        cdef Py_ssize_t it

        for it in range( len(keys) ):
            __erase_node_float128_t(self.__index, keys[it])


    cpdef list values(self):

        cdef rb_node_valued* it
        cdef list result = []

        for it in deref(self.__index):
            result.append( __deref_value_ptr_float128_t(it) )

        return result


    cpdef list items(self):

        cdef node_ptr it
        cdef list result = []

        for it in deref(self.__index):
            result.append( ( self.__get_key_from_ptr(it), 
                             __deref_value_ptr_float128_t(it) ) )

        return result


    def iteritems(self):

        cdef node_ptr it

        for it in deref(self.__index):
            yield ( self.__get_key_from_ptr(it), __deref_value_ptr_float128_t(it) )


    cpdef tuple floor(self, key):

        cdef pair[node_ptr, node_ptr] bounds
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)
        bounds = self.__index.tree_search(c_key)

        if bounds.first == self.link():
            return (None, None)

        return ( self.__get_key_from_ptr(bounds.first),
                 __deref_value_ptr_float128_t(bounds.first) )


    cpdef tuple ceil(self, key):

        cdef pair[node_ptr, node_ptr] bounds
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)  
        bounds = self.__index.tree_search(c_key)

        if bounds.second == self.link():
            return (None, None)

        return ( self.__get_key_from_ptr(bounds.second),
                 __deref_value_ptr_float128_t(bounds.second) )


    cpdef __TreeSeries_float128_t truncate(self, start, stop):

        cdef iterator begin
        cdef iterator end
        cdef pair[node_ptr, node_ptr] bounds_start 
        cdef pair[node_ptr, node_ptr] bounds_end
        cdef __TreeSeries_float128_t result
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_start = c_pyobject(<PyObject*>start)
        cdef c_pyobject c_stop = c_pyobject(<PyObject*>stop)

        result = __TreeSeries_float128_t.__new__( __TreeSeries_float128_t, 
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        bounds_start = self.__index.tree_search(c_start)
        bounds_end = self.__index.tree_search(c_stop)
        begin = deref(bounds_start.second).it_position
        end = deref(bounds_end.first).it_position

        if( bounds_start.second == self.link() ) \
            or ( bounds_end.first == self.link() ):

            return result

        if self.__get_key_from_iter(begin) > self.__get_key_from_iter(end):
            return result

        while True:
            result.insert( self.__get_key_from_iter(begin), 
                           __deref_value_ptr_float128_t( deref(begin) ) )

            if begin == end:
                break

            inc(begin)

        return result


    cpdef __TreeSeries_float128_t periodic(self, start, stop, step):

        cdef __TreeSeries_float128_t result
        cdef object it = start
        result = __TreeSeries_float128_t.__new__( __TreeSeries_float128_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        self.on_itermode()

        while it < stop:
            result.insert( it, self.__getitem__(it) )
            it = it + step

        self.off_itermode()

        return result


    cpdef __TreeSeries_float128_t map( self, method, tuple args=(), 
                                  dict kwargs={} ):

        cdef __TreeSeries_float128_t result
        cdef tuple it

        result = __TreeSeries_float128_t.__new__( __TreeSeries_float128_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        for it in self.iteritems():
            result.insert( it[0], method(it[1], *args, **kwargs) )

        return result


    cpdef void map_inplace( self, method, tuple args=(), 
                            dict kwargs={} ) except*:

        cdef node_ptr it

        for it in deref(self.__index):
            __set_value_float128_t( it, method( __deref_value_ptr_float128_t(it),
                                             *args, **kwargs ) )


    cpdef void set_interpolation(self, str interpolate):

        cdef string c_interpolate = bytearray(interpolate, 'utf8')
        self.interpolate_type = interpolate
        self.__interpolate = __INTERPOLATE_float128_t[c_interpolate]


    cpdef void set_arithmetic(self, str arithmetic):

        if arithmetic == "union":
            self.__arithmetic = self.__arithmetic_union

        if arithmetic == "left":
            self.__arithmetic = self.__arithmetic_left


    cpdef void on_itermode(self):

        __BaseTreeSeries.on_itermode(self)
        self.__get_item = self.__get_item_linear


    cpdef void off_itermode(self):

        __BaseTreeSeries.off_itermode(self)
        self.__get_item = self.__get_item_tree


    cpdef void copy_data(self, other, str to_type="numerical") except*:

        cdef tuple it
        cdef float128_t num
        self.clear()

        if to_type == "numerical":

            for it in other.iteritems():
                num = <float128_t>float(it[1])
                self.insert( it[0], num )

        elif to_type == "str":
            for it in other.iteritems():
                self.insert( it[0], str(it[1]) )

        else:
            for it in other.iteritems():
                self.insert(it[0], it[1])


    cpdef void clear(self) except*:

        for key in self.keys():
            self.erase(key)

        self.__index.clear()

    #--------------------------------------------------------------------------------------------
    # Special methods
    #--------------------------------------------------------------------------------------------
    def __dealloc__(self):

        del self.__index


    def __str__(self):

        cdef str out_format = '{key}: {value}\n'
        cdef str result = 'Series object ' + self.name + '\n'
        cdef tuple it
        cdef list items = self.items()

        if len(items) <= 50: 
            for it in items:
                result += out_format.format(key=it[0], value=it[1])

        else:
            for it in items[:25]:
                result += out_format.format(key=it[0], value=it[1])

            result += '...\n'

            for it in items[-25:]:
                result += out_format.format(key=it[0], value=it[1])

        return result


    def __repr__(self):

        return self.__str__()

    
    def __iter__(self):

        cdef node_ptr it

        for it in deref(self.__index):
            yield __deref_value_ptr_float128_t(it)


    def __getitem__(self, key):

        cdef int32_t error = TYPE_ERROR
        cdef float128_t result

        if self.__index.size() == 0:
            return self.extrapolate

        if isinstance(key, slice):
            return self.__get_item_slice(key)

        result = self.__get_item(self, key, &error)

        if error == 0:
            return result

        if error == INT_KEY_ERROR:
            raise KeyError( str(key) )

        if error == TYPE_ERROR:
            raise TypeError("Inconsistent key type: " + key.__class__.__name__)


    def __setitem__(self, key, float128_t value):

        cdef c_pyobject key_holder = to_c_pyobject(key)
        cdef node_ptr node = self.__index.insert_search(key_holder)

        if self.__index.size() == 0:
            __insert_node_by_ptr_float128_t(self.__index, node, key, value)

        else:
            if self.__get_key_from_ptr(node) == key:
                __set_value_float128_t(node, value)

            else:
                __insert_node_by_ptr_float128_t(self.__index, node, key, value)


    #--------------------------------------------------------------------------------------------
    # Emulating numeric types
    #--------------------------------------------------------------------------------------------
    cpdef __TreeSeries_float128_t add(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __add_float128_t)


    cpdef __TreeSeries_float128_t sub(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __sub_float128_t)


    cpdef __TreeSeries_float128_t mul(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __mul_float128_t)


    cpdef __TreeSeries_float128_t div(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __div_float128_t)


    cpdef __TreeSeries_float128_t lshift(self, shift_param):

        if isinstance(shift_param, int):
            return self.__lshift_int(shift_param)


    cpdef __TreeSeries_float128_t rshift(self, shift_param):

        if isinstance(shift_param, int):
            return self.__rshift_int(shift_param)


    #--------------------------------------------------------------------------------------------
    # Private methods
    #--------------------------------------------------------------------------------------------
    cdef void __init_tree(self, index, values) except*:

        cdef tuple it

        if len(index) != len(values):
            raise IndexError("Index must have the same length as values.")

        for it in zip(index, values):
            self.insert(it[0], it[1])


    cdef void __init_tree_index(self, index) except*:

        for it in index:
            self.insert(it, 0)


    cdef float128_t __get_item_linear(self, key, int32_t* error):

        #bug? cython 0.28.3 translate c_key to const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key) 
        cdef pair[node_ptr, node_ptr] bounds
        
        bounds = self.__index.linear_search_from(self.__last_call, c_key)
        error[0] = 0

        if bounds.first != self.link():
            self.__last_call = bounds.first

        else:
            self.__last_call = deref( self.__index.begin() )

        return self.__interpolate( bounds.first, bounds.second, key,
                                   self.link(), self.extrapolate,
                                   error )

    
    cdef float128_t __get_item_tree(self, key, int32_t* error):

        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)
        cdef pair[node_ptr, node_ptr] bounds

        bounds = self.__index.tree_search(c_key)
        error[0] = 0

        return self.__interpolate( bounds.first, bounds.second, key,
                                   self.link(), self.extrapolate, 
                                   error )

    
    cdef __TreeSeries_float128_t __get_item_slice(self, key):

        cdef c_pyobject c_key
        cdef node_ptr begin = deref( self.__index.begin() ) 
        cdef node_ptr end = deref( self.__index.back() )
        cdef __TreeSeries_float128_t result
        result = __TreeSeries_float128_t.__new__( __TreeSeries_float128_t,
                                             interpolate=self.interpolate_type,
                                             arithmetic=self.arithmetic )

        if self.__len__() == 0:
            return result

        if key.start is not None and key.stop is not None:

            if key.start < self.begin() and key.stop < self.begin():
                return result

            if key.start > self.end() and key.stop < self.end():
                return result

        if key.start is not None:
            c_key = c_pyobject(<PyObject*>key.start)
            begin = self.__index.tree_search(c_key).second

        if key.stop is not None:
            c_key = c_pyobject(<PyObject*>key.stop)
            end = self.__index.tree_search(c_key).first

        if compare_func(end.key, begin.key) \
            and ( not equal(end.key, begin.key) ):

            return result

        if begin == self.link() or end == self.link():
            return result 

        if key.step is None:
            return self.truncate( self.__get_key_from_ptr(begin),
                                  self.__get_key_from_ptr(end) )

        else: 
            return self.periodic( self.__get_key_from_ptr(begin), 
                                  self.__get_key_from_ptr(end), 
                                  key.step )


    cdef __TreeSeries_float128_t __lshift_int(self, Py_ssize_t shift_param):

        cdef __TreeSeries_float128_t result
        cdef list delay_holder = []
        cdef tuple pair
        cdef Py_ssize_t count = 0
        cdef bool append_status = False

        result = __TreeSeries_float128_t.__new__( __TreeSeries_float128_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        if shift_param <= 0:
            raise ValueError("Shift parameter must be more than zero.")

        for pair in self.iteritems():
            
            if append_status:
                result.insert(delay_holder[0], pair[1])
                delay_holder.pop(0)

            if count == shift_param - 1:
                append_status = True

            delay_holder.append(pair[0])
            count += 1

        return result


    cdef __TreeSeries_float128_t __rshift_int(self, Py_ssize_t shift_param):

        cdef __TreeSeries_float128_t result
        cdef list delay_holder = []
        cdef tuple pair
        cdef Py_ssize_t count = 0
        cdef bool append_status = False

        result = __TreeSeries_float128_t.__new__( __TreeSeries_float128_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        if isinstance(shift_param, int):

            if shift_param <= 0:
                raise ValueError("Shift parameter must be more than zero.")

            for pair in self.iteritems():
                
                if append_status:
                    result.insert(pair[0], delay_holder[0])
                    delay_holder.pop(0)

                if count == shift_param - 1:
                    append_status = True

                delay_holder.append(pair[1])
                count += 1

            return result


    cdef __TreeSeries_float128_t __arithmetic_left( self, __BaseTreeSeries other, 
                                                 arithmetic_float128_t action ):

        cdef float128_t current
        cdef node_ptr node
        cdef object key
        cdef __TreeSeries_float128_t result
        result = __TreeSeries_float128_t.__new__( __TreeSeries_float128_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        other.on_itermode()

        for node in deref(self.__index):

            key = self.__get_key_from_ptr(node)
            current = action(__deref_value_ptr_float128_t(node), other[key])
            __insert_node_float128_t(result.__index, key, current)

        other.off_itermode()

        return result


    cdef __TreeSeries_float128_t __arithmetic_union( self, __BaseTreeSeries other, 
                                                  arithmetic_float128_t action ):

        cdef float128_t current
        cdef object key
        cdef trees_iterator[rb_tree, rb_node_valued] iterator
        cdef vector[rb_tree_ptr] trees = vector[rb_tree_ptr](2)
        cdef __TreeSeries_float128_t result
        result = __TreeSeries_float128_t.__new__( __TreeSeries_float128_t,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        
        trees[0] = self.__index
        trees[1] = other.get_tree()
        iterator.set_equal(equal_pair)
        iterator.set_compare(comp_pair)
        iterator.set_iterator(trees, "forward")

        self.on_itermode()
        other.on_itermode()

        while not iterator.empty():
            key = <object>deref(iterator).key.data
            current = action(self.__getitem__(key), other[key])
            __insert_node_float128_t(result.__index, key, current)
            pinc(iterator)

        self.off_itermode()
        other.off_itermode()

        return result
@cython.internal
cdef class __TreeSeries_object(__BaseTreeSeries):

    cdef interpolate_object     __interpolate
    cdef itermode_search_object __get_item
    cdef arithmetic_type_object __arithmetic

    cdef public   str  name
    cdef public   str  arithmetic
    cdef readonly str  interpolate_type
    cdef public   object extrapolate


    def __init__(self):
        pass


    def __cinit__( self, object index=None, object values=None, str name="Untitled", 
                   str interpolate="floor", object extrapolate=0, str arithmetic="left" ):

        self.set_interpolation(interpolate)
        self.extrapolate = extrapolate
        self.name = name
        self.arithmetic = arithmetic

        self.__get_item = self.__get_item_tree
        self.set_arithmetic(arithmetic)

        if index is None:
            
            if values is not None:
                raise IndexError("Index cannot be None while values are not.")

        else:
            if values is not None:
                self.__init_tree(index, values)

            else:
                self.__init_tree_index(index)


    #--------------------------------------------------------------------------------------------
    # Public methods
    #--------------------------------------------------------------------------------------------

    cpdef void insert(self, key, object value) except*:

        __insert_node_object(self.__index, key, value)


    cpdef void insert_range(self, list pairs) except*:

        cdef tuple pair

        for pair in pairs:
            __insert_node_object(self.__index, pair[0], pair[1])


    cpdef void erase(self, key) except*:

        __erase_node_object(self.__index, key)


    cpdef void erase_range(self, list keys) except*:

        cdef Py_ssize_t it

        for it in range( len(keys) ):
            __erase_node_object(self.__index, keys[it])


    cpdef list values(self):

        cdef rb_node_valued* it
        cdef list result = []

        for it in deref(self.__index):
            result.append( __deref_value_ptr_object(it) )

        return result


    cpdef list items(self):

        cdef node_ptr it
        cdef list result = []

        for it in deref(self.__index):
            result.append( ( self.__get_key_from_ptr(it), 
                             __deref_value_ptr_object(it) ) )

        return result


    def iteritems(self):

        cdef node_ptr it

        for it in deref(self.__index):
            yield ( self.__get_key_from_ptr(it), __deref_value_ptr_object(it) )


    cpdef tuple floor(self, key):

        cdef pair[node_ptr, node_ptr] bounds
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)
        bounds = self.__index.tree_search(c_key)

        if bounds.first == self.link():
            return (None, None)

        return ( self.__get_key_from_ptr(bounds.first),
                 __deref_value_ptr_object(bounds.first) )


    cpdef tuple ceil(self, key):

        cdef pair[node_ptr, node_ptr] bounds
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)  
        bounds = self.__index.tree_search(c_key)

        if bounds.second == self.link():
            return (None, None)

        return ( self.__get_key_from_ptr(bounds.second),
                 __deref_value_ptr_object(bounds.second) )


    cpdef __TreeSeries_object truncate(self, start, stop):

        cdef iterator begin
        cdef iterator end
        cdef pair[node_ptr, node_ptr] bounds_start 
        cdef pair[node_ptr, node_ptr] bounds_end
        cdef __TreeSeries_object result
        #bug? cython 0.28.3 declare c_key as const, if do not declare it directly
        cdef c_pyobject c_start = c_pyobject(<PyObject*>start)
        cdef c_pyobject c_stop = c_pyobject(<PyObject*>stop)

        result = __TreeSeries_object.__new__( __TreeSeries_object, 
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        bounds_start = self.__index.tree_search(c_start)
        bounds_end = self.__index.tree_search(c_stop)
        begin = deref(bounds_start.second).it_position
        end = deref(bounds_end.first).it_position

        if( bounds_start.second == self.link() ) \
            or ( bounds_end.first == self.link() ):

            return result

        if self.__get_key_from_iter(begin) > self.__get_key_from_iter(end):
            return result

        while True:
            result.insert( self.__get_key_from_iter(begin), 
                           __deref_value_ptr_object( deref(begin) ) )

            if begin == end:
                break

            inc(begin)

        return result


    cpdef __TreeSeries_object periodic(self, start, stop, step):

        cdef __TreeSeries_object result
        cdef object it = start
        result = __TreeSeries_object.__new__( __TreeSeries_object,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        self.on_itermode()

        while it < stop:
            result.insert( it, self.__getitem__(it) )
            it = it + step

        self.off_itermode()

        return result


    cpdef __TreeSeries_object map( self, method, tuple args=(), 
                                  dict kwargs={} ):

        cdef __TreeSeries_object result
        cdef tuple it

        result = __TreeSeries_object.__new__( __TreeSeries_object,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )

        for it in self.iteritems():
            result.insert( it[0], method(it[1], *args, **kwargs) )

        return result


    cpdef void map_inplace( self, method, tuple args=(), 
                            dict kwargs={} ) except*:

        cdef node_ptr it

        for it in deref(self.__index):
            __set_value_object( it, method( __deref_value_ptr_object(it),
                                             *args, **kwargs ) )


    cpdef void set_interpolation(self, str interpolate):

        cdef string c_interpolate = bytearray(interpolate, 'utf8')
        self.interpolate_type = interpolate
        self.__interpolate = __INTERPOLATE_object[c_interpolate]


    cpdef void set_arithmetic(self, str arithmetic):

        if arithmetic == "union":
            self.__arithmetic = self.__arithmetic_union

        if arithmetic == "left":
            self.__arithmetic = self.__arithmetic_left


    cpdef void on_itermode(self):

        __BaseTreeSeries.on_itermode(self)
        self.__get_item = self.__get_item_linear


    cpdef void off_itermode(self):

        __BaseTreeSeries.off_itermode(self)
        self.__get_item = self.__get_item_tree


    cpdef void copy_data(self, other, str to_type="numerical") except*:

        cdef tuple it
        cdef object num
        self.clear()

        if to_type == "numerical":

            for it in other.iteritems():
                num = <object>float(it[1])
                self.insert( it[0], num )

        elif to_type == "str":
            for it in other.iteritems():
                self.insert( it[0], str(it[1]) )

        else:
            for it in other.iteritems():
                self.insert(it[0], it[1])


    cpdef void clear(self) except*:

        for key in self.keys():
            self.erase(key)

        self.__index.clear()

    #--------------------------------------------------------------------------------------------
    # Special methods
    #--------------------------------------------------------------------------------------------
    def __dealloc__(self):

        del self.__index


    def __str__(self):

        cdef str out_format = '{key}: {value}\n'
        cdef str result = 'Series object ' + self.name + '\n'
        cdef tuple it
        cdef list items = self.items()

        if len(items) <= 50: 
            for it in items:
                result += out_format.format(key=it[0], value=it[1])

        else:
            for it in items[:25]:
                result += out_format.format(key=it[0], value=it[1])

            result += '...\n'

            for it in items[-25:]:
                result += out_format.format(key=it[0], value=it[1])

        return result


    def __repr__(self):

        return self.__str__()

    
    def __iter__(self):

        cdef node_ptr it

        for it in deref(self.__index):
            yield __deref_value_ptr_object(it)


    def __getitem__(self, key):

        cdef int32_t error = TYPE_ERROR
        cdef object result

        if self.__index.size() == 0:
            return self.extrapolate

        if isinstance(key, slice):
            return self.__get_item_slice(key)

        result = self.__get_item(self, key, &error)

        if error == 0:
            return result

        if error == INT_KEY_ERROR:
            raise KeyError( str(key) )

        if error == TYPE_ERROR:
            raise TypeError("Inconsistent key type: " + key.__class__.__name__)


    def __setitem__(self, key, object value):

        cdef c_pyobject key_holder = to_c_pyobject(key)
        cdef node_ptr node = self.__index.insert_search(key_holder)

        if self.__index.size() == 0:
            __insert_node_by_ptr_object(self.__index, node, key, value)

        else:
            if self.__get_key_from_ptr(node) == key:
                __set_value_object(node, value)

            else:
                __insert_node_by_ptr_object(self.__index, node, key, value)


    #--------------------------------------------------------------------------------------------
    # Emulating numeric types
    #--------------------------------------------------------------------------------------------
    cpdef __TreeSeries_object add(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __add_object)


    cpdef __TreeSeries_object sub(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __sub_object)


    cpdef __TreeSeries_object mul(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __mul_object)


    cpdef __TreeSeries_object div(self, __BaseTreeSeries other):

        return self.__arithmetic(self, other, __div_object)


    cpdef __TreeSeries_object lshift(self, shift_param):

        if isinstance(shift_param, int):
            return self.__lshift_int(shift_param)


    cpdef __TreeSeries_object rshift(self, shift_param):

        if isinstance(shift_param, int):
            return self.__rshift_int(shift_param)


    #--------------------------------------------------------------------------------------------
    # Private methods
    #--------------------------------------------------------------------------------------------
    cdef void __init_tree(self, index, values) except*:

        cdef tuple it

        if len(index) != len(values):
            raise IndexError("Index must have the same length as values.")

        for it in zip(index, values):
            self.insert(it[0], it[1])


    cdef void __init_tree_index(self, index) except*:

        for it in index:
            self.insert(it, 0)


    cdef object __get_item_linear(self, key, int32_t* error):

        #bug? cython 0.28.3 translate c_key to const, if do not declare it directly
        cdef c_pyobject c_key = c_pyobject(<PyObject*>key) 
        cdef pair[node_ptr, node_ptr] bounds
        
        bounds = self.__index.linear_search_from(self.__last_call, c_key)
        error[0] = 0

        if bounds.first != self.link():
            self.__last_call = bounds.first

        else:
            self.__last_call = deref( self.__index.begin() )

        return self.__interpolate( bounds.first, bounds.second, key,
                                   self.link(), self.extrapolate,
                                   error )

    
    cdef object __get_item_tree(self, key, int32_t* error):

        cdef c_pyobject c_key = c_pyobject(<PyObject*>key)
        cdef pair[node_ptr, node_ptr] bounds

        bounds = self.__index.tree_search(c_key)
        error[0] = 0

        return self.__interpolate( bounds.first, bounds.second, key,
                                   self.link(), self.extrapolate, 
                                   error )

    
    cdef __TreeSeries_object __get_item_slice(self, key):

        cdef c_pyobject c_key
        cdef node_ptr begin = deref( self.__index.begin() ) 
        cdef node_ptr end = deref( self.__index.back() )
        cdef __TreeSeries_object result
        result = __TreeSeries_object.__new__( __TreeSeries_object,
                                             interpolate=self.interpolate_type,
                                             arithmetic=self.arithmetic )

        if self.__len__() == 0:
            return result

        if key.start is not None and key.stop is not None:

            if key.start < self.begin() and key.stop < self.begin():
                return result

            if key.start > self.end() and key.stop < self.end():
                return result

        if key.start is not None:
            c_key = c_pyobject(<PyObject*>key.start)
            begin = self.__index.tree_search(c_key).second

        if key.stop is not None:
            c_key = c_pyobject(<PyObject*>key.stop)
            end = self.__index.tree_search(c_key).first

        if compare_func(end.key, begin.key) \
            and ( not equal(end.key, begin.key) ):

            return result

        if begin == self.link() or end == self.link():
            return result 

        if key.step is None:
            return self.truncate( self.__get_key_from_ptr(begin),
                                  self.__get_key_from_ptr(end) )

        else: 
            return self.periodic( self.__get_key_from_ptr(begin), 
                                  self.__get_key_from_ptr(end), 
                                  key.step )


    cdef __TreeSeries_object __lshift_int(self, Py_ssize_t shift_param):

        cdef __TreeSeries_object result
        cdef list delay_holder = []
        cdef tuple pair
        cdef Py_ssize_t count = 0
        cdef bool append_status = False

        result = __TreeSeries_object.__new__( __TreeSeries_object,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        if shift_param <= 0:
            raise ValueError("Shift parameter must be more than zero.")

        for pair in self.iteritems():
            
            if append_status:
                result.insert(delay_holder[0], pair[1])
                delay_holder.pop(0)

            if count == shift_param - 1:
                append_status = True

            delay_holder.append(pair[0])
            count += 1

        return result


    cdef __TreeSeries_object __rshift_int(self, Py_ssize_t shift_param):

        cdef __TreeSeries_object result
        cdef list delay_holder = []
        cdef tuple pair
        cdef Py_ssize_t count = 0
        cdef bool append_status = False

        result = __TreeSeries_object.__new__( __TreeSeries_object,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        if isinstance(shift_param, int):

            if shift_param <= 0:
                raise ValueError("Shift parameter must be more than zero.")

            for pair in self.iteritems():
                
                if append_status:
                    result.insert(pair[0], delay_holder[0])
                    delay_holder.pop(0)

                if count == shift_param - 1:
                    append_status = True

                delay_holder.append(pair[1])
                count += 1

            return result


    cdef __TreeSeries_object __arithmetic_left( self, __BaseTreeSeries other, 
                                                 arithmetic_object action ):

        cdef object current
        cdef node_ptr node
        cdef object key
        cdef __TreeSeries_object result
        result = __TreeSeries_object.__new__( __TreeSeries_object,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        other.on_itermode()

        for node in deref(self.__index):

            key = self.__get_key_from_ptr(node)
            current = action(__deref_value_ptr_object(node), other[key])
            __insert_node_object(result.__index, key, current)

        other.off_itermode()

        return result


    cdef __TreeSeries_object __arithmetic_union( self, __BaseTreeSeries other, 
                                                  arithmetic_object action ):

        cdef object current
        cdef object key
        cdef trees_iterator[rb_tree, rb_node_valued] iterator
        cdef vector[rb_tree_ptr] trees = vector[rb_tree_ptr](2)
        cdef __TreeSeries_object result
        result = __TreeSeries_object.__new__( __TreeSeries_object,
                                               interpolate=self.interpolate_type,
                                               arithmetic=self.arithmetic )
        
        trees[0] = self.__index
        trees[1] = other.get_tree()
        iterator.set_equal(equal_pair)
        iterator.set_compare(comp_pair)
        iterator.set_iterator(trees, "forward")

        self.on_itermode()
        other.on_itermode()

        while not iterator.empty():
            key = <object>deref(iterator).key.data
            current = action(self.__getitem__(key), other[key])
            __insert_node_object(result.__index, key, current)
            pinc(iterator)

        self.off_itermode()
        other.off_itermode()

        return result
