def factorial(n):
    print("Entering factorial with n = ", n)

    # base case: handle directly
    if n == 1:
        result = 1
    # recursive case.  Make a recursive call and use the result to
    # produce result
    else:
        recResult = factorial(n-1)
        result = n * recResult

    print("About to leave factorial({}), returning value {}.".format(n, result))
    return result

def printList(inList):
    print("input is: ", inList)
    if inList == []:
        return
    else:
        print(inList[0])
        printList(inList[1:])

def printListReverse(inList):
    if inList == []:
        return
    else:
        print(inList[-1])
        printListReverse(inList[:-1])
    
def printListReverse2(inList):
    print("entering printListReverse with input: ", inList)
    if inList == []:
        return
    else:
        printListReverse2(inList[1:])
        print(inList[0])
    print("leaving printListReverse")
    
def reverseString(inString):
    if inString == '':
        return ''
    elif len(inString) == 1:
        return inString
    else:
        return(reverseString(inString[1:]) + inString[0])

def reverseString2(inString):
    if inString == '':
        return ''
    else:
        return( inString[-1] + reverseString2(inString[:-1]) )

def sumListItems(inList):
    if inList == []:
        return(0)
    else:
        return(inList[0] + sumListItems(inList[1:]))

def isPalindrome(inString):
    if len(inString) < 2:
        return True
    if (inString[0] != inString[-1]):
        return False
    return isPalindrome(inString[1:-1])

def isPalindrome2(inString):
    if len(inString) < 2:
        return True
    else:
        return((inString[0] == inString[-1]) and (isPalindrome(inString[1:-1])))

# Return the number of digits in non-negative integer n
def numDigits(n):
    if n < 10:
        return 1
    else:
        q = n // 10
        return 1 + numDigits(q)


# Return the sum of digits in non-negative integer n
def sumDigits(n):
    if n < 10:
        return n
    else:
        rem = n % 10 # last digit
        q = n // 10  # number with last removed
        return rem + sumDigits(q)

# Return a new string with each occurrence of fromChar replaced with toChar
def ch(s, fromChar, toChar):
    if len(s) == 0:
        return s
    elif s[0] == fromChar:
        return toChar + ch(s[1:], fromChar, toChar)
    else:
        return s[0] + ch(s[1:], fromChar, toChar)

# return number of substrings that have same first and last char
# sssCount(abaab) returns 9 because there are 5 single char substrings
#         plus 'aba', 'abaa', 'baab', 'aa'
#
def sssCount(s):
    if len(s) == 1:
        return 1
    if len(s) == 0:
        return 0
    c1 = sssCount(s[1:])   # count for all but last
    c2 = sssCount(s[0:-1]) # count for all but first
    c3 = sssCount(s[1:-1]) # count for middle
    total = c1 + c2 - c3
    if s[0] == s[-1]:
        return total + 1
    else:
        return total

# don't run this on large numbers (i.e. over 40)!
# We'll discuss why later in the semester ...
# 
def fib(n):
    if (n == 1) or (n == 2):
        result = 1
    else:
        fnm1 = fib(n-1)
        fnm2 = fib(n-2)
        result = fnm1 + fnm2
    return result


# Same as above but without intermediate variables.
# 
def fib1(n):
    if (n == 1) or (n == 2):
        return 1
    else:
        return fib1(n-1) + fib1(n-2)
    
import time
def timeFib(n):
    t1 = time.time()
    result = fib(n)
    t2 = time.time()
    print("fib({}) required {:.4f} seconds.".format(n, t2 - t1))
    
# a *much* faster version. Still recursive.
# you are not required to unnderstand this.
fibTable = ['-', 1, 1]
def fibFast(n):
    #print("fib called with", n, fibTable)
    if n < len(fibTable):
        result = fibTable[n]
    else:
        result = fibFast(n-1) + fibFast(n-2)
        #print("adding to table")
        fibTable.append(result)
    #print("leaving fib: ", n)
    return result

def timeFibFast(n):
    t1 = time.time()
    result = fibFast(n)
    t2 = time.time()
    print("fibFast({}) required {:.4f} seconds.".format(n, t2 - t1))



