## Loop ideas -- coming up with the loops to solve problems
## this is code for the class on Chapter 3 on loops.
##  successive versions are shown in functions with different names
##
##
## problem:  calculate 6 factorial 6! = 6*5*4*3*2*1
##  way 1:  fact = 6*5*4*3*2*1
## Ok looking ahead to be able to do something more general
##  4! =4*3*2*1
## 9! = 9*8*7*6*5*4*3*2*1
##  In general n! = n(n-1)*....*2*1
##  We need to see a step by step pattern:
## really can only do one multiplcation at a time:
##
## fact1 = 1
## fact2 = 2*1 = 2*fact1
## fact3 = 3*2*1 = 3*fact2
## ...

## or

def factorial6seq():
    fact1 = 1
    fact2 = 2*fact1
    fact3 = 3*fact2
    fact4 = 4*fact3
    fact5 = 5*fact4
    fact6 = 6*fact5

    print fact6

## This is a straight SEQUENCE:  one line of code for each computation.
## There is a pattern, however, so we have the potential to turn it into a loop,
## that both has less code and can handle different numbers of repetitions.
##
## Observations:
##    consistent pattern of multiplication,
##    each multiplication uses the result of the last
##    multiple by a clear sequence of factors 1,2, 3, 4, 5, 6
##
## The idea is to rewrite this so it can be written with identical code.
##
## we have a known factorial, a next factor and calculate the next factorial:
##    knownFact = 1
##
##    factor = 2
##    nextFact = factor*knownFact
##
## can't just repeat the last two lines -- reuse old knownFact:
## Next time what was newFact is the knownFact:


def factorial6seqReuseNames():
    knownFact = 1

    factor = 2
    nextFact = factor*knownFact
    knownFact = nextFact

    factor = 3
    nextFact = factor*knownFact
    knownFact = nextFact

    factor = 4
    nextFact = factor*knownFact
    knownFact = nextFact
    
    factor = 5
    nextFact = factor*knownFact
    knownFact = nextFact

    factor = 6
    nextFact = factor*knownFact
    knownFact = nextFact

    print knownFact

## Getting there!  still have different values of factor,
## but we can get a different value each time with a range expression!
##
## How go from 2 to 6?


def factorial6loop():
    knownFact = 1
    for factor in range(2, 7):
        nextFact = factor*knownFact
        knownFact = nextFact

    print knownFact    

## The ideas of knownFact and newFact are distinct, and useful as we develop this,
## but since we use our definition of newFact only once in the loop, we can
## drop it, and use the expression in the definition directly:

def factorial6loopShort():
    knownFact = 1
    for factor in range(2, 7):
        knownFact = factor*knownFact

    print knownFact    
     
## Now that we have only one name, I'll shorten it to fact,

def factorial6():
    fact = 1
    for factor in range(2, 7):
        fact = factor*fact

    print fact    

## and we can change it to allow the user to choose the value of n:

def factorial():
    n = input("Find factorial of what? ")
    fact = 1
    for factor in range(2, n+1):
        fact = factor*fact

    print fact    

## important idea is playing computer!  Start here, where it is easy
##    -- we have seen the sequence
    
