def infixToPostfix(infixexpr):
    precedence = {}
    precedence["*"] = 3
    precedence["/"] = 3
    precedence["+"] = 2
    precedence["-"] = 2
    precedence["("] = 1
    opStack = Stack()
    postfixList = []
    tokenList = infixexpr.split()

    for token in tokenList:
        if token in "ABCDEFGHIJKLMNOPQRSTUVWXYZ" or token in "0123456789":
            postfixList.append(token)
        elif token == '(':
            opStack.push(token)
        elif token == ')':
            topToken = opStack.pop()
            while topToken != '(':
                postfixList.append(topToken)
                topToken = opStack.pop()
        else:
            while (not opStack.isEmpty()) and \
               (precedence[opStack.peek()] >= precedence[token]):
                postfixList.append(opStack.pop())
            opStack.push(token)

    while not opStack.isEmpty():
        postfixList.append(opStack.pop())
    return " ".join(postfixList)
def parseTree(expression):
    tokens = expression.split()
    tree_holder = Stack()
    main_tree = BinaryTree('')
    tree_holder.push(main_tree)
    currentTree = main_tree
    for val in tokens:
        if val == '(':
            currentTree.add_left('')
            tree_holder.push(currentTree)
            currentTree = currentTree.get_left()

        elif val not in '+-*/':
            currentTree.set_root_val(val)
            parent = tree_holder.pop()
            currentTree = parent

        elif val in '+-*/':
            currentTree.set_root_val(val)
            currentTree.add_right('')
            tree_holder.push(currentTree)
            currentTree = currentTree.get_right()

        elif val == ')':
            currentTree = tree_holder.pop()
        else:
            raise ValueError

    return main_tree
Пример #3
0
def parenthesis_checker(symbolString):
    """ Read a string of parenthesis from left to right and decide
	whether the symbols are balanced
	"""

    S = Stack()

    #Input are valid
    if symbolString is None:
        raise TypeError("cannot be none")

    if not symbolString:
        raise ValueError("cannot be empty")

    for char in symbolString:
        if char == "(":
            S.push(char)

        elif char == "[":
            S.push(char)
        elif char == "{":
            S.push(char)

        else:
            if S.isEmpty():
                return False
            else:
                S.pop()

    return S.isEmpty()
Пример #4
0
def infixtopostfix(string):

    comp = string.split(' ')
    output = []
    operator_precedence = {"+": 1, "-": 1, "/": 2, "*": 2, "(": 3, ")": 3}
    operator_stack = Stack()

    for value in comp:
        if value in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' or value in '0123456789':
            output.append(value)
        elif value == '(':
            operator_stack.push(value)
        elif value == ')':
            operator = operator_stack.pop()
            while operator != '(':
                output.append(operator)
                operator = operator_stack.pop()
        else:
            while not operator_stack.isEmpty() and \
                    operator_precedence[operator_stack.peek()] <= operator_precedence[value]:
                output.append(operator_stack.pop())
            operator_stack.push(value)

    while not operator_stack.isEmpty():
        token = operator_stack.pop()
        output.append(token)

    return ' '.join(output)
Пример #5
0
def evaluate_postfix(expr):

    if len(expr) == 0:
        return "Expression is invalid"

    nums = Stack()

    for char in expr:
        if char in "+*-":
            num1 = nums.pop()
            num2 = nums.pop()
            if char == "+":
                subtotal = num1 + num2
            elif char == "*":
                subtotal = num1 * num2
            elif char == "-":
                subtotal = num2 - num1
            nums.push(subtotal)
        else:
            nums.push(int(char))

    if len(nums._items) > 1:
        return "Expression is invalid"

    return nums.pop()
Пример #6
0
 def test_pop(self):
     """
         Test Case to test the pop operation
     """
     stack = Stack()
     stack.push(1)
     num = stack.pop()
     self.assertEqual(num, 1)
     stack.push(2)
     stack.push(3)
     num = stack.pop()
     self.assertEqual(num, 3)
Пример #7
0
 def test_size(self):
     """
         Test Case to check the size of the stack after push and pop operations
     """
     stack = Stack()
     self.assertEqual(stack.length, 0, STACK_SIZE_ZERO_MSG)
     stack.push(1)
     self.assertEqual(stack.length, 1, INCORRECT_STACK_SIZE_MSG)
     stack.push(2)
     stack.push(3)
     self.assertEqual(stack.length, 3, INCORRECT_STACK_SIZE_MSG)
     stack.pop()
     self.assertEqual(stack.length, 2, INCORRECT_STACK_SIZE_MSG)
Пример #8
0
    def test_pop_on_empty_stack(self):
        """
            Test Case to test pop operation on Empty Stack
        """
        stack = Stack()
        stack.push(1)
        stack.pop()
        self.assertEqual(stack.length, 0, STACK_SIZE_ZERO_MSG)

        # Stack should be empty after one push and one pop operation
        # Should raise EmptyStackException on pop()
        with self.assertRaises(exceptions.EmptyStackException):
            stack.pop()
Пример #9
0
def postfixEval(postfixExpr):
    operandStack = Stack()
    tokenList = postfixExpr.split()

    for token in tokenList:
        if token.isdigit():
            operandStack.push(int(token))
        else:
            operand2 = operandStack.pop()
            operand1 = operandStack.pop()
            result = doMath(token, operand1, operand2)
            operandStack.push(result)
    return operandStack.pop()
    def get_digits(self):
        """
        This pushes numbers in stack. Then it converts that stack into a dictionary value with an alphabetic key.

        The reason we do this is that the calculations function cannot handle multiple digits because it is
        processing each element by character. For example, 2 + 2 can be processed but 10 + 10 cannot because it would
        be expecting a + - * / operator between the 1 and the 0.

        It is easier to have proxy variables, such as A + B rather than 10 + 10, and use that to pull dictionary values.
        The result would look like self.numbers.get('A') + self.numbers.get('B') with the self.numbers dictionary
        looking like {'A': 10, 'B': 10}. The calculator would read this as 10 + 10 without issue.

        Thus, the purpose of this function is to build that self.numbers dictionary and build self.final_infix with
        alphabetical variables substituting multi-digit numbers. self.final_infix is a word that looks like 'A+B'.
        """
        operands = Stack()
        alphabet_num = 65

        for char in self.infix_split:
            if char in '0123456789.':
                operands.push(char)
            elif not operands.isempty():
                alphabet = chr(alphabet_num)
                alphabet_num += 1
                number = ''
                # Converts number into a dictionary item.
                while not operands.isempty():
                    number = operands.pop() + number
                self.numbers[alphabet] = number
                self.final_infix += alphabet + char
            else:
                self.final_infix += char
