# math_recursion.py
# Walker M. White (wmw2), Lillian Lee (LJL2)
# Mar 5, 2014
"""A module of recursive mathematical functions."""

# The functions in this module use assert statements, in this case, to
# check that the types of the argument values are expected.  Assert statements
# can be used in any kind of code, not just recursive functions.
# They halt execution of Python, printing out the message you supply, if the
# boolean expression is false.
#
# In this code, assert statements are being used to verify that the types of
# the argument values are correct, as a way of explicitly enforcing preconditions.
# Their error messages involve calls to the function "repr", which returns a
# string that represents *unambiguously* the object given as argument. As such,
# its output can differ from that of str().  The book is a little light on this
# topic; a dense but perhaps useful post is this:
# http://stackoverflow.com/questions/1436703/difference-between-str-and-repr-in-python

def factorial(n):
    """Returns: n!

    Precondition: n is a nonnegative integer"""
    assert type(n) == int, repr(n) +' is not an int' # get in the habit
    assert n >= 0, repr(n) +' is negative' # get in the habit


    if n==0: # Base case
        return 1
    else:
        # Recursive case.
        return n*factorial(n-1)


def fibonacci(n):
    """Returns: the nth Fibonacci number a_n = a_{n-1}+a_{n-2}

    Precondition: n is a nonnegative integer"""
    assert type(n) == int, repr(n) +' is not an int' # get in the habit
    assert n >= 0, repr(n) +' is negative' # get in the habit

    # Warning: there exist much more efficient recursive implementations
    # involving "memoization".

    if (n==0 or n==1): # Base cases
        return 1
    else:
        # Recursive case.
        return fibonacci(n-1)+fibonacci(n-2)