40 # this is not a square number
49 = 7 * 7 # is a square number

limit = 40
number = 3
number * number

# Problem: how do we find the largest square number less than a limit?
limit = 40
# Strategy: we try the square of every integer and stop when the square is greater than the limit
number = 1
# TODO: while loop here
while (number + 1) **2 < limit:
number += 1
# get the result
result = number * number
print("The largest square number less than {} is {}.".format(limit, result))

# Example: check for prime number
# Prime numbers have only two multiplication factors: one and the number itself.
# For example, 7 is a prime number because its multiplication factors are 7 and 1.
# 6 is not a prime number because its multiplication factors are 1, 2, 3, and 6.
# Given a number, we want to check if it is prime.
number = 6

number = 5
i = 4
if number %i == 0:
print("{} is not a prime number because it is divisible by {}".format(number, i))

list(range(2, 6))

# Example: check for prime number
# Prime numbers have only two multiplication factors: one and the number itself.
# For example, 7 is a prime number because its multiplication factors are 7 and 1.
# 6 is not a prime number because its multiplication factors are 1, 2, 3, and 6.
# Given a number, we want to check if it is prime.
number = 6
for i in range(2, number):
if number % i == 0: # if the number is divisible by i, it is not a prime number
print("{} is not a prime number because it is divisible by {}".format(number, i))
break
if i == number - 1:
print("{} is a prime number".format(number))

# now instead of checking a single number, I want you to check a list of numbers
# check if the numbers below are prime or not
number_list = [26, 39, 51, 53, 57, 79, 85]
for number in number_list: # another outer loop
for i in range(2, number): # the original loop is now an inner loop
if number % i == 0: # if the number is divisible by i, it is not a prime number
print("{} is not a prime number because it is divisible by {}".format(number, i))
break
if i == number - 1: # if the number is not divisible by any number less than itself, it is a prime
print("{} is a prime number".format(number))

# use case for zip
letters = ['a', 'b', 'c']
numbers = [1, 2, 3]
# how do we iterate through two iterables at once?
# answer: zip
print(list(zip(letters, numbers)))

letters = ['a', 'b', 'c']
numbers = [1, 2, 3]

for element in zip(letters, numbers):
print(element[0], element[1])

for letter, number in zip(letters, numbers):
print(letter, number)

# create a zipped element
zipped = list(zip(letters, numbers))
print(zipped)
zipped[1]

letters = [1, 2, 3]
numbers = ['a', 'b', 'c']
booleans = [True, False, True]
print(list(zip(letters, numbers, booleans)))
letter, number, boolean = list(zip(letters, numbers, booleans))[1] # tuple unpacking
boolean

# create a zipped element
zipped = list(zip(letters, numbers))
print(zipped)
numbers, letters = list(zip(zipped[0], zipped[1], zipped[2]))

letters

# create a zipped element
zipped = list(zip(letters, numbers))
print(zipped)
# list is a zip
# we now pass the list, with each element as an argument
letters_unzipped, numbers_unzipped = zip(*zipped)
print(letters_unzipped, numbers_unzipped)

set1 = {1,2}
set2 = {'a', 'b'}
list(zip(set1, set2))

# lets say we have a list
letters = ['a', 'b', 'c', 'd']
# METHOD 1: to get the index and value of the element at each iteration
# by using a dummy variable
index = 0
## DEMO: for loop here
for letter in letters:
print(index, letter)
index += 1

# lets say we have a list
letters = ['a', 'b', 'c', 'd']
# METHOD 2: to get the index and value of the element at each iteration
# by using zip and range
for i, letter in zip(range(len(letters)), letters):
print(i, letter)

# now lets say we have a list
letters = ['a', 'b', 'c', 'd']
# METHOD 3: to get the index and value of the element at each iteration
# by using enumerate
for (i, letter) in enumerate(letters):
print(i, letter)

