{ "cells": [ { "cell_type": "markdown", "id": "6adb22ba-3b68-4bbc-ae3a-d6b80a8404dc", "metadata": {}, "source": [ "# Loop Detection Tutorial" ] }, { "cell_type": "code", "execution_count": 1, "id": "ef23cde7-c0a5-4afb-a0fe-de151878e757", "metadata": {}, "outputs": [], "source": [ "from loop_detection import loop_detection, Range, get_UC\n", "from loop_detection.loop_detection_code import get_rule_set, get_aliases\n", "from loop_detection.generation.gen import create_collection_rules, generate_fw_tables, print_from_fw_tables" ] }, { "cell_type": "markdown", "id": "d223de3b-1e3e-4f99-aeed-ebd8678e004d", "metadata": {}, "source": [ "Take the following fowarding tables" ] }, { "cell_type": "code", "execution_count": 2, "id": "7fabbe77-4418-4a95-a65c-d46e25b5e1b4", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{0: [('R1', [1, 5], 1),\n", " ('R2', [1, 4], 1),\n", " ('R3', [0, 1], None),\n", " ('H0', [0, 5], None)],\n", " 1: [('R4', [2, 4], 3), ('H1', [0, 5], None)],\n", " 2: [('R5', [0, 4], 3), ('H2', [0, 5], None)],\n", " 3: [('R6', [4, 5], None), ('R7', [2, 3], 1), ('H3', [0, 5], None)]}" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# each node has a base rule H but the actions for the base rule can differ\n", "\n", "fw_tables = {i : [] for i in range(4)}\n", "\n", "fw_tables[0] = [('R1', Range(1,5), 1), \n", " ('R2', Range(1,4), 1), \n", " ('R3', Range(0,1), None),\n", " ('H0', Range(0,5), None)]\n", "\n", "fw_tables[1] = [('R4', Range(2,4), 3), \n", " ('H1', Range(0,5), None)]\n", "\n", "fw_tables[2] = [('R5', Range(0, 4), 3), \n", " ('H2', Range(0,5), None)]\n", "\n", "fw_tables[3] = [('R6', Range(4, 5), None), \n", " ('R7', Range(2,3), 1),\n", " ('H3', Range(0,5), None)]\n", "\n", "fw_tables" ] }, { "cell_type": "markdown", "id": "0bd9ce12-b91a-471d-93b3-98e7e6def357", "metadata": {}, "source": [ "The keys of the dictionnary are the names of the nodes/routers of the network. Each node is associated with a list of rules, where each rule has a name, the set of headers it matches and an action. The action can be the name of another node in case of forwarding, or `None` in case of a drop." ] }, { "cell_type": "markdown", "id": "dadefdde-6ada-4412-8d5a-5091485b44fd", "metadata": {}, "source": [ "Let's see how the networks looks like." ] }, { "cell_type": "code", "execution_count": 3, "id": "736cfe2e-0545-4f57-b303-2a144bc31b6a", "metadata": {}, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "print_from_fw_tables(fw_tables)" ] }, { "cell_type": "markdown", "id": "1208213a-1ce1-4e6e-b6fe-25c50232dbd3", "metadata": {}, "source": [ "The network is a directed graph where an edge between node A and node B means that there is a rule in node A whose action is to forward to node B." ] }, { "cell_type": "markdown", "id": "865a7436-6d0a-486e-89bc-d296e9c91750", "metadata": {}, "source": [ "Note that in our example, there is a loop between 1 and 3." ] }, { "cell_type": "markdown", "id": "c5db87eb-a26c-4bd6-a932-7c37817f63cc", "metadata": {}, "source": [ "Let us collect all the rules found in our forwarding tables." ] }, { "cell_type": "code", "execution_count": 4, "id": "c0260192-140a-464c-a860-d4adb5732cfa", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'R1': (0, [1, 5], 1),\n", " 'R2': (0, [1, 4], 1),\n", " 'R3': (0, [0, 1], None),\n", " 'H0': (0, [0, 5], None),\n", " 'R4': (1, [2, 4], 3),\n", " 'H1': (1, [0, 5], None),\n", " 'R5': (2, [0, 4], 3),\n", " 'H2': (2, [0, 5], None),\n", " 'R7': (3, [2, 3], 1),\n", " 'R6': (3, [4, 5], None),\n", " 'H3': (3, [0, 5], None)}" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rule_set = get_rule_set(fw_tables)\n", "rule_set" ] }, { "cell_type": "markdown", "id": "9bd0d304-d4cd-4673-a819-365cf9dc7efd", "metadata": {}, "source": [ "Note that H0, H1, H2, H3 are different names for the same rule (at different locations). \n", "\n", "`get_aliases`gives all the possible names for a given rule." ] }, { "cell_type": "code", "execution_count": 6, "id": "03fdb70b-7553-4841-92ed-513b3efac6ee", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{[1, 5]: {'R1'},\n", " [1, 4]: {'R2'},\n", " [0, 1]: {'R3'},\n", " [0, 5]: {'H0', 'H1', 'H2', 'H3'},\n", " [2, 4]: {'R4'},\n", " [0, 4]: {'R5'},\n", " [2, 3]: {'R7'},\n", " [4, 5]: {'R6'}}" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "aliases = get_aliases(rule_set)\n", "aliases" ] }, { "cell_type": "markdown", "id": "6250468d-e5cd-4eb2-b3c4-ec7141472aeb", "metadata": {}, "source": [ "Let us remove redundant copies of the same rule. This will speed up the equivalence class computation." ] }, { "cell_type": "code", "execution_count": 7, "id": "1fc32b02-9a08-44df-aaad-afe86195f0bd", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{('H0', [0, 5]),\n", " ('R1', [1, 5]),\n", " ('R2', [1, 4]),\n", " ('R3', [0, 1]),\n", " ('R4', [2, 4]),\n", " ('R5', [0, 4]),\n", " ('R6', [4, 5]),\n", " ('R7', [2, 3])}" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "R = [(key, values[1]) for key, values in rule_set.items()]\n", "unique_count = {key: 0 for key in aliases.keys()}\n", "R_set = set()\n", "for rule in R:\n", " if unique_count[rule[1]] == 0:\n", " R_set.add(rule)\n", " unique_count[rule[1]] += 1\n", "R_set" ] }, { "cell_type": "markdown", "id": "07b38826-b6d2-4d0b-8d7d-fd0699b2f4bd", "metadata": {}, "source": [ "Let us get the uncovered combinations generated by these rules." ] }, { "cell_type": "code", "execution_count": 8, "id": "80341179-79a0-4aaf-8118-91d7b62ddb86", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "5 atoms :\n", "H0 & R5 & R3 , value = [0, 1]\n", "H0 & R1 & R5 & R4 & R6 , value = [4, 4]\n", "H0 & R1 & R5 & R3 , value = [1, 1]\n", "H0 & R1 & R5 & R4 & R7 , value = [2, 3]\n", "H0 & R1 & R6 , value = [4, 5]\n" ] } ], "source": [ "UC = get_UC(R_set)\n", "print(len(UC), 'atoms :')\n", "for uc in UC:\n", " print(uc.get_name(),', value =', uc)" ] }, { "cell_type": "markdown", "id": "e8650177-e518-41e8-af9d-a71adbe1fedb", "metadata": {}, "source": [ "Now, let us run the loop verification." ] }, { "cell_type": "code", "execution_count": 9, "id": "fac64503-3e9d-42a1-8dbb-7aa5b0838232", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[([2, 3], [[1, 3]])]" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "result = loop_detection(fw_tables)\n", "result" ] }, { "cell_type": "markdown", "id": "210726c1-1895-4ae2-9b0e-04e6bb3c653f", "metadata": {}, "source": [ "The output is a list of tuples, where the first element is the combination of rules creating loops, and the second element is a list of cycles associated to the combination." ] }, { "cell_type": "markdown", "id": "ec23804f-eaf0-4ec4-bb75-8a3624bf2fbd", "metadata": {}, "source": [ "Let us parse the output to have a more readable result." ] }, { "cell_type": "code", "execution_count": 10, "id": "d3e2c631-89bc-468c-8f42-272f9f42144f", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Found a loop\n", "Loop followed by: H0 & R1 & R5 & R4 & R7 , value = [2, 3]\n", "Nodes involved: {1, 3}\n" ] } ], "source": [ "if len(result) > 0:\n", " print('Found a loop')\n", " for res in result:\n", " print('Loop followed by:', res[0].get_name(), ', value =', res[0])\n", " nodes_involved = set()\n", " for edge in res[1]:\n", " nodes_involved.add(edge[0])\n", " nodes_involved.add(edge[1])\n", " print('Nodes involved:', nodes_involved)" ] }, { "cell_type": "markdown", "id": "5696d464-83f0-438d-9dc9-e633f0252292", "metadata": {}, "source": [ "Now, let's test on a random network." ] }, { "cell_type": "code", "execution_count": 15, "id": "382a7be3-1e5c-496d-a227-302f6d27e98a", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "------------------------------------------------------------\n", "Fowarding table:\n", "\n", "Node 0\n", "('H0', [0, 10], None)\n", "('R00', [9, 10], 3)\n", "\n", "Node 1\n", "('H1', [0, 10], 2)\n", "('R01', [8, 9], None)\n", "('R11', [2, 6], 4)\n", "\n", "Node 2\n", "('H2', [0, 10], 0)\n", "('R02', [4, 8], None)\n", "('R12', [1, 6], 4)\n", "\n", "Node 3\n", "('H3', [0, 10], 2)\n", "('R03', [6, 7], 2)\n", "('R13', [6, 9], 4)\n", "('R23', [1, 3], 0)\n", "\n", "Node 4\n", "('H4', [0, 10], None)\n", "('R04', [2, 4], None)\n", "\n", "------------------------------------------------------------\n", "Graph of the whole network\n" ] }, { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "------------------------------------------------------------\n", "1 loops detected\n", "\n", "Details:\n", "\n", "Atom: H0 & R13 & R00 , value = [9, 9]\n", "1 loops\n", "Nodes involved:\n", "Cycle 1 : [0, 3, 2]\n", "\n" ] } ], "source": [ "gen_fw_tables = generate_fw_tables(5, max_range = 10)\n", "print('-'*60)\n", "print('Fowarding table:')\n", "print()\n", "for key, value in gen_fw_tables.items():\n", " print('Node', key)\n", " for rule in value:\n", " print(rule)\n", " print()\n", "print('-'*60)\n", "print(\"Graph of the whole network\")\n", "print_from_fw_tables(gen_fw_tables)\n", "result = loop_detection(gen_fw_tables)\n", "print('-'*60)\n", "nb_loops = sum(len(res[1]) for res in result)\n", "print(nb_loops, 'loops detected')\n", "print()\n", "print('Details:')\n", "print()\n", "for res in result:\n", " print('Atom:', res[0].get_name(), ', value =', res[0])\n", " print(len(res[1]), 'loops')\n", " print('Nodes involved:')\n", " for i, cycle in enumerate(res[1]):\n", " print('Cycle', i + 1 , ':', cycle)\n", " print()" ] }, { "cell_type": "markdown", "id": "16a69c4c-40b9-46fb-8104-6e3527be4bf4", "metadata": {}, "source": [ "A second random example:" ] }, { "cell_type": "code", "execution_count": 17, "id": "4d552cc9-5b57-477f-9d1e-40e082756455", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "------------------------------------------------------------\n", "Fowarding table:\n", "\n", "Node 0\n", "('H0', [0, 10], None)\n", "('R00', [5, 6], None)\n", "\n", "Node 1\n", "('H1', [0, 10], 3)\n", "('R01', [4, 6], 3)\n", "('R11', [4, 8], 0)\n", "\n", "Node 2\n", "('H2', [0, 10], None)\n", "('R02', [0, 4], 0)\n", "('R12', [0, 8], 4)\n", "\n", "Node 3\n", "('H3', [0, 10], None)\n", "('R03', [3, 4], 1)\n", "('R13', [5, 6], 1)\n", "('R23', [3, 4], 1)\n", "\n", "Node 4\n", "('H4', [0, 10], None)\n", "('R04', [3, 7], 3)\n", "('R14', [1, 3], 2)\n", "\n", "------------------------------------------------------------\n", "Graph of the whole network\n" ] }, { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAaMAAADcCAYAAADdls5UAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/OQEPoAAAACXBIWXMAAA9hAAAPYQGoP6dpAAA3+ElEQVR4nO3dd1hU19YG8PfMwAwgMCAgICoo2AtgjDEW1BgVY2zYCHYxEb1JJFGDGqPxRiXXRL1qTOwFe+wtYowSNcZesARQQREFFJReBmZmfX/wOdcCSJlhCuv3PDwiZ2bvxb2Sl33OPusIRERgjDHGdEik6wIYY4wxDiPGGGM6x2HEGGNM5ziMGGOM6RyHEWOMMZ3jMGKMMaZzHEaMMcZ0jsOIMcaYznEYMcYY0zkOI8YYYzrHYcQYY0znOIwYY4zpHIcRY4wxnTPRdQGVUaAkpMmVUBJBLAiwlYohEQu6Losxxlg5GVwYpeYpcPVpPmIzCpBeoHrtuI1EBHeZBN52ZrA3N7hvjzHGqiXBUJ5nlC5XIjwhG/ezCiEAKK3o58fdrEzhW9cSNlJx1RTJGGOsQgwijCJT83HsYTaUVHoIvUoAIBaA7nUs4Wlvpq3yGGOMVZLeh9Hfybk4lZRb6XF8nC3Q3slCAxUxxhjTNL2+qBKZml9iECkK5Di96Wdc+20Xnj2Kh6m5Bep7t8N7H0+GS1PP115/KikXNUxF8LTjFRJjjOkbvV0ZpcuVWBOVBkUx1SkVCqz/dChiL5x67ZiJRIpRS7bC4x2f148JwLimtnwNiTHG9Ize3mcUnlB0jag453auUweRo0dTDPthPbqO+xJA0Ypp17efQVEgf+19SioalzHGmH7RyzBKzVPgflZhiZsVLuzaqP7cb+YitOj2IXpMnI6G73YFAGQ8TkT06d9fex8BuJ9ViNR8hRaqZowxVlF6GUZXn+ajpFtXczPS8OTebQCA2MQUdZp7q4+5erZVf37vyrli3y8AuJqar6lSGWOMaYBehlFsRkGJq6K0xAfqzy1sbCES/+/6j2VN+2Jf9yICEJdRoIkyGWOMaYjehZFcqSq2s8JzBXn/210nNpG8dExsYlrs616VVqBCQUkXpBhjjFU5vQujdHnJQQQAEvP/3SukKHx5k4JSUVjs64qTJldWoDrGGGPaoHdhpHzDTnPb2vXUn+dmpEGp+N9mhKzUJ8W+riLzMMYYqzp6F0ZiofSu2xYyW9Sq3wgAoFIo8PDWVfWxB9cvqj+v37pduechIqSnp5ejWsYYY5qgd2FkW4YbUtsOGqX+fO/cL3Hz+CH8vnw+7pz7EwAgc6yNJp16vHGexMREHDhwALNnz0avXr1gb28PW1tbHD58uFLfA2OMsfLRu3ZAErEAG4mo1E0M7QaPRdTJo4i9cAqPY6OxZeoY9TETiRSDvl0GE4m0xPfbSESwtbZEbm7RJgexWAyVSoXnzShcXV019N0wxhgrC71bGQGAu0xS4n1GACA2McHopVvR418z4ODWECYSKcxltmja2RdB6w8X2wroOQGAu7UErVu3Vn9NqVSqg0gmk8Hd3V1D3wljjLGy0MvedKl5CqyJTtfa+OOa2sBOKsbs2bPx3XffvXbc3NwcXbt2ha+vL3x9fdGwYUOt1cIYY0xPwwgAtt/NQHwpLYEqQgDgamUKfw+Z+murVq1CUFAQgKINDPPnz4epqSmOHDmC06dPo7CwEO7u7ujduzf8/PzQsWNHiMXcaJUxxjRJb8OotK7dFVVS1+6DBw9i8ODBkMvlSEpKgpOTEwAgOzsbJ06cwJEjR3DgwAEkJibCwcEBffv2hZ+fH7p16waptORrU4wxxspGb8MIKHqe0RENdtnuVc+yxOcZXbp0CZcuXVKvkl6lUqlw8eJF7NmzB3v27MHdu3dhZWWlXjH16tULlpaWGquVMcaqE70OI0A/n/RKRLh165Y6mCIjIyGVStGjRw/4+fmhT58+sLOz08hcjDFWHeh9GAFFK6RjD4ueb1SeYgUAYgHoXrfkFZEmxMXFYe/evdizZw/Onj0LkUiEzp07w8/PD/3794eLi4vW5maMMWNgEGEEFF1DCk/Ixv2sQggoPZSeH3ezMoVvXcsqfbJrUlIS9u/fjz179iAiIgIKhQLvvPMO/Pz84OfnBw8PjyqrhTHGDIXBhNFzqXkKXH2aj7iMAqQVc2OsrUSEBjIJvO3NYG+m23t609LScOjQIezZswdHjx5FXl4eWrZsiQEDBsDPzw+tWrWC8Ib2R4wxVh0YXBi9qEBJSJMroSTCuLFj4VHbAUsXL9J1WcXKycnB0aNHsWfPHhw6dAgZGRmoX78+Bg4ciOHDh8PT01PXJTLGmM4YdBg9FxsbCw8PD4jFYsTFxaFevdI7dutaQUEBIiIi1BsgUlNT4enpiZEjRyIgIEC9tZwxxqoLvWwHVF6rVq0CULT9Ojg4WLfFlIFEIkHPnj2xcuVKJCYmYv/+/fDw8MD06dNRp04d9O7dGzt27EB+Pj8enTFWPRj8ykgul8PJyemlRz+Eh4ejZ8+euiuqgp49e4YdO3YgLCwM586dg0wmw5AhQzBq1Ci0b9+ery8xxoyWwYfRtm3bEBAQoP67SCSCq6sroqKiDLo7wu3btxEWFoZNmzbhwYMHcHd3x4gRIzBy5EjUr19f1+UxxphGGXwYdejQAWfPnsWr30ZoaCimTZumo6o0R6VS4eTJkwgLC8OuXbuQnZ2NTp06YdSoURg0aBBkMtmbB2GMMT1n0NeMMjMz8ffff78WRFZWVsjJydFRVZolEonQtWtXrF+/HsnJydi0aRPMzMzw8ccfw8nJCQEBAQgPD4fihcevM8aYoTHolRER4dy5c1AoFPjtt9+wZMkSZGZmwsRE754ZqHEPHz7Eli1bsHHjRkRFRcHJyQnDhw/HyJEj0bJlS12Xxxhj5WLQYfSiZcuWISQkRP301uqCiHD58mWEhYVh69atePr0Kby8vDB69GiMHDkStra2ui6RMcbeyKBP071IEASoVCU/qtxYCYKANm3aYOnSpUhMTMS+ffvQoEEDTJ06FS4uLvj4449x7do1XZfJGGOlMqowMpJFXoVJJBL069cPu3fvRkJCAmbMmIEjR47A29sbHTt2xPbt21FQUKDrMhlj7DVGE0YikaharoxK4ujoiJkzZ+LevXvYtWsXTE1N8dFHH8HV1RWzZ89GYmKirktkjDE1owkjXhkVz9TUFAMHDkRERARu3ryJAQMGYOHChXB1dcWQIUNw6tQp/t+NMaZzHEbVSPPmzfHzzz/j0aNHWLRoEa5fv47OnTujVatWWLlyJbKzNfdUXcYYKw+jCSM+TVd2MpkMn332GaKionDs2DG4u7tj4sSJcHFxQXBwMG7fvq3rEhlj1YzRhBH3bSs/QRDw/vvvY9++fYiLi8PEiROxZcsWNG7cGD179sSBAwegVCp1XSZjrBowujDiU3UV4+rqitDQUCQkJGDjxo1IT09Hv3794O7ujgULFuDp06e6LpExZsSMLoz4VF3lmJmZYeTIkTh//jwuXLiALl26YNasWXBxccHo0aNx9epVXZfIGDNCRhNGIlHRt8IrI815++23sWHDBjx8+BBz5sxBREQEWrduDV9fX96FxxjTKKMJI14ZaY+9vT1CQkIQGxuLLVu2IDExEZ07d0bHjh1x6NAhDiXGWKUZTRg9v9D+fIXENM/ExAQBAQGIjIzEwYMHAQB9+vSBp6cntm3bxp3DGWMVZjT/5U5PT0eNGjWqRcduXRMEAR9++CH++usvnDx5ErVr10ZAQAAaN26MlStX8uPSGWPlZlRhxB2qq5YgCPDx8UF4eDguX76Mt956CxMmTED9+vXx448/IisrS9clMsYMhNGEUVpaGoeRDrVu3Rq//voroqKi8MEHH2DGjBlwdXXFrFmzkJqaquvyGGN6zqjCyMbGRtdlVHuNGzfG2rVrERcXh1GjRqn74AUHByMhIUHX5THG9JTRhBGfptMvderUweLFixEfH48pU6YgLCwM7u7uCAwM5HZDjLHXGE0Y8Wk6/WRvb485c+YgPj4eoaGhOHLkCJo0aYLBgwfjypUrui6PMaYnOIxYlbCyssLkyZMRFxeHFStW4OrVq3jrrbfQs2dPnD59WtflMcZ0zKjCiK8Z6T8zMzN88skniI6OxrZt25CcnAwfHx/06tWLWw0xVo0ZTRjxNSPDYmJiAn9/f1y7dg07d+7EvXv30Lp1a/j7++POnTu6Lo8xVsWMIozy8/ORn5/PYWSABEHAoEGDcPPmTaxZswZnzpxB06ZN8cknn+Dhw4e6Lo8xVkWMIozS0tIAgMPIgJmYmCAwMBB37tzBggULsGfPHjRs2BBTp07lx1cwVg0YRRilp6cDAF8zMgJmZmb48ssvERcXh5CQEKxYsQINGjTA3Llz+bHojBkxowij53f416xZU8eVME2xtrbGt99+i7i4OIwdOxbfffcd3N3dsWzZMsjlcl2XxxjTMKMIo3v37gEA3NzcdFsI0zgHBwcsXrwYt2/fRu/evREcHIwmTZogLCyMH4nOmBExijCKi4uDk5MTLCwsdF0K0xJXV1esW7cON2/eROvWrTFq1Ch4enpi//79/DwlxoyAUYRRbGwsGjRooOsyWBVo2rQpdu/ejfPnz8PJyQn9+/dH+/bt8eeff+q6NMZYJRhFGMXFxcHd3V3XZbAq1LZtW/zxxx84duwYFAoFunbtCl9fX24xxJiBMpow4pVR9fT+++/jwoUL2L17N+Lj4/HWW29hyJAhfOMsYwbG4MMoJycHycnJvDKqxgRBgJ+fH27cuIF169bh3LlzaN68OUJCQvgBf4wZCIMPo+c76XhlxExMTDBmzBjExMTgm2++wbJly9C4cWNs3ryZNzkwpucMPoxiY2MBgFdGTM3c3BzffPMNoqOj0alTJ4wYMQIdOnTA5cuXdV0aY6wEBh9GcXFxsLCwgKOjo65LYXqmXr162LFjByIiIpCdnY23334bn3zyCVJSUnRdGmPsFQYfRs+3dQuCoOtSmJ7q0qULrly5gqVLl2Lnzp1o2LAhli5dCoVCoevSGGP/z+DDiHfSsbIwMTHBp59+ijt37sDf3x/BwcHw8vLCiRMndF0aYwwcRqyasbe3x4oVK3D58mXY2NigW7duGDx4MOLj43VdGmPVmkGHkVKpxL179ziMWLl5e3vj9OnT2Lx5M86cOYMmTZpgzpw5yMvL03VpjFVLBh1Gd+7cQUFBAZo3b67rUpgBEgQBw4YNQ0xMDIKDgzF//nw0bdoUe/bs4a3gjFUxgw6ja9euAQA8PT11WwgzaFZWVggNDcXNmzfRsmVLDBw4EN27d8etW7d0XRpj1YZBh1FkZCTq1KkDOzs7XZfCjEDDhg1x8OBBHD58GA8ePICnpyeCg4PVD29kjGmPQYfRtWvX4OXlpesymJH54IMPcOPGDcyfPx9r165Fo0aNsHbtWqhUKl2XxpjRMugwioyM5FN0TCukUim++uorxMTEwNfXF+PGjcN7773HDVgZ0xKDCqMCJeFxrgKJOYWIevgET9MzeWXEtKp27doICwvD8ePHkZCQgFatWmHBggV8wyxjGiaQnm8bSs1T4OrTfMRmFCC94OXTJKRSwcoEaGxnAW87M9ibm+ioSlYd5ObmYvbs2Vi0aBG8vLywdu1a/mWIMQ3R2zBKlysRnpCN+1mFEACUVuTz425WpvCtawkbqbhqimTV0sWLFxEYGIh//vkHX331FWbNmgUzMzNdl8WYQdPLMIpMzcexh9lQUukh9CoBgFgAutexhKc9/8eBaU9BQQEWLFiA7777Dm5ublizZg06deqk67IYM1h6d83o7+RcHEnIhqKcQQQUvV5BwJGEbPydnKuN8hgDAEgkEsycORPXrl2Dvb09fHx88K9//QuZmZm6Lo0xg6RXK6PI1HwcSch+6WuJ0ddx/fd9uHflHNKTEpCT9hRmltao2/It+Iz6FPVbv1vieL3qWcLTjldITLtUKhV+/vlnTJs2Dba2tlixYgV69+6t67IYMyh6E0bpciXWRKVB8Uo1e+dNwYXdG4t9jyASIeA/a9Gi24fFHjcRgHFNbfkaEqsS8fHxCAoKQnh4OAICAvDf//4XDg4Oui6LMYOgN6fpwhOKrhEVx8q+FroGfoHRy7Zj6PyVcHDzAFC0m+7wolkljqmkonEZqwqurq747bffEBYWhvDwcDRr1gxbt27lPneMlYFerIxS8xRYE51e7LH7V8+hdpNWkJhbqL+WdPsmlvp3Vf/96z/+gWXNkn8DHdfUBvZmvO2bVZ0nT57g888/x44dO9C7d2/88ssvqFu3rq7LYkxv6cXK6OrTfJT0nFY373YvBREA2NV9+ZERpmbmJY4tALiaml/JChkrn1q1amH79u3Yv38/rl69iubNm+OXX37hlkKMlUAvwig2o6BcO+duHj+k/tzNux2kFpYlvpYAxGUUVLw4xiqhb9+++OeffxAQEICJEyeiS5cuiImJ0XVZjOkdnYeRXKl6rbNCaR79E4mDC6YDAEwkUnw4ee4b35NWoEJBSRekGNMymUyGFStWICIiAklJSfD09ERoaCi3FGLsBToPo3R52YPo/tVzWB00APnZmRCZmMB//kq4NCtbo9Q0ubKiJTKmEV26dMH169cxadIkzJw5E506dcLdu3d1XRZjekHnYaQs4/6J22cjsO5fQyHPzoKJRIphC9ah+Xtlv5ejrPMwpk3m5ub4z3/+g7/++gupqanw9PTEypUrK7zj7sXmwY9zFXwGgBksnW8xEwslbV34n1snDmPb9E+gLCyAxNwCIxZtgsc7Phqfh7Gq8u677+Lq1auYMmUKgoKCcODAAaxZswbOzs5vfG9pzYMBwEYigrtMws2DmUHR+dbuAiVh0fWnJR6/cWw/ts8YD5VSCUEQ4Pv5LNRr1eal19Rp7g0TibTUeb5sZQeJmAOJ6Z/Dhw8jMDAQCoUCK1euxMCBA4t9HTcPZsZM52EEACtuPStxE8PO2Z/iysEdpb7/q0OXYVu7XonHbSUijG9es1I1MqZNqampCAoKwu7duzFixAgsW7YMMplMfZybBzNjpxdreHeZBFdS8svdGLUsBAAZcbew9txtZGZmIjMzExkZGcjMzIS5uTkWLVoEU1NTLczMWNnZ29tj586d2Lx5Mz799FOcPHkSGzduRJcuXfB3ci5OJVWs8e+LzYNzFCq0d7J443sY0wW9WBmV1oFBExYNbI+Ue3cgCALEYjEEQYBCoYCZmRlSU1NhYcE/oEx/xMfHY/To0fjzzz8xP2w3hBavXx/NzUjDqbCf8CDyIh7+cw2F+XkAgNZ9hmLwnJ9KHJubBzN9pRcrI3tzE7hZmSI+q1CjqyMBQD1LE9R3sEXKPYCI1Pd2iMVijB49moOI6R1XV1ccP34c/121FvKmxXelT09+iJPrl5Z77GMJ2XC1NOVrSEzv6Hxr93O+dS2h6f0FYgHoVc8Kf/zxB5o2bQqx+H8/gEqlEikpKYiOjtbspIxpgEgkQu33h0AkLv4UsthUgvqt30XnMZ+jTb+AMo/LzYOZvtKbMLKRitG9TsltfSqiW21z2EjFsLKywpEjRyCTySASiSAWi+Hh4YG///4bzZo1w+DBg3H16lWNzs1YZaTmKXC/lDMFjg0a45M1B+D72Teo09y7zOMSgPtZhUjN5+4PTL/oTRgBgKe9GXycNXPa7NjPofhm7FDk5OQAKDr1cejQIYjFYiiVSixZsgRxcXFYtWoVrly5gtatW6N37944e/asRuZnrDJKax5cWdw8mOkjvQojAGjvZIFedS1hIqDcP4wCih6o16ueJUIG98TJkyfRtWtXPHnyBEDRjYY7duxAQEAAfH19IZVKMW7cOMTExGDz5s24f/8+2rdvj/feew/Hjx/n59AwnSlv8+Dy4ObBTB/pXRgBRSukcU1t4WpVdL78TaH0/LirlSnGNbWFp50ZevTogVOnTuHBgwfo0KEDYmNjAQADBgzAli1bIBL971s3MTHBsGHDcOPGDezevRsZGRl4//330b59exw6dIhDiVWp8jYPrghuHsz0jV6GEVB0DcnfQ4ZxTWzQ2sEMtpLiS7WViNDawQzjmtrA30P20i4hb29vnD17FiKRCO+++y4uXrxY6pwikQh+fn64dOkSfvvtN4jFYvTp0wfe3t7YuXMnlEputsq0rzzNgyuDmwczfaIXW7tLY29uUrSxoU5R66A0uRJKIogFAbZS8Rtb/NSvXx9nzpxB37590blzZ4SFhWHQoEGlvkcQBPTq1Qu+vr44deoU5s2bhyFDhqBx48aYPn06AgIC+EZZpjVV1dSXmwczfaK3K6PiSMQCHC1MULuGKRwtTMrca87e3h7Hjx9H//79MXjwYMydO7dMp94EQUDnzp3x+++/4/z582jcuDFGjx6NZs2aYceOHfzUTqYVVdXUl5sHM31iUGFUGebm5tiyZQv+/e9/45tvvsHw4cORn1/2HUVt27bF/v37ce3aNTRu3Bj+/v54++23cezYMS1Wzaoj2zLckFqQl4sbfxzAjT8OIDH6hvrr6UkP1V9PS0yo9DyMVRW9aAdU1X799VeMGjUKnp6e2LdvH5ycnMo9xunTpxESEoKzZ8+iW7duCA0Nxdtvv62Fall1VFrzYABIS3yABR++VeoYg75dirf6flTsMSsxofGTSDx69Ajx8fGIj49HbGws7t+/j6lTp+Jf//pXpepnrLz0/pqRNgwZMgT169dHv3790LZtWxw8eBCenmV7YuxznTp1wpkzZ3Dw4EFMnz4dbdu2xaBBgzBv3jw0atRIS5Wz6kLbzYMjfg3Dp/OmAMBL/RoBlOuMAWOaUi1XRs89evQIffv2RUxMDLZu3Yq+fftWaBylUolNmzZh1qxZSExMRGBgIGbPno3atWtruGJWXWi7ebB9zEmMHzbkteuegiAgISEBLi4uWpubseJUm2tGxXFxccGpU6fQs2dP9O/fHwsWLKjQPUXPm67evn0bCxYswK5du+Dh4YHp06cjPT1d84Uzo/e8ebCmtxgIKHrg3jj/gdi3b99L/RqBombCI0eOxKZNm5CXl6fh2RkrWbUOIwCoUaMGdu7ciRkzZiAkJARjx46FXC6v0FhmZmb48ssvERcXhy+//BJLly5FgwYNsGDBAj71wcpN082DiQgiEHzrFvWA7NOnD7Zu3QrhhV11Y8aMUQeSi4sLgoODERUVpbkiGCtBtT5N96rNmzcjMDAQ77zzDnbv3g0HB4dKjZeUlITvvvsOq1evRt26dbF48WL07dv3pR9+xkoTmZqPIxrssn100UzMHD0Y3bp1U38tLCwMo0aNgqmpKVJSUiCTyRAbG4vVq1dj3bp1SElJgY+PD4KCguDn5wepVKqxehh7rtqvjF40fPhw/Pnnn4iJiUHbtm0r3cnb2dkZP//8M27evIlGjRqhf//+6NWrF2JiYjRUMTN2mmwe3EYGSJ7cQ48ePfCf//xHfUp65MiR2LZtGxYuXKh+1Lm7uzu+//57JCQkYPv27RCJRAgICECdOnUwdepU3LlzRyM1MfYcr4yKER8fj4EDB+LmzZtYvnw5AgMDKz0mEeHgwYMIDg7Gw4cPERwcjG+++QZWVlYaqJgZu8jUfBx7mA0loVw77AQUPdere92iJ7wqlUrMmjUL8+fPx8CBA7F+/foy/xuMjo7GqlWrsGHDBqSlpaFbt24ICgpCv379uCMJqzQOoxLk5+dj0qRJWLVqFcaOHYuffvoJ5ubmGhn3xx9/xPz582FjY4MFCxZg2LBhfOqOvVG6XInwhGzczyqEgNJD6flxNytT+Na1fO3Jrvv378eIESPg4uKCvXv3okmTJmWuIy8vD7t27cLKlStx5swZODo6IjAwEB9//DHc3Nwq8J0xxmH0Rhs3bkRQUBCaNGmCXbt2wd3dXSPjPnjwAFOmTMHOnTvRoUMHLFu2DN7eZX9IGqu+UvMUuPo0H3EZBUgr5sZYW4kIDWQSeNubwd6s5FsJY2Ji4Ofnh4SEBKxZswZDhgwpdy03b97EypUrERYWhqysLPj6+mLixIn44IMPXuqMz9gbEXujyMhI8vDwIJlMRvv27dPo2MePH6fmzZuTIAgUFBREqampGh2fGTe5QkXJOYX0KLuAknMKSa5Qlev9WVlZ5O/vTwDok08+odzc3ArVkZ2dTWvXrqU2bdoQAGrSpAmtXr2a8vLyKjQeq344jMooPT2d+vfvTwAoJCSECgsLNTZ2QUEB/fe//yWZTEa2tra0fPlyUigUGhufsdKoVCpavXo1mZubU4sWLejWrVuVGu+vv/6i/v37kyAI5OjoSHPnzqWnT59qqFpmrDiMykGlUtEPP/xAYrGYunTpQsnJyRod//HjxxQYGEiCIJC3tzdduXJFo+MzVpobN25Qs2bNyNzcnNauXUsqVflWWa+Kjo6m8ePHk1QqJQsLC/rss88oLi5OQ9UyY8NhVAEnT54kJycncnZ2ptOnT2t8/PPnz1OrVq1ILBbTtGnTKnzqhLHyysnJocDAQAJAAQEBlJmZWekxHz9+TLNmzSI7OzsSiUQ0ZMgQunDhggaqZcaEw6iCEhMTycfHh8RiMS1cuLDSv0W+qqCggObOnUsSiYQaNWpEp06d0uj4jJVm69atZGlpSR4eHnT58mWNjJmTk0PLly8nd3d3AkA+Pj508OBBUiqVGhmfGTYOo0ooLCykqVOnEgDq378/paSkaHyOf/75h9q3b08AaMKECZSRkaHxORgrzp07d6h169YkkUho6dKlGvuFS6FQ0O7du6ldu3YEgJo2bUpr1qzhzQ7VHIeRBuzbt4/s7OzI2dmZjh49qvHxlUolLVu2jGrUqEF16tShQ4cOaXwOxoqTn59Pn3/+ufoXLk1uRFCpVPTXX39Rv3791JsdFi5cSDk5ORqbgxkODiMNefToEfXo0YMA0KRJk7TyW979+/fJ19dXfT7/yZMnGp+DseLs27ePbG1tqV69enTmzBmNjx8dHU2BgYEkFovJycmJlixZwiulaobDSIOUSiUtXryYpFIptWjRgq5fv67xOVQqFYWFhVHNmjXJ3t6etmzZovHrVYwVJz4+njp06EBisZjmz5+vlWs9d+/epdGjR5NIJKLatWvTTz/9RPn5+Rqfh+kfDiMtuH79OrVo0YKkUiktXrxYKz+0ycnJNHToUAJAvXv3pgcPHmh8DsZeVVhYSDNmzCBBEMjHx4diY2O1Mk9MTAwNHz6cRCIR1a1bl1asWEFyuVwrczH9wGGkJXl5eRQcHEwAqHv37vTo0SOtzLNv3z6qXbs2WVlZ0aZNm3iVxKpEREQEubq6Uo0aNeiXX37R2r+7qKgo+uijj0gQBHJ1daU1a9ZQQUGBVuZiusVhpGVHjx4lJycnsrOzoz179mhljrS0NBo+fDgBIH9/f0pLS9PKPIy9KDMzkz7++GMCQD169NDq6vzmzZs0ePBgAkANGjSg9evXa7QLCtM9DqMqkJKSom4lFBgYSFlZWVqZZ9u2bSSTyahu3br0559/amUOxl515MgRcnFxIWtra1q/fr1WV+eRkZHk5+dHAMjDw4M2bdrErbOMBIdRFXne/8vCwoI8PDzo7NmzWpknPj6eOnfuTIIg0LRp0/g8O6sSz549oxEjRhAA6tOnDyUlJWl1vitXrlDfvn0JADVu3Jh2797Np6gNHIdRFbt9+za1bduWBEGgSZMmaWWVpFAoKDQ0lExMTKh169YUFRWl8TkYK86+ffuoVq1aVLNmTdq2bZvWA+LixYvUs2dPAkCdOnXiNkMGjMNIBwoLC+nHH38kc3NzcnNzo99//10r81y6dIkaN25M5ubmWr3IzNiLUlJS1Nd3Bg0aVCX3w4WHh1OLFi0IAA0fPpx3lxogDiMdunv3LnXt2pUA0JgxY+jZs2canyM7O5uCgoLUp08eP36s8TkYK8727dupZs2aVKtWLdq7d6/W5yssLKRVq1ZRrVq1yMzMjL7++muNNHplVYPDSMeeX0uSyWTk6OhIu3bt0so8Bw4cIAcHB3J0dKTffvtNK3Mw9qqkpCTq06ePesWijV+4XpWZmUkzZswgMzMzcnR0pFWrVvEmBwPAYaQnHj16pN5x5+fnR4mJiRqfIykpiXr16kUA6LPPPuM721mVUKlUtGHDBrK2tiYnJ6cquZZEVLSZZ9iwYQSAWrZsqZW+kUxzOIz0iEqlop07d1KtWrXIxsZGIw84K26OZcuWkUQiobZt21J8fLxGx2esJAkJCTRw4ED1jeB37typknkvXLhAHTt2JADUq1evSj/JlmkHh5EeSk1NpVGjRhEAev/997XScuXixYvk6upKdnZ2FB4ervHxGSvJoUOHyM3NjaRSKc2ZM6dKVugqlYp27dpFDRo0ILFYTEFBQXz9VM9wGOmx8PBwcnV1JQsLC/rhhx803gYlNTWVevXqRYIg0LfffssPOWNVJicnh6ZNm0YmJibUqFEjOn78eJXMm5+fTwsXLiSZTEZWVla0ePFi7uSgJziM9FxWVhZNmjSJRCIRNWnSROPbwJVKJf373/8mQRCoZ8+eWnlAIGMluXnzpvoU2vDhwyk5OblK5k1NTaUJEyaQIAjk7e1N58+fr5J5Wck4jAxEZGQk+fj4EAAaMGAA3bt3T6Pj//7772RnZ0f16tXjH0xWpZRKJa1bt47s7OzIxsaGVqxYUWWr9PPnz5OXlxcJgkATJ07kvo46xGFkQFQqFW3bto1cXFzIzMyMZs+eTbm5uRob/8GDB/TOO++QqakpLV++nG+SZVUqJSWFxo4dSwCoXbt2dO3atSqZt7CwkBYvXkyWlpZVutuPvYzDyABlZWXR9OnTSSKRkKurK+3atUtjPzxyuZw+/fRTAkDDhg2j7OxsjYzLWFmdPHmSmjVrRmKxmCZPnqy1xsKv0tVuP1aEw8iA3blzh3r37k0AqFu3bhrdsrp161aysLCg5s2bU3R0tMbGZaws5HI5hYaGkrm5OdWtW5e2b99eZasVXez2YxxGRuHQoUPk4eFBJiYm9MUXX1B6erpGxr116xY1adKErKysqqSdC2OviouLU3fnbteuHf39999VMm9OTg5Nnz69ynf7VWccRkYiPz+fQkNDqUaNGuTo6Ejr16/XyEXgzMxMGjRoEAGg0NBQPpfOdOLEiRPk5eVFAGjo0KEa38BTEl3t9quOOIyMTEJCAn300UcEgFq1akWHDx+udIAolUqaNWsWAaCRI0fyaQumEwqFgtavX0/Ozs4klUopJCREY2cBSvPqbr+VK1fyL2VawGFkpP7++2/1VnAfHx+NnN7YunUrSaVS6tChA9+9znQmOzubZs+eTRYWFmRvb08///xzldy4mpKSQmPGjFFvcODHVGgWh5ERU6lUdPjwYWrVqhUBoH79+lV6k8O5c+fI0dGRXF1d6fr16xqqlLHye/jwIY0ePZoEQaCmTZtq5CxAWYSHh1fZY9arEw6jakCpVNLmzZupfv36JBKJaMyYMZX6rS4+Pp48PT3J0tKSDh06pMFKGSu/y5cvU5cuXdQrlsjISK3PmZaWpu4f+eGHH2qly351w2FUjcjlclq6dCk5ODiQVCqlyZMnU2pqaoXGysrKon79+pEgCLRw4UL+7ZDplEqlov3791PDhg1JJBLRuHHjKCkpSevz7t+/nxwdHavsMevGjMOoGsrMzKRvv/2WLC0tydramubOnVuhm1uVSiWFhIQQABo3bhzJ5XItVMtY2cnlclqyZAnVrFmTatSoQTNmzKCnT59qdc6UlBQaOnRolT5m3RhxGFVjT548oUmTJpGpqSk5OTnRTz/9RHl5eeUeZ8OGDWRqakqdO3eu8EqLMU169uwZffXVV2RhYUFWVlY0a9Ysrfed27FjB9nZ2VXZY9aNDYcRo3v37tGIESNIEARydnamhQsXlnuldPr0abK3tyd3d3eKiorSUqWMlc/jx49p8uTJZGZmRjKZjObMmaPV7eBJSUnqm3Sr6jHrxoLDiKnFxMTQmDFjyMTEhOzt7Wnu3Lnl+m0yLi6OmjdvTjY2NnTq1CntFcpYOSUmJtKkSZNIKpWSra0tzZs3jzIzM7Uyl0qloo0bN5JMJqPatWvTkSNHtDKPseEwYq+5f/8+TZw4kaRSKVlbW9PXX39d5uccpaenU9euXUkqldKuXbvK9B65QkXJOYX0KLuAknMKSa7gi8BMOx4+fEiffvopSSQSsrOzo++//15rjVgTEhKoZ8+e6muqVdXw1VAJRERgrBhJSUlYtGgRfvnlFxARxo8fjylTpqB27dqlvk8ul2PMmDHYvn07lixZgs8+++y116TmKXD1aT5iMwqQXqB67biNRAR3mQTedmawNzfR2PfEGAAkJCRg/vz5WLt2LWxsbBASEoIJEybAwsJCo/MQEdasWYMvvvgCLi4u2LFjB7y8vDQ6h7HgMGJv9PTpUyxZsgRLly5FXl4exo4di5CQELi5uQEo+oHr2bMnGjVqhGXLlkEQBKhUKoSEhODHH3/EV199hdDQUIhEIqTLlQhPyMb9rEIIAEr7x/f8uJuVKXzrWsJGKtb+N8uqlfj4eMybNw/r16+HnZ0dpk2bhvHjx8Pc3Fyj89y+fRtDhw5FVFQUFi1ahAkTJkAQBI3OYeg4jFiZZWRk4Oeff8aiRYuQlpaG4cOHY9q0aUhNTUWnTp0AAAsWLMDUqVPV71myZAm++OILBAQE4IuFKxGRlAcllR5CrxIAiAWgex1LeNqbafabYgzAvXv3MHfuXGzcuBG1atXC5MmT8fHHH8Pa2lpjc+Tn52Pq1Kn46aef4Ofnp16VsSIcRqzccnJysHr1avzwww9ITEyEk5MTUlJSoFQqIQgC9uzZg/79+6tfv3PnTqw+fhHvjf+q0nP7OFugvZNmT6Uw9tzdu3cxf/58bN68Gebm5hg/fjw+//xz1KlTR2Nz7N27F2PHjoVMJsP27dvRrl07jY1tyDiMWIXJ5XL89NNPmDJlyktfl0qlOHv2LLy9vQEAkan5OJKQ/cbxNnz+EWL++kP99y92/41a9Ru+9rpe9SzhaccrJKY9jx49wrJly7BixQrk5OQgICAAkydPRqtWrTQyfnx8PPz9/XHp0iXMmzcPU6ZMgUgk0sjYhqp6f/esUqRSKVJTUyEWv3wtRy6Xw8fHB7dv30a6XIljD98cRFd/2/VSEJXmWEI20uXKCtXMWFm4uLjg+++/R0JCAhYsWICIiAh4enqiZ8+e+OOPP1DZ3+FdXV1x6tQpTJ48GSEhIejduzeePHmioeoNE4cRq5R169ZBqXw9GLKzszFjxgyEJ2RD+Yaf25y0pzj840wIggCxqeSNcyoJCC/DSouxyrKyssIXX3yB2NhYbNmyBU+ePEH37t3h7e2NzZs3o7CwsMJjm5qa4vvvv0d4eDguX74MLy8vREREaLB6w8Kn6VilbN26FcnJybC2toZMJlP/KZFIULtRc2y4k/XGMXZ8PQHXjuxCW7+RuH02AulJCQBKPk333LimNrA3423frOoQEU6cOIEff/wR4eHhqFOnDoKDgyu92SEpKQnDhw9HREQEvvnmG8yaNeu1Mw7GjsOIac2xh9m4kpJf6s65mDPHseEzf1g7OOGLXWewxL9LmcJIANDawQzd61hqvnDGyuDGjRtYtGgRtmzZAnNzc3zyySeYNGlShTc7KJVKhIaGYvbs2ejYsSO2bt0KFxcXDVetv/g0HdOa2IyCUoNInpuNffOLtoH3m74AZlZl/82SAMRlFFSuQMYqoWXLlli/fj3u3buHCRMmYPXq1XBzc8OAAQMQHh4Oler1m7lLIxaLMXPmTERERCA2Nhaenp74/ffftVS9/uEwYlohV6qK7azwot+Xz0d6UgJadu+LZl16lXuOtAIVCt50QYoxLXtxs8OyZcsQFxeHXr16wcPDA6GhoXj8+HG5xvPx8cG1a9fQtm1b+Pr6Yv78+eUKtgIl4XGuAok5hXicqzCYnxE+Tce04nGuAutj0ks8/uTeHfx3cEeYWVrji91nYGVXCwDwn96ty3zNCADGNLaBowVfN2L6g4hw/vx5rFixAjt27IBCocCAAQMQFBSErl27lrnzgkqlwpw5c/Dvf/8b/fr1w8aNGyGTyYp9rTG01+IwYlqRmFOIsNsZJR6Pu3QGqz/p/8ZxnBs1x+fb/yzx+MhGMtSuYVqBChnTvrS0NISFhWHlypWIiopCw4YNMX78eIwaNQr29vZlGuPgwYMYMWIEHB0dsXfvXjRr1kx9zJjaa/FpOqYV4irqu8X/gJk+s7W1xaRJk3Dr1i2cPHkSbdq0wYwZM+Di4oLhw4fj9OnTr92ztHr1anz44YfIyiraidqnTx9cvHgREokEbdu2xc6dOwEU3Uy+JioN8VlF28vftKp4fjw+qxBrotIQmZqvyW+10nhlxLSiQElYdP1picczHifixh8HXvv6idULkZeZDgDoMmYSark3gfcHg4odg1Qq/DSgLd7yaoV27drh3Xffxdtvvw1LS95hx/RXSkoKNmzYgFWrVuHu3bto1qwZxo8fjxEjRsDGxgZubm548OABOnbsiKNHj6o7iefk5GDcuHHYvn07Fu07AXm9lpWuRZ/aa3EYMa1ZcevZGzcxvKo814wkinykH1yFc+fO4fz588jMzIRIJELLli3Rrl07dUA1bNiw2rdaYfpHpVLhxIkTWLlyJfbt2wexWIwOHTrgxIkTAACRSISuXbvi8OHDkEqlAIquRy3f9wey3bxfGy/5zj84uWEZHkVFIiv1MQryc2FmaQ3nhs3Rpv8wePUaWGwd+tJei8OIaU1Z7jN6VVnD6NX7jJRKJaKjo3H27FmcO3cOZ8+exT///AOg6FTJO++8gzZt2sDLywteXl6oX78+BxTTG8nJydi6dSvmzZuHZ8+eqb8uCAJ69+6NPXv2wNTUFOlyJdZEpUFRzA/V1cM78es3E0uco+enX6PL2ODXvm4iAOOa2ur8GhKHEdOa1DwF1kSna238N3VgSE9Px4ULF3Du3DmcO3cOV69eRXJyMoCiNi+enp7w8vJS/9miRQuYmen+N0RWPRUWFqJWrVpIT09/7dhbb72FS5cuYfvdDMRnFRb7C170X8fwT8QR1G/9LqzsHZGXmYa/tqzEg+sXAQBW9rUw4/dbr71PAOBqZQp/j+J36lUVDiOmVaX98FRUZX54kpOTERkZiWvXrqk/YmJiQEQQi8Vo0qSJevX0PKgcHBw0WD1jxTt69Ch8fX0hFotBRC/dW2RiYoK7yanY9qB8vfASY25g2UfvAQAk5haYcya+xNfqur2Wfm44Z0bDt65liacVKkosFI1bEU5OTnByckLPnj3VX8vJycHNmzdfCqm9e/ciNzcXQNFNjS+Gk5eXF9zd3fk0H9MoV1dX+Pv7w9HREa6urnBzc4OrqytcXV1Rs2ZN/PEoBwLK9oudSqVC9tMnuLA7TP21Bm06lvh6AcDV1HydttfilRHTurI+z6isquKCq1KpRGxs7EsrqGvXriEpKQkAYGlpiVatWqlDqkWLFmjYsCHs7Oz4cdJMK8q6Iejnkb5IuHlZ/XdBENC4Y3cMnP1fWNYseZVvKxFhfPOaGqm1InhlxLTO094MOQoVTiXlVnosH2eLKtn5IxaL0ahRIzRq1AhDhgxRf/3JkycvraD+/PNPrFixQn1KRSaToWHDhvDw8HjtT3t7ew4qVqKkpCRkZGSgSZMmrx0rS3utkggiEUQmJqA3tBR63l5LItbNv1FeGbEqE5maj2MPi55vVJ5/dAKKTs11r6sfW1BflZeXh9u3b+Pu3bu4c+fOS38mJiaqXyeTyeDh4VFsUDk4OHBQVXNDhw7Fr7/+iubNm2PkyJHw9/dHvXr1ALy5vdaLkm7fQl5WOjKSE3F+13rERxZtYHBp5oVPNx8r9b26bK/FYcSqlDG1LymLnJwcxMbGvhZSd+7ceSmorK2tiw2phg0bclBVE0FBQVi9ejVUKhVEIhFUKhXatGmDHj16YPSkqdj9SFHuMQvycvHde42hkBd1W/hy7zk4uLqX+Hpdttfi03SsStlIxfD3kKkbO8ZlFCCtmNMPthIRGsgk8LY3M+gH6NWoUQOtWrVCq1atXjv2PKheDKm7d+/ir7/+wqNHj9Svs7S0RO3ateHs7Kz+ePXvzs7OkMlkHFoGgojw7NkzPHjwAAkJCUhISEB0dLS6NdDz076XLl3CpUuX8KwQcP3oi1LHLMzPg6mZ+Wtff/HfRH5Wyf0igapr41UcXhkxnStQEtLkSiiJIBYE2ErFOjtvrS9yc3PVQRUbG4ukpCT1R2JiIpKSktS9y54zMzN7Y2A5OzvDzs6OdwJqGBEhPT0dqampSElJeeOfycnJyMvLU7/fxMQENjY2SE1NVX/t+f9HgYGBWLhkKZZHl74JaPGgjqjX8i24er0DG6c6yE5Lxflf1+H+tfMAAFMzc3x97B9Ia5S8Y+7LVnZ8zYgxVj45OTkvhVRJH0+fvtwj0MTEBE5OTupwqlmzJqysrGBtbQ1ra+s3fm5mZmbUKzAiQn5+PtLS0koNlBc/f/r0KRSK10+j2drawt7eHg4ODnBwcFB/XqtWLdStWxf16tVD3bp14ejoiAsXLuDdd98FULSaadq0KTZv3gxv76LWP2/aTfdi95Li9J32H7w7ZGyJx3W9m47DiDEjJ5fLkZycXGJYpaenIysrC5mZmeoPpVJZ4ngmJibqYHpTcJmbm0MsFkMkEqk/Svt7eV4rCALy8vKK/cjNzS3xWFmOF0cikbwUKG/6s2bNmjA1Lfv1l+TkZDg7O0MsFmPGjBmYOXMmJBKJ+vib2mv9vX0Nok8dxZN7t5GT9hREBGsHJ9Rr1QbvDBqN+q3fLXHuV9tr6QKHEWPsJc9XBpmZmeqQejGsyvN5Xl4eVCoVVCrVa49K0BYzMzOYm5vD3NwcFhYW6s9L+ijpNba2ti+Fi6WlpVZXhESE5cuXo0OHDurV0It03V5L2ziMGGNV4nmLmxc/lEplsZ+X5e9E9FLwmJubG/0pRH1rr6VJHEaMMWYgSuvaXVH60rWbt9QwxpiBsJGKNX5dp7ue3MPHYcQYYwbE094MPs6aeTprVbXXKgs+TccYYwbI2NprcRgxxpiBMqb2WhxGjDFm4IyhvRaHEWOMGRFDba/FYcQYY0zneDcdY4wxneMwYowxpnMcRowxxnSOw4gxxpjOcRgxxhjTOQ4jxhhjOsdhxBhjTOc4jBhjjOkchxFjjDGd4zBijDGmcxxGjDHGdI7DiDHGmM79H3yon5bwJWYjAAAAAElFTkSuQmCC", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "------------------------------------------------------------\n", "5 loops detected\n", "\n", "Details:\n", "\n", "Atom: H0 & R12 & R02 & R04 & R11 , value = [4, 4]\n", "1 loops\n", "Nodes involved:\n", "Cycle 1 : [1, 3]\n", "\n", "Atom: H0 & R12 & R02 & R04 & R14 , value = [3, 3]\n", "2 loops\n", "Nodes involved:\n", "Cycle 1 : [2, 4]\n", "Cycle 2 : [1, 3]\n", "\n", "Atom: H0 & R12 & R04 & R11 & R01 & R00 , value = [5, 6]\n", "1 loops\n", "Nodes involved:\n", "Cycle 1 : [1, 3]\n", "\n", "Atom: H0 & R12 & R02 & R14 , value = [1, 3]\n", "1 loops\n", "Nodes involved:\n", "Cycle 1 : [2, 4]\n", "\n" ] } ], "source": [ "gen_fw_tables = generate_fw_tables(5, max_range = 10)\n", "print('-'*60)\n", "print('Fowarding table:')\n", "print()\n", "for key, value in gen_fw_tables.items():\n", " print('Node', key)\n", " for rule in value:\n", " print(rule)\n", " print()\n", "print('-'*60)\n", "print(\"Graph of the whole network\")\n", "print_from_fw_tables(gen_fw_tables)\n", "result = loop_detection(gen_fw_tables)\n", "print('-'*60)\n", "nb_loops = sum(len(res[1]) for res in result)\n", "print(nb_loops, 'loops detected')\n", "print()\n", "print('Details:')\n", "print()\n", "for res in result:\n", " print('Atom:', res[0].get_name(), ', value =', res[0])\n", " print(len(res[1]), 'loops')\n", " print('Nodes involved:')\n", " for i, cycle in enumerate(res[1]):\n", " print('Cycle', i + 1 , ':', cycle)\n", " print()" ] }, { "cell_type": "code", "execution_count": null, "id": "fc346596-8d6b-435c-95e6-2d5538c9a548", "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.11.5" } }, "nbformat": 4, "nbformat_minor": 5 }