      "\n",
      "class Graph:\n",
      "\n",
      "    def __init__(self):\n",
      "        self.nodes = {}  # key = node id, val = node\n",
      "\n",
      "    def add_node(self, id):\n",
      "        node = Node(id)\n",
      "        self.nodes[id] = node\n",
      "        return node\n",
      "\n",
      "    def add_edge(self, id_source, id_dest, weight=0):\n",
      "        if id_source not in self.nodes:\n",
      "            self.add_node(id_source)\n",
      "        if id_dest not in self.nodes:\n",
      "            self.add_node(id_dest)\n",
      "        self.nodes[id_source].add_neighbor(self.nodes[id_dest], weight)\n",
      "\n",
      "    def add_undirected_edge(self, source, dest, weight=0):\n",
      "        self.add_edge(source, dest, weight)\n",
      "        self.nodes[dest].add_neighbor(self.nodes[source], weight)"
     ]
    },
    {
     "cell_type": "code",
     "execution_count": 2,
 
</s> remove         self.nodes = {}  # key = node id, val = node

    def add_node(self, id):
        node = Node(id)
        self.nodes[id] = node
        return node

    def add_edge(self, id_source, id_dest, weight=0):
        if id_source not in self.nodes:
            self.add_node(id_source)
        if id_dest not in self.nodes:
            self.add_node(id_dest)
        self.nodes[id_source].add_neighbor(self.nodes[id_dest], weight)

    def add_undirected_edge(self, source, dest, weight=0):
        self.add_edge(source, dest, weight)
        self.nodes[dest].add_neighbor(self.nodes[source], weight) </s> add  </s> remove     "        # TODO: Implement me\n",
    "        self.nodes = {}  # key = node id, val = node\n",
 </s> add     "        self.nodes = {}  # Key = key, val = Node\n", </s> remove     "        self.adjacent[neighbor] = weight\n",
 </s> add     "        if neighbor is None:\n",
    "            raise Exception('Invalid neighbor')\n",
    "        neighbor.incoming_edges += 1\n",
    "        self.adj_weights[neighbor.key] = weight\n",
    "        self.adj_nodes[neighbor.key] = neighbor\n",
    "\n",
    "    def remove_neighbor(self, neighbor):\n",
    "        if neighbor is None:\n",
    "            raise Exception('Invalid neighbor')\n",
    "        if neighbor.key in self.adj_nodes:\n",
    "            neighbor.incoming_edges -= 1\n",
    "            del self.adj_weights[neighbor.key]\n",
    "            del self.adj_nodes[neighbor.key]\n",
    "        else:\n",
    "            raise Exception('Invalid neighbor')\n", </s> remove     "        self.adjacent = {}  # key = node, val = weight\n",
 </s> add     "        self.incoming_edges = 0\n",
    "        self.adj_nodes = {}  # Key = key, val = Node\n",
    "        self.adj_weights = {}  # Key = key, val = weight\n",
    "\n",
    "    def __repr__(self):\n",
    "        return str(self.key)\n",
    "        self.nodes = {}  # Key = key, val = Node\n",
    "\n",
    "    def add_node(self, key):\n",
    "        if key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        if key in self.nodes:\n",
    "            return self.nodes[key]\n",
    "        self.nodes[key] = Node(key)\n",
    "        return self.nodes[key]\n",
    "\n",
    "    def add_edge(self, source_key, dest_key, weight=0):\n",
    "        if source_key is None or dest_key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        if source_key not in self.nodes:\n",
    "            self.add_node(source_key)\n",
    "        if dest_key not in self.nodes:\n",
    "            self.add_node(dest_key)\n",
    "        self.nodes[source_key].add_neighbor(self.nodes[dest_key],\n",
    "                                            weight)\n",
    "\n",
    "    def add_undirected_edge(self, source_key, dest_key, weight=0):\n",
    "        if source_key is None or dest_key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        self.add_edge(source_key, dest_key, weight)\n",
    "        self.nodes[dest_key].add_neighbor(self.nodes[source_key],\n",
    "                                          weight)"
      "\n",
      "class Graph:\n",
      "\n",
      "    def __init__(self):\n",
    "        self.nodes = {}  # Key = key, val = Node\n",
    "\n",
    "    def add_node(self, key):\n",
    "        if key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        if key in self.nodes:\n",
    "            return self.nodes[key]\n",
    "        self.nodes[key] = Node(key)\n",
    "        return self.nodes[key]\n",
    "\n",
    "    def add_edge(self, source_key, dest_key, weight=0):\n",
    "        if source_key is None or dest_key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        if source_key not in self.nodes:\n",
    "            self.add_node(source_key)\n",
    "        if dest_key not in self.nodes:\n",
    "            self.add_node(dest_key)\n",
    "        self.nodes[source_key].add_neighbor(self.nodes[dest_key],\n",
    "                                            weight)\n",
    "\n",
    "    def add_undirected_edge(self, source_key, dest_key, weight=0):\n",
    "        if source_key is None or dest_key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        self.add_edge(source_key, dest_key, weight)\n",
    "        self.nodes[dest_key].add_neighbor(self.nodes[source_key],\n",
    "                                          weight)"
    "        self.nodes = {}  # Key = key, val = Node\n",
    "\n",
    "    def add_node(self, key):\n",
    "        if key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        if key in self.nodes:\n",
    "            return self.nodes[key]\n",
    "        self.nodes[key] = Node(key)\n",
    "        return self.nodes[key]\n",
    "\n",
    "    def add_edge(self, source_key, dest_key, weight=0):\n",
    "        if source_key is None or dest_key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        if source_key not in self.nodes:\n",
    "            self.add_node(source_key)\n",
    "        if dest_key not in self.nodes:\n",
    "            self.add_node(dest_key)\n",
    "        self.nodes[source_key].add_neighbor(self.nodes[dest_key],\n",
    "                                            weight)\n",
    "\n",
    "    def add_undirected_edge(self, source_key, dest_key, weight=0):\n",
    "        if source_key is None or dest_key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        self.add_edge(source_key, dest_key, weight)\n",
    "        self.nodes[dest_key].add_neighbor(self.nodes[source_key],\n",
    "                                          weight)"
    "        self.nodes = {}  # Key = key, val = Node\n",
    "\n",
    "    def add_node(self, key):\n",
    "        if key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        if key in self.nodes:\n",
    "            return self.nodes[key]\n",
    "        self.nodes[key] = Node(key)\n",
    "        return self.nodes[key]\n",
    "\n",
    "    def add_edge(self, source_key, dest_key, weight=0):\n",
    "        if source_key is None or dest_key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        if source_key not in self.nodes:\n",
    "            self.add_node(source_key)\n",
    "        if dest_key not in self.nodes:\n",
    "            self.add_node(dest_key)\n",
    "        self.nodes[source_key].add_neighbor(self.nodes[dest_key],\n",
    "                                            weight)\n",
    "\n",
    "    def add_undirected_edge(self, source_key, dest_key, weight=0):\n",
    "        if source_key is None or dest_key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        self.add_edge(source_key, dest_key, weight)\n",
    "        self.nodes[dest_key].add_neighbor(self.nodes[source_key],\n",
    "                                          weight)"
    "        self.nodes = {}  # Key = key, val = Node\n",
    "\n",
    "    def add_node(self, key):\n",
    "        if key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        if key in self.nodes:\n",
    "            return self.nodes[key]\n",
    "        self.nodes[key] = Node(key)\n",
    "        return self.nodes[key]\n",
    "\n",
    "    def add_edge(self, source_key, dest_key, weight=0):\n",
    "        if source_key is None or dest_key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        if source_key not in self.nodes:\n",
    "            self.add_node(source_key)\n",
    "        if dest_key not in self.nodes:\n",
    "            self.add_node(dest_key)\n",
    "        self.nodes[source_key].add_neighbor(self.nodes[dest_key],\n",
    "                                            weight)\n",
    "\n",
    "    def add_undirected_edge(self, source_key, dest_key, weight=0):\n",
    "        if source_key is None or dest_key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        self.add_edge(source_key, dest_key, weight)\n",
    "        self.nodes[dest_key].add_neighbor(self.nodes[source_key],\n",
    "                                          weight)"
    "        self.nodes = {}  # Key = key, val = Node\n",
    "\n",
    "    def add_node(self, key):\n",
    "        if key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        if key in self.nodes:\n",
    "            return self.nodes[key]\n",
    "        self.nodes[key] = Node(key)\n",
    "        return self.nodes[key]\n",
    "\n",
    "    def add_edge(self, source_key, dest_key, weight=0):\n",
    "        if source_key is None or dest_key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        if source_key not in self.nodes:\n",
    "            self.add_node(source_key)\n",
    "        if dest_key not in self.nodes:\n",
    "            self.add_node(dest_key)\n",
    "        self.nodes[source_key].add_neighbor(self.nodes[dest_key],\n",
    "                                            weight)\n",
    "\n",
    "    def add_undirected_edge(self, source_key, dest_key, weight=0):\n",
    "        if source_key is None or dest_key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        self.add_edge(source_key, dest_key, weight)\n",
    "        self.nodes[dest_key].add_neighbor(self.nodes[source_key],\n",
    "                                          weight)"
    "        self.nodes = {}  # Key = key, val = Node\n",
    "\n",
    "    def add_node(self, key):\n",
    "        if key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        if key in self.nodes:\n",
    "            return self.nodes[key]\n",
    "        self.nodes[key] = Node(key)\n",
    "        return self.nodes[key]\n",
    "\n",
    "    def add_edge(self, source_key, dest_key, weight=0):\n",
    "        if source_key is None or dest_key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        if source_key not in self.nodes:\n",
    "            self.add_node(source_key)\n",
    "        if dest_key not in self.nodes:\n",
    "            self.add_node(dest_key)\n",
    "        self.nodes[source_key].add_neighbor(self.nodes[dest_key],\n",
    "                                            weight)\n",
    "\n",
    "    def add_undirected_edge(self, source_key, dest_key, weight=0):\n",
    "        if source_key is None or dest_key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        self.add_edge(source_key, dest_key, weight)\n",
    "        self.nodes[dest_key].add_neighbor(self.nodes[source_key],\n",
    "                                          weight)"
    "        self.nodes = {}  # Key = key, val = Node\n",
    "\n",
    "    def add_node(self, key):\n",
    "        if key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        if key in self.nodes:\n",
    "            return self.nodes[key]\n",
    "        self.nodes[key] = Node(key)\n",
    "        return self.nodes[key]\n",
    "\n",
    "    def add_edge(self, source_key, dest_key, weight=0):\n",
    "        if source_key is None or dest_key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        if source_key not in self.nodes:\n",
    "            self.add_node(source_key)\n",
    "        if dest_key not in self.nodes:\n",
    "            self.add_node(dest_key)\n",
    "        self.nodes[source_key].add_neighbor(self.nodes[dest_key],\n",
    "                                            weight)\n",
    "\n",
    "    def add_undirected_edge(self, source_key, dest_key, weight=0):\n",
    "        if source_key is None or dest_key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        self.add_edge(source_key, dest_key, weight)\n",
    "        self.nodes[dest_key].add_neighbor(self.nodes[source_key],\n",
    "                                          weight)"
    "        self.nodes = {}  # Key = key, val = Node\n",
    "\n",
    "    def add_node(self, key):\n",
    "        if key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        if key in self.nodes:\n",
    "            return self.nodes[key]\n",
    "        self.nodes[key] = Node(key)\n",
    "        return self.nodes[key]\n",
    "\n",
    "    def add_edge(self, source_key, dest_key, weight=0):\n",
    "        if source_key is None or dest_key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        if source_key not in self.nodes:\n",
    "            self.add_node(source_key)\n",
    "        if dest_key not in self.nodes:\n",
    "            self.add_node(dest_key)\n",
    "        self.nodes[source_key].add_neighbor(self.nodes[dest_key],\n",
    "                                            weight)\n",
    "\n",
    "    def add_undirected_edge(self, source_key, dest_key, weight=0):\n",
    "        if source_key is None or dest_key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        self.add_edge(source_key, dest_key, weight)\n",
    "        self.nodes[dest_key].add_neighbor(self.nodes[source_key],\n",
    "                                          weight)"
    "        self.nodes = {}  # Key = key, val = Node\n",
    "\n",
    "    def add_node(self, key):\n",
    "        if key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        if key in self.nodes:\n",
    "            return self.nodes[key]\n",
    "        self.nodes[key] = Node(key)\n",
    "        return self.nodes[key]\n",
    "\n",
    "    def add_edge(self, source_key, dest_key, weight=0):\n",
    "        if source_key is None or dest_key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        if source_key not in self.nodes:\n",
    "            self.add_node(source_key)\n",
    "        if dest_key not in self.nodes:\n",
    "            self.add_node(dest_key)\n",
    "        self.nodes[source_key].add_neighbor(self.nodes[dest_key],\n",
    "                                            weight)\n",
    "\n",
    "    def add_undirected_edge(self, source_key, dest_key, weight=0):\n",
    "        if source_key is None or dest_key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        self.add_edge(source_key, dest_key, weight)\n",
    "        self.nodes[dest_key].add_neighbor(self.nodes[source_key],\n",
    "                                          weight)"
    "        self.nodes = {}  # Key = key, val = Node\n",
    "\n",
    "    def add_node(self, key):\n",
    "        if key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        if key in self.nodes:\n",
    "            return self.nodes[key]\n",
    "        self.nodes[key] = Node(key)\n",
    "        return self.nodes[key]\n",
    "\n",
    "    def add_edge(self, source_key, dest_key, weight=0):\n",
    "        if source_key is None or dest_key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        if source_key not in self.nodes:\n",
    "            self.add_node(source_key)\n",
    "        if dest_key not in self.nodes:\n",
    "            self.add_node(dest_key)\n",
    "        self.nodes[source_key].add_neighbor(self.nodes[dest_key],\n",
    "                                            weight)\n",
    "\n",
    "    def add_undirected_edge(self, source_key, dest_key, weight=0):\n",
    "        if source_key is None or dest_key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        self.add_edge(source_key, dest_key, weight)\n",
    "        self.nodes[dest_key].add_neighbor(self.nodes[source_key],\n",
    "                                          weight)"
    "        self.nodes = {}  # Key = key, val = Node\n",
    "\n",
    "    def add_node(self, key):\n",
    "        if key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        if key in self.nodes:\n",
    "            return self.nodes[key]\n",
    "        self.nodes[key] = Node(key)\n",
    "        return self.nodes[key]\n",
    "\n",
    "    def add_edge(self, source_key, dest_key, weight=0):\n",
    "        if source_key is None or dest_key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        if source_key not in self.nodes:\n",
    "            self.add_node(source_key)\n",
    "        if dest_key not in self.nodes:\n",
    "            self.add_node(dest_key)\n",
    "        self.nodes[source_key].add_neighbor(self.nodes[dest_key],\n",
    "                                            weight)\n",
    "\n",
    "    def add_undirected_edge(self, source_key, dest_key, weight=0):\n",
    "        if source_key is None or dest_key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        self.add_edge(source_key, dest_key, weight)\n",
    "        self.nodes[dest_key].add_neighbor(self.nodes[source_key],\n",
    "                                          weight)"
    "        self.nodes = {}  # Key = key, val = Node\n",
    "\n",
    "    def add_node(self, key):\n",
    "        if key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        if key in self.nodes:\n",
    "            return self.nodes[key]\n",
    "        self.nodes[key] = Node(key)\n",
    "        return self.nodes[key]\n",
    "\n",
    "    def add_edge(self, source_key, dest_key, weight=0):\n",
    "        if source_key is None or dest_key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        if source_key not in self.nodes:\n",
    "            self.add_node(source_key)\n",
    "        if dest_key not in self.nodes:\n",
    "            self.add_node(dest_key)\n",
    "        self.nodes[source_key].add_neighbor(self.nodes[dest_key],\n",
    "                                            weight)\n",
    "\n",
    "    def add_undirected_edge(self, source_key, dest_key, weight=0):\n",
    "        if source_key is None or dest_key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        self.add_edge(source_key, dest_key, weight)\n",
    "        self.nodes[dest_key].add_neighbor(self.nodes[source_key],\n",
    "                                          weight)"
    "        self.nodes = {}  # Key = key, val = Node\n",
    "\n",
    "    def add_node(self, key):\n",
    "        if key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        if key in self.nodes:\n",
    "            return self.nodes[key]\n",
    "        self.nodes[key] = Node(key)\n",
    "        return self.nodes[key]\n",
    "\n",
    "    def add_edge(self, source_key, dest_key, weight=0):\n",
    "        if source_key is None or dest_key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        if source_key not in self.nodes:\n",
    "            self.add_node(source_key)\n",
    "        if dest_key not in self.nodes:\n",
    "            self.add_node(dest_key)\n",
    "        self.nodes[source_key].add_neighbor(self.nodes[dest_key],\n",
    "                                            weight)\n",
    "\n",
    "    def add_undirected_edge(self, source_key, dest_key, weight=0):\n",
    "        if source_key is None or dest_key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        self.add_edge(source_key, dest_key, weight)\n",
    "        self.nodes[dest_key].add_neighbor(self.nodes[source_key],\n",
    "                                          weight)"
    "        self.nodes = {}  # Key = key, val = Node\n",
    "\n",
    "    def add_node(self, key):\n",
    "        if key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        if key in self.nodes:\n",
    "            return self.nodes[key]\n",
    "        self.nodes[key] = Node(key)\n",
    "        return self.nodes[key]\n",
    "\n",
    "    def add_edge(self, source_key, dest_key, weight=0):\n",
    "        if source_key is None or dest_key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        if source_key not in self.nodes:\n",
    "            self.add_node(source_key)\n",
    "        if dest_key not in self.nodes:\n",
    "            self.add_node(dest_key)\n",
    "        self.nodes[source_key].add_neighbor(self.nodes[dest_key],\n",
    "                                            weight)\n",
    "\n",
    "    def add_undirected_edge(self, source_key, dest_key, weight=0):\n",
    "        if source_key is None or dest_key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        self.add_edge(source_key, dest_key, weight)\n",
    "        self.nodes[dest_key].add_neighbor(self.nodes[source_key],\n",
    "                                          weight)"
    "        self.nodes = {}  # Key = key, val = Node\n",
    "\n",
    "    def add_node(self, key):\n",
    "        if key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        if key in self.nodes:\n",
    "            return self.nodes[key]\n",
    "        self.nodes[key] = Node(key)\n",
    "        return self.nodes[key]\n",
    "\n",
    "    def add_edge(self, source_key, dest_key, weight=0):\n",
    "        if source_key is None or dest_key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        if source_key not in self.nodes:\n",
    "            self.add_node(source_key)\n",
    "        if dest_key not in self.nodes:\n",
    "            self.add_node(dest_key)\n",
    "        self.nodes[source_key].add_neighbor(self.nodes[dest_key],\n",
    "                                            weight)\n",
    "\n",
    "    def add_undirected_edge(self, source_key, dest_key, weight=0):\n",
    "        if source_key is None or dest_key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        self.add_edge(source_key, dest_key, weight)\n",
    "        self.nodes[dest_key].add_neighbor(self.nodes[source_key],\n",
    "                                          weight)"
    "        self.nodes = {}  # Key = key, val = Node\n",
    "\n",
    "    def add_node(self, key):\n",
    "        if key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        if key in self.nodes:\n",
    "            return self.nodes[key]\n",
    "        self.nodes[key] = Node(key)\n",
    "        return self.nodes[key]\n",
    "\n",
    "    def add_edge(self, source_key, dest_key, weight=0):\n",
    "        if source_key is None or dest_key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        if source_key not in self.nodes:\n",
    "            self.add_node(source_key)\n",
    "        if dest_key not in self.nodes:\n",
    "            self.add_node(dest_key)\n",
    "        self.nodes[source_key].add_neighbor(self.nodes[dest_key],\n",
    "                                            weight)\n",
    "\n",
    "    def add_undirected_edge(self, source_key, dest_key, weight=0):\n",
    "        if source_key is None or dest_key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        self.add_edge(source_key, dest_key, weight)\n",
    "        self.nodes[dest_key].add_neighbor(self.nodes[source_key],\n",
    "                                          weight)"
    "        self.nodes = {}  # Key = key, val = Node\n",
    "\n",
    "    def add_node(self, key):\n",
    "        if key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        if key in self.nodes:\n",
    "            return self.nodes[key]\n",
    "        self.nodes[key] = Node(key)\n",
    "        return self.nodes[key]\n",
    "\n",
    "    def add_edge(self, source_key, dest_key, weight=0):\n",
    "        if source_key is None or dest_key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        if source_key not in self.nodes:\n",
    "            self.add_node(source_key)\n",
    "        if dest_key not in self.nodes:\n",
    "            self.add_node(dest_key)\n",
    "        self.nodes[source_key].add_neighbor(self.nodes[dest_key],\n",
    "                                            weight)\n",
    "\n",
    "    def add_undirected_edge(self, source_key, dest_key, weight=0):\n",
    "        if source_key is None or dest_key is None:\n",
    "            raise Exception('Invalid key')\n",
    "        self.add_edge(source_key, dest_key, weight)\n",
    "        self.nodes[dest_key].add_neighbor(self.nodes[source_key],\n",
    "                                          weight)"
     ]
    },
    {
     "cell_type": "code",
     "execution_count": 2,
keep keep keep keep replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace keep keep keep keep keep
 Rework graph challenge and solution (#90) 
graphs_trees/graph/graph_solution.ipynb