{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Node 0 : [5, 7]\n",
      "Node 1 : [7, 5]\n",
      "Node 2 : [3]\n",
      "Node 3 : [5, 6, 2, 7]\n",
      "Node 4 : [7, 8]\n",
      "Node 5 : [0, 7, 3, 1]\n",
      "Node 6 : [3]\n",
      "Node 7 : [0, 5, 3, 8, 1, 4]\n",
      "Node 8 : [7, 4]\n"
     ]
    }
   ],
   "source": [
    "# Diapo 22\n",
    "\n",
    "N = [0,1,2,3,4,5,6,7,8]\n",
    "E = [(0,5), (0,7), (5,7), (5,3) , (3,6), (3,2), (7,3), (7,8), (7,1), (7,4), (4,8), (5,1)]\n",
    "\n",
    "# First, and for the sake of simplification, we will count the number of\n",
    "# segments / edges separating different points / nodes (of a graph)\n",
    "\n",
    "counted = [ [] for i in range(len(N))]\n",
    "\n",
    "for e in E:\n",
    "    if e[0] != e[1]:\n",
    "        if e[0] not in counted[e[1]]:\n",
    "            counted[e[1]].append(e[0])\n",
    "        if e[1] not in counted[e[0]]:\n",
    "            counted[e[0]].append(e[1])\n",
    "\n",
    "# Print counted\n",
    "for i in range(len(N)):\n",
    "    print(\"Node\", i, \":\", counted[i])\n",
    "\n",
    "# We want to write a program which, given a subset of these nodes\n",
    "# and edges, computes the list of nodes ordered in ascending order\n",
    "# of distances to a given particular node.\n",
    "# » For node 0 and the subset N0 = {1,3,4,5,7} and the associated edges E0 = {(0,5),\n",
    "# (0,7), (5,7), (7, 3), (7,1), (7,4), (5,1)}\n",
    "# and the expected result is L0 = {0,5,7,1,3,4}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0, 0, 0, 0, 0, 1, 0, 1, 0]\n",
      "[0, 0, 0, 0, 0, 1, 0, 1, 0]\n",
      "[0, 0, 0, 1, 0, 0, 0, 0, 0]\n",
      "[0, 0, 1, 0, 0, 1, 1, 1, 0]\n",
      "[0, 0, 0, 0, 0, 0, 0, 1, 1]\n",
      "[1, 1, 0, 1, 0, 0, 0, 1, 0]\n",
      "[0, 0, 0, 1, 0, 0, 0, 0, 0]\n",
      "[1, 1, 0, 1, 1, 1, 0, 0, 1]\n",
      "[0, 0, 0, 0, 1, 0, 0, 1, 0]\n"
     ]
    }
   ],
   "source": [
    "# Generate matrix from nodes and edges\n",
    "def graph_to_matrix(N, E):\n",
    "    M = [ [0 for i in range(len(N))] for j in range(len(N))]\n",
    "    for e in E:\n",
    "        M[e[0]][e[1]] = 1\n",
    "        M[e[1]][e[0]] = 1\n",
    "    return M\n",
    "\n",
    "# Print matrix\n",
    "matrix = graph_to_matrix(N, E)\n",
    "for i in range(len(N)):\n",
    "    print(matrix[i])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "def transitive_closure(matrix):\n",
    "    modification = True\n",
    "    while modification:\n",
    "        modification = False\n",
    "        for i in range(len(matrix)):\n",
    "            for j in range(len(matrix)):\n",
    "                for k in range(len(matrix)):\n",
    "                    if matrix[i][j] > 0 and matrix[j][k] > 0 and matrix[i][k] == 0:\n",
    "                        matrix[i][k] = matrix[i][j] + matrix[j][k]\n",
    "                        modification = True\n",
    "                    else:\n",
    "                        modification = False\n",
    "\n",
    "    return matrix\n",
    "\n",
    "def compute(node, subset, edges):\n",
    "    linked_edges = []\n",
    "\n",
    "    for edge in edges:\n",
    "        # Get only the edges linked to the node or between the node of the subset\n",
    "        if edge[0] == node or edge[1] == node:\n",
    "            linked_edges.append(edge)\n",
    "        elif edge[0] in subset and edge[1] in subset:\n",
    "            linked_edges.append(edge)\n",
    "\n",
    "    # Generate matrix from nodes and edges\n",
    "    matrix = graph_to_matrix(subset + [node], linked_edges)\n",
    "    matrix = transitive_closure(matrix)\n",
    "\n",
    "    # Make symmetrical\n",
    "    for i in range(len(matrix)):\n",
    "        for j in range(len(matrix)):\n",
    "            if matrix[i][j] > 0:\n",
    "                matrix[j][i] = matrix[i][j]\n",
    "\n",
    "    # Compute the distance between the node and each other\n",
    "    \n",
    "    distances = [0 for i in range(len(subset))]\n",
    "    for i in range(len(subset+1)):\n",
    "        if(i == node):\n",
    "            distances[i] = (0, 0)\n",
    "        distances[i] = (i, matrix[node][i]) \n",
    "\n",
    "    # Sort the distances\n",
    "    distances.sort(key=lambda x: x[1])\n",
    "\n",
    "    # Return the sorted list of nodes\n",
    "    return [x[0] for x in distances]\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[(0, 5), (0, 7), (5, 7), (5, 3), (7, 3), (7, 1), (7, 4), (5, 1)]"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "compute(0, [1,3,4,5,7], E)"
   ]
  }
 ],
 "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.10.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
