{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "173bdfb9",
   "metadata": {},
   "source": [
    "# Python 3 Tutorial Notebook"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "068cbd92",
   "metadata": {},
   "source": [
    "We'll be using this notebook to follow the slides from the workshop. You can also use it to experiment with Python yourself! Simply add a cell wherever you want, type in some Python code, and see what happens!"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "698d5cf0",
   "metadata": {},
   "source": [
    "##  Topic 1: Printing"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e98b3475",
   "metadata": {},
   "source": [
    "### 1.1 Printing Basics"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "069d08f2",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "hello world\n"
     ]
    }
   ],
   "source": [
    "# When a line begins with a '#' character, it designates a comment. This means that it's not actually a line of code\n",
    "\n",
    "# This is how you say hello world\n",
    "print('hello world')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 119,
   "id": "86f8d0be",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "                    ========\n",
      "                    |      |\n",
      "             ===============\n",
      "             |      |      |\n",
      "      ======================\n"
     ]
    }
   ],
   "source": [
    "# Can you make Python print the staircase below:\n",
    "#\n",
    "#                    ========\n",
    "#                    |      |\n",
    "#             ===============      \n",
    "#             |      |      |\n",
    "#      ======================\n",
    "\n",
    "print('                    ========')\n",
    "print('                    |      |')\n",
    "print('             ===============')\n",
    "print('             |      |      |')\n",
    "print('      ======================')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "602dbdad",
   "metadata": {},
   "source": [
    "### 1.2 Some other useful printing tips/tricks"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "f554f941",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "hello world i go to princeton\n"
     ]
    }
   ],
   "source": [
    "# The print(...) function can accept as many arguments as you'd like. It prints the arguments\n",
    "# in order, separated by a space. For example...\n",
    "\n",
    "print('hello', 'world', 'i', 'go', 'to', 'princeton')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "4f2419de",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "hello --> world --> i --> go --> to --> princeton\n"
     ]
    }
   ],
   "source": [
    "# You can change the delimiter that separates the comma-separated arguments by changing the 'sep' parameter:\n",
    "\n",
    "print('hello', 'world', 'i', 'go', 'to', 'princeton', sep=' --> ')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "ae31ea63",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "hello princeton\n",
      "ACM is so cool :)"
     ]
    }
   ],
   "source": [
    "# By default, python adds a newline to the end of any statement you print. However, you can change this\n",
    "# by changing the 'end' parameter. For example...\n",
    "\n",
    "# Here we've told Python to print 'hello world' and then an empty string. This effectively \n",
    "# removes the newline that Python adds by default.\n",
    "print('hello prince', end='')\n",
    "\n",
    "# The next line that we print then begins immediately after the previous thing we printed.\n",
    "print('ton')\n",
    "\n",
    "# We can also end our lines with something more exotic\n",
    "print('ACM is so cool', end=' :)')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4dc24edf",
   "metadata": {},
   "source": [
    "## Topic 2: Variables, Basic Operators, and Data Types"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6124d137",
   "metadata": {},
   "source": [
    "### 2.1 Playing with Numbers"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a017b2b1",
   "metadata": {},
   "source": [
    "For a description of all the operators that exist in Python, you can visit https://www.tutorialspoint.com/python/python_basic_operators.htm."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "0a3717cf",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "September 17 2021 was a Friday\n"
     ]
    }
   ],
   "source": [
    "# What does the following snippet of code do?\n",
    "\n",
    "day = 24\n",
    "month = 'September'\n",
    "year = '2021'\n",
    "dotw = 'Friday'\n",
    "\n",
    "print(month, day - 7, year, 'was a', dotw)\n",
    "\n",
    "# Explanation: Tells us that the day 7 days ago was the same day of the week as today. The more you know...?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "0daab14d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "20\n"
     ]
    }
   ],
   "source": [
    "age_in_weeks = 1057\n",
    "\n",
    "# What's the difference between the two statements below? Comment one of them out to check yourself!\n",
    "age_in_years = 1057 / 52\n",
    "age_in_years = 1057 // 52\n",
    "\n",
    "print(age_in_years)\n",
    "\n",
    "# Explanation: The '/' operator does float division in Python by default, while the '//' operator does\n",
    "# integer division (i.e. it returns the decimal answer, ALWAYS rounded down to an integer)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "20a42d86",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "36\n"
     ]
    }
   ],
   "source": [
    "# Try to calculate the following in your head and see if your answer matches what Python says\n",
    "\n",
    "mystery = 2 ** 4 * 3 ** 2 % 7 * (2 + 7)\n",
    "print(mystery)\n",
    "\n",
    "# Explanation: First evaluate anything in parentheses, then do the exponents, then do multiplication/modulo \n",
    "# FROM LEFT TO RIGHT. So\n",
    "# 2 ** 4 * 3 ** 2 % 7 * (2 + 7) = 2 ** 4 * 3 ** 2 % 7 * 9 = 16 * 9 % 7 * 9 = 144 % 7 * 9 = 4 * 9 = 36"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "id": "ec9fdcdf",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "86 degrees farenheit = 30.0 degrees celsius = 303.0 kelvin\n"
     ]
    }
   ],
   "source": [
    "# Write a function that converts a given temperature in Farenheit to Celsius and Kelvin\n",
    "# The relevant formulas are (degrees Celsius) = (degrees Farenheit - 32) * 5 / 9\n",
    "# and (Kelvin) = (degrees Celsius + 273)\n",
    "\n",
    "farenheit = 86 # Change the value here to test your solution\n",
    "celsius = (farenheit - 32) * 5 / 9\n",
    "kelvin = celsius + 273\n",
    "\n",
    "print(farenheit, 'degrees farenheit =', celsius, 'degrees celsius =', kelvin, 'kelvin')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dbff63fb",
   "metadata": {},
   "source": [
    "### 2.2 Playing with Strings"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "595a5af6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Tom Cruise is 9 outta 10\n"
     ]
    }
   ],
   "source": [
    "# You are given the following string:\n",
    "a = 'Thomas Cruise'\n",
    "\n",
    "# Your job is to put the phrase 'Tom Cruise is 9 outta 10' into variable b using ONLY operations on string a.\n",
    "# You may not concatenate letters or strings of your own. HINT: You can use the str(...) function to convert\n",
    "# numerical values into strings so that you can concatenate it with another string\n",
    "b = a[0] + a[2:4] + a[6:] + a[6] + a[10:12] + a[6] + str(a.find('u')) + a[6] + a[2] + a[9] + \\\n",
    "                                            (a[0].lower() * 2) + a[4] + a[6] + str(a.find('i'))\n",
    "\n",
    "print(b)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "id": "0dd726e7",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Input first word:\n",
      "Nalin\n",
      "Input second word:\n",
      "late meal\n",
      "Input third word:\n",
      "scooters everywhere\n",
      "Input fourth word:\n",
      "hammers\n",
      "\n",
      "Your mad libs is: \n",
      "Hi, my name is Nalin.\n",
      "One thing that I love about Princeton is late meal.\n",
      "One pet peeve I have about Princeton is scooters everywhere, but it's OK because I have hammers.\n"
     ]
    }
   ],
   "source": [
    "# Practice with string formatting with mad libs! For this, you'll need to know \n",
    "# how to receive input. It's really easy in Python:\n",
    "\n",
    "word_1 = input('Input first word:\\n') # This prompts the user with the phrase 'Input first word'\n",
    "                                      # and stores the result in the variable word_1\n",
    "word_2 = input('Input second word:\\n')\n",
    "word_3 = input('Input third word:\\n')\n",
    "word_4 = input('Input fourth word:\\n')\n",
    "\n",
    "# You want to print the following mad libs:\n",
    "#\n",
    "# Hi, my name is [first phrase]. \n",
    "# One thing that I love about Princeton is [second phrase].\n",
    "# One pet peeve I have about Princeton is [third phrase], but I can get over it because I have [fourth phrase].\n",
    "# \n",
    "# For the last sentence, use one print statement to print it!\n",
    "\n",
    "print('\\nYour mad libs is: ')\n",
    "print('Hi, my name is {}.'.format(word_1))\n",
    "print('One thing that I love about Princeton is {}.'.format(word_2))\n",
    "print('One pet peeve I have about Princeton is {}, but it\\'s OK because I have {}.'.format(word_3, word_4))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "656d0012",
   "metadata": {},
   "source": [
    "### 2.3 Playing with Booleans"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "id": "edf455a3",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "True\n"
     ]
    }
   ],
   "source": [
    "# Your objective is to write a boolean formula in Python that takes three boolean variables (a, b, c)\n",
    "# and returns True if and only if exactly one of them is True. This is called the xor of the variables\n",
    "\n",
    "# Toggle these to test your formula\n",
    "a = False\n",
    "b = True\n",
    "c = False\n",
    "\n",
    "# Write your formula here\n",
    "xor = (a and not b and not c) or (not a and b and not c) or (not a and not b and c)\n",
    "print(xor)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f4b1cdd9",
   "metadata": {},
   "source": [
    "### 2.4 Mixing Types"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "id": "a099e601",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "bool(3) = True\n",
      "bool(0) = False\n",
      "bool(\"\") = False\n",
      "bool(\" \") = True\n",
      "bool(False) = False\n",
      "bool(True) = True\n"
     ]
    }
   ],
   "source": [
    "# In Python, data types are divided into two categories: truthy and falsy. Falsy values include anything\n",
    "# (strings, lists, etc.) that is empty, the special value None, any zero number, and the boolean False. \n",
    "# You can use the bool(...) function to check whether a value is truthy or falsy:\n",
    "\n",
    "print('bool(3) =', bool(3))\n",
    "print('bool(0) =', bool(0))\n",
    "print('bool(\"\") =', bool(''))\n",
    "print('bool(\" \") =', bool(' '))\n",
    "print('bool(False) =', bool(False))\n",
    "print('bool(True) =', bool(True))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cb5ff6db",
   "metadata": {},
   "source": [
    "## Topic 3: If Statements, Ranges, and Loops"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5708f62d",
   "metadata": {},
   "source": [
    "### 3.1 Practice with the Basics"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "id": "be4ef675",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "5 is divisible by 5\n",
      "5 is positive\n",
      "\n",
      "5 is divisible by 5\n"
     ]
    }
   ],
   "source": [
    "x = 5\n",
    "\n",
    "# What is the difference between this snippet of code:\n",
    "\n",
    "if x % 2 == 0:\n",
    "    print(x, 'is even')\n",
    "if x % 5 == 0:\n",
    "    print(x, 'is divisible by 5')\n",
    "if x > 0:\n",
    "    print(x, 'is positive')\n",
    "    \n",
    "print()\n",
    "    \n",
    "# And this one:\n",
    "if x % 2 == 0:\n",
    "    print(x, 'is even')\n",
    "elif x % 5 == 0:\n",
    "    print(x, 'is divisible by 5')\n",
    "elif x > 0:\n",
    "    print(x, 'is positive')\n",
    "    \n",
    "# Explanation: the elif will only execute if all statements in the block above evaluated to false.\n",
    "# In the second block, the first if statement evaluates to false, so the first elif's condition is\n",
    "# tested. It is true, so then the second elif will not execute.\n",
    "\n",
    "# Follow-up: An if statement starts its own new block. So the first snippet of code is actually\n",
    "# three if statement blocks."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "id": "e1eea750",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "2\n",
      "Fizz\n",
      "4\n",
      "Buzz\n",
      "Fizz\n",
      "7\n",
      "8\n",
      "Fizz\n",
      "Buzz\n",
      "11\n",
      "Fizz\n",
      "13\n",
      "14\n",
      "FizzBuzz\n",
      "16\n",
      "17\n",
      "Fizz\n",
      "19\n",
      "Buzz\n",
      "Fizz\n",
      "22\n",
      "23\n",
      "Fizz\n",
      "Buzz\n",
      "26\n",
      "Fizz\n",
      "28\n",
      "29\n",
      "FizzBuzz\n",
      "31\n",
      "32\n",
      "Fizz\n",
      "34\n",
      "Buzz\n",
      "Fizz\n",
      "37\n",
      "38\n",
      "Fizz\n",
      "Buzz\n",
      "41\n",
      "Fizz\n",
      "43\n",
      "44\n",
      "FizzBuzz\n",
      "46\n",
      "47\n",
      "Fizz\n",
      "49\n",
      "Buzz\n"
     ]
    }
   ],
   "source": [
    "# FizzBuzz is a very well-known programming challenge. It's quite easy, but it can trip up people\n",
    "# who are trying to look for shortcuts to solving the problem. The problem is as follows:\n",
    "# \n",
    "# For every number k in order from 1 to 50, print\n",
    "# - 'FizzBuzz' if the number is divisible by 3 and 5\n",
    "# - 'Fizz' if the number is only divisible by 3\n",
    "# - 'Buzz' if the number is only divisble by 5\n",
    "# - the value of k if none of the above options hold\n",
    "#\n",
    "# Your task is to write a snippet of code that solves FizzBuzz.\n",
    "\n",
    "for i in range(1, 51):\n",
    "    div3 = ((i % 3) == 0)\n",
    "    div5 = ((i % 5) == 0)\n",
    "    \n",
    "    if div3:\n",
    "        if div5: print('FizzBuzz')\n",
    "        else: print('Fizz')\n",
    "    elif div5:\n",
    "        print('Buzz')\n",
    "    else: print(i)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "69800e1b",
   "metadata": {},
   "source": [
    "### 3.2 Ternary Statements in Python"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "0127606b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Wawa\n"
     ]
    }
   ],
   "source": [
    "# The following if statement construct is so common it has a name ('ternary statement'):\n",
    "#\n",
    "# if (condition): \n",
    "#    x = something1\n",
    "# elif (condition2):\n",
    "#    x = something2\n",
    "# else:\n",
    "#    x = something3\n",
    "#\n",
    "# In python, this can be shortened into a one-liner:\n",
    "#\n",
    "# x = something else something2 if (condition2) else something3\n",
    "#\n",
    "# And this works for an arbitrary number of elif statements in between the initial if and final else.\n",
    "\n",
    "# Can you convert the following block into a one-liner?\n",
    "budget = 3\n",
    "if budget > 50:\n",
    "    restaurant = 'Agricola'\n",
    "elif budget > 30:\n",
    "    restaurant = 'Mediterra'\n",
    "elif budget > 15:\n",
    "    restaurant = 'Thai Village'\n",
    "else:\n",
    "    retaurant = 'Wawa'\n",
    "    \n",
    "# Write your solution below:\n",
    "restaurant = 'Agricola' if budget > 50 else 'Mediterra' if budget > 30 \\\n",
    "                                else 'Thai Village' if budget > 15 else 'Wawa'\n",
    "\n",
    "print(restaurant)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e738967e",
   "metadata": {},
   "source": [
    "### 3.3 Practice with While Loops"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "2937bcc0",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Guess a number:\n",
      "50\n",
      "Nope. Guess was too high!\n",
      "25\n",
      "Nope. Guess was too low!\n",
      "37\n",
      "Nope. Guess was too high!\n",
      "31\n",
      "Nope. Guess was too low!\n",
      "34\n",
      "Nope. Guess was too low!\n",
      "36\n",
      "Nope. Guess was too high!\n",
      "35\n",
      "You got it!\n"
     ]
    }
   ],
   "source": [
    "# Your job is to create a 'guessing game' where the program thinks of an integer from 1 to 50\n",
    "# and will keep prompting you for a guess. It'll tell you each time whether your guess is\n",
    "# too high or too low until you find the number.\n",
    "\n",
    "# Don't touch these two lines of code; they choose a random number between 1 and 50\n",
    "# and store it in mystery_num\n",
    "from random import randint\n",
    "mystery_num = randint(1, 100)\n",
    "\n",
    "# Write your guessing game below:\n",
    "guess = int(input('Guess a number:\\n')) # First guess; don't forget to convert it to an int!\n",
    "while mystery_num != guess:\n",
    "    if guess > mystery_num: guess = int(input('Nope. Guess was too high!\\n'))\n",
    "    elif guess < mystery_num: guess = int(input('Nope. Guess was too low!\\n'))\n",
    "\n",
    "print('You got it!')\n",
    "\n",
    "# Follow-up: Using the best strategy, what's the worst-case number of guesses you should need?"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b975834b",
   "metadata": {},
   "source": [
    "## Topic 4: Data Structures in Python"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6484f6cc",
   "metadata": {},
   "source": [
    "### 4.1 Sequences"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f6ba8333",
   "metadata": {},
   "source": [
    "Strings, tuples, and lists are all considered *sequences* in Python, which is why there are many operations that work on all three of them."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d6544a85",
   "metadata": {},
   "source": [
    "#### 4.1.1 Iterating"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "efc65628",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "rhythm has no vowels\n",
      "owl has a vowel\n",
      "hymn has no vowels\n",
      "aardvark has a vowel\n"
     ]
    }
   ],
   "source": [
    "# When at the top of a loop, the 'in' keyword in Python will iterate through all of the sequence's \n",
    "# members in order. For strings, members are individual characters; for lists and tuples, they're \n",
    "# the items contained.\n",
    "\n",
    "# Task: Given a list of lowercase words, print whether the word has a vowel. Example: if the input is\n",
    "# ['rhythm', 'owl', 'hymn', 'aardvark'], you should output the following:\n",
    "# rhythm has no vowels\n",
    "# owl has a vowel\n",
    "# hymn has no vowels\n",
    "# aardvark has a vowel\n",
    "\n",
    "# HINT: The 'in' keyword can also test whether something is a member of another object.\n",
    "# Also, don't forget about break and continue!\n",
    "\n",
    "vowels = ['a', 'e', 'i', 'o', 'u']\n",
    "words = ['rhythm', 'owl', 'hymn', 'aardvark']\n",
    "\n",
    "for word in words:\n",
    "    has_vowel = False\n",
    "    for letter in word:\n",
    "        if letter in vowels: \n",
    "            has_vowel = True\n",
    "            break # Not necessary, but is more efficient\n",
    "    \n",
    "    if has_vowel:\n",
    "        print(word, 'has a vowel')\n",
    "    else:\n",
    "        print(word, 'has no vowels')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "id": "e6306ebb",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "True\n",
      "False\n",
      "True\n",
      "False\n",
      "False\n"
     ]
    }
   ],
   "source": [
    "# Given a tuple, write a program to check if the value at index i is equal to the square of i.\n",
    "# Example: If the input is nums = (0, 2, 4, 6, 8), then the desired output is\n",
    "#\n",
    "# True\n",
    "# False\n",
    "# True\n",
    "# False\n",
    "# False\n",
    "#\n",
    "# Because nums[0] = 0^2 and nums[2] = 4 = 2^2. HINT: Use enumerate!\n",
    "\n",
    "nums = (0, 2, 4, 6, 8)\n",
    "for i, num in enumerate(nums): print(num == i * i)\n",
    "    \n",
    "for i in range(len(nums)):\n",
    "    num = nums[i]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0d4ee6b0",
   "metadata": {},
   "source": [
    "#### 4.1.2 Slicing"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "86f83d27",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "nCe\n"
     ]
    }
   ],
   "source": [
    "# Slicing is one of the operations that work on all of them.\n",
    "\n",
    "# Task 1: Given a string s whose length is odd and at least 5, can you print \n",
    "# the middle three characters of it? Try to do it in one line.\n",
    "# Example: if the input is 'PrInCeToN', the the output should be 'nCe'\n",
    "s = 'PrInCeToN'\n",
    "print(s[len(s) // 2 - 1 : len(s) // 2 + 2])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "126a2a79",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(4, 'cow', 9.4)\n",
      "(4, 'cow', 9.4)\n",
      "(4, 'cow', 9.4)\n"
     ]
    }
   ],
   "source": [
    "# Task 2: Given a tuple, return a tuple that includes only every other element, starting\n",
    "# from the first. Example: if the input is (4, 5, 'cow', True, 9.4), then the output should\n",
    "# be (4, 'cow', 9.4). Again, try to do it in one line — there's an easy way to do it with slicing.\n",
    "\n",
    "t = (4, 5, 'cow', True, 9.4)\n",
    "print(t[::2])\n",
    "print(t[0::2]) # also acceptable\n",
    "print(t[0:len(t):2]) # also acceptable, but less ideal"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "a2f2ce11",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "('Tiger', 0, 9)\n"
     ]
    }
   ],
   "source": [
    "# Task 3: Do the same as task 2, except start from the last element and alternate backwards.\n",
    "# Example: if the input is (3, 9, 1, 0, True, 'Tiger'), output should be ('Tiger', 0, 9)\n",
    "\n",
    "t = (3, 9, 1, 0, True, 'Tiger')\n",
    "print(t[::-2])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3a8d29db",
   "metadata": {},
   "source": [
    "### 4.2 List Comprehension and Other Useful List Functions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "7ae3467e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['Nalin Ranjan', 'Howard Yen', 'Henry Tang', 'Austen Mazenko', 'Michael Tang', 'Dangely Canabal', 'Vicky Feng']\n"
     ]
    }
   ],
   "source": [
    "# Task 1: Given a list of names, return a new list where all the names which are more than 15\n",
    "# characters long are removed.\n",
    "\n",
    "names = ['Nalin Ranjan', 'Howard Yen', 'Sacheth Sathyanarayanan', 'Henry Tang', \\\n",
    "         'Austen Mazenko', 'Michael Tang', 'Dangely Canabal', 'Vicky Feng']\n",
    "\n",
    "# Write your solution below\n",
    "print([name for name in names if len(name) <= 15])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "id": "4bad8b9e",
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['yadrutaS', 'a no', 'kcolc-o', 'enin', 'stI']\n",
      "['yadrutaS', 'a no', 'kcolc-o', 'enin', 'stI']\n"
     ]
    }
   ],
   "source": [
    "# Task 2: Given a list of strings, return a list which is the reverse of the original, with\n",
    "# all the strings reversed. Example: if the input is ['Its', 'nine', 'o-clock', 'on a', 'Saturday'],\n",
    "# then the output should be ['yadrutaS', 'a no', 'kcolc-o', 'enin', 'stI']. Try to do it in one line!\n",
    "\n",
    "# HINT: Use list comprehension and negative indices!\n",
    "\n",
    "l = ['Its', 'nine', 'o-clock', 'on a', 'Saturday']\n",
    "print([word[::-1] for word in l[::-1]]) \n",
    "print([l[-i][::-1] for i in range(1, len(l) + 1)]) # Also acceptable, but a little less ideal"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "a17d31ed",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[5, 2, 6, 1, 8, 2, 4, 3]\n",
      "[5, 7, 2, 6, 1, 8, 2, 4, 3]\n",
      "[5, 7, 6, 1, 8, 2, 4, 3]\n",
      "[5, 7, 6, 1, 2, 4, 3]\n",
      "[1, 2, 3, 4, 5, 6, 7]\n",
      "[7, 6, 5, 4, 3, 2, 1]\n",
      "1\n",
      "[7, 6, 5, 4, 3, 2, 1, 6, 1, 2, 4]\n",
      "Sum: 41\n",
      "Minimum: 1\n",
      "Maximum: 7\n"
     ]
    }
   ],
   "source": [
    "l1 = [5, 2, 6, 1, 8, 2, 4]\n",
    "l2 = [6, 1, 2, 4]\n",
    "\n",
    "# Python has a bunch of useful built-in list functions. Some of them are\n",
    "\n",
    "l1.append(3) # adds the element 3 to the end of the the list\n",
    "print(l1)\n",
    "\n",
    "l1.insert(1, 7) # adds the element 7 as the second element of the list\n",
    "print(l1)\n",
    "\n",
    "l1.remove(2) # Removes the first occurrence of 7 in the list (DOES NOT REMOVE ALL)\n",
    "print(l1)\n",
    "\n",
    "l1.pop(4) # Remove the fifth item of the list (since everything is zero-indexed)\n",
    "print(l1)\n",
    "\n",
    "l1.sort() # Sorts the list in increasing order\n",
    "print(l1)\n",
    "\n",
    "l1.sort(reverse=True) # Sorts the list in decreasing order\n",
    "print(l1)\n",
    "\n",
    "print(l1.count(2)) # Counts the number of occurrences of the number 2 in the list\n",
    "\n",
    "l1.extend(l2) # Appends all elements in l2 to the end of l1\n",
    "print(l1)\n",
    "\n",
    "# If the list is numeric, we can find the min, max, and sum easily:\n",
    "print('Sum:', sum(l1))\n",
    "print('Minimum:', min(l1))\n",
    "print('Maximum:', max(l1))\n",
    "\n",
    "# You can see all the list methods at https://www.w3schools.com/python/python_ref_list.asp"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "efdfe550",
   "metadata": {},
   "source": [
    "### 4.3 Sets and Dictionaries"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "id": "77a40840",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'sign-in', 'bicker', '???'}\n",
      "{'sign-in', 'bicker', '???'}\n"
     ]
    }
   ],
   "source": [
    "# Task 1: In a dictionary, keys must be unique, but values need not be. Given a dictionary, write a script\n",
    "# that prints the set of all unique values in a dictionary. Example: if the dictionary is\n",
    "# {'Cap': 'bicker', 'Quad': 'sign-in', 'Colonial': 'sign-in', 'Tower': 'bicker', 'Charter': '???'}\n",
    "# The program should print {'sign-in', 'bicker', '???'}\n",
    "\n",
    "d = {'Cap': 'bicker', 'Quad': 'sign-in', 'Colonial': 'sign-in', 'Tower': 'bicker', 'Charter': '???'}\n",
    "unique_vals = set()\n",
    "for key in d: \n",
    "    unique_vals.add(d[key])\n",
    "\n",
    "print(unique_vals)\n",
    "\n",
    "# In fact, there's a more Pythonic way to do this in one line:\n",
    "print(set([val for val in d.values()]))\n",
    "\n",
    "# We used list comprehension to put every value into a list (which may have contained duplicates)\n",
    "# and then converting it to a set removed the duplicates (since every element in a set must be unique)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "id": "346a2d0e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[('e', 69), ('t', 48), ('i', 45), ('o', 44), ('s', 42), ('a', 28), ('h', 27), ('r', 27), ('n', 22), ('w', 21), ('f', 19), ('d', 13), ('g', 13), ('l', 11), ('p', 10), ('c', 7), ('b', 5), ('m', 5), ('u', 5), ('v', 5), ('y', 4), ('k', 2), ('j', 0), ('q', 0), ('x', 0), ('z', 0)]\n"
     ]
    }
   ],
   "source": [
    "# Task 2: Given a passage of text (a string), analyze the frequency of each individual letter. Sort\n",
    "# the letters by their frequency in the passage. Does your distribution look reasonable for English?\n",
    "\n",
    "passage = \"\"\"it was the best of times, it was the worst of times, it was the age of wisdom, it was \n",
    "            the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was \n",
    "            the season of Light, it was the season of Darkness, it was the spring of hope, it was the \n",
    "            winter of despair, we had everything before us, we had nothing before us, we were all going \n",
    "            direct to Heaven, we were all going direct the other way -- in short, the period was so far \n",
    "            like the present period that some of its noisiest authorities insisted on its being received, \n",
    "            for good or for evil, in the superlative degree of comparison only\"\"\"\n",
    "\n",
    "# Here's the alphabet to help you out; it'll help you ignore other characters\n",
    "alphabet = \"abcdefghijklmnopqrstuvwxyz\"\n",
    "\n",
    "# This adds a key in the dictionary for each letter of the alphabet\n",
    "d = dict.fromkeys(alphabet, 0)\n",
    "\n",
    "for char in passage:\n",
    "    if char in alphabet: d[char] += 1\n",
    "\n",
    "# Don't change the code below: it'll take your dictionary of frequencies and sort it from most frequent to least\n",
    "freqs = [(letter, d[letter]) for letter in d]\n",
    "freqs.sort(key = lambda x: x[1], reverse=True)\n",
    "\n",
    "print(freqs)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1f290d66",
   "metadata": {},
   "source": [
    "## Topic 5: Functions in Python"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "37b57d79",
   "metadata": {},
   "source": [
    "### 5.1 Practice with Basic Functions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 109,
   "id": "af5153b4",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Minimum of 6, 3, 7 is 3\n",
      "Minimum of 0, 3.333, -52 is -52\n",
      "Minimum of -3, -1, 3.14159 is -3\n"
     ]
    }
   ],
   "source": [
    "# Task 1: Write a function that returns the minimum of three numbers. Don't use the built-in min function\n",
    "def my_min(a, b, c):\n",
    "    if a <= b:\n",
    "        return a if a <= c else c\n",
    "    else:\n",
    "        return b if b <= c else c\n",
    "    \n",
    "print('Minimum of 6, 3, 7 is', my_min(6, 3, 7))\n",
    "print('Minimum of 0, 3.333, -52 is', my_min(0, -52, 3.333))\n",
    "print('Minimum of -3, -1, 3.14159 is', my_min(-3, -1, 3.14159))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 113,
   "id": "c077fed4",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(1, 2, 3, 4, 5, 7, 8) is increasing: True\n",
      "(1, 2, 3, 2, 5, 7, 8) is increasing: False\n",
      "(-1, 2, 3, 2.99, 5, 7, 8) is increasing: False\n"
     ]
    }
   ],
   "source": [
    "# Task 2: Write a function that checks if a given tuple of numbers is increasing (that is, each number\n",
    "# is at least the number before it)\n",
    "\n",
    "def my_increasing(t):\n",
    "    if len(t) == 0: return True\n",
    "    \n",
    "    prev = t[0]\n",
    "    for i in t:\n",
    "        if i < prev: return False\n",
    "        prev = i\n",
    "        \n",
    "    return True\n",
    "\n",
    "print('(1, 2, 3, 4, 5, 7, 8) is increasing:', my_increasing((1, 2, 3, 4, 5, 7, 8)))\n",
    "print('(1, 2, 3, 2, 5, 7, 8) is increasing:', my_increasing((1, 2, 3, 2, 5, 7, 8)))\n",
    "print('(-1, 2, 3, 2.99, 5, 7, 8) is increasing:', my_increasing((1, 2, 3, 2, 5, 7, 8)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 118,
   "id": "44f7c217",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3\n",
      "1\n"
     ]
    }
   ],
   "source": [
    "# Task 3: Given a list of numbers that is guaranteed to contain all but one of the consecutive integers\n",
    "# 1 to N (for some N), find the one that is missing. For example, if the input is [2, 1, 5, 4], your function\n",
    "# should return 3, because that's the number missing from 1-5.\n",
    "\n",
    "def my_missing(l):\n",
    "    s = sum(l)\n",
    "    n = len(l) + 1\n",
    "    return n * (n + 1) // 2 - s # Why does this work?\n",
    "\n",
    "print(my_missing([2, 1, 5, 4]))\n",
    "print(my_missing([3, 4, 6, 2, 5, 7, 9, 8]))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4927ea14",
   "metadata": {},
   "source": [
    "### 5.2 Recursion"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "id": "1ead05cf",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "9227465\n"
     ]
    }
   ],
   "source": [
    "# Task: The sequence of Fibonacci numbers starts with the numbers 1 and 1 and every subsequent term\n",
    "# is the sum of the previous two terms. So the sequence is 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 87, 144, ...\n",
    "# Can you write a simple recursive function that calculates the nth Fibonacci number?\n",
    "\n",
    "# WARNING: Don't call your function for anything more than 35 or pass a non-integer parameter. \n",
    "# Your notebook might crash if you do.\n",
    "\n",
    "def fib(n):\n",
    "    if n == 1 or n == 2: return 1\n",
    "    \n",
    "    return fib(n - 1) + fib(n - 2)\n",
    "\n",
    "print(fib(35))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3df824f1",
   "metadata": {},
   "source": [
    "### 5.3 Memoization"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "id": "132cbe1d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "354224848179261915075\n"
     ]
    }
   ],
   "source": [
    "# Part of the reason that we told you not to run your answer for 5.2 for large n is because the number of\n",
    "# function calls generated is exponentially large: for n = 35, the number of function calls you have is on\n",
    "# the order of 34 billion, which is a lot, even for a computer! If you did n = 75, the number of calls you\n",
    "# would make is approximately 37 sextillion, which is more than the number of seconds until the heat death\n",
    "# of the sun. \n",
    "\n",
    "# You can avert this issue, however, if you **memoize** your function, which is just a fancy way of saying\n",
    "# that you can remember values of your function instead of having to re-evaluate your function again. Python\n",
    "# has a handy memoization tool:\n",
    "\n",
    "from functools import lru_cache\n",
    "\n",
    "@lru_cache\n",
    "def fib(n):\n",
    "    if n == 1 or n == 2: return 1\n",
    "    \n",
    "    return fib(n - 1) + fib(n - 2)\n",
    "\n",
    "print(fib(100)) # Works no problem!\n",
    "\n",
    "# All we had to do was add the import statement and 'decorate' the function we wanted to remember\n",
    "# values from with the line @lru_cache"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "45aa0659",
   "metadata": {},
   "source": [
    "## Topic 6: Classes in Python"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "13a6a24f",
   "metadata": {},
   "source": [
    "### 6.1 Practice Writing Basic Classes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 78,
   "id": "0aba78c6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Name: Nalin Ranjan\n",
      "Year: 2022\n",
      "Concentration: COS\n",
      "Clubs: []\n",
      "Dining Halls from Most Favorite to Least: ['RoMa', 'Forbes', 'CJL', 'Whitman', 'WuCox']\n",
      "\n",
      "\n",
      "Name: Nalin Ranjan\n",
      "Year: 2022\n",
      "Concentration: COS\n",
      "Clubs: ['ACM', 'Taekwondo', 'Princeton Legal Journal', 'Badminton']\n",
      "Dining Halls from Most Favorite to Least: ['RoMa', 'Forbes', 'CJL', 'Whitman', 'WuCox']\n",
      "\n",
      "\n",
      "Name: Sacheth Sathyanarayanan\n",
      "Year: 2022\n",
      "Concentration: COS\n",
      "Clubs: ['ACM', 'Table Tennis']\n",
      "Dining Halls from Most Favorite to Least: ['CJL', 'RoMa', 'Forbes', 'Whitman', 'WuCox']\n",
      "\n",
      "Sacheth had a great meal at Whitman! It is now his favorite.\n",
      "\n",
      "Name: Sacheth Sathyanarayanan\n",
      "Year: 2022\n",
      "Concentration: COS\n",
      "Clubs: ['ACM', 'Table Tennis']\n",
      "Dining Halls from Most Favorite to Least: ['Whitman', 'CJL', 'RoMa', 'Forbes', 'WuCox']\n",
      "\n",
      "Sacheth is the same student as Nalin: False\n",
      "Sacheth's name comes before Nalin's: False\n"
     ]
    }
   ],
   "source": [
    "# Write a PrincetonStudent class, where a PrincetonStudent has a name, major, year,\n",
    "# set of clubs, and a preference ordering of dining halls. We want to have\n",
    "#\n",
    "# - a default constructor that initializes the PrincetonStudent with a name, major, PUID, year, no clubs,\n",
    "#   and a random preference ordering of dining halls\n",
    "# - a special constructor (class method) called detailed_student that initializes a PrincetonStudent \n",
    "#   with a name, major, year,\n",
    "#   a specific set of clubs, and a particular preference ordering of dining halls\n",
    "# - a __str__() method that prints all the data of the student\n",
    "# - a move_dhall_to_top() function that takes a dhall and moves it to the top\n",
    "#   of one's dining hall preference list\n",
    "# - a __lt__() method that returns true if and only if this student has a name that comes before\n",
    "#   the other's alphabetically\n",
    "# - an __eq__() method that returns true if and only if the PUIDs of students are equal \n",
    "\n",
    "# HINT: To generate a random dining hall preference order, you can take a particular preference order\n",
    "# and shuffle it using the random.shuffle(list) function\n",
    "\n",
    "from random import shuffle\n",
    "\n",
    "class PrincetonStudent():\n",
    "    def __init__(self, name, major, puid, year):\n",
    "        self.name = name\n",
    "        self.major = major\n",
    "        self.year = year\n",
    "        self.puid = puid\n",
    "        self.clubs = []\n",
    "        \n",
    "        # From least preferred to most\n",
    "        self.dhall_pref = ['WuCox', 'Whitman', 'Forbes', 'CJL', 'RoMa']\n",
    "        shuffle(self.dhall_pref)\n",
    "        \n",
    "    @classmethod\n",
    "    # Initialize a student with name, major, year, specific set of clubs, and a dhall preference ordering\n",
    "    def detailed_student(cls, name, major, puid, year, clubs, dhall_pref):\n",
    "        new_student = PrincetonStudent(name, major, puid, year)\n",
    "        new_student.clubs = clubs\n",
    "        new_student.dhall_pref = dhall_pref\n",
    "        \n",
    "        return new_student\n",
    "        \n",
    "    def move_dhall_to_top(self, dhall):\n",
    "        if dhall in self.dhall_pref:\n",
    "            move_to_top = self.dhall_pref.remove(dhall)\n",
    "            self.dhall_pref.append(dhall)\n",
    "        \n",
    "    # Returns a string description of the student. This allows us to call print(...) on a\n",
    "    # PrincetonStudent and get an intelligible result\n",
    "    def __str__(self):\n",
    "        str_version = \"Name: \" + self.name + \"\\n\"\n",
    "        str_version += \"Year: \" + str(self.year) + \"\\n\"\n",
    "        str_version += \"Concentration: \" + self.major + \"\\n\"\n",
    "        str_version += \"Clubs: \" + str(self.clubs) + \"\\n\"\n",
    "        str_version += \"Dining Halls from Most Favorite to Least: \" + str(self.dhall_pref[::-1]) + \"\\n\"\n",
    "        \n",
    "        return str_version\n",
    "    \n",
    "    def __lt__(self, other):\n",
    "        return (self.name.lower() < other.name.lower()) # This works because string comparison \n",
    "                                                        # is automatically alphabetical\n",
    "        \n",
    "        \n",
    "# Test your PrincetonStudent class using this test suite. Feel free to write your own too!\n",
    "nalin = PrincetonStudent('Nalin Ranjan', 'COS', '123456789', 2022)\n",
    "print(nalin, end=\"\\n\\n\")\n",
    "\n",
    "nalin.clubs.extend(['ACM', 'Taekwondo', 'Princeton Legal Journal', 'Badminton'])\n",
    "print(nalin, end=\"\\n\\n\")\n",
    "\n",
    "sacheth_clubs = ['ACM', 'Table Tennis']\n",
    "sacheth_prefs = ['WuCox', 'Whitman', 'Forbes', 'RoMa', 'CJL']\n",
    "sacheth = PrincetonStudent.detailed_student('Sacheth Sathyanarayanan', 'COS', \\\n",
    "                                '24681012', 2022, sacheth_clubs, sacheth_prefs)\n",
    "print(sacheth)\n",
    "\n",
    "print('Sacheth had a great meal at Whitman! It is now his favorite.\\n')\n",
    "sacheth.move_dhall_to_top('Whitman')\n",
    "print(sacheth)\n",
    "\n",
    "print('Sacheth is the same student as Nalin:', sacheth == nalin)\n",
    "print('Sacheth\\'s name comes before Nalin\\'s:', sacheth < nalin)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bba8a9a2",
   "metadata": {},
   "source": [
    "### 6.2 Inheritance"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 90,
   "id": "67e8cec4",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Name: Nalin Ranjan\n",
      "Year: 2022\n",
      "Concentration: COS\n",
      "Clubs: []\n",
      "Dining Halls from Most Favorite to Least: ['WuCox', 'CJL', 'Forbes', 'RoMa', 'Whitman']\n",
      "Position on the ACM Board: Chair\n",
      "Term expires in: 2022\n",
      "\n",
      "\n",
      "Name: Nalin Ranjan\n",
      "Year: 2022\n",
      "Concentration: COS\n",
      "Clubs: ['ACM', 'Taekwondo', 'Princeton Legal Journal', 'Badminton']\n",
      "Dining Halls from Most Favorite to Least: ['WuCox', 'CJL', 'Forbes', 'RoMa', 'Whitman']\n",
      "Position on the ACM Board: Chair\n",
      "Term expires in: 2022\n",
      "\n",
      "\n",
      "Name: Sacheth Sathyanarayanan\n",
      "Year: 2022\n",
      "Concentration: COS\n",
      "Clubs: ['ACM', 'Table Tennis']\n",
      "Dining Halls from Most Favorite to Least: ['CJL', 'RoMa', 'Forbes', 'Whitman', 'WuCox']\n",
      "Position on the ACM Board: Treasurer\n",
      "Term expires in: 2022\n",
      "\n",
      "Sacheth had a great meal at Whitman! It is now his favorite.\n",
      "\n",
      "Name: Sacheth Sathyanarayanan\n",
      "Year: 2022\n",
      "Concentration: COS\n",
      "Clubs: ['ACM', 'Table Tennis']\n",
      "Dining Halls from Most Favorite to Least: ['Whitman', 'CJL', 'RoMa', 'Forbes', 'WuCox']\n",
      "Position on the ACM Board: Treasurer\n",
      "Term expires in: 2022\n",
      "\n",
      "Sacheth is the same student as Nalin: False\n",
      "Sacheth's name comes before Nalin's: False\n"
     ]
    }
   ],
   "source": [
    "# Write an ACMOfficer class that inherits the PrincetonStudent class. An ACMOfficer has every attribute\n",
    "# a PrincetonStudent has, and also a position and term expiration date. You'll only need to overwrite\n",
    "# the constructors to accommodate these two additions. Remember that you can still call the parent's \n",
    "# functions as subroutines.\n",
    "\n",
    "class ACMOfficer(PrincetonStudent):\n",
    "    def __init__(self, name, major, puid, year, acm_pos, acm_term_exp):\n",
    "        PrincetonStudent.__init__(self, name, major, puid, year)\n",
    "        self.acm_pos = acm_pos\n",
    "        self.acm_term_exp = acm_term_exp\n",
    "        \n",
    "    @classmethod\n",
    "    def detailed_officer(cls, name, major, puid, year, clubs, dhall_pref, acm_pos, acm_term_exp):\n",
    "        new_officer = ACMOfficer(name, major, puid, year, acm_pos, acm_term_exp)\n",
    "        new_officer.clubs = clubs\n",
    "        new_officer.dhall_pref = dhall_pref\n",
    "        \n",
    "        return new_officer\n",
    "    \n",
    "    def __str__(self):\n",
    "        str_version = PrincetonStudent.__str__(self)\n",
    "        str_version += \"Position on the ACM Board: \" + self.acm_pos + \"\\n\"\n",
    "        str_version += \"Term expires in: \" + str(self.acm_term_exp) + \"\\n\"\n",
    "        \n",
    "        return str_version\n",
    "        \n",
    "# Test your PrincetonStudent class using this test suite. Feel free to write your own too!\n",
    "nalin = ACMOfficer('Nalin Ranjan', 'COS', '123456789', 2022, 'Chair', 2022)\n",
    "print(nalin, end=\"\\n\\n\")\n",
    "\n",
    "nalin.clubs.extend(['ACM', 'Taekwondo', 'Princeton Legal Journal', 'Badminton'])\n",
    "print(nalin, end=\"\\n\\n\")\n",
    "\n",
    "sacheth_clubs = ['ACM', 'Table Tennis']\n",
    "sacheth_prefs = ['WuCox', 'Whitman', 'Forbes', 'RoMa', 'CJL']\n",
    "sacheth = ACMOfficer.detailed_officer('Sacheth Sathyanarayanan', 'COS', '24681012', \n",
    "                                      2022, sacheth_clubs, sacheth_prefs, 'Treasurer', 2022)\n",
    "print(sacheth)\n",
    "\n",
    "print('Sacheth had a great meal at Whitman! It is now his favorite.\\n')\n",
    "sacheth.move_dhall_to_top('Whitman')\n",
    "print(sacheth)\n",
    "\n",
    "print('Sacheth is the same student as Nalin:', sacheth == nalin)\n",
    "print('Sacheth\\'s name comes before Nalin\\'s:', sacheth < nalin)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "983feed5",
   "metadata": {},
   "source": [
    "## Topic 7: Using Existing Python Libraries"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1355d3b2",
   "metadata": {},
   "source": [
    "Sigmoid activation functions are ubiquitous in machine learning. They all look somewhat like an S shape, starting\n",
    "out flat, and then somewhere in the middle jumping pretty quickly before leveling off. One example is the **Gudermannian Function**, which takes the form\n",
    "\n",
    "$$f(x, \\gamma) = \\gamma \\arctan \\left(\\tanh \\left( \\frac x \\gamma \\right) \\right)$$\n",
    "\n",
    "for some value $\\gamma$. You can think of $\\gamma$ as a parameter that specifies \"which\" Gudermannian function we're talking about. Can you plot the Gudermannian Function in the range $[-5, 5]$ with $\\gamma = \\{2, 4, 6\\}$? You will need access to `numpy` to find implementations of the arctan and tanh functions, and you will need `matplotlib` to create the actual plot.\n",
    "\n",
    "HINT: Since we have three different values of $\\gamma$, we'll have three different curves on the same graph."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 104,
   "id": "d07cb104",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.legend.Legend at 0x11d0eaa30>"
      ]
     },
     "execution_count": 104,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Numpy contains many mathematical functions/data analysis tools you might want to use\n",
    "import numpy as np\n",
    "\n",
    "# First: Write a function that returns the Gudermannian function evaluated at x.\n",
    "def gudermannian(x, gamma):\n",
    "    return gamma * np.arctan(np.tanh(x / gamma))\n",
    "\n",
    "# Next: use matplotlib to plot the function. HINT: Use matplotlib.pyplot\n",
    "from matplotlib import pyplot as plt # You'll refer to pyplot as plt from now on\n",
    "\n",
    "# HINT: pyplot requires that you have a set of x-values and a corresponding set of y-values.\n",
    "# To make your plot look like a continuous curve, just make your x-values close enough (say in\n",
    "# increments of 0.01). You'll have to use numpy's arange function (Google it!)\n",
    "x_vals = np.arange(-5, 5, 0.01)\n",
    "\n",
    "# Then, you'll have to make a set of y values for each gamma. HINT: If f(x) is a function\n",
    "# defined on a single number, then running it on x_vals evaluates the function at every x value\n",
    "# in x_vals. \n",
    "\n",
    "plt.plot(x_vals, gudermannian(x_vals, 2), label='gamma = 2')\n",
    "plt.plot(x_vals, gudermannian(x_vals, 4), label='gamma = 4')\n",
    "plt.plot(x_vals, gudermannian(x_vals, 6), label='gamma = 6')\n",
    "plt.legend()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
