{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "cell-000",
   "metadata": {},
   "source": [
    "# Lesson 1 — Python Fundamentals & Financial Logic\n",
    "\n",
    "**Companion notebook.** Work through it top to bottom alongside the Lesson 1 slides.\n",
    "\n",
    "* Run each cell with **Shift + Enter** and read the output before moving on.\n",
    "* Cells marked **Your turn** contain `...` placeholders — replace them with your own code.\n",
    "* The `check()` cells compare your answers with the expected values and print ✅ or ❌.\n",
    "\n",
    "Run the next cell first: it defines `check()`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-001",
   "metadata": {},
   "outputs": [],
   "source": [
    "def check(label, got, expected, tol=1e-6):\n",
    "    \"\"\"Compare your answer with the expected value; prints ✅ or ❌ (never raises).\"\"\"\n",
    "    import math\n",
    "    try:\n",
    "        if isinstance(expected, float) or isinstance(got, float):\n",
    "            ok = math.isclose(float(got), float(expected), rel_tol=tol, abs_tol=tol)\n",
    "        elif isinstance(expected, (list, tuple)) and expected and isinstance(expected[0], str):\n",
    "            ok = list(got) == list(expected)\n",
    "        elif hasattr(expected, \"__len__\") and not isinstance(expected, str):\n",
    "            import numpy as np\n",
    "            ok = np.allclose(np.asarray(got, dtype=float), np.asarray(expected, dtype=float), rtol=tol, atol=tol)\n",
    "        else:\n",
    "            ok = got == expected\n",
    "    except Exception:\n",
    "        ok = False\n",
    "    print((\"✅ \" if ok else \"❌ \") + f\"{label}: got {got!r}, expected {expected!r}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-002",
   "metadata": {},
   "source": [
    "## 1. The four core types\n",
    "\n",
    "Every value has a type. `type()` tells you which. Integers never overflow, and\n",
    "underscores make big numbers readable."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-003",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(type(360), type(0.05), type(\"TERM\"), type(False))\n",
    "sum_assured = 250_000\n",
    "2 ** 100"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-004",
   "metadata": {},
   "source": [
    "## 2. Arithmetic and precedence\n",
    "\n",
    "`/` always returns a float; `//` is *floor* division (it rounds toward minus infinity);\n",
    "`%` is the remainder; `**` is power. Note that `-2 ** 2` is `-4` because `**` binds\n",
    "tighter than the minus sign."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-005",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(7 / 2, 7 // 2, -7 // 2, 7 % 2, 2 ** 0.5)\n",
    "print(-2 ** 2, (-2) ** 2)\n",
    "print(True + True)          # bool is a kind of int"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-006",
   "metadata": {},
   "source": [
    "## 3. Floating point: never compare with `==`\n",
    "\n",
    "Floats are stored in binary, so most decimals are approximations. Compare with a\n",
    "tolerance using `math.isclose`, round only when presenting, and use `Decimal` when\n",
    "cents must reconcile exactly."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-007",
   "metadata": {},
   "outputs": [],
   "source": [
    "import math\n",
    "from decimal import ROUND_HALF_UP, Decimal\n",
    "\n",
    "print(0.1 + 0.2, 0.1 + 0.2 == 0.3, math.isclose(0.1 + 0.2, 0.3))\n",
    "print(round(2.5), round(3.5), round(2.675, 2))     # banker's rounding; binary 2.675 is below the half\n",
    "print(Decimal(\"0.10\") + Decimal(\"0.20\"))\n",
    "print(Decimal(\"2.675\").quantize(Decimal(\"0.01\"), rounding=ROUND_HALF_UP))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-008",
   "metadata": {},
   "source": [
    "## 4. Converting between types\n",
    "\n",
    "Data read from files arrives as text. Convert before calculating — and notice that\n",
    "`\"0\"` is truthy, because it is a non-empty string."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-009",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(int(\"42\"), float(\"3.5\"), str(1.5), int(3.9), int(-3.9))\n",
    "print(bool(0), bool(\"\"), bool([]), bool(None), bool(\"0\"))\n",
    "try:\n",
    "    int(\"3.5\")\n",
    "except ValueError as err:\n",
    "    print(\"ValueError:\", err)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-010",
   "metadata": {},
   "source": [
    "## 5. f-strings for financial output\n",
    "\n",
    "After the colon comes a *format spec*: alignment (`<`, `>`, `^`), width, a thousands\n",
    "separator, and precision/type (`.2f`, `.1%`, `.3e`)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-011",
   "metadata": {},
   "outputs": [],
   "source": [
    "rate, premium, claims, n = 0.0425, 1234.5, -5321.789, 360\n",
    "print(f\"{rate:.2%} | {premium:,.2f} | {claims:+,.0f} | {n:>6} | {premium:>12,.2f} | {12345678.9:.3e}\")\n",
    "print(f\"{rate=}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-012",
   "metadata": {},
   "outputs": [],
   "source": [
    "i = 0.05\n",
    "print(f\"{'Year':>4}  {'v^t':>8}  {'PV of 1,000':>12}\")\n",
    "for t in (1, 5, 10, 20):\n",
    "    v_t = (1 + i) ** -t\n",
    "    print(f\"{t:>4}  {v_t:>8.5f}  {1000 * v_t:>12,.2f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-013",
   "metadata": {},
   "source": [
    "## 6. Decisions with `if` / `elif` / `else`\n",
    "\n",
    "Conditions are checked top to bottom; the first true branch runs."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-014",
   "metadata": {},
   "outputs": [],
   "source": [
    "age, smoker = 47, False\n",
    "if age < 18:\n",
    "    rating = \"Decline\"\n",
    "elif age <= 50 and not smoker:\n",
    "    rating = \"Preferred\"\n",
    "elif age <= 65:\n",
    "    rating = \"Standard\"\n",
    "else:\n",
    "    rating = \"Refer to underwriter\"\n",
    "rating"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-015",
   "metadata": {},
   "source": [
    "`assert` stops the program when a sanity check fails. (Here we catch the error so the\n",
    "notebook keeps running.)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-016",
   "metadata": {},
   "outputs": [],
   "source": [
    "loss = -250.0\n",
    "try:\n",
    "    assert loss >= 0, f\"loss must be non-negative, got {loss}\"\n",
    "except AssertionError as err:\n",
    "    print(\"AssertionError:\", err)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-017",
   "metadata": {},
   "source": [
    "---\n",
    "## Your turn 1 — deductible and limit\n",
    "\n",
    "The insurer pays the loss above the deductible, but never more than the limit:\n",
    "`payment = min(max(loss − deductible, 0), limit)`. Write it with `if`/`elif`/`else`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-018",
   "metadata": {},
   "outputs": [],
   "source": [
    "def payment(loss, deductible=500, limit=10_000):\n",
    "    \"\"\"Insurer's payment on one loss.\"\"\"\n",
    "    ...  # your code here\n",
    "\n",
    "\n",
    "check(\"loss 300\", payment(300), 0)\n",
    "check(\"loss 2,500\", payment(2_500), 2_000)\n",
    "check(\"loss 12,000\", payment(12_000), 10_000)\n",
    "check(\"loss exactly 10,500\", payment(10_500), 10_000)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-019",
   "metadata": {},
   "source": [
    "## Your turn 2 — Social Security & Medicare (employee share, 2026)\n",
    "\n",
    "* Social Security: 6.2% of wages up to the wage base of **184,500**\n",
    "* Medicare: 1.45% of all wages, plus 0.9% on wages above 200,000\n",
    "\n",
    "Return the **total** tax."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-020",
   "metadata": {},
   "outputs": [],
   "source": [
    "WAGE_BASE = 184_500\n",
    "\n",
    "\n",
    "def payroll_tax(wages):\n",
    "    \"\"\"Employee Social Security + Medicare tax.\"\"\"\n",
    "    ...  # your code here\n",
    "\n",
    "\n",
    "check(\"wages 60,000\", payroll_tax(60_000), 4_590.00)\n",
    "check(\"wages 184,500\", payroll_tax(184_500), 14_114.25)\n",
    "check(\"wages 250,000\", payroll_tax(250_000), 15_514.00)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-021",
   "metadata": {},
   "source": [
    "## Your turn 3 — progressive tax brackets\n",
    "\n",
    "Each rate applies only to the income **within** its band (illustrative brackets):\n",
    "\n",
    "| Taxable income | Rate |\n",
    "|---|---|\n",
    "| 0 – 10,000 | 10% |\n",
    "| 10,000 – 40,000 | 15% |\n",
    "| 40,000 – 100,000 | 25% |\n",
    "| over 100,000 | 35% |\n",
    "\n",
    "Use an `if`/`elif` chain. (In Lesson 2 you'll replace it with a loop.)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-022",
   "metadata": {},
   "outputs": [],
   "source": [
    "def income_tax(income):\n",
    "    \"\"\"Tax under the illustrative brackets.\"\"\"\n",
    "    ...  # your code here\n",
    "\n",
    "\n",
    "check(\"income 8,000\", income_tax(8_000), 800.0)\n",
    "check(\"income 55,000\", income_tax(55_000), 9_250.0)\n",
    "check(\"income 150,000\", income_tax(150_000), 38_000.0)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-023",
   "metadata": {},
   "source": [
    "Finally, print a formatted table of income, tax, and **average** rate for a few\n",
    "incomes — use the f-string skills from section 5."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-024",
   "metadata": {},
   "outputs": [],
   "source": [
    "for inc in (8_000, 55_000, 150_000):\n",
    "    tax = income_tax(inc)\n",
    "    if tax is not None:\n",
    "        print(f\"{inc:>10,}  {tax:>10,.2f}  {tax / inc:>7.2%}\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
