# let's print all numbers between 0 and 9
for i in range(10): print(i)
# range(n) represents the list of all numbers from 0 to n-1
# i is the variable to take the values in the range(n) iteratively: 0, 1, ..., 9 in our example

# let's write the same code in two lines
for i in range(10): # do not forget to use colon
    print(i)
    # the second line is indented
    # this means that the command in the second line will be executed inside the for-loop
    # any other code executed inside the for-loop must be intented in the same way
    #my_code_inside_for-loop_2 will come here
    #my_code_inside_for-loop_3 will come here
    #my_code_inside_for-loop_4 will come here
# now I am out of the scope of for-loop
#my_code_outside_for-loop_1 will come here
#my_code_outside_for-loop_2 will come here

# let's calculate the summation 1+2+...+10 by using a for-loop

# we use variable total for the total summation
total = 0 
for i in range(11): # do not forget to use colon
    total = total + i # total is increased by i in each iteration
    # alternatively, the same assignment can shortly be written as total += i similarly to languages C, C++, Java, etc.
# now I am out of the scope of for-loop
# let's print the latest value of total
print(total)

# let's calculate the summation 10+12+14+...+44
# we create a list having all numbers in the summation
# for this purpose, this time we will use three parameters in range
total = 0
for j in range(10,45,2): # the range is defined between 10 and 44, and the value of j will be increased by 2 after each iteration
    total += j # let's use the shortened version of total = total + j
print(total)

# let's calculate the summation 1+2+4+8+16+...+256
# remark that 256 = 2*2*...*2 (8 times)
total = 0
current_number = 1 # this value will be multiplied by 2 after each iteration
for k in range(9):
    total = total + current_number # current_number is 1 at the beginning, and its value will be doubled after each iteration
    current_number = 2 * current_number # let's double the value of the current_number for the next iteration
    # short version of the same assignment: current_number *= 2 as in the languages C, C++, Java, etc.
# now I am out of the scope of for-loop
# let's print the latest value of total
print(total)

# instead of a range, we can directly use a list
for i in [1,10,100,1000,10000]:
    print(i)

# instead of [...], we can also use (...)
# but this time it is a tuple, not a list (keep in your mind that the values in a tuple cannot be changed)
for i in (1,10,100,1000,10000):
    print(i)

# let's create a range between 10 and 91 that contains the multiples of 7
for j in range(14,92,7): 
    # 14 is the first multiple of 7 greater than or equal to 10
    # 91 should be in the range, and so we write 92
    print(j)

# let's create a range between 11 and 22
for i in range(11,23):
    print(i)

# we can also use variables in range
n = 5
for j in range(n,2*n): 
    print(j) # we will print all numbers in {n,n+1,n+2,...,2n-1}

# we can use a list of strings
for name in ("Asja","Balvis","Fyodor"):
    print("Hello",name,":-)")

# any range can be converted to a list
L1 = list(range(10))
print(L1)

L2 = list(range(55,200,11))
print(L2)

#
# your solution is here
#
total1 = 0
total2 = 0

for i in range(3,52,3):
    total1 = total1 + i
    total2 += i # shorter form

print("The summation is",total1)
print("The summation is",total2)

#
# your solution is here
#
T = 0 
current_number = 1
for i in range(9):
    T = T + current_number
    print("3 to",i,"is",current_number)
    current_number = 3 * current_number
    
print("summation is",T)

# Python has also exponent operator: **
# we can also directly use it

T = 0

for i in range(9):
    print("3 to",i,"is",3**i)
    T = T + 3 ** i
    
print("summation is",T)

# let's calculate the summation 1+2+4+8+...+256 by using a while-loop
total = 0
i = 1

#while condition(s):
#    your_code1
#    your_code2
#    your_code3
while i < 257: # this loop iterates as long as i is less than 257
    total = total + i
    i = i * 2 # i is doubled in each iteration, and so soon it will be greater than 256
    
print(total)
# remember that we calculated this summation as 511 before

L = [0,1,2,3,4,5,11] # this is a list containing 7 integer values
i = 0
while i in L: # this loop will be iterated as long as i is in L
    print(i)
    i = i + 1 # the value of i iteratively increased, and so soon it will hit a value not in the list L
    
# the loop is terminated after i is set to 6, because 6 is not in L