# Example 1: use zip to label coordinates (physics)
# Given the x, y, and z coordinates of points and the corresponding labels,
# use zip to print and label the coordinates as so:
# label: x, y, z
x_coord = [23, 53, 2, -12, 95, 103, 14, -5]
y_coord = [677, 233, 405, 433, 905, 376, 432, 445]
z_coord = [4, 16, -6, -42, 3, -6, 23, -1]
labels = ["F", "J", "A", "Q", "Y", "B", "W", "X"]
# for example, the first coordinate should be printed as
# F: 23, 677, 4
points = []
for x, y, z, label in zip(x_coord, y_coord, z_coord, labels):
points.append("{}: {}, {}, {}".format(label, x, y, z))
for point in points:
print(point)

list(enumerate(zip(x_coord, y_coord)))

# Example 2.1: use zip to create a dictionary
# Given the names of movie stars and their heights, create a dictionary called cast
# the keys of cast are the names of the movie stars
# the values of cast are the heights
names = ["Andy", "Bobby", "Cindy", "Deborah"]
heights = [184, 177, 168, 172]
cast = dict(zip(names, heights))
print(cast)

cast.items()

# Example 2.2: unzip the cast into names and heights
names_unzipped, heights_unzipped = zip(*cast.items())
print(names_unzipped)
print(heights_unzipped)

# Example 2.3: transform a tuple from a 4x3 matrix to a 3x4 matrix
data = ((0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11))
data_transpose = tuple(zip(*data))
print(data_transpose)

%run -i students.py

# Exercise 2.4: modify the cast list so that each element contains the name and height of the movie star
# For example, the first element of cast should change from "Andy" to "Andy: 184"
cast = ["Andy", "Bobby", "Cindy", "Deborah"]
heights = [184, 177, 168, 172]
# Solution 1: using enumerate to directly change the original list

for name, height in zip(names, heights):
print(name + ': ' + str(height))

# Exercise 2.5: create a cast list so that each element contains the name and height of the movie star
# For example, the first element of cast should change from "Andy" to "Andy: 184"
names = ["Andy", "Bobby", "Cindy", "Deborah"]
heights = [184, 177, 168, 172]
cast = []
# Solution 2: append to a new list by iterating with zip
for names, height in zip(names, heights):
cast.append(name + ": " + str(height))
print(cast)

# Exercise 1: sum the first five odd numbers
# You can use either a while loop or a for loop
# Here is the list of numbers
num_list = [422, 136, 524, 85, 96, 719, 85, 92, 10, 17, 312, 542, 87, 23, 86, 191, 116, 35, 173, 45, 149, 59, 84, 69, 113, 166]

num = num_list[0]
odd5 = []
# check if it's odd
if num % 2 != 0:
odd5.append(num)
print(odd5)

# Solution 1: using while loop
# suggested variables (feel free to use or not use them)
count_odd = 0 # to count till 5
sum_odd = 0 # where the sum is stored
i = 0 # to iterate through the list
# TODO: your while loop here

# Solution 2: using for loop
sum_odd = []
# TODO: your for loop here

# Exercise 2: HTML List Generator
items = ['Hello World', 'World Hello']
html_str = "<ul>\n<li>Hello World</li>\n<li>I am the best</li>\n</ul>"
print(html_str)

item = items[0]
html_str = "<ul>\n"
html_str += "<li>" + item + "</li>\n"
print(html_str)

# Try your solution here!

number = 4
number * (number - 1) * (number - 2) * (number - 3)

# SOLUTION 1: WHILE LOOP
# number to find the factorial of
number = 4 # 24
number = 5 #120
number = 6 #720
# start with our product equal to one
product = 1
# track the current number being multiplied
current = 1
# TODO: write your while loop here
# print the factorial of number
print("The factorial of {} is: {}".format(number, product))

# SOLUTION 2: FOR LOOP
# number to find the factorial of
number = 6
# start with our product equal to one
product = 1
# write your for loop here

# Exercise 4: news ticker

# Exercise 5: fruits in a basket