{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "6730c124",
   "metadata": {},
   "source": [
    "![logo](IPUpy.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "d2f96ff1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[01;34m-------[5888x]-------\u001b[0m\n",
      "Hello world\r\n"
     ]
    }
   ],
   "source": [
    "print('Hello world')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "fc31b208",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[01;34m-------[1963x]-------\u001b[0m\n",
      "I am tile 0\r\n",
      "\n",
      "\u001b[01;34m-------[1963x]-------\u001b[0m\n",
      "I am tile 1\r\n",
      "\n",
      "\u001b[01;34m-------[1962x]-------\u001b[0m\n",
      "I am tile 2\r\n"
     ]
    }
   ],
   "source": [
    "print('I am tile', __tileid % 3)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3fb03cd1",
   "metadata": {},
   "source": [
    "# Word Search"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "78e1b604",
   "metadata": {},
   "source": [
    "Each tile contains a fragment of the file sent to the IPU in special variable `__tiledata`. In this example we uploaded `/usr/share/dict/words`, i.e., the linux builin dictionary which is a newline-separated list of words. We can then operate on `__tiledata` in every interpreter, giving us an instantly parallelised interactive search scripting environment."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "2368e871",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": []
    }
   ],
   "source": [
    "words = __tiledata.splitlines()\n",
    "# print(words)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "e4d506fd",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[01;34m-------[1x]-------\u001b[0m\n",
      "['Itaipu']\r\n",
      "\n",
      "\u001b[01;34m-------[1x]-------\u001b[0m\n",
      "['Jaipur', \"Jaipur's\"]\r\n",
      "\n",
      "\u001b[01;34m-------[1x]-------\u001b[0m\n",
      "['Lilliput', \"Lilliput's\", 'Lilliputian', \"Lilliputian's\", 'Lilliputians']\r\n",
      "\n",
      "\u001b[01;34m-------[1x]-------\u001b[0m\n",
      "['Oedipus', \"Oedipus's\"]\r\n",
      "\n",
      "\u001b[01;34m-------[1x]-------\u001b[0m\n",
      "['manipulate', 'manipulated', 'manipulates', 'manipulating', 'manipulation', \"manipulation's\", 'manipulations', 'manipulativ']\r\n",
      "\n",
      "\u001b[01;34m-------[1x]-------\u001b[0m\n",
      "['manipulator', \"manipulator's\", 'manipulators']\r\n",
      "\n",
      "\u001b[01;34m-------[1x]-------\u001b[0m\n",
      "['multipurpose']\r\n",
      "\n",
      "\u001b[01;34m-------[1x]-------\u001b[0m\n",
      "['stipulate', 'stipulated', 'stipulates', 'stipulating', 'stipulation', \"stipulation's\", 'stipulations']\r\n",
      "\n",
      "\u001b[01;34m-------[5880x]-------\u001b[0m\n",
      "[]\r\n"
     ]
    }
   ],
   "source": [
    "print([w for w in words if 'ipu' in w])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "967a9ad4",
   "metadata": {},
   "source": [
    "# MandleBrot\n",
    "For fun, we give each python interpreter control over 1 pixel using special function `setPixel`. If any tiles call this function, the notebook will display a visualisation of the 'colour' of every IPU tile. \n",
    "\n",
    "For example, if we visualise clock cycles, we sometimes glimpse the structure of the IPU in terms of relative latencies."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "id": "3ad1ce05",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": []
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAFwAAABACAIAAAAYmYfvAAAGwklEQVR4nO1cS5LjNgwlKbWPkKPkNDlELpQzZJebpabKFsEsQMMwfqRk2T1VGaxaPSAI8PMIPHI6//37vznnpKSUon%2B5LEutlT7NhkK/tRbrcDvCPkpggfQBYNhLa820Lzypta6XywX1zAYibADQAZjDR36QnaFDpZTWGllbluV2u2kfhD/UNrCPajln03/uKvqw5pxRL2hghi0CGM5VrZXrxxPQWqu1UremPoVKn9p/CjXwHwBIH0djNcMWAVMDL%2BDJGaPgAYCMiRUnQmUBP/S3bfNCJf%2Bpr8AfvjrQWimllLLir8iEGTCfgSFG1FpjjEChkFpLfAJSckNFZ2qtekDNUOlTT7A59IkwhbbPezAizW%2BZFG5hcyMI%2B3MY1O2Y/pRSWqu4Uvr2OxcjUOI9T67ohuae9%2BSOQZE/FgYlU/%2BxUnT7tA8joi1zDLP0ntf2fQx6%2BDOBQY9Y1nXFI/mi9e4mLqlvdcMhb6Fy/Zk8ZV3X5OcRes2iflKnjCeXyyWNV9wlpVTKV611ba2hRdHAnD3u3wwG4RSlOQzS9ql5YJ%2BaDE8Z7o/WJzsAsAIAfpsBo5hhUwe11hMxiDzjCjOZrhlw4BX9E65QtIZLZK21cneDDl7PU8hOMAHmRhD%2B4CkTCACQnWGmm1Iid3Jecs59%2BwRh87EeYgQO8a48hQLGPR%2BvOHI1sC8sCP%2BfMUjGgvZx%2BzzVPmdhRCkl2MN6GlprM3ueiwj43tydtm3bMFht/x4XADCg3Y8R11KiJSrCNmcMhSf4JPTF97wnAFDrNaUIdGcwCFf6A2hFH%2BTuHEbcYocszNIDKsNme961fy4GLctSSlkB4HK5eM1mErOZVD0GaS%2BznKnFhsf20H8RCwCspZTb7eYlTmoLHDi2W5rekrQiPrlluFc559baShmBCJib85borlMmODXMapVm2JswPg1eXiOAnDcnfQERfaXomF9kzM5i8HYxZsL/wwxeKcUuCLl8pBruFqYZs97R6dVz51PI3bdVw0noe3Mg6K5pxkza16HyX8bVc%2BdoyZX9jJnrkJcZzzB4xxgzbh8xKKVxbaVPvZxzzp15e2ofz4AZtggY1c9i8NIR1t69NvExqNsv5WvIp6DZi%2BrgK53Hp6D%2B63wK9WvGcsegNfCklOUXn/KLT1FdkB3GpywBn/L4%2BX/ApwBl0jkD8Sk15FOoGKsTfEqqdXA3jPICn/L4DDEoKia9KDifAmkHRvThC7dAt2OuuK5RUzL4FFdfhGoGPIdBMZ%2BSAaADLWZy1P0wMbsTQnKJ8iAVP9ID4FuMzxhlGfgpMIXnHeZqEnmHN0A8D%2BL2eXdPaX6cOHHXhZx4tfq%2BxIyLd7WKtU/%2B7a8/hri1q/bhMnOKmfrDsyzNvTcRdoan2Lqu27Z16uD1UwPl9VuBnSz8yadYaw0AVpqZYWLm5QUcdL0ZLqWQx94MU506DFgwKfhDkNeYAyTyJhwgtObyKZ%2B/bNehxvojeuHI5XznU4S7Wj522Y4r7n0PcrQ/uuY23qe8cmpwOXyKDe17p0ZSIB2fkvdTzPD8iU/5SShIHv9hSnGOXrBvHXLONh35EQpyFrP2UoqePzMYhPqPJ6OmoOrivE9B0WOq9eMMxbPvDRbnUxCDPCZFdxRjECq4fAqKyCzeTS%2BY5XygfDq90GsfpA4%2Bk5iRnW9JzHRHOlHEJfJ0l6zLed4gpBfcizQtNA3aIa/J%2B59r9M8OtLR90ndQkPy5Rpxo7aIUA/9jesHlU8hd3eZ0CpJPQ3Pep%2BylFAPl4QpCl57ep7zjbtjDiOFdNRnkGOQN0Il3z3ae8pnEjOzsYvlfScx4116p0fmUP3/8YwK1GdJMqi70Z%2B594hkOts8MSHM7wwn4%2Bvq6Xq8rsEc8zxzqp0FX2//8vU8ppecp4t7H41C5HOV0pUPBWhPH9iucq6DHyT7X5wZtPuXd1fAuTvfOuX6iekb9wfuU7wXdGBTTe6rnWutjpXx7NcwnYC9j5vm/t3rufAqFfZgxS3MYMZwxfu%2BzlzHTAR9j8Iw85Se%2B9xkwZkL5MAZ1jlbzKXp0Nd8xzFDSNKebUlpS9D6FQqWf9fsUii09HyUiomH17PIpUTV8A7rMnaqG045TDAAgGXlQ78Xqats2/P1s9XytZMerxZ4Kwk9Uw45D/efnsLdtg3rygxwuZqKIM/1EHXCHyN3Tq%2BGkVtzrjJmQw9VzB1pB%2BQQdfOZq9Q3/wWkHg2fwKccYMy3HGLxXGLOkMciSIYPXOVr6Dhwyb5RFwLwDbw/PZ8beAJm3yML/rF5ua4kTxW/8Wwd2KfC%2BxMxTK%2BpvHfwHPOwSxzW/BRcAAAAASUVORK5CYII%3D"
     },
     "metadata": {
      "image/png": {
       "height": 347,
       "width": 500
      }
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "r = getTime()[0] % 256\n",
    "g = getTime()[0] % 256\n",
    "b = getTime()[0] % 256\n",
    "setPixel(r, g, b)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c62f3b3c",
   "metadata": {},
   "source": [
    "With sufficient processors controlling 1 pixel each, we can generate some recognisable images!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "4eb1d883",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": []
    }
   ],
   "source": [
    "# Find position of my pixel\n",
    "H = [h for h in range(int(__numtiles ** 0.5), 0, -1) if __numtiles % h == 0][0]\n",
    "W = __numtiles // H\n",
    "X, Y = __tileid % W, __tileid // W\n",
    "\n",
    "# Set animations consts\n",
    "x_start = (X / W) * 4.94 - 3\n",
    "y_start = (Y / H) * 4.48 - 2.24\n",
    "x_end = (X / W) * 0.014 - 0.632\n",
    "y_end = (Y / H) * 0.012 - 0.474\n",
    "\n",
    "max_iter_start = 18\n",
    "\n",
    "# Define colour palette\n",
    "def setColour(t):\n",
    "    T = 0.5\n",
    "    if t < T:\n",
    "        val = 256 * t / T\n",
    "        return setPixel(0, 255 - val, 255)\n",
    "    val = 256 * (t - T) / (1 - T)\n",
    "    return setPixel(val, 0, 255 - val)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "309968e8",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": []
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAFwAAABACAIAAAAYmYfvAAAODklEQVR4nNWbO1Qb2xWGP7GomIHOHrm0BOmuhjIWpAuivcIpAbeG1kAZQ7oYKINpeaSLrVsGKV2wnC5I6S5oUqLhVoE5dLFSbM3x0TwkYTtc7l5erJmjM%2Bfxz378e59xhrkOvwhRKe0BwKPm1U/OYyywgfCvFusuE9mM3H119ysq/BeXoIsIAWvsdPxMx8voloRBhpTgIYPSfyeBceGzz0vgMT5%2BCFYQe2RoaB4kKANXbyDyd2%2B2ozJF6qfMvGfhT2r1qefhQysdmkGSeUA%2BZUgNN3VEgc93NA9ZnqYhzQcsv%2BBwkaM/O4s4QMzFiKQ7mocByl0M/vNfuqB0yMjdOZNTXCxylKc1R%2B03zmmq6xVJweVnBWV4LOhVEP14AIqneHlaeVplKhZKYS3w/taysMNta0Ti0CThMnK3lX0T6RNQ0kQrSBA%2B64fXEGA7%2BAH2PNU6xR3WurMQmyXuZZKWMRr94U4h/U7yZehHPEgAin8yXaW0w5rCytNq4p5Qmqf6ip0WeR/HwQf%2BHeSww3nt2Ji6RToYG49FH9X772vka4aKhNUAfDp%2B5vdqSzyIR%2B4KJ0/LQgHzVM9wbYIs7SL1JY488r9WH7sK5YWaFZkistpQBoVk9RX/vkASKUb4Yq3wh2kab9g4oVSk7uDvsQL4ODm8l%2Bzvs1KkDiis72j%2BUW10yOCHihbB2pwrXPPD4ClpdIuwMVzuEUtyscXreaoOvktjlbfAMz6%2B4HCaxiVZB/%2BSbBN3keMNtvdY6ZD57I/MGZNwyVD8OaJP4v77dNM7UTzFswn2WJ3lA/COhee8T3z6lJk6RYVVpdQi/xOPu45DMYa6tawxpRR2JtfB7nE69wXKkCikPWKA8ogrm8AjD1wzDiisJ7TjA2jaEmDbBFVKwBVOgUYTd48VH2eLzYzTidCZ/w8oXwBB/8dVt3FMKQuVpe3gF2juSvTtK1qV5qgCixxnac9TvWZ8lb0K5VvH%2BgwKYH8TUL4SgoGjhWoi2j5HNU9LfsnhbbCdNtI5kz5OntYT2qfMHLMIlKnMU9V9LII4KKPfZt3fRBKHNRDZYxWoUQJOKLXIy4VozQjRtzvFRZ2i381/2GflHQv610uy6yagQYhLMAwow0AwJExxlt3/wRCR36pajdIZrjQLVbtmfIKb%2BEOfyAhAZ7gKq0lBYQEt8qJWByx75Bx8B/%2BQ5d/573B6yFsMlDtpQW%2B87EofTjz84L1j/o25x/hXOAJHnWKdYoGmOAtxqNLzDNdCye00jWkar9gJsANsKxy0SaFO8SNFuf09W3/gtTnd6BfaghEs/%2BNPTHDzGP8nHgM/MvUr6zw5K/2KiRRWjpaDP0%2B1gQvoTXrkaswVqTdwbYICTXMAH8fHCbB9nGfUFVaADeRoHbK8zOG/rVxkzrv7lFikfMKlwr7COWVmlg/vWHikrgSghIxD0SHzjPo/rGfRDv3nAnnhBRqAhapTPGBZtq2wKpQlRfbITXFxzXiNOWOlXSwCbBUq82847XGxoWQo3CX6xPOFgI7KmOb9jHqXKQEwhroNF/GIqzZZMfiuZkVS%2B/SJxlCChU1gE8i1JH7SIrcOfp7WLB8%2BkdnnpSTQLzgs0BBMBZHukkxEjJXcheanZFAWgSDyhnVgm/Us7d9Se4r3FE9h/5X5R1w94uqIpQpleaqB%2Bxee/1p97ElJEvl%2BLI0Kwq3ody6bX2NHCivACJ0CzQ22X3B4wHIT1w7HvcXC6vV9vX5waFDSE5NbrMf4Yq5AhXKB5iLHa%2BxcMHlCCZC1vmWlRf6cSen2nPcfKY6h%2BiWQCuhRE0D0wsTFJlBYPs4sHzS71c7lBYfnTIpa3ZoApGjoEKD0L/%2BGiu3gb7EJ7LJWpvKCQ%2BEO4hfX2PlIsUxlg20JDRXKBRq5kIZ1x1TEb01ERB3ktWsjku4Ovkn2IwFbYQnBgUE1o2CgT0mLTWG9R6obBRoOfpH6K3YnuEljEMAeK6u8PaG0yaYEhR4j75UxlN6zTZCnJRt7xY6mZBJuZEyTqp5QEpua5cMb1mvMNXAVVtd2tER8CjDA0fZBBPAZQy1xJFz7DLdCWZQlIp/ICLOUspjCkgvxfBiuISJ5Wq/Y9chVKX2k%2BJK3QtLPcI9Ykqe0hpoi9MxCScFliosCjTbZLigmBHcDpT8iAX9UGyWq0zQ0gwTOcHN4iWpyygxwzKJQBtERMyKYIgqSpV2mssix5maEBF80ZYvNiIKc4VYpCfTAPitnuNM0JCZ%2B1sq4sgwApQ%2BdU5/7SHr2ms0i9QrlN2ykmYwpb1j3cXZZW%2BTIJmiT9ci1ycZ72gRlKjZBXBHENNLKKK9Dw5SKwTFLc1Rb5BNMNaIv4W0MlOEQkVuxefF5Tdwz3DrFMpUAO09L1EdKHoLXJdkmhRZ5qZUB50yusmeaksABOPglqmn22CJvqg9wyoxYqEZZIDBVUjf2uDCTK1kRUIbJzXpBIfSFUuCQbFXIku57hisOz0xP9GkesMiRNgctgksOz6VRpqL7m6YakT1WxEBMkzQvMAiOsS1LeHD3nAj4TPOHR0SL9bl9kWOP3D4rZm52SbZO0Xw/Ok8zh8nTEg2P4CJucoNt4TXAGW4k7moR6BO3LXu2CcSapFEKmnWKNeZqlAo0/qUK8gODE8I4HEn96xTfh6WKE0pz1EboPKEtdmETPONj3OOcMlOgKQYip1nmrzZBk8JrNk06H0n2PpEBRujohDgRCz0gkMP7gfIrdmb5YBNssC1p9BjqVnVtqi95G%2B6YwiZwaYhWT3FRpbTF61NmRJ8lSY0gcs24BBGPHPCG9QJN28BbriVO%2BTgeObELM8cDWuSbFAQdiW6a8gouOiHS1zbBKTO7rF0zLjq7y5pH3jJ2m5Il9/1sKCKSswOv2NllTQpijTDXkL9vWDfrhhPcyIYrlIVxmPUOEXHh4o%2B0ppjRl7AsIDOadI4wFdhl7ZLsTrgqiQl1irN8iLynJY4U1pFaurWsGCh3P8SSKrnkPgUaOkORRYj22r1wXjNeprITKzubuMgGNDc70SS9V0xXGplFMsMntHV9%2B4RSlZLCOmA5T6tIfYSOMJ1d1l6xY6FulQnKQDhSkldx4C3ysg3NxMwd%2Bjjy2YjcylsyO9MLnDjCHdZEvz6RyeGJvvQuIcr6tOHEC9pnuG2ygtQLDq8ZH6FzzmQD94ilAHub9SOWxlAZnOHqKSmIjIVXpoLI6t2wGiRrlbRID3DNuEdumoZwLYzAIZpfpJ5Gz/QmfRwh7%2BEae0ZwaUQiXZp8T0UX6Bi28jbE5wv0KrCDv8/KAcva1I9ZLNCUYz1ggptpGieULCPrE7FQuv7aRzTcukUqTA3cInWZ95JsYggXOWdSSKbCklAluxgESl84xtJNrkwFEHuRomGWtkbE3Aah6zET4ghnSZQpLsxukhAT8iAHv//J4TsWJK6JT9QhnFRNGU41EkXGrVD2cTbYviSbpyV%2BJ15SKNAUpdXZmjmI8NfEQoT8ZJZUIohPcJOWizUp1JgTDy2kQSudhfqBcgYr3aekYxHXEZ0EyUWNktSxTWLeh6S/Y0GipoiMo7OH%2BAu/ZlwCuUBpoXS2lSjS30JJ8iVlOsJALq7EwZc4lRySJaMxX53OoBJNxjLc7SabVUrXjMurMxeatuhLss95f8ByXFmkNJG2VTP2p/UBrhmXYoKQI2kUDi1/I349BMXY6RhKvpl6Rl3zdHoPB%2BJwiJpIEShCsdLkExnt84WYRnhKDo8UKE2P3kdBws1FsRYGNMuHS7JSSzeTjAzGiLra%2BpJ9Hee/p/IDZSBHK55uxWdy8OeomYky6YZzSdYsOOl27TX7SB9jjHTT42sXHqmEnTMpB6lSVx41zUH7G9OrCyKnzORpSakmDoe%2BCLBrSdRTqF3cNeiWyMvsbw4iwyAi3ezwEEbPGDHJKS6muNAoj4pf9MgXQp4j0Z7wVbxh3SMncAyz1u%2BpiIM060NiJj5OnE09of2EtpwBm%2B1DKsIwEh8ncWTdOCIbAF6zJVTaQq3y9oBl6RThy4m4mI0Kq01WK9Q7Fg5Y1umyLo5EJJIxkVQZuTcZnaOmsM5wi9TF222xKQlbvLfJcOKZnnlApbDko0XdTaKswkrkHVJ/kjChB7EJvpWyDCMStoHRMhWFNU3jnElNrnVJtU7xOe813dQXGhpiIUMudC1a8xcZ%2BTKpRq1F5tXU4z4RITyoD7AzetqIDX8ic8SS6LxYhP5JNhl3uhhJKoZ2AAOzO3NerVz3A4oOT5rOfSZvegWSv%2BqjE5EsbSCHt8SRhZrgZpGjCMGjl7YQ2o4UFvrnZuYy5Nl7VhNC52ChEnKfCW4kc5XSfIWywnLwV3l7yoze2Bw1%2BXJKSKHOu4nFaTnxHAYRkXuGQ6azQhLv4GcGzi%2BeBYjrv5RFNcsyj3gxTKlMZSATe1AyGBR6v71M7CD1ngauFKK1WCFFLlL/BeEyFCiJIv5J4oX%2B4Fuf8kScixTrc3gSejWhvH/HMYxEfYo%2BSRn4pPmBXfwcK3Kr%2B8jXaD5OIut/IDIaicQ6uY6c1MZFI6L9q8YizuskRWiTPTHKXA8XlIhS6HAtfjgtMb9mXLO4PqNHjIhe8j5kkL5/6TGfM1xd/urvF3WxT3zwc96fMqOw5qlK7dPcfOQ7LN0YP7J4IBJ1tO9Y0Mo/JAeN5DJnuI3we/GImPzFwR/y/OH%2BJfNfw6ecMtOkoF%2By/ib1C8YV3zFPVVQpkb%2BY32c8KMn8yOQUF1L1l/8XJD9oilGmMswnSgNFn3uajkYs6KF5llFh8XKTWMKY4Mb86uQLRGJw3KcQRqVrxn%2BufCdR/gfejGGRN4CUbwAAAABJRU5ErkJggg%3D%3D"
     },
     "metadata": {
      "image/png": {
       "height": 347,
       "width": 500
      }
     },
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": []
    }
   ],
   "source": [
    "x0, y0, max_iteration = x_start, y_start, max_iter_start\n",
    "\n",
    "!RepeatBlock(900)\n",
    "\n",
    "x, y, iteration = 0, 0, 0\n",
    "while ((x*x + y*y) <= 4) & (iteration < max_iteration):\n",
    "    x, y = x*x - y*y + x0, 2*x*y + y0\n",
    "    iteration += 1\n",
    "setColour(iteration / max_iteration)\n",
    "\n",
    "x0 += (x_end - x0) * 0.01\n",
    "y0 += (y_end - y0) * 0.01\n",
    "max_iteration *= 1.001"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5bc59d56",
   "metadata": {},
   "source": [
    "# Black Hat Snek\n",
    "Graphcore have received an encrypted message, but have lost their super secure key for the decrypt function!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "de2b67b7",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": []
    }
   ],
   "source": [
    "message = b'v\\xa4q\\x0f\\xa8eA\\xebp]\\xb2$@\\xbep\\x0f\\xbewF\\xa5c\\x0f\\xff$f\\x9bQ\\\\\\xebmA\\xebe\\x0f\\xa5k[\\xaef@\\xa4o\\x0f\\xadk]\\xebb]\\xaea\\x0f\\xa4j\\x0f\\x9be_\\xaev\\\\\\xbbeL\\xae%'\n",
    "\n",
    "def decrypt(key, data):\n",
    "    return bytes(d ^ (((key % 0xfffff) >> (8 * (i % 3))) & 0xff) for i, d in enumerate(data))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a5b9ebf0",
   "metadata": {},
   "source": [
    "But it's easy to exhaust a 2<sup>20</sup> search space when we have 2<sup>12</sup> python interpreters!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "751b957e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[01;34m-------[1x]-------\u001b[0m\n",
      "Hit: b'You can try out using 4 IPUs in a notebook for free on Paperspace!'\r\n",
      "Key: 314159\r\n",
      "\n",
      "\u001b[01;34m-------[1x]-------\u001b[0m\n",
      "Hit: b'\\x7fvp\\x06zdH9qT`%Ilq\\x06lvOwb\\x06-%oIPU9lH9d\\x06wjR|gIvn\\x06\\x7fjT9cT|`\\x06vk\\x06IdV|wUidE|$'\r\n",
      "Key: 119305\r\n"
     ]
    }
   ],
   "source": [
    "for key in range(__tileid, 1 << 20, __numtiles):\n",
    "    result = decrypt(key, message)\n",
    "    for crib in ('Graphcore', 'IPU', 'POD4'):\n",
    "        if crib in result:\n",
    "            print(\"Hit:\", result)\n",
    "            print(\"Key:\", key)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9daa32f1",
   "metadata": {},
   "source": [
    "We've also forgotten the password to our login function!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "73abd175",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": []
    }
   ],
   "source": [
    "def login(X):\n",
    "    masked_password = '@ufwodhub654'\n",
    "    for x, y in zip(X, masked_password):\n",
    "        if ord(x) ^ 7 != ord(y):\n",
    "            return False\n",
    "    return len(X) == len(masked_password)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d1678e6d",
   "metadata": {},
   "source": [
    "We tried a timing attack (timing the login function to estimate how many characters we have correctly guessed) but system noise made it fail. If we try 5888 timing attacks, surely some will succeed?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "76caf3a2",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[01;34m-------[5882x]-------\u001b[0m\n",
      "Failed :(\r\n",
      "\n",
      "\u001b[01;34m-------[6x]-------\u001b[0m\n",
      "Found password: Graphcore123\r\n"
     ]
    }
   ],
   "source": [
    "password = ''\n",
    "for _ in range(30):\n",
    "    max_time = 0\n",
    "    for c in 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_!:;~<>()*&^%$+=[]':\n",
    "    \n",
    "        t = getTime()\n",
    "        _ = login(password + c)\n",
    "        t = deltaTime(t, getTime())\n",
    "            \n",
    "        if t > max_time:\n",
    "            best_c, max_time = c, t\n",
    "        \n",
    "    password += best_c\n",
    "    if login(password):\n",
    "        print('Found password:', password)\n",
    "        break\n",
    "else:\n",
    "    print('Failed :(')"
   ]
  }
 ],
 "metadata": {
  "celltoolbar": "Raw Cell Format",
  "kernelspec": {
   "display_name": "IPUMultiKernel",
   "language": "python",
   "name": "ipumultikernel"
  },
  "language_info": {
   "file_extension": ".txt",
   "mimetype": "text/plain",
   "name": "Any text"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
