#############################################################
# compounding interest over the rest of the year
#############################################################


# In Monday's lecture, we worked through an example of compounding
# interest in a bank account over the month of February while making a
# withdrawal and then a deposit. Suppose we wanted to continue this over
# the rest of the year. We could simply copy the code ten more times,
# but that would be repetitive and tedious. What if we could express
# that idea of repeating code a certain number of times?
#
# In many programming languages, the most basic construct for doing so
# is a `while` loop. The body of a loop is indented just like for `if`
# blocks. However, whereas an `if` block runs once if its condition is
# True, a `while` block keeps running as long as its condition stays
# True. Hence, to avoid an infinite loop, the body of a `while` loop
# must be related to its condition, so that the condition eventually
# becomes False.
#
# Below is our example from Monday's lecture, but with the balance-
# updating code inside the loop, so that it repeats. Note how we express
# the looping condition: We set a variable `month` that starts at 2 and
# increments at the end of each loop iteration until it goes beyond 12.


########################################
# set interest rate and calculate multiplier
interest_rate = 0.05
days_in_year = 365
daily_rate = interest_rate / days_in_year
daily_multiplier = 1 + daily_rate

# set initial balance
balance = 1000

# set monthly withdrawals and deposits
withdrawal_date = 10
withdrawal = 500
deposit_date = 20
deposit = 600
end_date = 28

# set boundaries on iteration
month = 2
last_month = 12
while month <= last_month:

    # apply interest and withdrawal
    balance *= daily_multiplier ** withdrawal_date
    balance -= withdrawal

    # apply interest and deposit
    balance *= daily_multiplier ** (deposit_date - withdrawal_date)
    balance += deposit

    # apply remaining interest and print
    balance *= daily_multiplier ** (end_date - deposit_date)
    print(balance)

    # advance the iterating variable
    month += 1
########################################


# When you run this code, you should see output like the following:
#
#     1103.266254416137
#     1206.9293319403432
#     1310.9907574519707
#     1415.452061690053
#     ...
#
# These are your account's balance at the end of each month. Recall that
# we mentioned your bank account shouldn't have fractional cents.
# EXERCISE: Update the code above so that the balance at the end of each
# month is rounded to the nearest cent, and the balances are displayed
# like so:
#
#     $1103.27
#     $1206.93
#     $1310.99
#     $1415.45
#     ...
#
# Note also the code is not quite correct, because by using the code for
# February, we're assuming that each month has 28 days. Thus, we're
# calculating less interest than we should be receiving. We'll address
# this in lecture tomorrow.


#############################################################
# iterating with `for` loops
#############################################################


# One of the inconveniences of using `while` loops is that you (the
# programmer) have to be responsible for determining when to exit the
# loop. This is a common source of bugs, even for experienced
# programmers. Hence, many programming languages also support the
# concept of **iteration** over a collection of values. This allows you
# to specify upfront what values you plan to consider, rather than
# relying on the interaction between the loop body and the loop
# condition.

# In Python, the iteration mechanism is the `for` loop, and it's a bit
# different than the basic `for` loops in other languages. Below is the
# same code as above, but with the `while` loop replaced with a `for`.
# Uncomment and run it to verify it gives the same output.


########################################
# # set interest rate and calculate multiplier
# interest_rate = 0.05
# days_in_year = 365
# daily_rate = interest_rate / days_in_year
# daily_multiplier = 1 + daily_rate
#
# # set initial balance
# balance = 1000
#
# # set monthly withdrawals and deposits
# withdrawal_date = 10
# withdrawal = 500
# deposit_date = 20
# deposit = 600
# end_date = 28
#
# # set boundaries on iteration
# for month in range(2, 13):
#
#     # apply interest and withdrawal
#     balance *= daily_multiplier ** withdrawal_date
#     balance -= withdrawal
#
#     # apply interest and deposit
#     balance *= daily_multiplier ** (deposit_date - withdrawal_date)
#     balance += deposit
#
#     # apply remaining interest and print
#     balance *= daily_multiplier ** (end_date - deposit_date)
#     print(balance)
########################################


# The difference is that we've collapsed the following lines:
#
#     month = 2
#     last_month = 12
#     while month <= last_month:
#         ...
#         month += 1
#
# into just the following:
#
#     for month in range(2, 13):
#
# This lets us express that the `month` variable will step through the
# integers from 2 and up to but not including 13. In this sense, the
# `range()` parameters are similar to the `start` and `stop` parameters
# of `str` slicing. In fact, you can use an optional third parameter to
# `range()` to customize the stepping to be any integer (even negative).
#
# In class, we'll say more about what `range()` really means and how the
# `for` mechanism interacts with it.


#############################################################
# iterating over `str`s
#############################################################


# As a hint about how `for` works, conside the following code. We've got
# a str `donut`, and in the first `for` loop, we're directly iterating
# over `donut`. The loop variable is `char`, and when we print it out,
# we see that the `for` loop retrieves each character of `donut` in
# sequence and assigns it to `char`.
#
# In the second `for` loop, we're iterating over a `range()`, and the
# index variable `i` goes from 0 and up to but not including 11. When we
# index `i` into `donut` and print, the output is exactly the same.
#
# What does this tell you about how `for` works? Which form -- iterating
# directly over `donut` or over a range of indices -- do you prefer?


########################################
# donut = "whole-wheat"
#
# for char in donut:
#     print(char)
#
# print()
#
# for i in range(0, len(donut)):
#     print(donut[i])
########################################