Пример #11
0
    def dft(self, starting_vertex_id):
        """
        Traverse and print each vertex in depth-first order (DFT) beginning from starting_vertex.
        """
        # Initialize empty stack and set of visited nodes:
        stack = Stack()
        visited = set()

        # Check if provided starting vertex is in our graph:
        if starting_vertex_id in self.vertices.keys():
            # If so, add starting vertex to stack:
            stack.push(starting_vertex_id)
        else:
            return IndexError(
                f"Provided starting vertex {starting_vertex_id} does not exist in graph!"
            )

        # Process all vertices via BFT:
        while stack.size() > 0:
            # Get next vertex to process as first item in stack:
            current_vertex = stack.pop()
            # If the current vertex has not already been visited+processed, process it:
            if current_vertex not in visited:
                # Process current vertex:
                print(current_vertex)
                # Add adjacent vertices to stack for future processing:
                for adjacent_vertex in self.get_neighbors(current_vertex):
                    stack.push(adjacent_vertex)
                # Mark current vertex as "visited" by adding to our set of visited vertices:
                visited.add(current_vertex)
def parChecker(symbolString):
    '''
    in: a string containing all symbols
    return: boolean, true for balaced, vice versa
    Completed extended parenthesis checker for: [,{,(,),},]
    '''
    leftPar = Stack()
    strList = list(symbolString)
    balanced = True
    symbol = {'[':']','{':'}','(':')'}
    i = 0
    
    while i < len(symbolString) and balanced:

        if strList[i] in ['[','(','{']:
            leftPar.push(strList[i])
            
        else:
            if leftPar.isEmpty():
                balanced = False
                
            else:
                if not strList[i] == symbol[leftPar.pop()]:
                    balanced = False
        i += 1
    
    if not leftPar.isEmpty():
        balanced = False
    
    
    return balanced
Пример #13
0
    def randomize(maze):
        stack = Stack()
        current = maze.board[0][0]
        visited_count = 0

        while True:
            q = current.get_random_nbr()
            if not current.visited:
                visited_count += 1

            current.visited = True
            if q is None:
                if stack.size > 0:
                    current = stack.pop()
                else:
                    raise StopIteration()
            else:
                next, x = q
                stack.push(current)
                current.unlock(x)
                next.unlock((x + 2) % 4)
                current = next

            if visited_count >= maze.length * maze.height:
                raise StopIteration()

            yield current
Пример #14
0
    def test_top_on_empty_stack(self):
        """
            Test Case to test top property on Empty Stack
        """
        stack = Stack()
        stack.push(1)
        top_item = stack.top
        self.assertEqual(top_item, 1)
        self.assertEqual(stack.length, 1)

        stack.pop()

        self.assertEqual(stack.length, 0, STACK_SIZE_ZERO_MSG)

        # Should raise EmptyStackException on top on empty stack
        with self.assertRaises(exceptions.EmptyStackException):
            stack.top
Пример #15
0
def test_stack():
	stack = Stack()
	stack.push(3)
	stack.push(2)
	assert stack.peek() == 2
	assert stack.pop() == 2
	assert stack.pop() == 3
	assert stack.is_empty()
def paranthesis_checker(string):
    stack = Stack()
    balanced = True
    index = 0
    while index < len(string) and balanced:
        symbol = string[index]
        if symbol == '(':
            stack.push(symbol)
        else:
            if stack.isEmpty():
                balanced = False
            else:
                stack.pop()
        index = index + 1

    if balanced and stack.isEmpty():
        return True
    else:
        return False
    def insert_node(self, node):
        lessFreq_stack = Stack()

        while self.stack.num_elements>0 and self.stack.top().get_freq() < node.get_freq():
            lessFreq_stack.push(self.stack.pop())

        self.stack.push(node)

        while lessFreq_stack.num_elements>0:
            self.stack.push(lessFreq_stack.pop())
Пример #18
0
def test_pop():
    stack, arr = Stack(), []
    assert(stack.is_empty())
    with pytest.raises(AssertionError):
        stack.pop()
    for _ in range(ITERS):
        expected = random.randrange(MAX_VAL)
        stack.push(expected)
        actual = stack.peek()
        arr = [expected] + arr
        assert(len(stack) == len(arr))
        assert(actual == expected)
    for _ in range(ITERS):
        expected, actual = arr[0], stack.pop()
        arr = arr[1:]
        assert(len(stack) == len(arr))
        assert(actual == expected)
    assert(stack.is_empty())
    with pytest.raises(AssertionError):
        stack.pop()
def parens_checker(parens_string):
    """Checks if parentheses in given string is balanced"""

    open_parens = Stack()

    for paren in parens_string:
        if paren == "(":
            open_parens.push(paren)
        else:
            if open_parens.is_empty():
                return False
            open_parens.pop()
    
    # if open_parens:
    #     return False
    # else:
    #     return True

    # The above can be simplified to a boolean check of is the stack empty

    return open_parens.is_empty()
Пример #20
0
def HexToBinary(number):
    bin_holder = Stack()
    while number > 0:
        rem = number % 2
        bin_holder.push(rem)
        number = number // 2

    bin_string = ""
    while not bin_holder.isEmpty():
        bin_string = bin_string + str(bin_holder.pop())

    return bin_string
def rev_string(my_string):
    """Uses stack to reverse characters in string"""
    chars = Stack()

    for char in my_string:
        chars.push(char)

    reversed_string = ''

    while not chars.is_empty():
        reversed_string += chars.pop()

    return reversed_string
Пример #22
0
def dfs(graph: Graph, start_node: str, target_node: str, visited_nodes: set):
    stack = Stack()
    stack.push(start_node)

    while not stack.is_empty():
        current = stack.pop()
        if current == target_node:
            return True

        adj = graph.get_edges_node(current)
        for node in adj:
            if node not in visited_nodes:
                stack.push(node)
    return False
Пример #23
0
def test_stack():
    f = Stack()

    test_array = [i for i in range(100)]

    for i in test_array:
        f.push(i)

    result = []

    while not f.is_empty():
        result.append(f.pop())
    test_array.reverse()
    assert test_array == result
    def calculations(self):
        """
        This is where the calculator does its magic.

        To see how this function works, please reference the textbook material on Chapter 3 Stacks. This algorithm was
        built by making an algorithm that takes an infix expression and transforms it into a postfix expression
        (2+2 --> 22+). Then, by taking that postfix expression and processing the operators and operands it until there
        is only 1 element in the stack, we get the postfix answer, which is the same answer for the infix expression.
        (22+ --> 4)

        It is possible to build a calculator with simply those 2 algorithms linked up to each other. However, this
        function is a hybrid of the two where it is able to simultaneously work with operators and operands, such that
        when an infix expression is determined to be in postfix expression, we immediately calculate the answer.
        """
        precedent = {'*': 3, '/': 3, '+': 2, '-': 2, '(': 1}
        operator_stack = Stack()
        operand_stack = Stack()

        for item in self.final_infix:
            if item in string.ascii_letters:
                operand_stack.push(self.numbers.get(item))
            elif item == '(':
                operator_stack.push(item)
            elif item == ')':
                # Here be dragons.
                top_of_stack = operator_stack.pop()
                while top_of_stack != '(':
                    operand2 = float(operand_stack.pop())
                    operand1 = float(operand_stack.pop())
                    operand_stack.push(self.do_math(top_of_stack, operand1, operand2))
                    top_of_stack = operator_stack.pop()
            elif item in precedent:
                while (not operator_stack.isempty()) and precedent[operator_stack.peek()] >= precedent[item]:
                    operand2 = float(operand_stack.pop())
                    operand1 = float(operand_stack.pop())
                    operand_stack.push(self.do_math(operator_stack.pop(), operand1, operand2))
                operator_stack.push(item)
            elif item == '~':
                # This is a helper variable to signal the end of infix expression and empty all stacks.
                while not operator_stack.isempty():
                    operand2 = float(operand_stack.pop())
                    operand1 = float(operand_stack.pop())
                    operand_stack.push(self.do_math(operator_stack.pop(), operand1, operand2))
            else:
                raise TypeError('Error: Calculations blew up.')

        self.answer = operand_stack.pop()
