# L-6 MCS 275 Mon 28 Jan 2008 : factorial.py

# a first example of a recursive function

def factorial(n):
   """
   computes the factorial of n recursively
   """
   if n <= 1:
      return 1
   else:
      return n*factorial(n-1)

def factexcept(n):
   """
   when the recursion depth is exceeded
   the factorial of n is computed iteratively
   """
   if n <= 1:
      return 1
   else:
      try:
         return n*factexcept(n-1)
      except RuntimeError:
         print 'run time error raised'
         f = 1
         for i in range(2,n+1): f = f*i
         return f

def factotrace(n,k):
   """
   prints out trace information in call k
   the initial value for k should be zero
   """
   s = k*' '
   s = s + 'factotrace(%d,%d):' % (n,k)
   if n <= 1:
      print s + ' base case, return 1'
      return 1
   else:
      print s + ' call for n-1 = ' + str(n-1)
      y = n*factotrace(n-1,k+1)
      print s + ' returning %d' % y
      return y

def factaccu(n,f):
   """
   accumulates the factorial in f
   call factaccu initially with f = 1
   """
   if n <= 1:
      return f
   else:
      return factaccu(n-1,f*n)

def factatrace(n,f,k):
   """
   accumulates the factorial in f, 
   k is used to trace the calls
   initialize f to 1 and k to 0
   """
   s = k*' '
   s = s + 'factatrace(%d,%d,%d)' % (n,f,k)
   if n <= 1:
      print s + ' returning ' + str(f)
      return f
   else:
      print s + ' call for n-1 = ' + str(n-1)
      y = factatrace(n-1,f*n,k+1)
      print s + ' returning %d' % y
      return y

def main():
   """
   prompts the user for a number
   and returns the factorial of it
   """
   n = input('give a number : ')
   if n < 998:
      f = factorial(n)
      print 'n! = ', f
      f = factaccu(n,1)
      print 'n! = ', f
   else:
      f = factexcept(n)
      print 'n! = ', f
   print 'len(n!) = ', len(str(f))
   if n < 21:
      print 'tracing the basic factorial :'
      f = factotrace(n,0)
      print 'tracing factorial with accumulator :'
      f = factatrace(n,1,0)

if __name__ == "__main__": main()