# let's use negation in the condition of while-loop
L = [10] # this list has a single element
i = 0
while i not in L: # this loop will be iterated as long as i is not equal to 10
    print(i)
    i = i+1 # the value of i will hit 10 after ten iterations
    

# let's rewrite the same loop by using a direct inequality
i = 0
while i != 10: # "!=" is used for "not equal to" operator 
    print(i) 
    i=i+1

# let's rewrite the same loop by using negation of equality
i = 0
while not (i == 10): # "==" is used for "equal to" operator 
    print(i) 
    i=i+1
    
# while-loop seems having more fun :-)
# but we should be more careful when writing the condition(s)!

# summation and n is zero at the beginning
S = 0
n = 0
while S < 1000: # this loop will stop after S exceeds 999 (S = 1000 or S > 1000)
    n = n +1
    S = S + n
# let's print n and S
print("n =",n," S =",S)

# three examples for the operator "less than or equal to"
#print (4 <= 5)
#print (5 <= 5)
#print (6 <= 5)
# you may comment out the above three lines and see the results by running this cell

#
# your solution is here
#
T = 0
n = 2  # this value iteratively will be first halved and then added to the summation T
how_many_terms = 0

while T<=1.99:
    n = n/2 # half the value of n
    print("n = ",n)
    T = T + n # update the value of T
    how_many_terms = how_many_terms + 1
    
print("T = ",T)
print("how many terms in the summation:",how_many_terms)

# our result says that there should be 8 terms in our summation
# let's calculate the summations of the first seven and eight terms, and verify our results

T7 = 0 
n = 2 # this value iteratively will be first halved and then added to the summation

for i in range(7):
    n = n/2
    print("n =",n)
    T7 = T7 + n
    
print("the summation of the first seven terms is",T7)

T8 = 0 
n = 2 # this value iteratively will be first halved and then added to the summation

for i in range(8):
    n = n/2
    print("n =",n)
    T8 = T8 + n
    
print("the summation of the first eight terms is",T8)

print("(the summation of the first seven terms is",T7,")")

# this is the code for including function randrange into our program 
from random import randrange
# randrange(n) picks a number from the list [0,1,2,...,n-1] randomly
#r = randrange(100)
#print(r)

#
# your solution is here
#
from random import randrange

r = 0
attempt = 0

while r != 3: # the loop iterates as long as r is not equal to 3
    r = randrange(10) # randomly pick a number
    attempt = attempt + 1 # increase the number of attempts by 1
    print (attempt,"->",r) # print the number of attempt and the randomly picked number
    
print("total number of attempt(s) is",attempt)

# here is a schematic example for double nested loops
#for i in range(10):
#    your_code1
#    your_code2
#    while j != 7:
#        your_code_3
#        your_code_4

#
# your solution is here
#
# be aware of single and double indentions

number_of_execution = 2000 # change this with 200, 2000, 20000, 200000 and reexecute this cell
total_attempts = 0  

from random import randrange

for i in range(number_of_execution): # the outer loop iterates number_of_execution times
    r = 0 
    attempt = 0
    while r != 3: # the while-loop iterates as long as r is not equal to 3
        r = randrange(10) # randomly pick a number
        attempt = attempt + 1 # increase the number of attempts by 1
    # I am out of scope of while-loop
    total_attempts = total_attempts + attempt # update the total number of attempts
    
# I am out of scope of for-loop
print(number_of_execution,"->",total_attempts/number_of_execution)

# let's use triple nested loops
for number_of_execution in [20,200,2000,20000,200000]: # we will use the same code by indenting all lines by one more level
    total_attempts = 0 
    for i in range(number_of_execution): # the middle loop iterates number_of_execution times
        r = 0 
        attempt = 0
        while r != 3: # the while-loop iterates as long as r is not equal to 3
            r = randrange(10) # randomly pick a number
            attempt = attempt + 1 # increase the number of attempts by 1
        # I am out of scope of while-loop
        total_attempts = total_attempts + attempt # update the total number of attempts
    # I am out of scope of for-loop
    print(number_of_execution,"->",total_attempts/number_of_execution)

# you can include 2 million to the list, but you should WAIT for a while to see the result
# can your computer compete with exponential growth?
# if you think "yes", please try 20 million, 200 million, and so on