def baseConverter(decNum, base):
    digits = "0123456789ABCDEF"

    remstack = Stack()

    while decNum > 0:
        remainder = decNum % base
        remstack.push(remainder)
        decNum = decNum // base

    newString = ""
    while not remstack.isEmpty():
        newString = newString + digits[remstack.pop()]

    return newString
def check_balanced_symbols(symbols_string):
    """Checks if opening and closing symbols in a string are balanced"""

    open_symbols = Stack()

    for symbol in symbols_string:
        if symbol in "({[":
            open_symbols.push(symbol)
        elif symbol in ")}]":
            if open_symbols.is_empty():
                return False
            open_symbol = open_symbols.pop()
            if not is_matching_symbols(open_symbol, symbol):
                return False

    return open_symbols.is_empty()
def revstring(mystr):
    """
    :type mystr: string
    :rtype: string
    use Stack Last In First Out feature to reverse a string
    """
    aStack = Stack()

    for i in list(mystr):
        aStack.push(i)

    revstr = ""

    while not aStack.isEmpty():
        revstr += aStack.pop()

    return revstr
Пример #28
0
def dfs(grid:np.array, start:tuple, end:tuple, _stack: Stack = Stack()) -> Tuple[list, list]:
    """ A function that searches for a path in a grid using the Depth-first-search algorithm

        This function searches through a grid searching for a path using the Depth-first-search algorithm. The function
        first pushes the first Node object into the stack. A VisitedNodes object is created (visited). While there are
        objects in the stack, the first node of the stack is removed and appended in visited. The node's position is
        checked to see if it is equal to the end position; if it is, then a list of visited nodes and a path list is
        returned. Otherwise, the function iterates over each child of the current node. The child is checked to see
        if it is in the stack or in visited nodes, and is accessible. If true, the child node is pushed into the stack
        and the process repeats.

        Parameters
        ----------
            grid : np.array
                a numpy array detailing the grid
            start : tuple
                a tuple detailing the starting node's position
            end : tuple
                a tuple detailing the ending node's position
            _stack : Stack = Stack()
                a stack object
        Returns
        -------
            Tuple[list,list]
                A list of visited nodes and a list of nodes that are included in the path
    """

    if grid[start[0]][start[1]] == 1 or grid[end[0]][end[1]] == 1:
        return [None],[None]

    _stack.push(Node(grid, start))
    visited = VisitedNodes()

    while len(_stack) > 0:
            node = _stack.pop()
            visited._store_node(node)
            if node.pos == end:
                visited_list,path_list = visited.create_path(node.pos, start)
                return visited_list,path_list
            else:
                for child in node.children:
                    if child["node"] not in visited.visited_nodes and child["node"] not in _stack.stacked_nodes and child["accessibility"]:
                            _stack.push(Node(grid, child["node"], parent=node.pos))
    return [None],[None]
Пример #29
0
def dfs(initial, goal_test, successors):
    """
    Depth-first search.

    Parameters
    ----------
    initial : Generic
        Starting point of the search.
    goal_test : Callable
        Callable returing boolean value indicating search success.
    successors : Callable
        Callable returning list of next possible locations in search space.

    Returns
    -------
    found : Generic
        Node corresponding to successful goal_test.
        Returns None if search fails.
    """
    # References to candidate and previously-explored nodes in search space
    frontier = Stack()
    explored = Stack()

    # Initialize candidate search locations with initial condition
    frontier.push(Node(initial, None))

    # Continue search as long as their are candidates in the search space
    while not frontier.empty:
        current_node = frontier.pop()
        current_state = current_node.state
        # If current node meets goal, then search completes successfully
        if goal_test(current_state):
            return current_node
        # Populate next step in search
        for child in successors(current_state):
            # Skip previously-explored states
            if child in explored: 
                continue
            explored.push(child)
            frontier.push(Node(child, current_node))
    # Search terminates without finding goal
    return None
def parChecker(string):
    s = Stack()
    i = 0
    balanced = True

    while i < len(string) and balanced:
        symbol = string[i]
        if symbol in "([{":
            s.push(symbol)
        elif s.isEmpty():
            balanced = False
        elif symbol in ")]}":
            top = s.pop()
            if not matches(top, symbol):
                balanced = False
        i += 1

    if balanced and s.isEmpty():
        return True
    else:
        return False
Пример #31
0
    def dfs(self, starting_vertex, target_vertex):
        """
        Return a list containing the shortest path from starting_vertex to destination_vertex, 
        after searching for and finding it with a depth-first search (DFS) algorithm.
        """
        # Initialize empty stack and set of visited nodes:
        stack = Stack()
        visited = set()

        # Initialize path (we will add the rest of the path from starting vertex to target vertex below):
        path = [starting_vertex]

        # Check if provided starting vertex is in our graph:
        if starting_vertex in self.vertices.keys():
            # If so, add starting vertex to stack:
            stack.push(path)
        else:
            return IndexError(
                f"Provided starting vertex {starting_vertex} does not exist in graph!"
            )

        # Process all vertices via BFT:
        while stack.size() > 0:
            # Get next vertex to process as first item in stack:
            current_path = stack.pop()
            current_vertex = current_path[-1]
            # If the current vertex has not already been visited+processed, check and process it:
            if current_vertex not in visited:
                # Check if it is the target --> if so, return its full path:
                if current_vertex == target_vertex:
                    return current_path
                # If not, then get its neighbor vertices and add their paths to the stack for future processing:
                for adjacent_vertex in self.get_neighbors(current_vertex):
                    adjacent_vertex_path = current_path + [adjacent_vertex]
                    stack.push(adjacent_vertex_path)
                # Mark current vertex as "visited" by adding to our set of visited vertices:
                visited.add(current_vertex)

        # If no path found in entire graph, return None:
        return None
