Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions threadedtree/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@
# along with threadedtree. If not, see <http://www.gnu.org/licenses/>.

from threadedtree import ThreadedTree
from bidirectionaliterator import BidirectionalIterator
from treenodes import *
69 changes: 69 additions & 0 deletions threadedtree/bidirectionaliterator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
class BidirectionalIterator(object):
"""A generic bi-directional itertaor to any object that implements the BidirectionalIterator interface. A BidirectionalIterator does have its own state, but does not copy the data or mutate
the state of the underlying object. In essense it is simply a wrapper with a pointer to a given element in the reference object. The next() and prev() methods
call the _next() and _prev() methods of the reference object, relievin them of the duty of calculating exactly 'how' to get the next value...just ask the
container it wraps instead!"""

def __init__(self, reference_object):
"""
Initialize a BidirectionalIterator with a ``reference_object`` that implements the BidirectionalIterator interface. Sets the current pointer to the head of the tree.

Args:
reference_object(object): an object that implements the BidirectionalIterator interface.

Returns:
None
"""
self.reference = reference_object
self.current_pointer = None
self.head()

def __len__(self):
return len(self.reference)

def __repr__(self):
return str(self.peek())

def next(self):
"""Moves the current pointer to the next value, in order, in the tree. After advancing the pointer, the value is returned"""
n = self.reference._next(self.current_pointer)
if n == None:
return None
else:
self.current_pointer = n
return self.peek()

def prev(self):
"""Moves the current pointer to the previous value, in order, in the tree. After advancing the pointer, the value is returned"""
p = self.reference._prev(self.current_pointer)
if p == None:
return None
else:
self.current_pointer = p
return self.peek()

def has_next(self):
"""Returns True if the current pointer is not at the tail."""
if self.reference._next(self.current_pointer) == None:
return False
return True

def has_prev(self):
"""Returns True if the current pointer is not at the head."""
if self.reference._prev(self.current_pointer) == None:
return False
return True

def head(self):
"""Moves the current pointer to the head and returns that value."""
self.current_pointer = self.reference._head()
return self.peek()

def tail(self):
"""Moves the current pointer to the tail and returns that value."""
self.current_pointer = self.reference._tail()
return self.peek()

def peek(self):
"""Returns the value of the current pointer."""
return self.reference._peek(self.current_pointer)
80 changes: 80 additions & 0 deletions threadedtree/tests/test_bidirectional_iterator_unbalanced_TBST.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# This file is part of Threadedtree.

# Threadedtree is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# Threadedtree is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with threadedtree. If not, see <http://www.gnu.org/licenses/>.

from unittest import TestCase

import random
import threadedtree

class TestUnbalancedThreadedBST(TestCase):

@classmethod
def setup_class(self):
self.upper_bound = 1000
self.samples = 100 #This value MUST be greater than self.small_samples
self.small_samples = 10
assert self.samples > self.small_samples
self.trials = 100
self.bag = range(self.upper_bound)

@classmethod
def tearDown(self):
pass

def test_next_hasnext(self):
tree = threadedtree.ThreadedTree([])
x = tree.bi_iter()
assert x.next() == None
assert x.prev() == None
for trial in xrange(self.trials):
test_suite = random.sample(self.bag, self.samples)
tree = threadedtree.ThreadedTree(test_suite)
known = list(tree)
x = tree.bi_iter()
assert x.has_next() == True
for val in known:
assert x.peek() == val
assert str(x) == str(val) # TEST __repr__ WHILE IM AT IT
x.next()
assert x.has_next() == False
assert x.next() == None
assert x.next() == None
if len(x) > 0:
assert x.prev() != None

def test_prev_hasprev(self):
for trial in xrange(self.trials):
test_suite = random.sample(self.bag, self.samples)
tree = threadedtree.ThreadedTree(test_suite)
known = list(tree)
x = tree.bi_iter()
x.tail()
assert x.has_prev() == True
for val in known[::-1]:
assert x.peek() == val
x.prev()
assert x.has_prev() == False
assert x.prev() == None
assert x.prev() == None
if len(x) > 0:
assert x.next() != None

def test_special_methods(self):
for trial in xrange(self.trials):
test_suite = random.sample(self.bag, self.samples)
tree = threadedtree.ThreadedTree(test_suite)
x = tree.bi_iter()
assert len(x) == len(tree)

10 changes: 10 additions & 0 deletions threadedtree/tests/test_unbalanced_threaded_bst.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,16 @@ def test_repr(self):
test_suite.sort()
assert str(test_suite) == str(tree)

def test_reverse(self):
for trial in xrange(self.trials):
test_suite = random.sample(self.bag, self.small_samples)
tree = threadedtree.ThreadedTree(test_suite)
test_suite.sort()
idx = len(test_suite)-1
for val in tree.reverse():
assert test_suite[idx] == val
idx -= 1

def test_add(self):
for trial in xrange(5):
test_suite_1 = random.sample(self.bag, self.small_samples)
Expand Down
127 changes: 102 additions & 25 deletions threadedtree/threadedtree.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,35 +15,31 @@

"""This module contains an unbalanced double threaded binary search tree which is optimized for in-order traversal and uses no stack or recursion to perform its functions."""

import types, treenodes
import types, treenodes, bidirectionaliterator

