Done challenges 1 and 2
[cipher-tools.git] / 2019 / 2019-challenge9b.ipynb
index 10a38943f0cd7c428f7a7c11aeeac0325c49e95e..77d4004a306dfbc96f8c446754261e5ba129816d 100644 (file)
@@ -45,7 +45,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 19,
+   "execution_count": 4,
    "metadata": {},
    "outputs": [],
    "source": [
     "open(plaintext_b_filename, 'w').write(lcat(tpack(segment(pb))))"
    ]
   },
+  {
+   "cell_type": "code",
+   "execution_count": 13,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def ioc_n(text):\n",
+    "    counts = collections.Counter(text)\n",
+    "    ltrs = set(text)\n",
+    "    denom = len(text) * (len(text) - 1) / len(ltrs)\n",
+    "    return (\n",
+    "        sum(max(counts[l] * counts[l] - 1, 0) for l in ltrs)\n",
+    "        /\n",
+    "        denom\n",
+    "    )"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 24,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def ioc_scan_n(text, max_key_length=20):\n",
+    "    \"\"\"Finds the index of coincidence of the text, using different chunk sizes.\"\"\"\n",
+    "    iocs = {}\n",
+    "    for i in range(1, max_key_length + 1):\n",
+    "        splits = every_nth(text, i)\n",
+    "        mean_ioc = sum(ioc_n(s) for s in splits) / i\n",
+    "        iocs[i] = mean_ioc\n",
+    "    return iocs"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 25,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "1.313469334794217"
+      ]
+     },
+     "execution_count": 25,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "ioc_n(scb)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 30,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "[(28, 1.029237612412878),\n",
+       " (56, 1.0323741101856603),\n",
+       " (14, 1.0886873582571082),\n",
+       " (42, 1.0923459702787721),\n",
+       " (1, 1.313469334794217),\n",
+       " (3, 1.3136724474583705),\n",
+       " (5, 1.3139446514243536),\n",
+       " (7, 1.3141262063060093),\n",
+       " (2, 1.3142346883552642),\n",
+       " (9, 1.3145299178890573),\n",
+       " (6, 1.3146522525866469),\n",
+       " (11, 1.314872121624653),\n",
+       " (10, 1.3153476328760576),\n",
+       " (17, 1.3156082632437882),\n",
+       " (19, 1.3157537470690668),\n",
+       " (15, 1.3158533079430723),\n",
+       " (18, 1.316306072127611),\n",
+       " (4, 1.3163246528314725),\n",
+       " (23, 1.3163277281569055),\n",
+       " (21, 1.3163473471920573),\n",
+       " (25, 1.316655118103681),\n",
+       " (8, 1.317040603398334),\n",
+       " (13, 1.3171605413165643),\n",
+       " (12, 1.3174113735517243),\n",
+       " (22, 1.317528984722072),\n",
+       " (33, 1.317594905094905),\n",
+       " (31, 1.317605231763728),\n",
+       " (27, 1.317688521561938),\n",
+       " (29, 1.3179986584465913),\n",
+       " (16, 1.3180422696887168),\n",
+       " (35, 1.318663902511151),\n",
+       " (38, 1.3186715456762172),\n",
+       " (37, 1.318782979178807),\n",
+       " (30, 1.3189277795375356),\n",
+       " (20, 1.319112577563282),\n",
+       " (41, 1.319293715271584),\n",
+       " (24, 1.319320503943391),\n",
+       " (34, 1.319372128500673),\n",
+       " (26, 1.3195881414435318),\n",
+       " (45, 1.319847955451339),\n",
+       " (51, 1.3198862305977204),\n",
+       " (43, 1.3202263262432186),\n",
+       " (32, 1.3204297829974077),\n",
+       " (46, 1.32083091670564),\n",
+       " (47, 1.320835215197834),\n",
+       " (49, 1.3208992566951445),\n",
+       " (36, 1.320917879222574),\n",
+       " (39, 1.3212746271240052),\n",
+       " (57, 1.3216014658027582),\n",
+       " (55, 1.3216742585772436),\n",
+       " (50, 1.321775386202746),\n",
+       " (40, 1.3220152407245682),\n",
+       " (48, 1.322371313582251),\n",
+       " (54, 1.3229352276740334),\n",
+       " (59, 1.322950472958544),\n",
+       " (44, 1.3231503579952266),\n",
+       " (53, 1.3232741220713358),\n",
+       " (58, 1.3236374480105653),\n",
+       " (52, 1.3262568828142518),\n",
+       " (60, 1.3264573374508233)]"
+      ]
+     },
+     "execution_count": 30,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "sorted(ioc_scan_n(scb, max_key_length=60).items(), key=lambda kv: kv[1])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 33,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAa0AAAEmCAYAAADRIc8sAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDMuMC4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvOIA7rQAAHZlJREFUeJzt3Xu8nVV95/HPzwS0Ihc1B8UEDI6hNfWKKaJWxOsEYqGiKLzU6QXMWAdFUUcY23DxClgvzKCIwmC9IXXUphIFbKXYIkiQWwIEIgQTLhIgXENIAmv+WL/NebI5J+cUdkgW+bxfr/M6e6+9znrWc1vf9Tx7ZydKKUiS1IInbewOSJI0XoaWJKkZhpYkqRmGliSpGYaWJKkZhpYkqRmGliSpGYaWJKkZhpYkqRmGliSpGRM31oInTZpUpk6durEWL0nahFx88cW3lVKGxqq30UJr6tSpzJ8/f2MtXpK0CYmIG8ZTz9uDkqRmGFqSpGYYWpKkZhhakqRmGFqSpGYYWpKkZhhakqRmGFqSpGYYWpKkZhhakqRmbLSvcZIkbXxTDz/zEWVLPjfrEeVLPjdrxPq98seLoSVtZOMdNDZ0+ViD0kjlm0rfB1W+OW6D1hhaY3giHqSeqJvWNpA0fr6nJUlqhqElSWqGoSVJaoahJUlqhqElSWqGoSVJaoahJUlqhqElSWqGoSVJaoahJUlqhqElSWqGoSVJaoahJUlqhqElSWqGoSVJaoahJUlqhqElSWrGmKEVEadGxK0RsWCU198VEZdHxBURcX5EvGTw3ZQkaXxXWqcBM9fz+vXAa0spLwI+CZw8gH5JkvQIE8eqUEo5LyKmruf18ztPLwCmPPZuSZL0SIN+T+sg4KejvRgRsyNifkTMX758+YAXLUl6ohtYaEXE66ih9fHR6pRSTi6lzCilzBgaGhrUoiVJm4kxbw+OR0S8GPgGsFcp5fZBtClJUr/HfKUVETsBPwTeU0q55rF3SZKkkY15pRUR3wP2BCZFxDLgSGALgFLKScAc4JnAVyICYG0pZcaG6rAkafM1nk8PHjjG6wcDBw+sR5IkjcJvxJAkNcPQkiQ1w9CSJDXD0JIkNcPQkiQ1w9CSJDXD0JIkNcPQkiQ1w9CSJDXD0JIkNcPQkiQ1w9CSJDXD0JIkNcPQkiQ1w9CSJDVjzP9P64lm6uFnrvN8yedmrbdckrTp8EpLktQMQ0uS1AxDS5LUDENLktQMQ0uS1AxDS5LUDENLktQMQ0uS1AxDS5LUDENLktSM5r/Gqf/rl6B+BdNo5ZKkdnmlJUlqxpihFRGnRsStEbFglNcjIk6IiMURcXlE7Dr4bkqSNL4rrdOAmet5fS9gWv7MBr762LslSdIjjRlapZTzgDvWU2Vf4B9KdQGwXUTsMKgOSpLUM4j3tCYDSzvPl2WZJEkD9bh+ECMiZkfE/IiYv3z58sdz0ZKkJ4BBhNaNwI6d51Oy7BFKKSeXUmaUUmYMDQ0NYNGSpM3JIEJrLvDf8lOEuwN3lVJuHkC7kiStY8x/XBwR3wP2BCZFxDLgSGALgFLKScA8YG9gMbAS+KsN1VlJ0uZtzNAqpRw4xusF+B8D65EkSaPwGzEkSc0wtCRJzTC0JEnNMLQkSc0wtCRJzTC0JEnNMLQkSc0wtCRJzTC0JEnNMLQkSc0wtCRJzTC0JEnNMLQkSc0wtCRJzTC0JEnNMLQkSc0wtCRJzTC0JEnNMLQkSc0wtCRJzTC0JEnNMLQkSc0wtCRJzTC0JEnNMLQkSc0wtCRJzTC0JEnNMLQkSc0YV2hFxMyIWBQRiyPi8BFe3ykifhERl0TE5RGx9+C7Kkna3I0ZWhExATgR2AuYDhwYEdP7qv0tcEYp5WXAAcBXBt1RSZLGc6W1G7C4lHJdKWU1cDqwb1+dAmyTj7cFbhpcFyVJqiaOo85kYGnn+TLgFX11jgLOjogPAFsBbxxI7yRJ6hjUBzEOBE4rpUwB9ga+FRGPaDsiZkfE/IiYv3z58gEtWpK0uRhPaN0I7Nh5PiXLug4CzgAopfwKeAowqb+hUsrJpZQZpZQZQ0NDj67HkqTN1nhC6yJgWkTsHBFbUj9oMbevzu+ANwBExAuooeWllCRpoMYMrVLKWuAQ4CzgKuqnBBdGxDERsU9W+wjw3oi4DPge8JellLKhOi1J2jyN54MYlFLmAfP6yuZ0Hl8JvHqwXZMkaV1+I4YkqRmGliSpGYaWJKkZhpYkqRmGliSpGYaWJKkZhpYkqRmGliSpGYaWJKkZhpYkqRmGliSpGYaWJKkZhpYkqRmGliSpGYaWJKkZhpYkqRmGliSpGYaWJKkZhpYkqRmGliSpGYaWJKkZhpYkqRmGliSpGYaWJKkZhpYkqRmGliSpGYaWJKkZhpYkqRnjCq2ImBkRiyJicUQcPkqdd0TElRGxMCK+O9huSpIEE8eqEBETgBOBNwHLgIsiYm4p5cpOnWnAEcCrSykrImL7DdVhSdLmazxXWrsBi0sp15VSVgOnA/v21XkvcGIpZQVAKeXWwXZTkqTxhdZkYGnn+bIs69oF2CUi/iMiLoiImYPqoCRJPWPeHvxPtDMN2BOYApwXES8qpdzZrRQRs4HZADvttNOAFi1J2lyM50rrRmDHzvMpWda1DJhbSllTSrkeuIYaYusopZxcSplRSpkxNDT0aPssSdpMjSe0LgKmRcTOEbElcAAwt6/Oj6lXWUTEJOrtwusG2E9JksYOrVLKWuAQ4CzgKuCMUsrCiDgmIvbJamcBt0fElcAvgI+VUm7fUJ2WJG2exvWeVillHjCvr2xO53EBDssfSZI2CL8RQ5LUDENLktQMQ0uS1AxDS5LUDENLktQMQ0uS1AxDS5LUDENLktQMQ0uS1AxDS5LUDENLktQMQ0uS1AxDS5LUDENLktQMQ0uS1AxDS5LUDENLktQMQ0uS1AxDS5LUDENLktQMQ0uS1AxDS5LUDENLktQMQ0uS1AxDS5LUDENLktQMQ0uS1AxDS5LUjHGFVkTMjIhFEbE4Ig5fT723RUSJiBmD66IkSdWYoRURE4ATgb2A6cCBETF9hHpbA4cCFw66k5IkwfiutHYDFpdSriulrAZOB/Ydod4ngWOBVQPsnyRJDxtPaE0GlnaeL8uyh0XErsCOpZQzB9g3SZLW8Zg/iBERTwK+AHxkHHVnR8T8iJi/fPnyx7poSdJmZjyhdSOwY+f5lCzr2Rp4IXBuRCwBdgfmjvRhjFLKyaWUGaWUGUNDQ4++15KkzdJ4QusiYFpE7BwRWwIHAHN7L5ZS7iqlTCqlTC2lTAUuAPYppczfID2WJG22xgytUspa4BDgLOAq4IxSysKIOCYi9tnQHZQkqWfieCqVUuYB8/rK5oxSd8/H3i1Jkh7Jb8SQJDXD0JIkNcPQkiQ1w9CSJDXD0JIkNcPQkiQ1w9CSJDXD0JIkNcPQkiQ1w9CSJDXD0JIkNcPQkiQ1w9CSJDXD0JIkNcPQkiQ1w9CSJDXD0JIkNcPQkiQ1w9CSJDXD0JIkNcPQkiQ1w9CSJDXD0JIkNcPQkiQ1w9CSJDXD0JIkNcPQkiQ1w9CSJDXD0JIkNWNcoRURMyNiUUQsjojDR3j9sIi4MiIuj4h/iYjnDr6rkqTN3ZihFRETgBOBvYDpwIERMb2v2iXAjFLKi4EfAMcNuqOSJI3nSms3YHEp5bpSymrgdGDfboVSyi9KKSvz6QXAlMF2U5Kk8YXWZGBp5/myLBvNQcBPH0unJEkaycRBNhYR7wZmAK8d5fXZwGyAnXbaaZCLliRtBsZzpXUjsGPn+ZQsW0dEvBH4BLBPKeWBkRoqpZxcSplRSpkxNDT0aPorSdqMjSe0LgKmRcTOEbElcAAwt1shIl4GfI0aWLcOvpuSJI0jtEopa4FDgLOAq4AzSikLI+KYiNgnqx0PPA34x4i4NCLmjtKcJEmP2rje0yqlzAPm9ZXN6Tx+44D7JUnSI/iNGJKkZhhakqRmGFqSpGYYWpKkZhhakqRmGFqSpGYYWpKkZhhakqRmGFqSpGYYWpKkZhhakqRmGFqSpGYYWpKkZhhakqRmGFqSpGYYWpKkZhhakqRmGFqSpGYYWpKkZhhakqRmGFqSpGYYWpKkZhhakqRmGFqSpGYYWpKkZhhakqRmGFqSpGYYWpKkZowrtCJiZkQsiojFEXH4CK8/OSK+n69fGBFTB91RSZLGDK2ImACcCOwFTAcOjIjpfdUOAlaUUp4PfBE4dtAdlSRpPFdauwGLSynXlVJWA6cD+/bV2Rf4Zj7+AfCGiIjBdVOSpPGF1mRgaef5siwbsU4pZS1wF/DMQXRQkqSeKKWsv0LE24GZpZSD8/l7gFeUUg7p1FmQdZbl899mndv62poNzM6nfwgsGtSKAJOA255g5ZtSXzZW+abUl41Vvin1ZWOVb0p92Vjlm1Jf1lf+aD23lDI0Zq1Synp/gFcCZ3WeHwEc0VfnLOCV+XhirkiM1fYgf4D5T7TyTakvbgO3gdvAbTCe8g39M57bgxcB0yJi54jYEjgAmNtXZy7wF/n47cC/llwrSZIGZeJYFUopayPiEOrV1ATg1FLKwog4hpq0c4FTgG9FxGLgDmqwSZI0UGOGFkApZR4wr69sTufxKmD/wXbtP+3kJ2D5ptSXjVW+KfVlY5VvSn3ZWOWbUl82Vvmm1Jf1lW9QY34QQ5KkTYVf4yRJasfG+PTHY/0BdgR+AVwJLAQOzfKXAg8A9wMrgSuz/FTgnnxtIXBcp62PAoX8hCTwHepH8Rfk3/1f4FZgQedvngL8Grgs2zu689oS4ArgUvLTNdSP91/a+bkb+NBo65F/c2j2YSHwoU75dtR/wH0nsBb4bee1/YEVuT6LO+XPAG7M+vcCT8/y7wO3A2uA1cClWf6SXOe12ddtsvwo4L6sfz+wd2e7X9BbZ+o/SJ+Q/b9thP305ezHqvz9d1n+vmy/AL/t1D8q+9/bfidneyuyjYWddf0k9X3VNbnPn5PlJ2Xbq3KdDs/yL+S63J/ln8vy06jH0P3Uf3f4mSz/2ywvwNWdPs7N5a3Kbbksy3+UfVkF/HNnW344216V2+joLP8Ww8fw/cBJWT4z+39//j4hy/+9U74GuDrLX5Xrszr7f1jnWDgnl7scePp61ql3nN+Urx3Xd/7dDjzIusftB7KN+8ljMLdl75i5v7e/qMfrQuCh/JufjLHtu+fm7cCZWR7AZ3Kd7gE+CPyS4eNlDXBL1n09cEm2f0cu/+jOuv4+t//ybOf1+XxV1p/fOXdWZx9XAys7x+razrpe27fdr8390htvdgYuBBZTj+feOr2B4ePgXuCKvjHm5twn3XP2V9Sx5y7gZ331b8t91ev/Jzv9vxu4LMtv6mz7+6jn8vHANdQx557cB6/sW6dzyHFlg4//GzuAHlWnYQdg13y8dW7Q6cDZedBNAvYGzu0EwIUMnyzbd06+q3Pn9A6ivfMkCOB7wN8Du7JuaAXwtHy8Rba9e+cAmbSevk8AbgGeu571eCH1xHwq9X3HnwPPz3rfBA4G9sgD6spO2y8A3kUNjm5oHUcdtHfNZR/beW2PLL8NmJNlF1EHn12p/5j8k50T8sQRtsfZwF6d7XcucBjwQ+C8EdZvWme9P5Ynw3TgNcBb8+/36NQ/Cvho1p8MXA/8QdY5mwyIfH2bzjrdxPCgfwDwJ/n4i9QBaDrwZmDbLD8+/2b3bKe3j78E/C7L3wTMyv383E4fu/vyhNye04Grcn8tAP6aOli8LvfpM7L+c8hjiDpgv7v/2MrlvDzLD6EOqruz7rH4Q2rY704N0V4wn0I9L6ZTj4WfAN/Nvh27nnUK6oTrLOAG4OJsewfqcfYt6oDaq99br49l+2fn8k+jToIm9Z0LL8j2FwM/Yzi0Rtv2vXPzsOzrFVnnr6jH7Hdz3bbvLOOw/PtLqHeWlgK7AE8DjqH+u9HeNn4f8A/Allk2K+svo44pxwAH9YXBJOoYMadzjtwxwroeBxye/bmU4UA/g3psHkYd/HvrdA11ojYJeD9wWqetpcC/5j6Z1DlnX5vtXABc06l/Uy7n3r7zpNf/DzJ8ntwPvLPvXH5zbpeDqcfL8dTJ83EMH2OH0xlXNuRPk7cHSyk3l1J+k4/voZ58k6kzj97XR21L3VkAr6YOJCX/5tYsP4k6K7mn0/a8kqgzr94Mq7v8Ukq5N59ukT/jfXPwDdSroxvWsx4vAC4spaws9RtG/g3YLyK2pQ7Ip5RSzqMOBA91+nVVKeU71Jle177U2eQd1ID4887fnJfl21JDGupJ/X+y/F7gbZ22ru/fHrnu2+TjbakzxlnZxt3961dKuba33tRQXpHlvyyl/CjLV3a2R7+J1NA6nzoJeHh9Syl3d9bpSQzv89NLKRdltfOoE5XJpZSzSyl3Zfl86oy7ZDv35teRbZXLKaWUc0opZ2b9+zrrdHMp5TdZ/63UgWkyNZB+nvXPyW35N9Qrut52vJN1j6FV+bt7bBWGPzj1DOqsuXeo3hsR21BD456s+yrg81n/G9TzYnIuf9ssWwb8+XrWqQCfBv5nZ7sX6nF3cKe8t5/+Jtudmb9Xsx6llKtyec8EftwpH23bz8vlzALOpO4rqIPumlzmw+d3REwB9qFeTf4+l7O6lHJNnr/nAPt1tvFfU4NpYpbdluvQO756+6/fOxg+d0azL3WCNQv4FPDsXL/XU6+QZlHvQDyrtxkYeSyDuv+PZt0xZxfqhGUWdVK2Q26DCbn+R3c7U0q5u/N0q762tu5b7oXUCeUp1EB8TinlTtb9+r5v0hlXNqjHIxk35A8wlTqT2oY62K+lHmirGZ4FXEqdsa2kBsCf5Aa/Fng59erjrL52twB+Q91ZU+lcWeTrE7Lde1n3yuX6/LuLgdkj9PdU4JBxrMc11JPsqdSD+n9Tb8P9mjpzvYT6PZALR2jrAta90rqzs4wFveed198B3N95fj71AJxKvQ1xTxmeRS6hDlIrGL7N+ILs+1Lq7HBebtc9GZ49P7x++fzTWX8RdfDcprP8c4E/62yP3nIvz+13eG735dTBrn/ffJp6sq0ChkbYPj+nDki9vvT25Vrgnzv1TqMOhmuBv+9rYwnwsu46Zfke2c9e38+nzuYXUGfB9+SyjqYOBvdSj8tjO8tcxPBtp89n+Wuot8RWUycqX+w7Fm/Ifh5LnT139/8r8++2yd8P75vusdC/TtRz5ITs70MM35I8FPhwPr6vU/9S6q3g3u3xX3bWqXcr6vfA+zvL/AF1svDfyWNlfds+67+Cen6cn2UPUO8ALKIG6rRO3TnUc/4n1BC4AZiR22w5Nfx72/526vH+IHAd9Y7ADdRj+jfZ9+5V/fXZj/vIc516rPZuhd5G3tqnTkx+0Nn2a3r7qVO+P3B3Z38/2NluH8zyfakT7d/kevdu+56fPy/PbbG2s69uz/oP0hmTsk+99nvtLGN4/FxBvfLujjt3UW8Nb8W6x07QN65ssDH/8VjIBut8vcS/GNgvn58AvDcfH0wdIPagDhin5e/dqCfnIuAbWXek0Po68KV8PJW+gbFTb7vciS/M55Pz9/bU97z26NTdMg/kZ61vPbLsoCw7D/gqNXRnUE/iV2SdU4FbR+jTWKG1oq/+t4GbO8//iDorvIJ6ot6e5c+inuw7UweHUzvb/W35+HPA0ny8J3WweMT6ddb7RuD7feW/pAbjfn3LfRL1VsxNwBB1YnF2b3l9bUzN/Xp0X/nR1JOxvy+fyL4+vC+zfAJ1Bn9VX/kN1IG5v52vU8N4v862PI86MBxJHUAWUCchQT0eb+gtlzpDDuDJ1Ntd12X5Dzv7/e9yG3T781PgPdnOnzJ8++lpDIfoW4AH+vbNipHWiTpZupDhW6e/o75/tkf+nphtP9hZ1xtyWUG91bYyH+9AvUJ6MvW9oFuynbcAX6FOUtYJrZG2faf+16kD/U+ybA3wkVyn+dTjp1f3p9Tg6k2eXpmv/5p6xXNFZ9vfm+1sl+UXZ/0Ls37v/c89euc69dw8kjzXqcfqjtRjtXcbeo9s+yudbb+aGlo3dcq7ofVD4C35+CjqcbNH9uWPOvtkQZa/j3rcXUx9H3419Sr/34Gdsv59dMYkhseqT3X2ySnUq8ntqePkfIbHna9S36P9MvU2d//kd51xZYON+4/HQjZIx+uAdRY5Q8iyuxj+GH9QZyIfpd4vP4AMnty5K3NHrKVeGj8E/CBfP5I6g39SZwAcMbTy9Tnkey595Ud1y8lbBGOtxwjtfIZ6X/vZwJJO+f7kVVBf/f7QWkQdOKZS38Nb1HltInXGefUI7UylziR/PUL5os727G73z+a2XJInwkrqiXnYKOt9DOu+P7YF9UT/wijb4v3AHZ3nh5Ghur4+ZtlB1Nnlx/vq/iX1avapI+1L6sm8iOH31bagDl5/11fvKXnM9QflVOrgsgt18PsZ8LrO67+lhn3/cvfM5X6MdT9wsxN10tDrzyTqoPaU7P/HqJOjp+Q2/nL+/ix1gF/a2Td3jbROwItyGUvyZ21uu+Pzb5dk/YcYDsjf5rHU2/cPAf84wjpdQz0vP0ud2fc+jLIS+PZo2z7r35X1ev1fkOu0rFO2OuveRA3VW0Zp+83U93rmZPtXAzt3zun7R6i/sLPdJ1IndVPoO9c7+/3WbPv27M8Shm/rfyf7tSzLex9sOXOE/b2cOlnp3yd3USdin+2007uCXNHZV0tymXeM0M+HjyfWPZePyn3z7Nw/vXPkNdnHRcAOWXcHOuPKhvxp8j2tvBd8CnBVKeULnZdupt5Ph/om4hrqQf1j6oyJiNglX9+qlDKxlNI78M4ppbw9Ig4G/itwYCnlIUYQEUMRsV0+/gPqG9lXR8RWEbF1lm9FPcgXdP70QDr3vtezHkTE9vl7J+rM97ullFuApRHxh1nt1Qy//7E+3a/Z2g74p85rb6QONg+/L9RbNjX4h6jv/RERO3T+bpvOut1EfRMY6q23S0opU6kThTuAM7rrFxHTeutNBmbf9lhJvcro1e8udxowISKemvVfRQ2Kbts9W3fankm9Svt+KeXYTv13Um837kOdvLwJWBQRu0XEdrmM/ai3Q67u9HENdebZayeoM//lpZQjR9iWUD+ldxL1eJyV7e9CvQJ5dbb/x53lvo06SFwFbBcRL8929qbO5K+OiCHqFVbv9tebsv4vqFehV2X7/1RKOYJ6xX5i7psbgK+NtE6llCuAPwZemvvyxmzr36gD1HnA16ifmnt+9ut44KtZ/6O5X94REc+LiK1zOW/PdVpQSjmilDKFOsn6BPXDBe8ZbdtTj9OF1NvmB1C/Lu6FuV/nZNll1E/CHUGdEH27U/fdEbF9nr/bAx+n3q14E/UY/inwujyn304916Zm359M/d7VAizI8/vPsl8ryHO9t665Pd6Z+6n3SeQTctv8GLiulPIu6uD/0Sz/D+rt1X2BbSPiZdnOrGznIupdjhd19skiaph8Mbfl86i3AheUUp4O/JdO/ZXZ3wUR8eJOP/fP9q+lhtxrc/32p05wXkp9D/nDpZSV1Pflr2TdceUvWHdc2XAej2Qc9A/19keh3orofax1b+p7M72Pa64ETsz638+yQp2FHd/X3sO3B8mPkXfavZwahr3Z3EHAi6nvKV1OPSB7nxx6HnnSUE+uT3SWsRV1JrXtWOuRr/2SemBcBryh8zcvpV6y30kNrG6/3srwR5dLvn4Q9SS/JdftIerBflC2dx11dtVt51DqByjWUmdsvfJv5XLXZDs3ZfmfUm9LXEa9fdH7lNsHRtlP52Z57yPNC7P8fzF81bsm+7B3LveKbGcudZC6Ovuysq/v/2+UPi7rLLP3fsPe1FssqzvlF1FP4EsY/kj6ncCnc52+1Nm+a3Kf7t3Zl8v61vVihq/m786+bEkNmd4yr2f4GLqwb7mfyvIPderfR76nRT0W78n92D0W35HLfCDbuSz780zgX7Kfy6lv6o+2Tt3jfDXD/xyge9w+2FnXLakhsYB6NfWrrH9+p+93Mvzx/rdmPx6gTm5uHWPbd8/NxeTMnjoROzO3wQrgJVl+LnUSuyfDtwePz3qrqMfGAmrgvTjX5+58bSn1Y+Qn5/NV1HHgE51z/Y7s/8JO+Y8663o38Nks7233a6nHxFmddn6d63MuMC/L39e3vz8/whizurNtDs1tfg35KcoR6j/Y6efPOu3fQ30v9HnZj5WdbfDyLOtdrfaO16f3rdPDn4bd0D9+I4YkqRlN3h6UJG2eDC1JUjMMLUlSMwwtSVIzDC1JUjMMLUlSMwwtSVIzDC1JUjP+P/cYT/ivslEgAAAAAElFTkSuQmCC\n",
+      "text/plain": [
+       "<Figure size 432x288 with 1 Axes>"
+      ]
+     },
+     "metadata": {
+      "needs_background": "light"
+     },
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "ic = ioc_scan_n(scb, max_key_length=60)\n",
+    "plot_frequency_histogram(ic, sort_key=ic.get)"
+   ]
+  },
   {
    "cell_type": "code",
    "execution_count": null,