Пример #32
0
def baseConverter(decNumber,base):
    '''
    decNumber: decimal number
    base: int between 2 and 16
    
    Algorithm for binary conversion can easily be extended to perform the 
    conversion for any base. In computer science it is common to use a number
    of different encodings. The most common of these are binary, octal (base 8)
    , and hexadecimal (base 16).
    '''
    
    baseNumber = ''
    reminders = Stack()
    
    while decNumber > 0:
        reminders.push(decNumber%base)
        decNumber = decNumber//base
    
    while not reminders.isEmpty():
        baseNumber += str(reminders.pop())
    
    return baseNumber
Пример #33
0
    def attempt_2(self, head: ListNode, k: int) -> ListNode:
        """
        Better solution: Insert all elements into a stack and pop k elements. The last popped is returned.
        Don't need to know the length of the list in this algorithm.
        :param head: head of list
        :param k: parameter, k
        :return: returns the k'th to last element
        :Time: O(N)
        :Space: O(N)
        """
        stack = Stack()
        curr = head
        while curr is not None:
            stack.push(curr)
            curr = curr.next

        ret_val = None
        for i in range(k):
            popped = stack.pop()
            if i == k - 1:
                ret_val = popped

        return ret_val
Пример #34
0
class TestStack(unittest.TestCase):  # LIFO

    def setUp(self):
        self.emptyStack = Stack()
        self.Stack = Stack()

        for i in range(5):
            self.Stack.push(i)

    def test_repr(self):
        '''Test returning the stack as a string literal.'''
        self.assertEqual(repr(self.emptyStack), str(()))
        tup = (1, 3.14, 'foo', True)

        for i in tup:
            self.emptyStack.push(i)

        self.assertEqual(repr(self.emptyStack), str((True, 'foo', 3.14, 1)))

    def test_len_size(self):
        '''Test returning the size of the stack.'''
        for i in range(100):
            self.emptyStack.push(i)

        self.assertEqual(len(self.emptyStack), 100)
        self.assertEqual(self.emptyStack.size(), 100)

        self.emptyStack.pop()
        self.assertEqual(len(self.emptyStack), 99)
        self.assertEqual(self.emptyStack.size(), 99)

    def test_tuple(self):
        '''Test returning the stack as a tuple literal.'''
        self.assertEqual(tuple(self.emptyStack), ())
        tup = (1, 3.14, 'foo', True)

        for i in tup:
            self.emptyStack.push(i)

        self.assertEqual(tuple(self.emptyStack), (True, 'foo', 3.14, 1))

    def test_list(self):
        '''Test returning the stack as a list literal.'''
        self.assertEqual(list(self.emptyStack), [])
        li = [1, 3.14, 'foo', True]

        for i in li:
            self.emptyStack.push(i)

        self.assertEqual(list(self.emptyStack), [True, 'foo', 3.14, 1])

    def test_push(self):
        '''Test pushing items on top of the stack.'''
        self.Stack.push(True)
        self.assertEqual(tuple(self.Stack), (True, 4, 3, 2, 1, 0))

    def test_pop(self):
        '''Test popping off and returning the top of the stack.'''
        self.assertEqual(self.Stack.pop(), 4)
        self.assertEqual(tuple(self.Stack), (3, 2, 1, 0))
        self.assertEqual(self.Stack.pop(), 3)
        self.assertEqual(self.Stack.pop(), 2)
        self.assertEqual(self.Stack.pop(), 1)
        self.assertEqual(self.Stack.pop(), 0)
        self.assertEqual(tuple(self.Stack), ())

        with self.assertRaises(ValueError):
            self.Stack.pop()

    def test_peek(self):
        '''Test peeking at the top of the stack w/o modifying the stack.'''
        self.assertEqual(self.Stack.peek(), 4)

        with self.assertRaises(ValueError):
            self.emptyStack.peek()
