{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "l03-c000",
   "metadata": {},
   "source": [
    "# Lecture 3 - Conditions and logic\n",
    "\n",
    "**Authors:** Fiona McNeill, Matteo Degiacomi, Rayo Verweij, and Nigel Topham\n",
    "\n",
    "<!-- <a rel=\"license\" href=\"https://creativecommons.org/licenses/by/4.0/\" target=\"_blank\"><img alt=\"Creative Commons Licence\" style=\"border-width:0\" src=\"https://licensebuttons.net/l/by/4.0/88x31.png\" title=\"This work is licensed under a Creative Commons Attribution 4.0 International License.\" align=\"right\"/></a> -->\n",
    "\n",
    "## Introduction\n",
    "\n",
    "So far, our programs have followed every instruction in order. Here, we will start building programs that do different things based on whether certain conditions are true or false.\n",
    "\n",
    "### Learning outcomes\n",
    "\n",
    "In this notebook, we'll discuss:\n",
    "\n",
    "- constructing comparisons with `==`, `!=`, `<`, `<=`, `>`, and `>=`;\n",
    "- combining conditions with `and`, `or`, and `not`;\n",
    "- selecting one or more paths with `if`, `elif`, and `else`;\n",
    "- using indentation to show which statements belong to a branch; and\n",
    "- recognising reserved keywords in Python.\n",
    "\n",
    "### Using the notebook\n",
    "\n",
    "Remember, this notebook is _yours_! You can edit each cell, for example by adding new examples or changing values to see what happens. Feel free to experiment!\n",
    "\n",
    "A common approach for learning how to read and write code in a structured way is called **PRIMM**:\n",
    "1. **Predict**: before running the code, look at it - do you understand each line? What do you think will happen?\n",
    "2. **Run**: now run the code and see what output you get.\n",
    "3. **Investigate**: did it go as you thought it would? If not, can you figure out why?\n",
    "4. **Modify**: now change the code and once again try to predict the output. If you got it wrong the first time, did you get it right now?\n",
    "5. **Make**: finally, try to write your own program from scratch using the skills you learned.\n",
    "\n",
    "You can apply steps 1-4 to each code cell in the notebook and many will have prediction prompts to help you get started. In addition, there will be challenges that ask you to put everything together yourself and execute step 5. There will often be multiple ways to solve these, but each challenge has a potential answer listed.\n",
    "\n",
    "### Navigating the notebook\n",
    "\n",
    "A Jupyter notebook can be operated with your keyboard. Here is a **cheat sheet**:\n",
    "\n",
    "- To run the selected cell and move to the next cell, press <kbd>&#x21E7; Shift</kbd> + <kbd>&#x23ce; Enter</kbd>.\n",
    "- To run the selected cell and keep focus there, press <kbd>⌃ Ctrl</kbd> + <kbd>&#x23ce; Enter</kbd>.\n",
    "- To create a cell under the one currently highlighted, press <kbd>B</kbd>;\n",
    "- To delete the currently highlighted cell, press <kbd>D</kbd> twice (be careful with this one!) or press <kbd>X</kbd> to cut it;\n",
    "- To get help for a specific function, place the cursor within the function's brackets, hold <kbd>&#x21E7; Shift</kbd>, and press <kbd>&#x21E5; Tab</kbd>.\n",
    "\n",
    "Watch out: **code cells remember what happened in cells before**. So, especially in the more complicated notebooks later down the line, make sure to always run **every** cell from top to bottom, as one might rely on a piece of code that came before it! If your results become confusing, use **Kernel > Restart Kernel** and then rerun all the completed cells in order.\n",
    "\n",
    "### Further reading and practice\n",
    "\n",
    "- Official Python tutorial: [`if` statements](https://docs.python.org/3/tutorial/controlflow.html#if-statements).\n",
    "- W3Schools: [Booleans](https://www.w3schools.com/python/python_booleans.asp) and [If...Else](https://www.w3schools.com/python/python_conditions.asp).\n",
    "- Helsinki Python MOOC: [Conditional statements](https://programming-26.mooc.fi/part-1/5-conditional-statements), [More conditionals](https://programming-26.mooc.fi/part-2/2-else-elif) and [Combining conditions](https://programming-26.mooc.fi/part-2/3-combining-conditions).\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b6636556",
   "metadata": {},
   "source": [
    "## 1. Comparison operators and Boolean values"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l03-c001",
   "metadata": {},
   "source": [
    "So far, we have seen assignment and arithmetic operators. We'll start today with a third type: **comparison operators**. An expression with a comparison operator evaluates to one of two values: `True` or `False`.\n",
    "\n",
    "In Python, `True` and `False` are special values that have the type **Boolean** or `bool`. They are named after George Boole, a 19th-century English mathematician who developed Boolean algebra.\n",
    "\n",
    "| Operator | Meaning |\n",
    "|---|---|\n",
    "| `==` | equal to |\n",
    "| `!=` | not equal to |\n",
    "| `<` | less than |\n",
    "| `<=` | less than or equal to |\n",
    "| `>` | greater than |\n",
    "| `>=` | greater than or equal to |\n",
    "\n",
    "**Predict:** Before running the code, what do you think will be printed on each line?\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l03-c002",
   "metadata": {},
   "outputs": [],
   "source": [
    "temperature = 12\n",
    "print(temperature < 15)\n",
    "print(temperature == 12)\n",
    "print(temperature != 12)\n",
    "print(type(temperature >= 10))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "546abe33",
   "metadata": {},
   "source": [
    "<div class=\"alert alert-warning\">\n",
    "<b>Watch out: single and double equals</b>\n",
    "\n",
    "In mathematics, we are used to reading `=` as 'is equal to'. However, in programming, this is usually not the case.\n",
    "\n",
    "Python, like most programming languages, uses the single equals sign `=` for *variable assignment*, whereas the double equals sign `==` is used for checking whether two values are the same. \n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "65d9f2a9-74a4-4c24-8e10-79ed6fe0bda4",
   "metadata": {},
   "source": [
    "### String comparisons\n",
    "\n",
    "There is a key difference between strings and numbers. When comparing numbers, Python compares their numerical size, as we are used to from mathematics.\n",
    "\n",
    "When comparing strings, however, Python compares their characters from left to right using their **Unicode code points**. This is called **lexicographic comparison**.\n",
    "\n",
    "What does this mean? Computers are calculators: ultimately, everything is expressed in numbers, so under the hood, computers _encode_ each possible character as a number in a long list, called a **code point**.\n",
    "\n",
    "Modern computers almost universally use a character encoding standard called **Unicode**. There are thousands of characters represented in Unicode, with a [full list available on Wikipedia](https://en.wikipedia.org/wiki/List_of_Unicode_characters). In terms of basic Latin characters, the digits <code>0</code> to <code>9</code> have code points 48-57, uppercase <code>A</code> to <code>Z</code> have 65-90, and lowercase <code>a</code> to <code>z</code> have 97-122.\n",
    "\n",
    "When comparing strings, then, Python compares the Unicode code points of their characters. Say that we ask"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e4d7e020",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(\"apple\" < \"orange\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "501e2861",
   "metadata": {},
   "source": [
    "These strings have code points `97 112 112 108 101` and `111 114 97 110 103 101`, respectively. We start with the first character, and as 97 is smaller than 111, this expression evaluates to `True`.\n",
    "\n",
    "If the starting characters are the same, we move on to the next one:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "580aa360",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(\"bee\" > \"bus\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cc78054e",
   "metadata": {},
   "source": [
    "...evaluates to `False`, because `e` is represented by a smaller number than `u`.\n",
    "\n",
    "If one string is a prefix of the other, the shorter string comes first:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "73c14151",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(\"app\" < \"apple\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c973e380",
   "metadata": {},
   "source": [
    "For equality, the strings must have the same length and matching characters at every position. For example, in"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7096808f",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(\"McNeill\" == \"Mcneill\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3a3e9555",
   "metadata": {},
   "source": [
    "the strings have code points `77 99 78 101 105 108 108` and `77 99 110 101 105 108 108`, respectively. As these are not the same, the expression evaluates to `False`.\n",
    "\n",
    "**You do not need to memorise the code points of characters.** (The entire point of programming is to have computers do tedious work like this for us.) What you should know, however, is the following:\n",
    "\n",
    "1. Unicode assigns characters numerical code points.\n",
    "2. Within the basic Latin characters, digits come before uppercase letters, which come before lowercase letters.\n",
    "3. Python compares strings **lexicographically**, meaning from left to right using their Unicode code points.\n",
    "\n",
    "Let's try it out ourselves.\n",
    "\n",
    "**Predict:** Find a notepad and write your predictions for each line before running it. After running the code once, change the values and operators to different ones to make sure you get a feel for how comparisons work."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l03-c004",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(\"Rain\" == \"rain\")\n",
    "print(\"Appleton\" > \"Forum\")\n",
    "print(\"banana\" < \"KIWI\")\n",
    "\n",
    "print(\"3\" == 3)\n",
    "print(2.0 >= 2)\n",
    "print(str(5) == \"5\")\n",
    "\n",
    "print(type(1) != type(2))\n",
    "\n",
    "print(\"10\" < \"2\")\n",
    "print(10 < 2)\n",
    "print(10 < \"2\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l03-c005",
   "metadata": {
    "tags": [
     "exercise"
    ]
   },
   "source": [
    "Note the final line: the less than and greater than operators only work on values of similar types. The equality operators, however, work across types.\n",
    "\n",
    "<div class=\"alert alert-success\">\n",
    "<b>Try it yourself: sorting alphabetically</b>\n",
    "\n",
    "You might wonder - what is string comparison good for? One of the most common use cases is to order words alphabetically. However, there is one snag: because of the way letters are represented in Unicode, Python first separates them by whether they are uppercase or lowercase before looking at their place in the alphabet.\n",
    "\n",
    "Based on the Python functionality you have seen so far, **how would you update the following piece of code** so that it compares these words *regardless* of whether they contain uppercase or lowercase letters?\n",
    "</div>\n",
    "\n",
    "<details>\n",
    "<summary><b>Click for a hint</b></summary>\n",
    "\n",
    "Stuck? Check the methods we discussed in the lecture 2 notebook for inspiration!\n",
    "\n",
    "</details>\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l03-c006",
   "metadata": {
    "tags": [
     "exercise"
    ]
   },
   "outputs": [],
   "source": [
    "# This piece of code checks whether some string input comes before \"ChEcKpOiNt\" in the alphabet\n",
    "base_text = \"ChEcKpOiNt\"\n",
    "user_text = input(\"Enter a string to compare: \")\n",
    "print(user_text < base_text)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l03-c007",
   "metadata": {
    "tags": [
     "solution"
    ]
   },
   "source": [
    "<details>\n",
    "<summary><b>Click for a possible solution</b></summary>\n",
    "\n",
    "An easy way to do this is to convert both values to be either completely uppercase or lowercase. For example:\n",
    "\n",
    "```python\n",
    "base_text = \"ChEcKpOiNt\"\n",
    "user_text = input(\"Enter a string to compare: \")\n",
    "print(user_text.upper() < base_text.upper())\n",
    "```\n",
    "<br>\n",
    "Remember, <code>.upper()</code> and <code>.lower()</code> return a new string and leave the original unchanged. So if you wanted to use <code>base_text</code> or <code>user_text</code> for something else after the print statement, they would still have their original capitalisation.\n",
    "</details>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b3a0a604",
   "metadata": {},
   "source": [
    "## 2. Combining conditions"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l03-c008",
   "metadata": {},
   "source": [
    "We have one more type of operator to introduce: the **logical operator**. Logical operators let us _combine_ or _reverse_ conditions.\n",
    "\n",
    "There are just three: `and`, `or`, and `not`. That's right - while some languages use symbols for these (like `&&`), Python simply uses the words.\n",
    "\n",
    "- `A and B` is true only when **both** operands are true.\n",
    "- `A or B` is true when **at least one** operand is true.\n",
    "- `not A` reverses true and false.\n",
    "\n",
    "Just like in mathematical expressions, you can use parentheses to group statements that should be evaluated first.\n",
    "\n",
    "**Predict:** Once again, trace each line before running the following code."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l03-c009",
   "metadata": {},
   "outputs": [],
   "source": [
    "has_ticket = True\n",
    "doors_open = False\n",
    "on_guest_list = True\n",
    "\n",
    "print(has_ticket and doors_open)\n",
    "print(has_ticket or doors_open)\n",
    "print(has_ticket or on_guest_list)\n",
    "print(not doors_open)\n",
    "print((has_ticket or on_guest_list) and doors_open)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l03-c010",
   "metadata": {},
   "source": [
    "Now, here is where Python gets a little funky. In addition to `False`, Python also treats some non-Boolean values as false in a condition: specifically, `0`, `0.0`, and the empty string `\"\"`. Other numbers and non-empty strings are treated as true."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l03-c011",
   "metadata": {},
   "outputs": [],
   "source": [
    "alcohol_percentage = 0.0\n",
    "allergens = \"\"\n",
    "\n",
    "print(alcohol_percentage == True)\n",
    "print(not allergens)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ca6cc4d5",
   "metadata": {},
   "source": [
    "When given non-Boolean operands, `and` and `or` return one of those operands:\n",
    "- `A and B` returns `A` if it is _false_ in a condition; otherwise it returns `B`.\n",
    "- `A or B` returns `A` if it is _true_ in a condition; otherwise it returns `B`.\n",
    "\n",
    "The `not` operator always returns `True` or `False`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "75ade37e",
   "metadata": {},
   "outputs": [],
   "source": [
    "# \"tea\" is a non-empty string, so in a condition it counts as True\n",
    "print(\"tea\" or \"coffee\") \n",
    "print(\"tea\" and \"coffee\")\n",
    "\n",
    "# 0 counts as False\n",
    "print(0 and 7) \n",
    "print(0 or 7)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d6f33a9f",
   "metadata": {},
   "source": [
    "You can also convert these values to Boolean values using the `bool()` function.\n",
    "\n",
    "**Predict:** What value and type will each print statement display?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a17387e9",
   "metadata": {},
   "outputs": [],
   "source": [
    "new_bool = bool(\"hello\")\n",
    "print(new_bool, type(new_bool))\n",
    "\n",
    "newer_bool = bool(0.0)\n",
    "print(newer_bool, type(newer_bool))\n",
    "\n",
    "surprising_bool = bool(\"false\")\n",
    "print(surprising_bool, type(surprising_bool))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l03-c012",
   "metadata": {
    "tags": [
     "exercise"
    ]
   },
   "source": [
    "<div class=\"alert alert-success\">\n",
    "<b>Try it yourself: combine access rules</b>\n",
    "\n",
    "Admission requires a valid card and either staff status or a booking. Use the three variables to print the condition for whether someone may enter or not.\n",
    "</div>\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l03-c013",
   "metadata": {
    "tags": [
     "exercise"
    ]
   },
   "outputs": [],
   "source": [
    "has_valid_card = True\n",
    "is_staff = False\n",
    "has_booking = True\n",
    "\n",
    "can_enter = ???\n",
    "print(can_enter)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l03-c014",
   "metadata": {
    "tags": [
     "solution"
    ]
   },
   "source": [
    "<details>\n",
    "<summary><b>Click for a possible solution</b></summary>\n",
    "\n",
    "```python\n",
    "has_valid_card = True\n",
    "is_staff = False\n",
    "has_booking = True\n",
    "\n",
    "can_enter = has_valid_card and (is_staff or has_booking)\n",
    "print(can_enter)\n",
    "```\n",
    "</details>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cb6017b9",
   "metadata": {},
   "source": [
    "## 3. Choosing paths with `if`, `elif`, and `else`"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l03-c015",
   "metadata": {},
   "source": [
    "An `if` statement connects a Boolean question to an action, allowing us to branch our program in different directions depending on whether conditions are true or false.\n",
    "\n",
    "`if` statements use the following syntax:\n",
    "\n",
    "```python\n",
    "if condition:\n",
    "  do_something()\n",
    "```\n",
    "\n",
    "The header starts with the keyword `if` and ends with a colon.\n",
    "\n",
    "The body is **indented**, that is, there is some blank space between the beginning of the line and the body of the `if` statement. Indentation marks which part of the code runs only as part of the `if` statement, and which part of the code runs regardless.\n",
    "\n",
    "<div class=\"alert alert-warning\">\n",
    "<b>Watch out: indentation</b>\n",
    "\n",
    "In Python, indentation is not only used to help code readability: it is *meaningful*.\n",
    "You can indent text using either spaces (usually 2 or 4) or tabs, whichever you prefer. However, ***be consistent***: Python will throw an error if you mix spaces and tabs, or if you use a different number of spaces in the same block!\n",
    "</div>\n",
    "\n",
    "**Predict:** What is printed when you run the following code? What would be printed if the value of `battery` is `30`, instead?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l03-c016",
   "metadata": {},
   "outputs": [],
   "source": [
    "battery = 18\n",
    "\n",
    "if battery < 20:\n",
    "    print(\"Charge soon!\")\n",
    "\n",
    "print(\"Battery check complete.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l03-c017",
   "metadata": {},
   "source": [
    "We can define what will run in case the condition is false using the `else` keyword.\n",
    "\n",
    "Like the `if` statement, `else` is followed by a colon.\n",
    "\n",
    "Note the indentation in the following example.\n",
    "* `result = \"pass\"` is indented because it should only run under the `if` condition;\n",
    "* `else:` is back to the beginning of the line because it is part of the main program;\n",
    "* `result = \"fail\"` is indented again because it should only run under the `else` condition; and\n",
    "* the print statement is back to the beginning of the line because it should run regardless of the `if/else` conditions."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l03-c018",
   "metadata": {},
   "outputs": [],
   "source": [
    "mark = 58\n",
    "\n",
    "if mark >= 40:\n",
    "    result = \"pass\"\n",
    "else:\n",
    "    result = \"fail\"\n",
    "\n",
    "print(f\"Result: {result}\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l03-c019",
   "metadata": {},
   "source": [
    "The `elif` keyword (else-if) allows us to add more mutually exclusive alternatives between `if` and `else`. *Python checks each condition from top to bottom and stops at the first branch that is true*, so order the branches so an earlier condition does not capture cases intended for a later one. \n",
    "\n",
    "**Predict:** What is printed when `mark` is `70`, or `60`, or `39`?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l03-c020",
   "metadata": {},
   "outputs": [],
   "source": [
    "mark = 70\n",
    "\n",
    "if mark >= 70:\n",
    "    band = \"distinction\"\n",
    "elif mark >= 60:\n",
    "    band = \"merit\"\n",
    "elif mark >= 40:\n",
    "    band = \"pass\"\n",
    "else:\n",
    "    band = \"fail\"\n",
    "\n",
    "print(f\"Result: {band}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l03-c021",
   "metadata": {
    "tags": [
     "exercise"
    ]
   },
   "source": [
    "<div class=\"alert alert-success\">\n",
    "<b>Try it yourself: classifying temperatures</b>\n",
    "\n",
    "Write a program that labels a temperature below 5 as `cold`, from 5 up to but not including 15 as `cool`, from 15 up to but not including 25 as `mild`, and 25 or above as `warm`. Make sure you test the boundary values to check that you used the comparison operators correctly.\n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l03-c022",
   "metadata": {
    "tags": [
     "exercise"
    ]
   },
   "outputs": [],
   "source": [
    "temperature = 15\n",
    "# Add the conditional and print the label"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l03-c023",
   "metadata": {
    "tags": [
     "solution"
    ]
   },
   "source": [
    "<details>\n",
    "<summary><b>Click for a possible solution</b></summary>\n",
    "\n",
    "```python\n",
    "temperature = 15\n",
    "if temperature < 5:\n",
    "    label = \"cold\"\n",
    "elif temperature < 15:\n",
    "    label = \"cool\"\n",
    "elif temperature < 25:\n",
    "    label = \"mild\"\n",
    "else:\n",
    "    label = \"warm\"\n",
    "print(label)\n",
    "```\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0d14715a",
   "metadata": {
    "tags": [
     "exercise"
    ]
   },
   "source": [
    "<div class=\"alert alert-success\">\n",
    "<b>Try it yourself: classifying time of day</b>\n",
    "\n",
    "One more round of practice. Ask for an integer hour from 0 to 23. Print `morning` for 5–11, `afternoon` for 12–17, `evening` for 18–22, and `night` otherwise. Also print an error message when the hour is outside 0–23.\n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a07acabb",
   "metadata": {
    "tags": [
     "exercise"
    ]
   },
   "outputs": [],
   "source": [
    "# Write your code here"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a155313f",
   "metadata": {},
   "source": [
    "<details>\n",
    "<summary><b>Click for a possible solution</b></summary>\n",
    "\n",
    "However you have implemented your solution, make sure you print `night` both for the early hours (0-4) and for 23!\n",
    "\n",
    "```python\n",
    "hour = int(input(\"Enter an hour between 0 and 23: \"))\n",
    "\n",
    "if hour < 0 or hour > 23:\n",
    "    print(\"error: hour must be from 0 to 23\")\n",
    "elif hour >= 5 and hour <= 11:\n",
    "    print(\"morning\")\n",
    "elif hour >= 12 and hour <= 17:\n",
    "    print(\"afternoon\")\n",
    "elif hour >= 18 and hour <= 22:\n",
    "    print(\"evening\")\n",
    "else:\n",
    "    print(\"night\")\n",
    "```\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6c1da17f-4395-486a-935f-6f6b2f6ce634",
   "metadata": {},
   "source": [
    "## 4. Common errors"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l03-c024",
   "metadata": {},
   "source": [
    "Let's discuss some common errors when using conditions.\n",
    "\n",
    "### Assignment vs comparison\n",
    "\n",
    "Remember: `=` is for assignment and `==` for comparison. The following is **invalid** Python and will throw a `SyntaxError`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "61bfa7b3-c2a7-41fd-9c30-6f7a9685585f",
   "metadata": {},
   "outputs": [],
   "source": [
    "if status = \"open\":\n",
    "    print(\"Come in\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "05164f91-f19e-4c4a-baa9-3011f1a06c6e",
   "metadata": {},
   "source": [
    "### Mistaken indentation\n",
    "\n",
    "These programs mean different things:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c6506ea5-2ac8-4120-b8a8-f98d747610d0",
   "metadata": {},
   "outputs": [],
   "source": [
    "raining = False\n",
    "\n",
    "if raining:\n",
    "    print(\"Take a coat\")\n",
    "    print(\"Check the forecast\")  # Only happens if raining == True\n",
    "\n",
    "if raining:\n",
    "    print(\"Take a coat\")\n",
    "print(\"Check the forecast\")      # Always runs"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c4aa2f92-de47-4edd-9579-926787965a31",
   "metadata": {},
   "source": [
    "Remember to be consistent in your indentation!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a88c813f",
   "metadata": {},
   "outputs": [],
   "source": [
    "plant_name = \"rose\"\n",
    "  quantity = 3\n",
    "print(f\"I have {quantity} {plant_name}s\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7f3acb43",
   "metadata": {},
   "source": [
    "If you use indentation where Python does not expect it, it will raise an `IndentationError`."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l03-c025",
   "metadata": {},
   "source": [
    "### Branch order and boundaries\n",
    "\n",
    "While this next piece of code does not contain a syntax error, it does contain a logic bug:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d2397726-c42c-412c-80b0-180755d07df3",
   "metadata": {},
   "outputs": [],
   "source": [
    "mark = 74\n",
    "\n",
    "if mark >= 40:\n",
    "    print(\"pass\")\n",
    "elif mark >= 70:\n",
    "    print(\"distinction\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f9e8bf90-a766-4939-8b54-07d78ddf8321",
   "metadata": {},
   "source": [
    "`if` statements always follow the first condition that is satisfied. Because any mark that is greater than or equal to 70 is *also* greater than or equal to 40, no-one will ever get a distinction!\n",
    "\n",
    "When writing conditions, it can be useful to perform **boundary tests**. Run your code with values on the boundaries of your conditions, in this case, 39, 40, 69, and 70, to expose any faulty ordering or incorrect operator usage.\n",
    "\n",
    "<div class=\"alert alert-success\">\n",
    "<b>Try it yourself: repair a fare rule</b>\n",
    "\n",
    "A person aged under 5 travels free, a person under 18 pays £2.50, and everyone else pays £4... but that's not what the following code has implemented. Edit it to adhere to the rules and test your work with ages 4, 5, 17, and 18.\n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l03-c028",
   "metadata": {
    "tags": [
     "exercise"
    ]
   },
   "outputs": [],
   "source": [
    "age = 17\n",
    "\n",
    "# Fix the code!\n",
    "if age <= 18:\n",
    "    fare = 2.5\n",
    "elif age <= 5:\n",
    "    fare = 0.0\n",
    "else:\n",
    "    fare = 4\n",
    "\n",
    "print(f\"Fare: £{fare:.2f}\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l03-c029",
   "metadata": {
    "tags": [
     "solution"
    ]
   },
   "source": [
    "<details>\n",
    "<summary><b>Click for the solution</b></summary>\n",
    "\n",
    "Two mistakes needed to be corrected.\n",
    "1. The rules specified reduced fare for people *below* ages 5 and 18, not *below or at* these ages. Therefore, the operators needed to be `<` instead of `<=`.\n",
    "2. The second condition is never reached as 5 is lower than 18, so these needed to be swapped.\n",
    "\n",
    "```python\n",
    "age = 17\n",
    "\n",
    "if age < 5:\n",
    "    fare = 0.0\n",
    "elif age < 18:\n",
    "    fare = 2.5\n",
    "else:\n",
    "    fare = 4.0\n",
    "\n",
    "print(f\"Fare: £{fare:.2f}\")\n",
    "```\n",
    "</details>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "51beedb5-4474-44a0-9217-38a1825de9c2",
   "metadata": {},
   "source": [
    "## 5. Reserved keywords"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6356f96d",
   "metadata": {},
   "source": [
    "One final note. In this lecture, we have introduced a few **keywords**: words in Python that take on specific functionality, such as `if`, `elif`, `else`, `and`, `or`, and `not`.\n",
    "\n",
    "Keywords are **reserved** in Python. This means that you cannot name your variables after any!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9d0ade56",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Invalid code!\n",
    "else = 2"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "771fa5cf",
   "metadata": {},
   "source": [
    "Keywords differ from functions (like `print`, `input`, and `type`) in that functions can be _called_ (using parentheses, such as `print()`) whereas keywords cannot. While keywords are reserved, it's perfectly fine to name your variable after an already existing function. That means you override the built-in functionality, however!\n",
    "\n",
    "There are not many keywords, so this is not usually an issue when coding, and we will see most of them over the course of this semester.\n",
    "\n",
    "For a full list of keywords, see [Python keywords on W3Schools](https://www.w3schools.com/python/python_ref_keywords.asp)."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "70bf4dfc",
   "metadata": {},
   "source": [
    "## Consolidation challenge"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l03-consolidation-prompt",
   "metadata": {
    "tags": [
     "exercise"
    ]
   },
   "source": [
    "If you worked through lecture 1's self-guided notebook, you might remember our catering business *Algorithmic Appetites*. Unfortunately, due to its inability to cater to people's dietary requirements, it was sued under the 2010 Equality Act and went out of business. But no reason to panic: we'll just start a new one!\n",
    "\n",
    "<div class=\"alert alert-success\">\n",
    "<b>Try it yourself: creating a menu based on dietary requirements</b>\n",
    "    \n",
    "Here at *Conditional Cuisines* we are going to go the extra mile and **generate a personalised menu** for people based on their dietary requirements. Our menu looks like this:\n",
    "\n",
    "**Starters**\n",
    "* Default: haggis bites with mango chutney\n",
    "* Vegetarian: one large veggie samosa\n",
    "* Gluten-free: tomato and basil soup\n",
    "\n",
    "**Mains**\n",
    "* Default: steak and ale pie\n",
    "* Gluten-free but not vegetarian: salmon with asparagus and new potatoes\n",
    "* Vegetarian: goat cheese salad with croutons\n",
    "* Vegetarian and gluten-free: goat cheese salad WITHOUT croutons\n",
    "\n",
    "**Dessert**\n",
    "* Cranachan\n",
    "* Gluten-free: a slightly sad scoop of vanilla ice cream\n",
    "\n",
    "Create a program that asks a user for their dietary requirements and prints out their menu as a single string. Ask whether they are vegetarian and whether they need gluten-free food. Assume the answers are `yes` or `no`, but allow for different capitalisation and surrounding spaces.\n",
    "</div>\n",
    "\n",
    "Check all four combinations before opening the solution:\n",
    "\n",
    "| Vegetarian | Gluten-free | Starter | Main | Dessert |\n",
    "| --- | --- | --- | --- | --- |\n",
    "| no | no | haggis bites with mango chutney | steak and ale pie | cranachan |\n",
    "| yes | no | one large veggie samosa | goat cheese salad with croutons | cranachan |\n",
    "| no | yes | tomato and basil soup | salmon with asparagus and new potatoes | a slightly sad scoop of vanilla ice cream |\n",
    "| yes | yes | tomato and basil soup | goat cheese salad WITHOUT croutons | a slightly sad scoop of vanilla ice cream |\n",
    "\n",
    "Also try answers such as `\" YES \"` and `\" No \"` to check that whitespace and capitalisation do not change the selected menu."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l03-consolidation-work",
   "metadata": {
    "tags": [
     "exercise"
    ]
   },
   "outputs": [],
   "source": [
    "# Your solution here!"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l03-consolidation-solution",
   "metadata": {
    "tags": [
     "solution"
    ]
   },
   "source": [
    "<details><summary><b>Click for a possible solution</b></summary>\n",
    "\n",
    "Our version uses three different `if` statements. Alternatively, you could use one statement that sets the starter, main, and dessert all at once. How you go about it is up to you!\n",
    "\n",
    "```python\n",
    "# Get user input\n",
    "is_veggie_text = input(\"Are you vegetarian? (yes/no) \").strip()\n",
    "needs_gluten_free_text = input(\"Do you need gluten-free food? (yes/no) \").strip()\n",
    "\n",
    "# Convert the input to booleans we can use\n",
    "# Let's make sure it doesn't matter whether the user used capital letters or not\n",
    "if is_veggie_text.upper() == \"YES\":\n",
    "    is_veggie = True\n",
    "else:\n",
    "    is_veggie = False\n",
    "\n",
    "if needs_gluten_free_text.upper() == \"YES\":\n",
    "    needs_gluten_free = True\n",
    "else:\n",
    "    needs_gluten_free = False\n",
    "\n",
    "# Determine the dishes served\n",
    "if needs_gluten_free:\n",
    "    starter = \"tomato and basil soup\"\n",
    "elif is_veggie:\n",
    "    starter = \"one large veggie samosa\"\n",
    "else:\n",
    "    starter = \"haggis bites with mango chutney\"\n",
    "\n",
    "if is_veggie and needs_gluten_free:\n",
    "    main = \"goat cheese salad WITHOUT croutons\"\n",
    "elif is_veggie:\n",
    "    main = \"goat cheese salad with croutons\"\n",
    "elif needs_gluten_free:\n",
    "    main = \"salmon with asparagus and new potatoes\"\n",
    "else:\n",
    "    main = \"steak and ale pie\"\n",
    "\n",
    "if needs_gluten_free:\n",
    "    dessert = \"a slightly sad scoop of vanilla ice cream\"\n",
    "else:\n",
    "    dessert = \"cranachan\"\n",
    "\n",
    "# Print the result\n",
    "print(f\"Welcome to Conditional Cuisines! Tonight, you will first receive {starter}, then our delicious {main}, and finally {dessert} to end the night. Enjoy!\")\n",
    "```\n",
    "</details>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l03-consolidation-summary",
   "metadata": {
    "tags": [
     "summary"
    ]
   },
   "source": [
    "## Summary\n",
    "\n",
    "Well done! In order to cover conditions we also had to cover some fundamental computer science topics around logic and text encoding. You'll encounter these again in more detail in future Informatics courses but for now, you are starting to build a good understanding of the basics of programming.\n",
    "\n",
    "### Key terms\n",
    "\n",
    "<dl>\n",
    "\t<dt><b>Boolean</b></dt>\n",
    "\t<dd>A type of value that is either true or false.</dd>\n",
    "\t<dt><b>Indentation</b></dt>\n",
    "\t<dd>Whitespace at the beginning of lines of code that, in Python, groups pieces of code together.</dd>\n",
    "\t<dt><b>Boundary test</b></dt>\n",
    "\t<dd>When writing an if statement using numbers, a test that checks the statement by using values that are on the boundaries of the conditions.</dd>\n",
    "\t<dt><b>Lexicographic comparison</b></dt>\n",
    "\t<dd>Comparing strings from left to right using their code points.</dd>\n",
    "\t<dt><b>Code point</b></dt>\n",
    "\t<dd>A number assigned to a character so computers can process it.</dd>\n",
    "\t<dt><b>Unicode</b></dt>\n",
    "\t<dd>A common standard for code points, used by almost all computers.</dd>\n",
    "\t<dt><b>Reserved keyword</b></dt>\n",
    "\t<dd>A word with special functionality in Python that cannot be used as a variable name.</dd>\n",
    "</dl>\n",
    "\n",
    "### New syntax\n",
    "\n",
    "<dl>\n",
    "\t<dt><code>True</code> and <code>False</code></dt>\n",
    "\t<dd>Boolean values in Python. Remember to capitalise them!</dd>\n",
    "\t<dt><code>bool</code> and <code>bool()</code></dt>\n",
    "\t<dd>The Boolean type in Python and the function to convert a value to the <code>bool</code> type in Python, respectively.</dd>\n",
    "\t<dt><code>==</code> and <code>!=</code></dt>\n",
    "\t<dd>Equal to and not equal to comparison operators. Work across types.</dd>\n",
    "\t<dt><code><</code>, <code><=</code>, <code>></code>, <code>>=</code></dt>\n",
    "\t<dd>Less than; less than or equal to; greater than; and greater than or equal to comparison operators. Only work when their operands are of similar types.</dd>\n",
    "\t<dt><code>and</code> and <code>or</code></dt>\n",
    "\t<dd>Logical operators that combine conditions. Return a Boolean when their operands are Booleans; return one of their operands when they are not.</dd>\n",
    "\t<dt><code>not</code></dt>\n",
    "\t<dd>Logical operator that reverses a condition. Always returns a Boolean.</dd>\n",
    "\t<dt><code>if</code>, <code>elif</code>, <code>else</code></dt>\n",
    "\t<dd>Keywords that define if statements and their branches. Remember that for each branch, the head ends with a colon and the body is indented.</dd>\n",
    "    <dt><code>SyntaxError</code></dt>\n",
    "\t<dd>A type of error that highlights when you break the rules of Python, such as naming a variable after a reserved keyword or attempting to assign a variable as part of a condition (by using <code>=</code> instead of <code>==</code>, for example).</dd>\n",
    "\t<dt><code>IndentationError</code></dt>\n",
    "\t<dd>A type of error that highlights when you misuse indentation.</dd>\n",
    "</dl>\n"
   ]
  }
 ],
 "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.11.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
