#-------------------------------------
# Lecture 6 -- Examples of Loops
#-------------------------------------
# additional references
#  * Python built-in functions. https://docs.python.org/3/library/functions.html

#%%
#-------------------------------------
# A. `while` loops
#-------------------------------------
#
# The `while` loop is one of the basic loop structures in python.  It is
# especially useful if you don't # know how many iterations you will need
# beforehand.

# The syntax is: 
#
# while (<condition>):
#   <statement>
#
# The while loop runs if the condition is `True` and ends when it is `False`.

# Basic example of a while loop
i=0
while (i < 20):
  i += 1
  print(i)

# This is an infinite loop. It will never end.
# *** ctrl-c will interrupt the execution. ***
print('\nInfinite while loop')
i = 0
while True:          
    i += 1           # same as i = i+1
    print("i=",i)

# Can you predict what will print before running this code?
i = 5
while i < 5:
    i += 1
    print("i=",i)
print("i=",i)

# Determine if a number from 0 to 20 is a factor of 3 or not.
Count=0
while(Count<=20):
    tmp = Count%3 # % is the mod operator in Python (i.e. remainder from division)
    if (tmp == 0):
      print('3 is a factor')
    Count+=1 # What happens if I forget this?

#%%
# ** Practice ** 
# Use a while loop and an if statement to determine which years are leap years
# between 1990 and 2020. Print "Leap Year" for years that are.

#%%
#-------------------------------------
# B. `for` Loops
#-------------------------------------

# A for loop makes it easier to create loops with "counters"
# The syntax is as follows:
#
# for <counter variable> in <range>:
#     <statement>
# 

for i in range(5):
    print(i)
    
# The statement `for i in range(5):` means: 
# (1) Start a `for` loop.
# (2) Execute the statements indented below the `for` 
# (3) Each time through the loop, the variable `i` takes 
#     a sequential value between 0 and 4.
# ** Note: `i` can be any variable name you choose, but `i` is a common one.

# The range function can actually do a bit more. It will take one, two or three
# arguments:
# (1 argument): range(stop) assuming start = 0, increment = 1
# (2 arguments): range(start, stop) assuming increment = 1
# (3 arguments): range(start, stop, increment)

# Examples:
for i in range(1, 5):
    print(i) # 1, 2, 3, 4

for i in range(0, 6, 2):
    print(i) # 0, 2, 4

for j in range(0, -4, -1):
    print(j) # 0, -1, -2, -3

    
#%%
# ** Practice **
# Predict the output of these loops, without running the code. Then check your
# answer

for i in range(3,8):
    print(i)

for j in range(1,6,2):
    print(j)

#%%
#-------------------------------------
# C. Loop controls
#-------------------------------------
#
# Sometimes we want to stop a loop or skip ahead.
# The keywords `break` and `continue` allow us to do this.
    
# `break` stops the current loop.
#   * Convenient for special conditions.
#   * Example: loop for some maximum possible iterations, 
#     but break early if some condition is met. Like, if your 
#     error is within some tolerance, then break out early.

print()
n = 6
for i in range(n):
    if i==3:
        print("    i=3, quit the loop")
        break                     # break out early from the loop
    print("i = ", i, "of", n-1)

# `continue` skips everything in the loop below the `continue statement
#  and goes back to the top of the loop for the next iteration. 

print()
n = 6
for i in range(n):
    if i==3:
        print("    i=3, go to next iteration")
        continue                  # skip remaining stuff in loop below and return to top
    print("i = ", i, 'of', n-1)

# Just like we can nest `if`-statements, we can nest `for` loops. This allows
# us to repeat tasks with two different counters (like rows and columns of an
# Excel sheet).

n_outer = 2
n_inner  = 4

for i_outer in range(n_outer):
    print("i_outer = ", i_outer)
    for i_inner in range(n_inner):
        print("   i_inner = ", i_inner)
        
# Example: nested loops are useful for accessing elements in a matrix.

#%%
# ** Practice **
# Write a loop that prints all of the even numbers between 10 and 39 but skips
# 16 and 32