Пример #35
0
class TestDataStructures(unittest.TestCase):

    def setUp(self):
        self.stack = Stack(range(1, 6))
        self.queue = Queue(range(1, 6))
        self.single = SinglyLinkedList()
        self.double = DoublyLinkedList()
        self.btree = BinaryTree()

    # STACK
    def test_stack_push(self):
        self.stack.push(6)
        self.assertEqual(self.stack.stack, range(1, 7)) 

    def test_stack_pop(self):
        self.assertEqual(self.stack.pop(), 5)
        self.assertEqual(self.stack.pop(), 4)

    def test_stack_peek(self):
        self.assertEqual(self.stack.peek(), 5)

    # QUEUE
    def test_queue_push(self):
        self.queue.push(6)
        self.assertEqual(self.queue.queue, range(1, 7))

    def test_queue_pop(self):
        self.assertEqual(self.queue.pop(), 1)
        self.assertEqual(self.queue.pop(), 2)

    def test_queue_peek(self):
        self.assertEqual(self.queue.peek(), 1)
        self.queue.pop()
        self.assertEqual(self.queue.peek(), 2)

    # SINGLY-LINKED LIST
    def test_add_nodes_to_single(self):
        node1, node2, node3 = Node(1), Node(2), Node(3)
        self.single._insert(node1)
        self.single._insert(node2, node1)
        self.assertEqual(self.single.head.value, 1)
        next = self.single.head.next
        self.assertEqual(next, node2)
        self.assertEqual(next.value, 2)
        self.single._insert(node3, node2)
        nextnext = self.single.head.next.next
        self.assertEqual(nextnext, node3)

    def test_add_node_to_beginning_of_single(self):
        node0, node1 = Node(0), Node(1)
        self.single._insert(node1)
        self.single._insert(node0)
        self.assertEqual(self.single.head.value, 0)
        self.assertEqual(self.single.head.next.value, 1)

    def test_remove_nodes_single(self):
        node1, node2, node3, node4 = Node(1), Node(2), Node(3), Node(4)
        self.single._insert(node1)
        self.single._insert(node2, node1)
        self.single._insert(node3, node2)
        self.single._insert(node4, node3)
        self.single._remove(node2)
        self.assertEqual(node2.next, node4)

    def test_remove_node_from_beginning_single(self):
        node1, node2, node3 = Node(1), Node(2), Node(3)
        self.single._insert(node1)
        self.single._insert(node2, node1)
        self.single._insert(node3, node2)
        self.single._remove()
        self.assertEqual(self.single.head, node2)

    def test_single_iteration(self):
        node1, node2, node3, node4 = Node(1), Node(2), Node(3), Node(4)
        self.single._insert(node1)
        self.single._insert(node2, node1)
        self.single._insert(node3, node2)
        self.single._insert(node4, node3)
        self.assertEqual([node for node in self.single], [node1, node2, node3, node4])

    def test_add_node_to_single_for_real(self):
        self.single.insert(2, 0)
        self.assertEqual(self.single[0].value, 2)
        self.single.insert(1, 0)
        self.assertEqual(self.single[0].value, 1)
        self.single.insert(3, 2)
        self.assertEqual(self.single[2].value, 3)
        self.single.insert(4, 100)
        self.assertEqual(self.single[3].value, 4)

    def test_remove_node_from_single_for_real(self):
        for i in range(4, 0, -1):
            self.single.insert(i, 0)
        # [1, 2, 3, 4]
        self.single.remove(1)
        # [1, 3, 4]
        self.assertEqual(self.single[1].value, 3)
        self.single.remove(2)
        # [1, 3]
        self.assertEqual(self.single[0].value, 1)
        self.assertEqual(self.single[1].value, 3)

   # DOUBLE-LINKED LIST
    def test_iteration_double(self):
        node1, node2, node3 = Node(1), Node(2), Node(3)
        node1.next, node2.next, node3.next = node2, node3, None
        node1.prev, node2.prev, node3.prev = None, node1, node2
        self.double.head = node1
        self.double.tail = node3
        # test __iter__
        self.assertEqual([str(i) for i in self.double], [str(i) for i in range(1, 4)])
        # test iterating over reversed
        self.assertEqual([str(i) for i in reversed(self.double)], [str(i) for i in range(3, 0, -1)])

    def test_double_slicing(self):
        node1, node2, node3 = Node(1), Node(2), Node(3)
        node1.next, node2.next, node3.next = node2, node3, None
        node1.prev, node2.prev, node3.prev = None, node1, node2
        self.double.head = node1
        self.double.tail = node3
        self.assertEqual(self.double[0].value, 1)
        self.assertEqual(self.double[1].value, 2)
        self.assertEqual(self.double[2].value, 3)
        self.assertRaises(IndexError, lambda val: self.double[val], 3)

    def test_base_insert_moving_forwards_with_double(self):
        node1, node2, node3 = Node(1), Node(2), Node(3)
        self.double._insert(node1)
        self.assertTrue(test_node(self.double[0], 1, None, None))
        self.assertTrue(test_linked_list(self.double, self.double[0], self.double[0]))

        self.double._insert(node3, node1)
        self.assertTrue(test_node(self.double[0], 1, 3, None))
        self.assertTrue(test_node(self.double[1], 3, None, 1))
        self.assertTrue(test_linked_list(self.double, self.double[0], self.double[1]))

        self.double._insert(node2, node1)
        self.assertTrue(test_node(self.double[0], 1, 2, None))
        self.assertTrue(test_node(self.double[1], 2, 3, 1))
        self.assertTrue(test_node(self.double[2], 3, None, 2))
        self.assertTrue(test_linked_list(self.double, self.double[0], self.double[2]))

        self.assertEqual(str(self.double), str([str(i) for i in range(1, 4)]))

    def test_base_insert_moving_backwards_with_double(self):
        node1, node2, node3 = Node(1), Node(2), Node(3)
        # insert node3 at the beginning/end of the list
        self.double._rev_insert(node3)
        self.assertTrue(test_node(self.double[0], 3, None, None))
        self.assertTrue(test_linked_list(self.double, self.double[0], self.double[0]))
        # insert node one before node 3 ([node1, node3])
        self.double._rev_insert(node1, node3)
        self.assertTrue(test_node(self.double[0], 1, 3, None))
        self.assertTrue(test_node(self.double[1], 3, None, 1))
        self.assertTrue(test_linked_list(self.double, self.double[0], self.double[1]))
        # insert node2 before node3 ([node1, node2, node3])
        self.double._rev_insert(node2, node3)
        self.assertTrue(test_node(self.double[0], 1, 2, None))
        self.assertTrue(test_node(self.double[1], 2, 3, 1))
        self.assertTrue(test_node(self.double[2], 3, None, 2))
        self.assertTrue(test_linked_list(self.double, self.double[0], self.double[2]))
        # check that the array is [node1, node2, node3]
        self.assertEqual(str(self.double), str([str(i) for i in range(1, 4)]))

    def test_insert_at_beginning_of_double(self):
        self.double.insert(2, 0)
        self.assertTrue(test_node(self.double[0], 2, None, None))
        self.assertTrue(test_linked_list(self.double, self.double[0], self.double[0]))
        self.double.insert(1, 0)
        self.assertTrue(test_node(self.double[0], 1, 2, None))
        self.assertTrue(test_node(self.double[1], 2, None, 1))
        self.assertTrue(test_linked_list(self.double, self.double[0], self.double[1]))

    def test_insert_in_middle_and_end_of_double(self):
        self.double.insert(1, 0)
        self.double.insert(3, 1)
        self.assertTrue(test_node(self.double[0], 1, 3, None))
        self.assertTrue(test_node(self.double[1], 3, None, 1))
        self.assertTrue(test_linked_list(self.double, self.double[0], self.double[1]))
        self.double.insert(2, 1) 
        self.assertTrue(test_node(self.double[0], 1, 2, None))
        self.assertTrue(test_node(self.double[1], 2, 3, 1))
        self.assertTrue(test_node(self.double[2], 3, None, 2))
        self.assertTrue(test_linked_list(self.double, self.double[0], self.double[2]))
        self.double.insert(5, 3) 
        self.assertTrue(test_node(self.double[0], 1, 2, None))
        self.assertTrue(test_node(self.double[1], 2, 3, 1))
        self.assertTrue(test_node(self.double[2], 3, 5, 2))
        self.assertTrue(test_node(self.double[3], 5, None, 3))
        self.assertTrue(test_linked_list(self.double, self.double[0], self.double[3]))
        self.double.insert(4, 3) 
        self.assertTrue(test_node(self.double[0], 1, 2, None))
        self.assertTrue(test_node(self.double[1], 2, 3, 1))
        self.assertTrue(test_node(self.double[2], 3, 4, 2))
        self.assertTrue(test_node(self.double[3], 4, 5, 3))
        self.assertTrue(test_node(self.double[4], 5, None, 4))
        self.assertTrue(test_linked_list(self.double, self.double[0], self.double[4]))

    # Binary Tree
    def test_binary_node_equality(self):
        one = BinaryNode(1)
        one2 = BinaryNode(1)
        self.assertTrue(one == one2)
        two = BinaryNode(2)
        self.assertFalse(one == two)

    def test_add_internal_child_to_binary_node(self):
        one = BinaryNode(1)
        two = BinaryNode(2)
        three = BinaryNode(3)
        one.left = three
        # one = {left: 3, right: None}
        one.insert(two, three)
        self.assertTrue(test_binary_node(one, 1, 2, None, None))
        self.assertTrue(test_binary_node(two, 2, 3, None, 1))
        self.assertTrue(test_binary_node(three, 3, None, None, 2))

    def test_add_external_child_to_binary_node(self):
        one = BinaryNode(1)
        two = BinaryNode(2)
        one.insert(two)
        self.assertTrue(test_binary_node(one, 1, 2, None, None))
        self.assertTrue(test_binary_node(two, 2, None, None, 1))
        three = BinaryNode(3)
        one.insert(three)
        self.assertTrue(test_binary_node(one, 1, 2, 3, None))
        self.assertTrue(test_binary_node(two, 2, None, None, 1))
        self.assertTrue(test_binary_node(three, 3, None, None, 1))
        four = BinaryNode(4)
        two.insert(four)
        self.assertTrue(test_binary_node(two, 2, 4, None, 1))
        self.assertTrue(test_binary_node(four, 4, None, None, 2))

    def test_depth_first_generation(self):
        self.btree.root = deep_btree()
        self.assertEquals([int(str(n)) for n in self.btree.depth_gen(self.btree.root)], range(1, 9))

    def test_breadth_first_generation(self):
        self.btree.root = wide_btree()
        self.assertEqual([int(str(n)) for n in self.btree.breadth_gen([self.btree.root])], range(1, 9))

    def test_binary_tree_iteration(self):
        self.btree.root = deep_btree()
        self.assertEquals([int(str(n)) for n in self.btree], range(1, 9))

    def test_contains(self):
        self.btree.root = deep_btree()
        for i in range(1, 9):
            self.assertTrue(i in self.btree)
        self.assertFalse(0 in self.btree)
        self.assertFalse(9 in self.btree)

    def test_insert_into_binary_tree(self):
        ## note - indexing grabs the node with that VALUE, not the node that exists at that index
        #       1
        self.btree.insert(1)
        self.assertTrue(test_binary_node(self.btree[1], 1, None, None, None))
        self.btree.insert(4, 1)
        #       1
        #      /
        #     4
        self.assertTrue(test_binary_node(self.btree[1], 1, 4, None, None))
        self.assertTrue(test_binary_node(self.btree[4], 4, None, None, 1))
        self.btree.insert(3, 1)
        #       1
        #      / \
        #     4   3
        self.assertTrue(test_binary_node(self.btree[1], 1, 4, 3, None))
        self.assertTrue(test_binary_node(self.btree[4], 4, None, None, 1))
        self.assertTrue(test_binary_node(self.btree[3], 3, None, None, 1))
        self.btree.insert(2, 1, 4)
        #       1
        #      / \
        #     2   3
        #    /
        #   4
        self.assertTrue(test_binary_node(self.btree[1], 1, 2, 3, None))
        self.assertTrue(test_binary_node(self.btree[2], 2, 4, None, 1))
        self.assertTrue(test_binary_node(self.btree[3], 3, None, None, 1))
        self.assertTrue(test_binary_node(self.btree[4], 4, None, None, 2))


    # Binary Search Tree
    def test_binary_search_node(self):
        root = binary_search_tree()
        for i in range(1, 9):
            self.assertTrue(root.search(i))
        self.assertFalse(root.search(0))
        self.assertFalse(root.search(9))

    def test_binary_search_insert(self):
    #               5
    #             /    \
    #           3        8
    #         /  \     /
    #       2     4   6

        root = BinarySearchNode(5)
        self.assertTrue(test_binary_node(root, 5, None, None, None))
        root.insert(3)
        self.assertTrue(test_binary_node(root, 5, 3, None, None))
        self.assertTrue(test_binary_node(root[3], 3, None, None, 5))
        root.insert(8)
        self.assertTrue(test_binary_node(root, 5, 3, 8, None))
        self.assertTrue(test_binary_node(root[3], 3, None, None, 5))
        self.assertTrue(test_binary_node(root[8], 8, None, None, 5))
        root.insert(4)
        self.assertTrue(test_binary_node(root, 5, 3, 8, None))
        self.assertTrue(test_binary_node(root[3], 3, None, 4, 5))
        self.assertTrue(test_binary_node(root[8], 8, None, None, 5))
        self.assertTrue(test_binary_node(root[4], 4, None, None, 3))
        root.insert(2)
        self.assertTrue(test_binary_node(root, 5, 3, 8, None))
        self.assertTrue(test_binary_node(root[3], 3, 2, 4, 5))
        self.assertTrue(test_binary_node(root[8], 8, None, None, 5))
        self.assertTrue(test_binary_node(root[2], 2, None, None, 3))
        self.assertTrue(test_binary_node(root[4], 4, None, None, 3))
        root.insert(6)
        self.assertTrue(test_binary_node(root, 5, 3, 8, None))
        self.assertTrue(test_binary_node(root[3], 3, 2, 4, 5))
        self.assertTrue(test_binary_node(root[8], 8, 6, None, 5))
        self.assertTrue(test_binary_node(root[2], 2, None, None, 3))
        self.assertTrue(test_binary_node(root[4], 4, None, None, 3))
        self.assertTrue(test_binary_node(root[6], 6, None, None, 8))

    # def test_binary_search_remove_external_node(self):
    #     root = binary_search_tree()
    #     root.remove(1)
    #     self.assertTrue(test_binary_node(root, 5, 3, 8, None))
    #     self.assertTrue(test_binary_node(root[3], 3, 2, 4, 5))
    #     self.assertTrue(test_binary_node(root[8], 8, 6, None, 5))
    #     self.assertTrue(test_binary_node(root[2], 2, None, None, 3))
    #     self.assertTrue(test_binary_node(root[4], 4, None, None, 3))
    #     self.assertTrue(test_binary_node(root[6], 6, None, 7, 8))
    #     self.assertTrue(test_binary_node(root[7], 7, None, None, 6))
    #     self.assertFalse(1 in root)
    #     self.assertFalse(root.search(1))

    def test_binary_search_remove_internal_node_without_children(self):
        root = binary_search_tree()
        root.remove(3, root[4])
        self.assertTrue(test_binary_node(root, 5, 4, 8, None))
        self.assertTrue(test_binary_node(root[4], 4, 2, None, 5))
        self.assertTrue(test_binary_node(root[8], 8, 6, None, 5))
        self.assertTrue(test_binary_node(root[2], 2, 1, None, 4))
        self.assertTrue(test_binary_node(root[6], 6, None, 7, 8))
        self.assertTrue(test_binary_node(root[7], 7, None, None, 6))
        self.assertFalse(3 in root)

    def test_binary_search_remove_internal_node_with_children1(self):
        root = binary_search_tree()
        root.insert(3.5)
        root.insert(4.5)
        root.remove(3, root[4])

        self.assertTrue(test_binary_node(root, 5, 4, 8, None))
        self.assertTrue(test_binary_node(root[4], 4, 3.5, 4.5, 5))
        self.assertTrue(test_binary_node(root[8], 8, 6, None, 5))
        self.assertTrue(test_binary_node(root[2], 2, 1, None, 3.5))
        self.assertTrue(test_binary_node(root[6], 6, None, 7, 8))
        self.assertTrue(test_binary_node(root[1], 1, None, None, 2))
        self.assertTrue(test_binary_node(root[3.5], 3.5, 2, None, 4))
        self.assertTrue(test_binary_node(root[4.5], 4.5, None, None, 4))
        self.assertTrue(test_binary_node(root[7], 7, None, None, 6))
        self.assertFalse(3 in root)

    def test_binary_search_remove_internal_node_with_children2(self):
        root = binary_search_tree()
        root.insert(3.5)
        root.insert(4.5)
        root.remove(3, root[2])

        self.assertTrue(test_binary_node(root, 5, 2, 8, None))
        self.assertTrue(test_binary_node(root[2], 2, 1, 4, 5))
        self.assertTrue(test_binary_node(root[8], 8, 6, None, 5))
        self.assertTrue(test_binary_node(root[6], 6, None, 7, 8))
        self.assertTrue(test_binary_node(root[1], 1, None, None, 2))
        self.assertTrue(test_binary_node(root[4], 4, 3.5, 4.5, 2))
        self.assertTrue(test_binary_node(root[3.5], 3.5, None, None, 4))
        self.assertTrue(test_binary_node(root[4.5], 4.5, None, None, 4))
        self.assertTrue(test_binary_node(root[7], 7, None, None, 6))
        self.assertFalse(3 in root)

    # HEAP
    def test_heap_iteration(self):
        heap = Heap(max_heap())
        self.assertEqual([int(str(n)) for n in heap.breadth()], [17, 15, 10, 6, 10, 7])
        self.assertEqual([int(str(n)) for n in heap], [17, 15, 10, 6, 10, 7])
        self.assertEqual(heap.flatten(), [17, 15, 10, 6, 10, 7])

    def test_find_open_with_empty_aunt(self):
        node17 = HeapNode(17)
        node15 = HeapNode(15)
        node11 = HeapNode(11)
        node6 = HeapNode(6)
        node10 = HeapNode(10)
        node17.left = node15
        node17.right = node11
        node15.left = node6
        node15.right = HeapNode(10)

        heap = Heap(node17)
        self.assertEqual(heap[-1].value, 10)
        self.assertEqual(heap.find_open(), (node11, 'left'))

    def test_find_open_with_single_child_aunt(self):
        node17 = HeapNode(17)
        node15 = HeapNode(15)
        node11 = HeapNode(11)
        node6 = HeapNode(6)
        node10 = HeapNode(10)
        node7 = HeapNode(7)
        node17.left = node15
        node17.right = node11
        node15.left = node6
        node15.right = HeapNode(10)
        node11.left = node7

        heap = Heap(node17)
        self.assertEqual(heap[-1].value, 7)
        self.assertEqual(heap.find_open(), (node11, 'right'))

    def test_find_open_with_non_empty_aunt(self):
        node17 = HeapNode(17)
        node15 = HeapNode(15)
        node11 = HeapNode(11)
        node6 = HeapNode(6)
        node10 = HeapNode(10)
        node7 = HeapNode(7)
        node5 = HeapNode(5)
        node17.left = node15
        node17.right = node11
        node15.left = node6
        node15.right = HeapNode(10)
        node11.left = node7
        node11.right = node5
        
        heap = Heap(node17)
        self.assertEqual(heap[-1].value, 5)
        self.assertEqual(heap.find_open(), (node6, 'left'))

    def test_insert(self):
        node17 = HeapNode(17)
        heap = Heap(node17)
        heap.insert(15)
        self.assertTrue(test_binary_node(heap.root, 17, 15, None, None))
        self.assertTrue(test_binary_node(heap.root.left, 15, None, None, 17))
        heap.insert(10)
        self.assertTrue(test_binary_node(heap.root, 17, 15, 10, None))
        self.assertTrue(test_binary_node(heap.root.left, 15, None, None, 17))        
        self.assertTrue(test_binary_node(heap.root.right, 10, None, None, 17))
        heap.insert(6)
        self.assertTrue(test_binary_node(heap.root, 17, 15, 10, None))
        self.assertTrue(test_binary_node(heap.root.left, 15, 6, None, 17))        
        self.assertTrue(test_binary_node(heap.root.right, 10, None, None, 17))
        self.assertTrue(test_binary_node(heap.root.left.left, 6, None, None, 15))
        heap.insert(10)
        self.assertTrue(test_binary_node(heap.root, 17, 15, 10, None))
        self.assertTrue(test_binary_node(heap.root.left, 15, 6, 10, 17))        
        self.assertTrue(test_binary_node(heap.root.right, 10, None, None, 17))
        self.assertTrue(test_binary_node(heap.root.left.left, 6, None, None, 15))
        self.assertTrue(test_binary_node(heap.root.left.right, 10, None, None, 15))
        heap.insert(7)
        self.assertTrue(test_binary_node(heap.root, 17, 15, 10, None))
        self.assertTrue(test_binary_node(heap.root.left, 15, 6, 10, 17))        
        self.assertTrue(test_binary_node(heap.root.right, 10, 7, None, 17))
        self.assertTrue(test_binary_node(heap.root.left.left, 6, None, None, 15))
        self.assertTrue(test_binary_node(heap.root.left.right, 10, None, None, 15))
        self.assertTrue(test_binary_node(heap.root.right.left, 7, None, None, 10))

    def test_bubble(self):

        #               17                      17                  100
        #             /    \                  /    \               /    \
        #          15        10    -->     15       100    -->   15       17
        #         /  \      /  \          /  \      /  \        /  \     /  \
        #       6     10   7   100      6     10   7   10     6     10  7   10

        heap = Heap(max_heap())
        parent = heap.root.right
        last = HeapNode(100)
        parent.right = last
        last.parent = parent
        heap.bubble(last)

        self.assertTrue(test_binary_node(heap.root, 100, 15, 17, None))
        self.assertTrue(test_binary_node(heap.root.right, 17, 7, 10, 100))
        self.assertTrue(test_binary_node(heap.root.right.left, 7, None, None, 17))
        self.assertTrue(test_binary_node(heap.root.right.right, 10, None, None, 17))

        #             100                  100                  100
        #            /    \               /    \               /    \
        #          15       17   -->    15       17    -->  99       17
        #         /  \     /  \        /  \     /  \        /  \     /  \
        #       6     10  7   10     99   10  7   10     15     10  7   10
        #      /                     /                   /
        #     99                   6                   6

        parent = heap.root.left.left
        last = HeapNode(99)
        last.parent = parent
        parent.left = last
        heap.bubble(last)
        self.assertTrue(test_binary_node(heap.root, 100, 99, 17, None))
        self.assertTrue(test_binary_node(heap.root.left, 99, 15, 10, 100))
        self.assertTrue(test_binary_node(heap.root.left.left, 15, 6, None, 99))
        self.assertTrue(test_binary_node(heap.root.left.right, 10, None, None, 99))
        self.assertTrue(test_binary_node(heap.root.left.left.left, 6, None, None, 15))

        #             100                  100                  100
        #            /    \               /    \               /    \
        #          99       17   -->    99       17    -->  100       17
        #         /  \     /  \        /  \     /  \        /  \      /  \
        #       15    10  7   10     15  100  7   10     15    99   7   10
        #      /  \   /             / \   /              /  \   /
        #     6   12 100          6  12 10             6   12 10

        fifteen = heap.root.left.left
        twelve = HeapNode(12)
        twelve.parent = fifteen
        fifteen.right = twelve

        ten = heap.root.left.right
        last = HeapNode(100)
        ten.left = last
        last.parent = ten
        heap.bubble(last)
        self.assertTrue(test_binary_node(heap.root, 100, 100, 17, None))
        self.assertTrue(test_binary_node(heap.root.left, 100, 15, 99, 100))
        self.assertTrue(test_binary_node(heap.root.left.left, 15, 6, 12, 100))
        self.assertTrue(test_binary_node(heap.root.left.right, 99, 10, None, 100))


    def test_insertion_with_percolation(self):
        #               17
        #             /    \
        #          15        10
        #         /  \      /
        #       6     10   7

        #     6
        six = HeapNode(6)
        heap = Heap(six)
        self.assertTrue(test_binary_node(heap.root, 6, None, None, None))

        #     6             10
        #    /    -->     /
        #  10           6
        heap.insert(10)
        self.assertTrue(test_binary_node(heap.root, 10, 6, None, None))
        self.assertTrue(test_binary_node(heap.root.left, 6, None, None, 10))          
        #     10             10
        #    /  \  -->     /   \
        #  6     7       6      7
        heap.insert(7)
        self.assertTrue(test_binary_node(heap.root, 10, 6, 7, None))
        self.assertTrue(test_binary_node(heap.root.left, 6, None, None, 10)) 
        self.assertTrue(test_binary_node(heap.root.right, 7, None, None, 10))
        #      10            15
        #     /  \  -->     /   \
        #   6     7       10      7
        #  /              / 
        # 15             6
        heap.insert(15)
        self.assertTrue(test_binary_node(heap.root, 15, 10, 7, None))
        self.assertTrue(test_binary_node(heap.root.left, 10, 6, None, 15)) 
        self.assertTrue(test_binary_node(heap.root.right, 7, None, None, 15))
        self.assertTrue(test_binary_node(heap.root.left.left, 6, None, None, 10))
        #      15            17
        #     /  \  -->     /   \
        #   10    7       15      7
        #  /  \          /  \
        # 6    17       6    10
        heap.insert(17)
        self.assertTrue(test_binary_node(heap.root, 17, 15, 7, None))
        self.assertTrue(test_binary_node(heap.root.left, 15, 6, 10, 17)) 
        self.assertTrue(test_binary_node(heap.root.right, 7, None, None, 17))
        self.assertTrue(test_binary_node(heap.root.left.left, 6, None, None, 15))
        self.assertTrue(test_binary_node(heap.root.left.right, 10, None, None, 15))
        #       17             17    
        #      /   \  -->     /   \   
        #    15     7       15     11
        #   /  \   /       /  \   /  
        #  6   10 11      6   10 7  
        heap.insert(11)
        self.assertTrue(test_binary_node(heap.root, 17, 15, 11, None))
        self.assertTrue(test_binary_node(heap.root.left, 15, 6, 10, 17)) 
        self.assertTrue(test_binary_node(heap.root.right, 11, 7, None, 17))
        self.assertTrue(test_binary_node(heap.root.left.left, 6, None, None, 15))
        self.assertTrue(test_binary_node(heap.root.left.right, 10, None, None, 15))
        self.assertTrue(test_binary_node(heap.root.right.left, 7, None, None, 11))

    def test_build_from_array(self):
        heap1 = [17, 15, 11, 6, 10, 7]
        heap2 = [6, 10, 7, 15, 17, 11]
        heap = Heap(array=heap1)
        self.assertTrue(test_binary_node(heap.root, 17, 15, 11, None))
        self.assertTrue(test_binary_node(heap.root.left, 15, 6, 10, 17)) 
        self.assertTrue(test_binary_node(heap.root.right, 11, 7, None, 17))
        self.assertTrue(test_binary_node(heap.root.left.left, 6, None, None, 15))
        self.assertTrue(test_binary_node(heap.root.left.right, 10, None, None, 15))
        self.assertTrue(test_binary_node(heap.root.right.left, 7, None, None, 11)) 
        heap = Heap(array=heap2)
        self.assertTrue(test_binary_node(heap.root, 17, 15, 11, None))
        self.assertTrue(test_binary_node(heap.root.left, 15, 6, 10, 17)) 
        self.assertTrue(test_binary_node(heap.root.right, 11, 7, None, 17))
        self.assertTrue(test_binary_node(heap.root.left.left, 6, None, None, 15))
        self.assertTrue(test_binary_node(heap.root.left.right, 10, None, None, 15))
        self.assertTrue(test_binary_node(heap.root.right.left, 7, None, None, 11)) 

    def test_delete(self):

        #          17                  7                  15                  15          
        #        /    \              /    \              /    \              /    \    
        #     15        11  -->   15        11  -->   7        11   -->   10        11
        #    /  \      /         /  \                /  \                /  \        
        #  6     10   7        6     10            6     10            6     7       
        heap = Heap(max_heap())
        heap[2].value = 11
        heap.delete_root()
        self.assertTrue(test_binary_node(heap.root, 15, 10, 11, None))
        self.assertTrue(test_binary_node(heap.root.left, 10, 6, 7, 15)) 
        self.assertTrue(test_binary_node(heap.root.right, 11, None, None, 15))
        self.assertTrue(test_binary_node(heap.root.left.left, 6, None, None, 10))
        self.assertTrue(test_binary_node(heap.root.left.right, 7, None, None, 10))
        #         15                  7                  11    
        #       /    \              /    \             /    \  
        #     10      11  -->    10       11  -->   10       7
        #    /  \               /                  /           
        #  6     7            6                  6             
        heap.delete_root()
        self.assertTrue(test_binary_node(heap.root, 11, 10, 7, None))
        self.assertTrue(test_binary_node(heap.root.left, 10, 6, None, 11)) 
        self.assertTrue(test_binary_node(heap.root.right, 7, None, None, 11))
        self.assertTrue(test_binary_node(heap.root.left.left, 6, None, None, 10))
        #          11               6                 10           
        #        /    \           /    \            /    \        
        #     10       7  -->   10      7   -->   6       7
        #    /                                                      
        #  6                                                       
        heap.delete_root()
        self.assertTrue(test_binary_node(heap.root, 10, 6, 7, None))
        self.assertTrue(test_binary_node(heap.root.left, 6, None, None, 10)) 
        self.assertTrue(test_binary_node(heap.root.right, 7, None, None, 10))
        #      10             7        
        #    /    \  -->    /       
        #  6       7      6     
        heap.delete_root()
        self.assertTrue(test_binary_node(heap.root, 7, 6, None, None))
        self.assertTrue(test_binary_node(heap.root.left, 6, None, None, 7)) 
        #      7                
        #    /   -->   6
        #  6             
        heap.delete_root()
        self.assertTrue(test_binary_node(heap.root, 6, None, None, None))
        #   6   -->  None
        heap.delete_root()
        self.assertFalse(heap.root)