class ThreadedTree(object):
"""A carefully implemented unbalanced double threaded binary search tree. Threaded binary search trees are optimized for in-order (ascending or descending) traversal and use no stack or recursion to perform its functions."""
def __init__(self, iterable=[], duplicate_strategy="none", root=None):
"""
Creates and empty unbalanced double threaded binary search tree.
Creates and empty unbalanced double threaded binary search tree.

A tree can be intialized using a valid python iterable object as a parameter to the constructor.

Parameters
----------
iterable : collections.Iterable
A python iterable used to initialize an empty tree. Items are added to tree in order of iteration (i.e a sorted list will create a tree that is the equivalent of a doubly linked list).
duplicate_strategy : str
``none`` - do not allow duplicates.
``stack`` - aggregate duplicate keys using an integer. (not yet implemented)
``duplicate`` - allow duplicate nodes in tree. (not yet implemented)
root : Threaded_Tree_Node
A Threaded_Tree_Node to assign to ``root``. Could be useful if you assembled a tree manually and wanted to mutate it via the tree interface.

Returns
-------
None

"""
if not isinstance(root, treenodes.Tree_Node) and root != None:
raise TypeError("You can only initialize the root of a ThreadedTree with an object with a base class of Tree_Node, or None.")
self.root = root
Args:
iterable(collections.Iterable): A python iterable used to initialize an empty tree. Items are added to tree in order of iteration (i.e a sorted list will create a tree that is the equivalent of a doubly linked list).
duplicate_strategy(str):
``none`` - do not allow duplicates.
``stack`` - aggregate duplicate keys using an integer. (not yet implemented)
``duplicate`` - allow duplicate nodes in tree. (not yet implemented)
root(Threaded_Tree_Node): A Threaded_Tree_Node to assign to ``root``. Could be useful if you assembled a tree manually and wanted to mutate it via the tree interface.

Returns:
None

"""
if not isinstance(root, treenodes.Threaded_Tree_Node) and root != None:
raise TypeError("You can only initialize the root of a ThreadedTree with an object with a base class of Threaded_Tree_Node, or None.")
self.root = self.head = self.tail = root
self._len = 0
self.duplicate_strategy = duplicate_strategy
if isinstance(iterable, ThreadedTree):
Expand Down Expand Up @@ -147,21 +143,22 @@ def __contains__(self, item):

def insert(self, value):
"""
Inserts a new node containing ``value`` into the tree.
Inserts a new node containing ``value`` into the tree.

Args:
value (object): A python object that implements ``__cmp__()`` or rich comparisons, to be inserted into the tree.

Returns:
None
"""
"""
if not self._implements_comparisons(value):
return

self._len += 1

if self.root == None:
self.root = self._new_node(value)
self.head = self.tail = self.root
return

current = self.root
Expand Down Expand Up @@ -200,23 +197,103 @@ def insert(self, value):
current.rthreaded = True
new_node.left = current

try:
if new_node.left == None:
self.head = new_node
elif new_node.right == None:
self.tail = new_node
except UnboundLocalError:
pass

def remove(self, value):
"""
Removes a node containing ``value`` from the tree.
Removes a node containing ``value`` from the tree.

Args:
value (object): A python object that implements ``__cmp__()`` or rich comparisons, to be removed from the tree.

Returns:
boolean: operation success
"""
"""
if not self._implements_comparisons(value):
return False
if self._len > 0 and self._remove(value): #take advantage of python short circuiting
self._len -= 1
return True
return False

def bi_iter(self):
"""
Returns a BidrectionalIterator to the tree, allowing a user to step through the tree in either the forward or backward direction.

Returns:
BidirectionalIterator: iterator allowing forward or backward traversal of the underlying tree.
"""
return bidirectionaliterator.BidirectionalIterator(self)

def reverse(self):
"""
Returns a generator that yields values from the tree in reverse order, from the tail to the head of the tree.

Returns:
Generator : yields values from the tree in reverse order.
"""
if self._len > 0:
current = self.root
while current.rthreaded:
current = current.right
while current != None:
yield current.val
if not current.lthreaded:
current = current.left
else:
node = current.left
while node.rthreaded:
node = node.right
current = node

def _next(self, pointer):
""" Private method that returns the next value in the tree, in order, given a random pointer. The time complexity of this method is O(n)."""
current = pointer
while current != None:
if current != pointer: # Pretty likely this is Theta(1)
return current
if not current.rthreaded:
current = current.right
else:
node = current.right
while node.lthreaded:
node = node.left
current = node

def _prev(self, pointer):
""" Private method that returns the previous value in the tree, in order, given a random pointer. The time complexity of this method is O(n)."""
current = pointer
while current != None:
if current != pointer: # Pretty likely this is Theta(1)
return current
if not current.lthreaded:
current = current.left
else:
node = current.left
while node.rthreaded:
node = node.right
current = node

def _head(self):
""" Private method that returns the head of the tree in constant time."""
return self.head

def _tail(self):
""" Private method that returns the tail of the tree in constant time."""
return self.tail

def _peek(self, pointer):
try:
return pointer.val
except:
return pointer

def _implements_comparisons(self, value):
"""Private method that determines if value implements either __cmp__ or both __lt__ and __gt__"""
if not hasattr(value, "__cmp__") and not (hasattr(value, "__lt__") or hasattr(value, "__gt__")):
Expand Down