From 58d9ce6bbc3922251f35f2a7403460096de58457 Mon Sep 17 00:00:00 2001 From: Tyler Springer Date: Wed, 22 Feb 2017 21:47:29 -0800 Subject: [PATCH 01/12] Added Bidirectional Iterator interface to ThreadedTree --- threadedtree/threadedtree.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/threadedtree/threadedtree.py b/threadedtree/threadedtree.py index 5c11fae..7810db0 100644 --- a/threadedtree/threadedtree.py +++ b/threadedtree/threadedtree.py @@ -217,6 +217,36 @@ def remove(self, value): return True return False + def bi_iter(self): + class BidirectionalIterator(object): + def __init__(self, reference_object): + self.reference = reference_iterator + self.current_pointer = self.head() + + def next(self): + pass + + def prev(self): + pass + + def head(self): + pass + + def tail(self): + pass + + def _next(self, pointer): + pass + + def _previous(self, pointer): + pass + + def _head(self, pointer): + pass + + def _tail(self, pointer): + pass + 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__")): From 45b914d84d8f6e2485759cb5414af56fc1a4d813 Mon Sep 17 00:00:00 2001 From: Tyler Springer Date: Wed, 22 Feb 2017 22:41:42 -0800 Subject: [PATCH 02/12] Implemented basic next functionality...seems a bit buggy. Head() and tail() both work fine. --- threadedtree/threadedtree.py | 68 +++++++++++++++++++++++++++++++----- 1 file changed, 59 insertions(+), 9 deletions(-) diff --git a/threadedtree/threadedtree.py b/threadedtree/threadedtree.py index 7810db0..936b962 100644 --- a/threadedtree/threadedtree.py +++ b/threadedtree/threadedtree.py @@ -220,32 +220,82 @@ def remove(self, value): def bi_iter(self): class BidirectionalIterator(object): def __init__(self, reference_object): - self.reference = reference_iterator - self.current_pointer = self.head() + self.reference = reference_object + self.current_pointer = None + self.head() def next(self): - pass + select = self.peek() + self.current_pointer = self.reference._next(self.current_pointer) + return select def prev(self): + select = self.peek() + self.current_pointer = self.reference._prev(self.current_pointer) + return select + + def forward(self): + # This should return an iterable of some sort. Probably a generator. pass - def head(self): + def reverse(self): + # This should return an iterable of some sort. Probably a generator. pass + def head(self): + self.current_pointer = self.reference._head(self.current_pointer) + return self.peek() + def tail(self): - pass + self.current_pointer = self.reference._tail(self.current_pointer) + return self.peek() + + def peek(self): + select = self.reference._peek(self.current_pointer) + if select == None: + raise StopIteration + return self.reference._peek(self.current_pointer) + + return BidirectionalIterator(self) def _next(self, pointer): - pass + selected = False + current = pointer + while current.lthreaded: + current = current.left + if current != pointer: + return current + while current != None: + if current != pointer: + return current + if not current.rthreaded: + current = current.right + else: + node = current.right + while node.lthreaded: + node = node.left + current = node - def _previous(self, pointer): + def _prev(self, pointer): pass def _head(self, pointer): - pass + current = self.root + while current.lthreaded: + current = current.left + return current def _tail(self, pointer): - pass + current = self.root + while current.rthreaded: + current = current.right + return current + + def _peek(self, pointer): + try: + return pointer.val + except AttributeError: + return pointer def _implements_comparisons(self, value): """Private method that determines if value implements either __cmp__ or both __lt__ and __gt__""" From cb3a3bd59e4a4bb8ed879b70372de00ad3d1f27b Mon Sep 17 00:00:00 2001 From: Tyler Springer Date: Thu, 23 Feb 2017 11:19:14 -0800 Subject: [PATCH 03/12] Fully implemented the bi_directional iterator step functions. But there are still some bugs... --- threadedtree/threadedtree.py | 39 ++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/threadedtree/threadedtree.py b/threadedtree/threadedtree.py index 936b962..ceb704a 100644 --- a/threadedtree/threadedtree.py +++ b/threadedtree/threadedtree.py @@ -25,24 +25,20 @@ def __init__(self, iterable=[], duplicate_strategy="none", root=None): 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 + 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.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.") + 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 = root self._len = 0 self.duplicate_strategy = duplicate_strategy @@ -259,12 +255,7 @@ def peek(self): return BidirectionalIterator(self) def _next(self, pointer): - selected = False current = pointer - while current.lthreaded: - current = current.left - if current != pointer: - return current while current != None: if current != pointer: return current @@ -277,7 +268,11 @@ def _next(self, pointer): current = node def _prev(self, pointer): - pass + current = pointer + while current != None: + if current != pointer: + return current + current = current.left def _head(self, pointer): current = self.root From 6958a79f4e2104117e9b5bd26c2bb316a66b507f Mon Sep 17 00:00:00 2001 From: Tyler Springer Date: Thu, 23 Feb 2017 12:28:06 -0800 Subject: [PATCH 04/12] Pretty much finished the bidirectional iterator. But I think it has a major design flaw. Going to try a different way... --- threadedtree/threadedtree.py | 37 ++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/threadedtree/threadedtree.py b/threadedtree/threadedtree.py index ceb704a..bfeb508 100644 --- a/threadedtree/threadedtree.py +++ b/threadedtree/threadedtree.py @@ -218,16 +218,41 @@ class BidirectionalIterator(object): def __init__(self, reference_object): self.reference = reference_object self.current_pointer = None + self.at_head = True + self.at_tail = False + self.forward_dir = True self.head() def next(self): + if self.at_tail: + self.forward_dir = False + return None + if self.forward_dir == False: + self.current_pointer = self.reference._next(self.current_pointer) select = self.peek() - self.current_pointer = self.reference._next(self.current_pointer) + self.at_head = False + if self.current_pointer == self.reference._tail(): + self.at_tail = True + else: + self.current_pointer = self.reference._next(self.current_pointer) + self.forward_dir = True return select def prev(self): + if self.at_head: + self.forward_dir = True + return None + if self.forward_dir == True: + if not self.at_tail: + self.current_pointer = self.reference._prev(self.current_pointer) + self.current_pointer = self.reference._prev(self.current_pointer) select = self.peek() - self.current_pointer = self.reference._prev(self.current_pointer) + self.at_tail = False + if self.current_pointer == self.reference._head(): + self.at_head = True + else: + self.current_pointer = self.reference._prev(self.current_pointer) + self.forward_dir = False return select def forward(self): @@ -239,11 +264,11 @@ def reverse(self): pass def head(self): - self.current_pointer = self.reference._head(self.current_pointer) + self.current_pointer = self.reference._head() return self.peek() def tail(self): - self.current_pointer = self.reference._tail(self.current_pointer) + self.current_pointer = self.reference._tail() return self.peek() def peek(self): @@ -274,13 +299,13 @@ def _prev(self, pointer): return current current = current.left - def _head(self, pointer): + def _head(self): current = self.root while current.lthreaded: current = current.left return current - def _tail(self, pointer): + def _tail(self): current = self.root while current.rthreaded: current = current.right From 1901923f71d648837758ff139fa56f0ca25116a4 Mon Sep 17 00:00:00 2001 From: Tyler Springer Date: Thu, 23 Feb 2017 12:54:15 -0800 Subject: [PATCH 05/12] Fully implemented what I think is a fucntioning bi-directional iterator...time to test --- threadedtree/threadedtree.py | 66 ++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 36 deletions(-) diff --git a/threadedtree/threadedtree.py b/threadedtree/threadedtree.py index bfeb508..8b56708 100644 --- a/threadedtree/threadedtree.py +++ b/threadedtree/threadedtree.py @@ -218,50 +218,23 @@ class BidirectionalIterator(object): def __init__(self, reference_object): self.reference = reference_object self.current_pointer = None - self.at_head = True - self.at_tail = False - self.forward_dir = True self.head() def next(self): - if self.at_tail: - self.forward_dir = False + n = self.reference._next(self.current_pointer) + if n == None: return None - if self.forward_dir == False: - self.current_pointer = self.reference._next(self.current_pointer) - select = self.peek() - self.at_head = False - if self.current_pointer == self.reference._tail(): - self.at_tail = True else: - self.current_pointer = self.reference._next(self.current_pointer) - self.forward_dir = True - return select + self.current_pointer = n + return self.peek() def prev(self): - if self.at_head: - self.forward_dir = True + p = self.reference._prev(self.current_pointer) + if p == None: return None - if self.forward_dir == True: - if not self.at_tail: - self.current_pointer = self.reference._prev(self.current_pointer) - self.current_pointer = self.reference._prev(self.current_pointer) - select = self.peek() - self.at_tail = False - if self.current_pointer == self.reference._head(): - self.at_head = True else: - self.current_pointer = self.reference._prev(self.current_pointer) - self.forward_dir = False - return select - - def forward(self): - # This should return an iterable of some sort. Probably a generator. - pass - - def reverse(self): - # This should return an iterable of some sort. Probably a generator. - pass + self.current_pointer = p + return self.peek() def head(self): self.current_pointer = self.reference._head() @@ -279,6 +252,21 @@ def peek(self): return BidirectionalIterator(self) + def reverse(self): + 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): current = pointer while current != None: @@ -297,7 +285,13 @@ def _prev(self, pointer): while current != None: if current != pointer: return current - current = current.left + if not current.lthreaded: + current = current.left + else: + node = current.left + while node.rthreaded: + node = node.right + current = node def _head(self): current = self.root From 659977acad37e1c75fb2d472238e2b7bc5a7d34a Mon Sep 17 00:00:00 2001 From: Tyler Springer Date: Thu, 23 Feb 2017 12:59:14 -0800 Subject: [PATCH 06/12] Fixed some minor formatting problems --- threadedtree/threadedtree.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/threadedtree/threadedtree.py b/threadedtree/threadedtree.py index 8b56708..ad07f96 100644 --- a/threadedtree/threadedtree.py +++ b/threadedtree/threadedtree.py @@ -21,22 +21,22 @@ 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. 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) + 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 + 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 = root @@ -143,14 +143,14 @@ 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 @@ -198,14 +198,14 @@ def insert(self, value): 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 From fe178527af78a028d56ef5967a5fd4da78d8f177 Mon Sep 17 00:00:00 2001 From: Tyler Springer Date: Thu, 23 Feb 2017 13:34:33 -0800 Subject: [PATCH 07/12] Did some testing of the bidirectional iterator and I think it works. Need to write an official test suite to confirm. --- threadedtree/threadedtree.py | 84 ++++++++++++++++++++---------------- 1 file changed, 47 insertions(+), 37 deletions(-) diff --git a/threadedtree/threadedtree.py b/threadedtree/threadedtree.py index ad07f96..5ac7646 100644 --- a/threadedtree/threadedtree.py +++ b/threadedtree/threadedtree.py @@ -214,42 +214,6 @@ def remove(self, value): return False def bi_iter(self): - class BidirectionalIterator(object): - def __init__(self, reference_object): - self.reference = reference_object - self.current_pointer = None - self.head() - - def next(self): - n = self.reference._next(self.current_pointer) - if n == None: - return None - else: - self.current_pointer = n - return self.peek() - - def prev(self): - p = self.reference._prev(self.current_pointer) - if p == None: - return None - else: - self.current_pointer = p - return self.peek() - - def head(self): - self.current_pointer = self.reference._head() - return self.peek() - - def tail(self): - self.current_pointer = self.reference._tail() - return self.peek() - - def peek(self): - select = self.reference._peek(self.current_pointer) - if select == None: - raise StopIteration - return self.reference._peek(self.current_pointer) - return BidirectionalIterator(self) def reverse(self): @@ -482,4 +446,50 @@ def _delete_with_right_child(self, current, parent): else: parent.left = current.right del current - return True \ No newline at end of file + return True + +class BidirectionalIterator(object): + def __init__(self, reference_object): + self.reference = reference_object + self.current_pointer = None + self.head() + + def next(self): + n = self.reference._next(self.current_pointer) + if n == None: + return None + else: + self.current_pointer = n + return self.peek() + + def prev(self): + p = self.reference._prev(self.current_pointer) + if p == None: + return None + else: + self.current_pointer = p + return self.peek() + + def has_next(self): + if self.reference._next(self.current_pointer) == None: + return False + return True + + def has_prev(self): + if self.reference._prev(self.current_pointer) == None: + return False + return True + + def head(self): + self.current_pointer = self.reference._head() + return self.peek() + + def tail(self): + self.current_pointer = self.reference._tail() + return self.peek() + + def peek(self): + select = self.reference._peek(self.current_pointer) + if select == None: + raise StopIteration + return self.reference._peek(self.current_pointer) \ No newline at end of file From 152e7868e8c7a219d8ffb3a2885f62647ffd4dba Mon Sep 17 00:00:00 2001 From: Tyler Springer Date: Thu, 23 Feb 2017 17:39:21 -0800 Subject: [PATCH 08/12] Polishing... --- threadedtree/threadedtree.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/threadedtree/threadedtree.py b/threadedtree/threadedtree.py index 5ac7646..cd34830 100644 --- a/threadedtree/threadedtree.py +++ b/threadedtree/threadedtree.py @@ -258,12 +258,14 @@ def _prev(self, pointer): current = node def _head(self): + # TODO: This should be a O(1) operation. It is currently O(n) current = self.root while current.lthreaded: current = current.left return current def _tail(self): + # TODO: This should be a O(1) operation. It is currently O(n) current = self.root while current.rthreaded: current = current.right @@ -454,6 +456,9 @@ def __init__(self, reference_object): self.current_pointer = None self.head() + def __repr__(self): + return str(self.peek()) + def next(self): n = self.reference._next(self.current_pointer) if n == None: From 44b20d43c7cc61483199d94da450ef7756e73f17 Mon Sep 17 00:00:00 2001 From: Tyler Springer Date: Thu, 23 Feb 2017 17:53:44 -0800 Subject: [PATCH 09/12] Iterator needed a __len__() --- threadedtree/threadedtree.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/threadedtree/threadedtree.py b/threadedtree/threadedtree.py index cd34830..c43e3fc 100644 --- a/threadedtree/threadedtree.py +++ b/threadedtree/threadedtree.py @@ -456,6 +456,9 @@ def __init__(self, reference_object): self.current_pointer = None self.head() + def __len__(self): + return len(self.reference) + def __repr__(self): return str(self.peek()) From 6395d0222fa4bf8c8947b6709bfec8e8e831b56a Mon Sep 17 00:00:00 2001 From: Tyler Springer Date: Thu, 23 Feb 2017 20:14:45 -0800 Subject: [PATCH 10/12] Fixed head() and tail() to be O(1) instead of O(n). Checking in the test pattern. --- threadedtree/threadedtree.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/threadedtree/threadedtree.py b/threadedtree/threadedtree.py index c43e3fc..66ced60 100644 --- a/threadedtree/threadedtree.py +++ b/threadedtree/threadedtree.py @@ -39,7 +39,7 @@ def __init__(self, iterable=[], duplicate_strategy="none", root=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 = root + self.root = self.head = self.tail = root self._len = 0 self.duplicate_strategy = duplicate_strategy if isinstance(iterable, ThreadedTree): @@ -158,6 +158,7 @@ def insert(self, value): if self.root == None: self.root = self._new_node(value) + self.head = self.tail = self.root return current = self.root @@ -196,6 +197,11 @@ def insert(self, value): current.rthreaded = True new_node.left = current + if new_node.left == None: + self.head = new_node + elif new_node.right == None: + self.tail = new_node + def remove(self, value): """ Removes a node containing ``value`` from the tree. @@ -262,6 +268,7 @@ def _head(self): current = self.root while current.lthreaded: current = current.left + assert self.head == current return current def _tail(self): @@ -269,6 +276,7 @@ def _tail(self): current = self.root while current.rthreaded: current = current.right + assert self.tail == current return current def _peek(self, pointer): From 5e9f6dd03bd1b7da63053e6d6c2357169e3f9dcd Mon Sep 17 00:00:00 2001 From: Tyler Springer Date: Thu, 23 Feb 2017 20:16:26 -0800 Subject: [PATCH 11/12] _head() and tail() are now O(1) instead of O(n). --- threadedtree/threadedtree.py | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/threadedtree/threadedtree.py b/threadedtree/threadedtree.py index 66ced60..1f34a95 100644 --- a/threadedtree/threadedtree.py +++ b/threadedtree/threadedtree.py @@ -264,20 +264,10 @@ def _prev(self, pointer): current = node def _head(self): - # TODO: This should be a O(1) operation. It is currently O(n) - current = self.root - while current.lthreaded: - current = current.left - assert self.head == current - return current + return self.head def _tail(self): - # TODO: This should be a O(1) operation. It is currently O(n) - current = self.root - while current.rthreaded: - current = current.right - assert self.tail == current - return current + return self.tail def _peek(self, pointer): try: From e1347f688c1232dbb52505339471092cce23b6b5 Mon Sep 17 00:00:00 2001 From: Tyler Springer Date: Thu, 23 Feb 2017 21:23:36 -0800 Subject: [PATCH 12/12] Finished implementing BidirectionalIterator for ThreadedTree. Completed documentation of all relevant methods and put iterator code in its own module. --- threadedtree/__init__.py | 1 + threadedtree/bidirectionaliterator.py | 69 ++++++++++++++ ..._bidirectional_iterator_unbalanced_TBST.py | 80 ++++++++++++++++ .../tests/test_unbalanced_threaded_bst.py | 10 ++ threadedtree/threadedtree.py | 91 ++++++------------- 5 files changed, 189 insertions(+), 62 deletions(-) create mode 100644 threadedtree/bidirectionaliterator.py create mode 100644 threadedtree/tests/test_bidirectional_iterator_unbalanced_TBST.py diff --git a/threadedtree/__init__.py b/threadedtree/__init__.py index 27b5a62..8c8438e 100644 --- a/threadedtree/__init__.py +++ b/threadedtree/__init__.py @@ -14,4 +14,5 @@ # along with threadedtree. If not, see . from threadedtree import ThreadedTree +from bidirectionaliterator import BidirectionalIterator from treenodes import * \ No newline at end of file diff --git a/threadedtree/bidirectionaliterator.py b/threadedtree/bidirectionaliterator.py new file mode 100644 index 0000000..fa97f05 --- /dev/null +++ b/threadedtree/bidirectionaliterator.py @@ -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) \ No newline at end of file diff --git a/threadedtree/tests/test_bidirectional_iterator_unbalanced_TBST.py b/threadedtree/tests/test_bidirectional_iterator_unbalanced_TBST.py new file mode 100644 index 0000000..f051ec3 --- /dev/null +++ b/threadedtree/tests/test_bidirectional_iterator_unbalanced_TBST.py @@ -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 . + +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) + \ No newline at end of file diff --git a/threadedtree/tests/test_unbalanced_threaded_bst.py b/threadedtree/tests/test_unbalanced_threaded_bst.py index f4dc6d6..6966d68 100644 --- a/threadedtree/tests/test_unbalanced_threaded_bst.py +++ b/threadedtree/tests/test_unbalanced_threaded_bst.py @@ -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) diff --git a/threadedtree/threadedtree.py b/threadedtree/threadedtree.py index 1f34a95..07a90f7 100644 --- a/threadedtree/threadedtree.py +++ b/threadedtree/threadedtree.py @@ -15,7 +15,7 @@ """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.""" @@ -197,10 +197,13 @@ def insert(self, value): current.rthreaded = True new_node.left = current - if new_node.left == None: - self.head = new_node - elif new_node.right == None: - self.tail = new_node + 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): """ @@ -220,9 +223,21 @@ def remove(self, value): return False def bi_iter(self): - return BidirectionalIterator(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: @@ -238,9 +253,10 @@ def reverse(self): 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: + if current != pointer: # Pretty likely this is Theta(1) return current if not current.rthreaded: current = current.right @@ -251,9 +267,10 @@ def _next(self, pointer): 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: + if current != pointer: # Pretty likely this is Theta(1) return current if not current.lthreaded: current = current.left @@ -264,15 +281,17 @@ def _prev(self, pointer): 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 AttributeError: + except: return pointer def _implements_comparisons(self, value): @@ -446,56 +465,4 @@ def _delete_with_right_child(self, current, parent): else: parent.left = current.right del current - return True - -class BidirectionalIterator(object): - def __init__(self, reference_object): - 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): - n = self.reference._next(self.current_pointer) - if n == None: - return None - else: - self.current_pointer = n - return self.peek() - - def prev(self): - p = self.reference._prev(self.current_pointer) - if p == None: - return None - else: - self.current_pointer = p - return self.peek() - - def has_next(self): - if self.reference._next(self.current_pointer) == None: - return False - return True - - def has_prev(self): - if self.reference._prev(self.current_pointer) == None: - return False - return True - - def head(self): - self.current_pointer = self.reference._head() - return self.peek() - - def tail(self): - self.current_pointer = self.reference._tail() - return self.peek() - - def peek(self): - select = self.reference._peek(self.current_pointer) - if select == None: - raise StopIteration - return self.reference._peek(self.current_pointer) \ No newline at end of file + return True \ No newline at end of file