# module-level functions to run on a binary tree.

from btnode import BTNode

# In class, we noted that a BTNode needs to distinguish between having a single
# child on the left from having a single child on the right.  This means, one
# or both children could be None.  Such a thing never happens with our class
# Tree.  It stores children in a list.  While the list could be empty, whatever
# elements (children) it has are never None.

# We also discussed 5 different cases to consider when writing contains.
# One of them was a call to contains with node == None.  We either have to 
# write the function to handle that case, or prevent such calls.

# This version of contains prevents such calls.  Doing so complicates the code.
def contains_v1(node, v):
    ''' (BTNode, v) -> bool

    Return whether the binary tree rooted at node contains v.

    >>> b = BTNode(5, BTNode(3), BTNode(7))
    >>> contains(b, 17)
    False
    >>> contains(b, 5)
    True
    >>> contains(b, 3)
    True
    >>> contains(b, 7)
    True
    '''
    if v == node.data:
        return True
    else:
        return (
            (contains(node.left, v) if node.left else False)
            or
            (contains(node.right, v) if node.right else False)
        )
    
# This version of contains doesn't prevent such calls.  It recurses right 
# past the leaves to None.  The resulting code is simpler.
def contains_v2(node, v):
    ''' (BTNode, v) -> bool

    Return whether the binary tree rooted at node contains v.

    >>> b = BTNode(5, BTNode(3), BTNode(7))
    >>> contains(b, 17)
    False
    >>> contains(b, 5)
    True
    >>> contains(b, 3)
    True
    >>> contains(b, 7)
    True
    '''
    if node == None:
        return False
    else:
        return (node.data == v or 
                contains(node.left, v) or 
                contains(node.right, v))

# Now we are searching in a BST, so we can take advantage of the order within
# the tree to search more efficiently. At each node, if we don't find v, we know
# whether to keep looking on the left or on the right, and we can completely
# ignore the other side!

# Again, this version prevents recursing past the leaves, so it does not handle
# None.
def bst_contains_v1(node, v):
    ''' (BTNode, v) -> bool
    
    Return whether the binary tree rooted at node contains v.
    
    Assumption: node is the root of a binary search tree.

    >>> b = BTNode(5, BTNode(3, BTNode(1), BTNode(4)), BTNode(7))
    >>> contains(b, 17)
    False
    >>> contains(b, 5)
    True
    >>> contains(b, 3)
    True
    >>> contains(b, 4)
    True
    '''
    if v == node.data:
        return True
    elif v < node.data:
        return contains(node.left, v) if node.left else False
    else:
        return contains(node.right, v) if node.right else False  
    
# This version recurses all the way to None.
def bst_contains_v1(node, v):
    ''' (BTNode, v) -> bool
    
    Return whether the binary tree rooted at node contains v.
    
    Assumption: node is the root of a binary search tree.

    >>> b = BTNode(5, BTNode(3, BTNode(1), BTNode(4)), BTNode(7))
    >>> contains(b, 17)
    False
    >>> contains(b, 5)
    True
    >>> contains(b, 3)
    True
    >>> contains(b, 4)
    True
    '''
    if v == None:
        return False
    elif v == node.data:
        return True
    elif v < node.data:
        return contains(node.left, v)
    else:
        return contains(node.right, v)
    
def contents_inorder(node):
    #''' (BTNode) -> list
    
    #Return a list containing the data from the binary tree rooted at Node,
    #in "in-order" order.
    
    #>>> b = BTNode(5, BTNode(3), BTNode(7))
    #>>> contents_inorder(b)
    #[3, 5, 7]
    #'''

    pass

if __name__ == '__main__':
    import doctest
    doctest.testmod()
