From 4eb6f5c315386e467751a7636dbfc3ecce49aa40 Mon Sep 17 00:00:00 2001
From: JINLANG WANG <jwang2775@wisc.edu>
Date: Thu, 8 Feb 2024 14:19:12 -0600
Subject: [PATCH] add lab4

---
 Labs/Lab4/bst-groups/README.md      | 163 +++++++++++++++
 Labs/Lab4/debug/self/generate.ipynb | 250 ++++++++++++++++++++++
 Labs/Lab4/debug/self/main.ipynb     | 314 ++++++++++++++++++++++++++++
 Labs/Lab4/debug/self/scores.csv     |  11 +
 Labs/Lab4/lab4.md                   |   7 +
 5 files changed, 745 insertions(+)
 create mode 100644 Labs/Lab4/bst-groups/README.md
 create mode 100644 Labs/Lab4/debug/self/generate.ipynb
 create mode 100644 Labs/Lab4/debug/self/main.ipynb
 create mode 100644 Labs/Lab4/debug/self/scores.csv
 create mode 100644 Labs/Lab4/lab4.md

diff --git a/Labs/Lab4/bst-groups/README.md b/Labs/Lab4/bst-groups/README.md
new file mode 100644
index 0000000..7d90b14
--- /dev/null
+++ b/Labs/Lab4/bst-groups/README.md
@@ -0,0 +1,163 @@
+# BSTs (Binary Search Trees)
+
+In this lab, you'll create a BST that can be used to lookup values by
+a key (it will behave a bit like a Python dict where the all the dict
+values are lists of values).  You'll use the BST for P2.
+
+## Basics Node and BST classes
+
+Start by pasting+completing the following:
+
+```python
+class Node():
+    def __init__(self, key):
+        self.key = ????
+        self.values = []
+        self.left = None
+        ????
+```
+
+Let's create a `BST` class with an `add` method that automatically
+places a node in a place that preserves the search property (i.e., all
+keys in left subtree are less than a parent's value, which is less
+than those in the right tree).
+
+Add+complete with the following.  Note that this is a non-recursive
+version of `add`:
+
+```python
+class BST():
+    def __init__(self):
+        self.root = None
+
+    def add(self, key, val):
+        if self.root == None:
+            self.root = ????
+
+        curr = self.root
+        while True:
+            if key < curr.key:
+                # go left
+                if curr.left == None:
+                    curr.left = Node(key)
+                curr = curr.left
+            elif key > curr.key:
+                 # go right
+                 ????
+                 ????
+                 ????
+            else:
+                # found it!
+                assert curr.key == key
+                break
+
+        curr.values.append(val)
+```
+
+## Dump
+
+Let's write some methods to BST to dump out all the keys and values (note
+that "__" before a method name is a hint that it is for internal use
+-- methods inside the class might call `__dump`, but code outside the
+class probably shouldn't):
+
+```python
+    def __dump(self, node):
+        if node == None:
+            return
+        self.__dump(node.right)            # 1
+        print(node.key, ":", node.values)  # 2
+        self.__dump(node.left)             # 3
+
+    def dump(self):
+        self.__dump(self.root)
+```
+
+Try it:
+
+```python
+tree = BST()
+tree.add("A", 9)
+tree.add("A", 5)
+tree.add("B", 22)
+tree.add("C", 33)
+tree.dump()
+```
+
+You should see this:
+
+```
+C : [33]
+B : [22]
+A : [9, 5]
+```
+
+Play around with the order of lines 1, 2, and 3 in `__dump()` above.  Can you
+arrange those three so that the output is in ascending alphabetical
+order, by key?
+
+## Length
+
+Add a special method `__len__` to `Node` so that we can find the size
+of a tree.  Count every entry in the `.values` list of each `Node`.
+
+```python
+    def __len__(self):
+        size = len(self.values)
+        if self.left != None:
+            size += ????
+        ????
+            ????
+        return size
+```
+
+```python
+t = BST()
+t.add("B", 3)
+assert len(t.root) == 1
+t.add("A", 2)
+assert len(t.root) == 2
+t.add("C", 1)
+assert len(t.root) == 3
+t.add("C", 4)
+assert len(t.root) == 4
+```
+
+Discuss with your neighbour: why not have a `Node.__dump(self)` method
+instead of the `BST.__dump(self, node)` method?
+
+<details>
+<summary>Answer</summary>
+
+Right now, it is convenient to check at the beginning if `node` is
+None.  A receiver (the `self` parameter) can't be None if the
+`object.method(...)` syntax is used (you would get the
+"AttributeError: 'NoneType' object has no attribute 'method'" error).
+We could have a `Node.__dump(self)` method, but then we would need to do the None checks on both `.left` and `.right`, which is slightly longer.
+</details>
+
+## Lookups
+
+Write a `lookup` method in `Node` that returns all the values that match a given key.  Some examples:
+
+* `t.root.lookup("A")` should return `[2]`
+* `t.root.lookup("C")` should return `[1, 4]`
+* `t.root.lookup("Z")` should return `[]`
+
+Some pseudocode for you to translate to Python:
+
+```
+lookup method (takes key)
+    if key matches my key, return my values
+    if key is less than my key and I have a left child
+        call lookup on my left child and return what it returns
+    if key is greater than my key and I have a right child
+        call lookup on my right child and return what it returns
+    otherwise return an empty list
+```
+
+## `search.py` module
+
+If you've been developing your `BST` and `Node` classes in a notebook,
+you should now move them to a module called `search.py` in your `p2`
+directory.
diff --git a/Labs/Lab4/debug/self/generate.ipynb b/Labs/Lab4/debug/self/generate.ipynb
new file mode 100644
index 0000000..64287c8
--- /dev/null
+++ b/Labs/Lab4/debug/self/generate.ipynb
@@ -0,0 +1,250 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# This just generates random data -- look at main.ipynb to debug"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import names\n",
+    "import numpy as np\n",
+    "import pandas as pd"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "<div>\n",
+       "<style scoped>\n",
+       "    .dataframe tbody tr th:only-of-type {\n",
+       "        vertical-align: middle;\n",
+       "    }\n",
+       "\n",
+       "    .dataframe tbody tr th {\n",
+       "        vertical-align: top;\n",
+       "    }\n",
+       "\n",
+       "    .dataframe thead th {\n",
+       "        text-align: right;\n",
+       "    }\n",
+       "</style>\n",
+       "<table border=\"1\" class=\"dataframe\">\n",
+       "  <thead>\n",
+       "    <tr style=\"text-align: right;\">\n",
+       "      <th></th>\n",
+       "      <th>name</th>\n",
+       "      <th>P1</th>\n",
+       "      <th>P2</th>\n",
+       "      <th>P3</th>\n",
+       "      <th>P4</th>\n",
+       "      <th>P5</th>\n",
+       "      <th>Final</th>\n",
+       "      <th>Participation</th>\n",
+       "    </tr>\n",
+       "  </thead>\n",
+       "  <tbody>\n",
+       "    <tr>\n",
+       "      <th>0</th>\n",
+       "      <td>Elsie</td>\n",
+       "      <td>0.954955</td>\n",
+       "      <td>0.913779</td>\n",
+       "      <td>0.921565</td>\n",
+       "      <td>0.936532</td>\n",
+       "      <td>0.901380</td>\n",
+       "      <td>0.928387</td>\n",
+       "      <td>0.914671</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>1</th>\n",
+       "      <td>Brian</td>\n",
+       "      <td>0.947351</td>\n",
+       "      <td>0.952920</td>\n",
+       "      <td>0.925073</td>\n",
+       "      <td>0.875950</td>\n",
+       "      <td>0.857365</td>\n",
+       "      <td>0.938826</td>\n",
+       "      <td>0.958818</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>2</th>\n",
+       "      <td>Loretta</td>\n",
+       "      <td>0.958606</td>\n",
+       "      <td>0.891525</td>\n",
+       "      <td>0.950882</td>\n",
+       "      <td>0.946470</td>\n",
+       "      <td>0.989340</td>\n",
+       "      <td>0.933632</td>\n",
+       "      <td>0.969530</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>3</th>\n",
+       "      <td>Esther</td>\n",
+       "      <td>0.985102</td>\n",
+       "      <td>0.872918</td>\n",
+       "      <td>0.977284</td>\n",
+       "      <td>0.988530</td>\n",
+       "      <td>0.930378</td>\n",
+       "      <td>0.724164</td>\n",
+       "      <td>0.995753</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>4</th>\n",
+       "      <td>Dawn</td>\n",
+       "      <td>0.966695</td>\n",
+       "      <td>0.927002</td>\n",
+       "      <td>0.991770</td>\n",
+       "      <td>0.959826</td>\n",
+       "      <td>0.895863</td>\n",
+       "      <td>0.859567</td>\n",
+       "      <td>0.908425</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>5</th>\n",
+       "      <td>Crystal</td>\n",
+       "      <td>0.859427</td>\n",
+       "      <td>0.952088</td>\n",
+       "      <td>0.965462</td>\n",
+       "      <td>0.899423</td>\n",
+       "      <td>0.995269</td>\n",
+       "      <td>0.989677</td>\n",
+       "      <td>0.987587</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>6</th>\n",
+       "      <td>Clarence</td>\n",
+       "      <td>0.851693</td>\n",
+       "      <td>0.926668</td>\n",
+       "      <td>0.906261</td>\n",
+       "      <td>0.880833</td>\n",
+       "      <td>0.932816</td>\n",
+       "      <td>0.834454</td>\n",
+       "      <td>0.943060</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>7</th>\n",
+       "      <td>Virginia</td>\n",
+       "      <td>0.928037</td>\n",
+       "      <td>0.934979</td>\n",
+       "      <td>0.874236</td>\n",
+       "      <td>0.955648</td>\n",
+       "      <td>0.997138</td>\n",
+       "      <td>0.715540</td>\n",
+       "      <td>0.959519</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>8</th>\n",
+       "      <td>Ernest</td>\n",
+       "      <td>0.893501</td>\n",
+       "      <td>0.959971</td>\n",
+       "      <td>0.938698</td>\n",
+       "      <td>0.887911</td>\n",
+       "      <td>0.881159</td>\n",
+       "      <td>0.978723</td>\n",
+       "      <td>0.928907</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>9</th>\n",
+       "      <td>Jane</td>\n",
+       "      <td>0.922780</td>\n",
+       "      <td>0.964439</td>\n",
+       "      <td>0.926576</td>\n",
+       "      <td>0.937013</td>\n",
+       "      <td>0.853827</td>\n",
+       "      <td>0.700346</td>\n",
+       "      <td>0.998765</td>\n",
+       "    </tr>\n",
+       "  </tbody>\n",
+       "</table>\n",
+       "</div>"
+      ],
+      "text/plain": [
+       "       name        P1        P2        P3        P4        P5     Final  \\\n",
+       "0     Elsie  0.954955  0.913779  0.921565  0.936532  0.901380  0.928387   \n",
+       "1     Brian  0.947351  0.952920  0.925073  0.875950  0.857365  0.938826   \n",
+       "2   Loretta  0.958606  0.891525  0.950882  0.946470  0.989340  0.933632   \n",
+       "3    Esther  0.985102  0.872918  0.977284  0.988530  0.930378  0.724164   \n",
+       "4      Dawn  0.966695  0.927002  0.991770  0.959826  0.895863  0.859567   \n",
+       "5   Crystal  0.859427  0.952088  0.965462  0.899423  0.995269  0.989677   \n",
+       "6  Clarence  0.851693  0.926668  0.906261  0.880833  0.932816  0.834454   \n",
+       "7  Virginia  0.928037  0.934979  0.874236  0.955648  0.997138  0.715540   \n",
+       "8    Ernest  0.893501  0.959971  0.938698  0.887911  0.881159  0.978723   \n",
+       "9      Jane  0.922780  0.964439  0.926576  0.937013  0.853827  0.700346   \n",
+       "\n",
+       "   Participation  \n",
+       "0       0.914671  \n",
+       "1       0.958818  \n",
+       "2       0.969530  \n",
+       "3       0.995753  \n",
+       "4       0.908425  \n",
+       "5       0.987587  \n",
+       "6       0.943060  \n",
+       "7       0.959519  \n",
+       "8       0.928907  \n",
+       "9       0.998765  "
+      ]
+     },
+     "execution_count": 2,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "df = pd.DataFrame({\"name\": [names.get_first_name() for i in range(10)]})\n",
+    "for i in range(5):\n",
+    "    df[f\"P{i+1}\"] = np.random.random(size=len(df)) * 0.15 + 0.85\n",
+    "df[f\"Final\"] = np.random.random(size=len(df)) * 0.3 + 0.7\n",
+    "df[f\"Participation\"] = np.random.random(size=len(df)) * 0.1 + 0.9\n",
+    "df"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "df.to_csv(\"scores.csv\", index=False)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "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.6.9"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/Labs/Lab4/debug/self/main.ipynb b/Labs/Lab4/debug/self/main.ipynb
new file mode 100644
index 0000000..a54d1d2
--- /dev/null
+++ b/Labs/Lab4/debug/self/main.ipynb
@@ -0,0 +1,314 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import pandas as pd"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "<div>\n",
+       "<style scoped>\n",
+       "    .dataframe tbody tr th:only-of-type {\n",
+       "        vertical-align: middle;\n",
+       "    }\n",
+       "\n",
+       "    .dataframe tbody tr th {\n",
+       "        vertical-align: top;\n",
+       "    }\n",
+       "\n",
+       "    .dataframe thead th {\n",
+       "        text-align: right;\n",
+       "    }\n",
+       "</style>\n",
+       "<table border=\"1\" class=\"dataframe\">\n",
+       "  <thead>\n",
+       "    <tr style=\"text-align: right;\">\n",
+       "      <th></th>\n",
+       "      <th>name</th>\n",
+       "      <th>P1</th>\n",
+       "      <th>P2</th>\n",
+       "      <th>P3</th>\n",
+       "      <th>P4</th>\n",
+       "      <th>P5</th>\n",
+       "      <th>Final</th>\n",
+       "      <th>Participation</th>\n",
+       "    </tr>\n",
+       "  </thead>\n",
+       "  <tbody>\n",
+       "    <tr>\n",
+       "      <th>0</th>\n",
+       "      <td>Elsie</td>\n",
+       "      <td>0.954955</td>\n",
+       "      <td>0.913779</td>\n",
+       "      <td>0.921565</td>\n",
+       "      <td>0.936532</td>\n",
+       "      <td>0.901380</td>\n",
+       "      <td>0.928387</td>\n",
+       "      <td>0.914671</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>1</th>\n",
+       "      <td>Brian</td>\n",
+       "      <td>0.947351</td>\n",
+       "      <td>0.952920</td>\n",
+       "      <td>0.925073</td>\n",
+       "      <td>0.875950</td>\n",
+       "      <td>0.857365</td>\n",
+       "      <td>0.938826</td>\n",
+       "      <td>0.958818</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>2</th>\n",
+       "      <td>Loretta</td>\n",
+       "      <td>0.958606</td>\n",
+       "      <td>0.891525</td>\n",
+       "      <td>0.950882</td>\n",
+       "      <td>0.946470</td>\n",
+       "      <td>0.989340</td>\n",
+       "      <td>0.933632</td>\n",
+       "      <td>0.969530</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>3</th>\n",
+       "      <td>Esther</td>\n",
+       "      <td>0.985102</td>\n",
+       "      <td>0.872918</td>\n",
+       "      <td>0.977284</td>\n",
+       "      <td>0.988530</td>\n",
+       "      <td>0.930378</td>\n",
+       "      <td>0.724164</td>\n",
+       "      <td>0.995753</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>4</th>\n",
+       "      <td>Dawn</td>\n",
+       "      <td>0.966695</td>\n",
+       "      <td>0.927002</td>\n",
+       "      <td>0.991770</td>\n",
+       "      <td>0.959826</td>\n",
+       "      <td>0.895863</td>\n",
+       "      <td>0.859567</td>\n",
+       "      <td>0.908425</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>5</th>\n",
+       "      <td>Crystal</td>\n",
+       "      <td>0.859427</td>\n",
+       "      <td>0.952088</td>\n",
+       "      <td>0.965462</td>\n",
+       "      <td>0.899423</td>\n",
+       "      <td>0.995269</td>\n",
+       "      <td>0.989677</td>\n",
+       "      <td>0.987587</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>6</th>\n",
+       "      <td>Clarence</td>\n",
+       "      <td>0.851693</td>\n",
+       "      <td>0.926668</td>\n",
+       "      <td>0.906261</td>\n",
+       "      <td>0.880833</td>\n",
+       "      <td>0.932816</td>\n",
+       "      <td>0.834454</td>\n",
+       "      <td>0.943060</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>7</th>\n",
+       "      <td>Virginia</td>\n",
+       "      <td>0.928037</td>\n",
+       "      <td>0.934979</td>\n",
+       "      <td>0.874236</td>\n",
+       "      <td>0.955648</td>\n",
+       "      <td>0.997138</td>\n",
+       "      <td>0.715540</td>\n",
+       "      <td>0.959519</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>8</th>\n",
+       "      <td>Ernest</td>\n",
+       "      <td>0.893501</td>\n",
+       "      <td>0.959971</td>\n",
+       "      <td>0.938698</td>\n",
+       "      <td>0.887911</td>\n",
+       "      <td>0.881159</td>\n",
+       "      <td>0.978723</td>\n",
+       "      <td>0.928907</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>9</th>\n",
+       "      <td>Jane</td>\n",
+       "      <td>0.922780</td>\n",
+       "      <td>0.964439</td>\n",
+       "      <td>0.926576</td>\n",
+       "      <td>0.937013</td>\n",
+       "      <td>0.853827</td>\n",
+       "      <td>0.700346</td>\n",
+       "      <td>0.998765</td>\n",
+       "    </tr>\n",
+       "  </tbody>\n",
+       "</table>\n",
+       "</div>"
+      ],
+      "text/plain": [
+       "       name        P1        P2        P3        P4        P5     Final  \\\n",
+       "0     Elsie  0.954955  0.913779  0.921565  0.936532  0.901380  0.928387   \n",
+       "1     Brian  0.947351  0.952920  0.925073  0.875950  0.857365  0.938826   \n",
+       "2   Loretta  0.958606  0.891525  0.950882  0.946470  0.989340  0.933632   \n",
+       "3    Esther  0.985102  0.872918  0.977284  0.988530  0.930378  0.724164   \n",
+       "4      Dawn  0.966695  0.927002  0.991770  0.959826  0.895863  0.859567   \n",
+       "5   Crystal  0.859427  0.952088  0.965462  0.899423  0.995269  0.989677   \n",
+       "6  Clarence  0.851693  0.926668  0.906261  0.880833  0.932816  0.834454   \n",
+       "7  Virginia  0.928037  0.934979  0.874236  0.955648  0.997138  0.715540   \n",
+       "8    Ernest  0.893501  0.959971  0.938698  0.887911  0.881159  0.978723   \n",
+       "9      Jane  0.922780  0.964439  0.926576  0.937013  0.853827  0.700346   \n",
+       "\n",
+       "   Participation  \n",
+       "0       0.914671  \n",
+       "1       0.958818  \n",
+       "2       0.969530  \n",
+       "3       0.995753  \n",
+       "4       0.908425  \n",
+       "5       0.987587  \n",
+       "6       0.943060  \n",
+       "7       0.959519  \n",
+       "8       0.928907  \n",
+       "9       0.998765  "
+      ]
+     },
+     "execution_count": 2,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "# Point vs Grade Distribution:\n",
+    "# Projects: 10% each\n",
+    "# Final: 30%\n",
+    "# Participation: 20%\n",
+    "df = pd.read_csv(\"scores.csv\")\n",
+    "df"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "class Student:\n",
+    "    def __init__(self,name):\n",
+    "        self.name = name\n",
+    "        self.grade = 0\n",
+    "\n",
+    "    def compute_grade(self, category, points):\n",
+    "        grade = 0\n",
+    "        if category == \"Participation\":\n",
+    "            grade += points*20\n",
+    "        if \"P\" in category:\n",
+    "            grade += points*10\n",
+    "        if category == \"Final\":\n",
+    "            grade += points*30\n",
+    "        self.grade += self.grade\n",
+    "\n",
+    "    def get_grade(self):\n",
+    "        return self.grade"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "cs320 = {}\n",
+    "for i in range(len(df)):\n",
+    "    student = Student(df[\"name\"][i])\n",
+    "    for col in df.columns:\n",
+    "        student.compute_grade(col, df.at[i, col])\n",
+    "    cs320[student.name] = student.get_grade()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "{'Elsie': 0,\n",
+       " 'Brian': 0,\n",
+       " 'Loretta': 0,\n",
+       " 'Esther': 0,\n",
+       " 'Dawn': 0,\n",
+       " 'Crystal': 0,\n",
+       " 'Clarence': 0,\n",
+       " 'Virginia': 0,\n",
+       " 'Ernest': 0,\n",
+       " 'Jane': 0}"
+      ]
+     },
+     "execution_count": 5,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "# max score should be 100; Crystal should have highest score, about 96.16\n",
+    "cs320"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Hints\n",
+    "\n",
+    "Debugging is about asking good questions related to the issue, then finding answers to those questions (often with print statements).  Some good questions to ask here:\n",
+    "\n",
+    "* what line is supposed to update `self.grade` (which appears to remain zero, incorrectly)?  Does this line run?  You could add a `print(\"DEBUG\")` before to find out.\n",
+    "* what is getting added to `self.grade` each time `compute_grade` is called?  A print can help with this question too.\n",
+    "* is any category getting counted more than once?  You could print the category inside `compute_grade` and print \"ADD\" inside each `if` statement to look for double counting."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "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.6"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/Labs/Lab4/debug/self/scores.csv b/Labs/Lab4/debug/self/scores.csv
new file mode 100644
index 0000000..9437eb2
--- /dev/null
+++ b/Labs/Lab4/debug/self/scores.csv
@@ -0,0 +1,11 @@
+name,P1,P2,P3,P4,P5,Final,Participation
+Elsie,0.9549550658129085,0.913778536271882,0.9215645170508429,0.9365319073397634,0.9013803540759843,0.9283869569882017,0.914671094034935
+Brian,0.9473514567792112,0.9529196735053641,0.9250734196408118,0.8759504151368779,0.8573646255287061,0.9388263403512875,0.958818414131339
+Loretta,0.9586059945221289,0.8915254116636335,0.9508819366200232,0.9464696745374475,0.9893403649836676,0.933631602588951,0.9695295462457724
+Esther,0.9851022156424294,0.8729183336955997,0.977283721063346,0.9885295844870531,0.9303782853727577,0.7241638599763238,0.995752825762694
+Dawn,0.9666952120841795,0.9270022970580513,0.9917699643450418,0.9598264328786889,0.8958627959043414,0.8595672515103249,0.9084248987279755
+Crystal,0.8594269068766791,0.9520883722829931,0.965461916205357,0.8994228809152749,0.9952691008971792,0.9896774016995538,0.9875868086271352
+Clarence,0.8516934263744823,0.926667638882182,0.9062605836013653,0.8808329786074112,0.9328156387216657,0.8344540588006799,0.9430598375038679
+Virginia,0.9280369533547376,0.9349792379562916,0.8742357364765729,0.9556482581209289,0.9971375119681434,0.7155395489950207,0.9595187884592306
+Ernest,0.893501309690879,0.9599711435403204,0.9386984759172361,0.8879113516975059,0.8811594481923767,0.9787231284971425,0.9289073086234929
+Jane,0.9227803346581137,0.9644389789471546,0.9265759728084851,0.9370125768424054,0.8538274635091643,0.7003457399712552,0.9987653286592659
diff --git a/Labs/Lab4/lab4.md b/Labs/Lab4/lab4.md
new file mode 100644
index 0000000..97f6559
--- /dev/null
+++ b/Labs/Lab4/lab4.md
@@ -0,0 +1,7 @@
+# Lab 4: BST
+
+1. We have the detailed mortgage dataset we're using for P2 thanks to the 1975 Home Mortgage Disclosure Act. Discuss with your group: *If you could pass a law requiring the collection and release of a new dataset, what data would you choose?* Feel free to answer based on what you think would be fun or interesting, or you can think about how your dataset might bring more transparency to a societal issue (like how the HDMA data makes it easier to monitor for discriminatory lending practices).
+
+2. Inside this folder, there is a notebook `debug/self/main.ipynb`. Open it and fix the bugs. 
+
+3. Create a [binary search tree](./bst-groups) for use in P2. 
\ No newline at end of file
-- 
GitLab