Implemented polybius ciphers, updated challenge 3
authorNeil Smith <neil.git@njae.me.uk>
Tue, 24 Oct 2017 09:37:04 +0000 (10:37 +0100)
committerNeil Smith <neil.git@njae.me.uk>
Tue, 24 Oct 2017 09:37:04 +0000 (10:37 +0100)
2017/2017-challenge3.ipynb
2017/polybius-development.ipynb [new file with mode: 0644]
cipher.py
cipherbreak.py

index dc537775210d3c9bb733de1038ebdec5c2e23edf..715c7f2eb5aa66144bf450db4123ec21b8878c0f 100644 (file)
@@ -2,7 +2,7 @@
  "cells": [
   {
    "cell_type": "code",
-   "execution_count": 47,
+   "execution_count": 116,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -22,7 +22,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 48,
+   "execution_count": 117,
    "metadata": {},
    "outputs": [
     {
@@ -31,7 +31,7 @@
        "(24, -2521.8292883073464)"
       ]
      },
-     "execution_count": 48,
+     "execution_count": 117,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -43,7 +43,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 49,
+   "execution_count": 118,
    "metadata": {
     "scrolled": true
    },
@@ -77,7 +77,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 50,
+   "execution_count": 119,
    "metadata": {},
    "outputs": [
     {
@@ -86,7 +86,7 @@
        "((7, 22, True), -2146.2748427553324)"
       ]
      },
-     "execution_count": 50,
+     "execution_count": 119,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -98,7 +98,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 51,
+   "execution_count": 120,
    "metadata": {},
    "outputs": [
     {
   },
   {
    "cell_type": "code",
-   "execution_count": 52,
+   "execution_count": 121,
    "metadata": {},
    "outputs": [
     {
        "2163"
       ]
      },
-     "execution_count": 52,
+     "execution_count": 121,
      "metadata": {},
      "output_type": "execute_result"
     }
   },
   {
    "cell_type": "code",
-   "execution_count": 53,
+   "execution_count": 122,
    "metadata": {},
    "outputs": [
     {
        "Counter({'c': 1064, 'd': 1368, 'l': 756, 'm': 566, 'x': 1092})"
       ]
      },
-     "execution_count": 53,
+     "execution_count": 122,
      "metadata": {},
      "output_type": "execute_result"
     }
   },
   {
    "cell_type": "code",
-   "execution_count": 54,
+   "execution_count": 123,
    "metadata": {},
    "outputs": [
     {
        " ('ll', 45),\n",
        " ('lx', 41),\n",
        " ('md', 36),\n",
-       " ('xl', 27),\n",
        " ('mx', 27),\n",
+       " ('xl', 27),\n",
        " ('lm', 12),\n",
        " ('mc', 5),\n",
        " ('dx', 1)]"
       ]
      },
-     "execution_count": 54,
+     "execution_count": 123,
      "metadata": {},
      "output_type": "execute_result"
     }
   },
   {
    "cell_type": "code",
-   "execution_count": 55,
+   "execution_count": 183,
    "metadata": {
     "scrolled": true
    },
        "'etoainhsrdlumwycfgpbvkxqz'"
       ]
      },
-     "execution_count": 55,
+     "execution_count": 183,
      "metadata": {},
      "output_type": "execute_result"
     }
   },
   {
    "cell_type": "code",
-   "execution_count": 56,
+   "execution_count": 184,
    "metadata": {},
    "outputs": [
     {
      "data": {
       "text/plain": [
-       "'etoanisrhdlufcmwgybpvkxqz'"
+       "'etoanisrhdlufcmwgbypvkxqz'"
       ]
      },
-     "execution_count": 56,
+     "execution_count": 184,
      "metadata": {},
      "output_type": "execute_result"
     }
   },
   {
    "cell_type": "code",
-   "execution_count": 77,
+   "execution_count": 185,
    "metadata": {},
    "outputs": [],
    "source": [
   },
   {
    "cell_type": "code",
-   "execution_count": 78,
+   "execution_count": 186,
    "metadata": {},
    "outputs": [
     {
        " 'll',\n",
        " 'lx',\n",
        " 'md',\n",
-       " 'xl',\n",
        " 'mx',\n",
+       " 'xl',\n",
        " 'lm',\n",
        " 'mc',\n",
        " 'dx']"
       ]
      },
-     "execution_count": 78,
+     "execution_count": 186,
      "metadata": {},
      "output_type": "execute_result"
     }
   },
   {
    "cell_type": "code",
-   "execution_count": 79,
+   "execution_count": 187,
    "metadata": {},
    "outputs": [
     {
        "24"
       ]
      },
-     "execution_count": 79,
+     "execution_count": 187,
      "metadata": {},
      "output_type": "execute_result"
     }
   },
   {
    "cell_type": "code",
-   "execution_count": 80,
+   "execution_count": 188,
    "metadata": {},
    "outputs": [
     {
        " 'mc': 'X',\n",
        " 'md': 'B',\n",
        " 'ml': 'M',\n",
-       " 'mx': 'V',\n",
+       " 'mx': 'P',\n",
        " 'xc': 'U',\n",
        " 'xd': 'D',\n",
-       " 'xl': 'P',\n",
+       " 'xl': 'V',\n",
        " 'xm': 'E',\n",
        " 'xx': 'O'}"
       ]
      },
-     "execution_count": 80,
+     "execution_count": 188,
      "metadata": {},
      "output_type": "execute_result"
     }
   },
   {
    "cell_type": "code",
-   "execution_count": 81,
+   "execution_count": 189,
    "metadata": {},
    "outputs": [
     {
        " ...]"
       ]
      },
-     "execution_count": 81,
+     "execution_count": 189,
      "metadata": {},
      "output_type": "execute_result"
     }
   },
   {
    "cell_type": "code",
-   "execution_count": 82,
+   "execution_count": 190,
    "metadata": {},
    "outputs": [
     {
      "data": {
       "text/plain": [
-       "'MHALEATASTIFETHOTSFETNRAFSSETFWTHEYAROLPOTTLEMATHPNFDAUUOOTMOTLARGSTIEETHEHODREATHEITHETINNWSRNITHETOUTAUOLOPALATBTNNVEIUNCETHEHFGERFCEIAUOLDASODVORTOGEYOUEDPBHASOICBMATHNFTTHESFWWNITNYOGIAUNLOORDHASTOLERTEDSUNFTSDEYEOTMNFLDHOVEPEERAREVATOPLEORDMATHATTHELNSSNYPIATORRAOHOVARGIEOLASEDTHOTTHEAUERAMEIEIEODARGHASUNCCFRAUOTANRSOGIAUNLOOIIORGEDTNWLORTYOLSEARYNICOTANROPNFTTHEWLORREDDASWNSATANRNYTINNWSSETTARGOTIOWARTNMHAUHTHEAUERAYELLATASSTALLFRULEOIHNMTHESOVOGETIAPESHODLEOIREDTNDEUAWHEINFIOICBSUNCCFRAUOTANRSOSTHEBHODRNMIATARGNYTHEAINMRHNMEVEIEVEIBOICBHOSATSTIOATNISORDATSEECSLAKELBTNCETHOTNRENICNIENYTHELNUOLTIAPESWENWLETFIREDSNCENRESHEODLNVENICNREBATASOLLTIEOUHEIBPFTTHASOUTTHIEOTEREDTNFRDEICARETHEECWAIESERTAIESBSTECNYSEUFIEUNCCFRAUOTANRTHEUOESOIUAWHEIWEIHOWSCNIETHORINODSHODEROPLEDTHEECWAIETNYFRUTANRSEUFIELBORDATSLNSSMOSODASOSTEITFIRARGTHOTLNSSARTNOVAUTNIBNRTHEPOTTLEYAELDDESEIVEDCNIEIEUNGRATANRTHORATIEUEAVEDWEIHOWSSFETNRAFSREVEIKREMNIWEIHOWSHEYOALEDTNFRDEISTORDTHEUFRRARGNYTHEWLORPFTEATHEIMOBDESWATETHEVAUTNIBOGIAUNLOORDTHERARTHLEGANRIECOAREDARDASGIOUENRHEOIARGTHASREMSULOFDAFSUOESOIOFGFSTFSGEICORAUFSREINECWEINIASSFEDOSEUIETWINULOCOTANRESTOPLASHARGOUAWHEISUHNNLTNDEVELNWOREMSBSTECNYACWEIAOLUAWHEISATMNIKEDARSEUIETYNINVEITMERTBBEOISDEVELNWARGREMCETHNDSTNSEUFIEUNCCFRAUOTANRSOUINSSTHEECWAIESTFDBARGTHEMNIKSNYTHEGIEEKSOGESEFULADORDHBWOTAOORDEVERNLDEIMNIKSYINCORUAERTPOPBLNRTHESUHNNLDEVELNWEDREMMOBSTNSOYEGFOIDUNCCFRAUOTANRSYINCINCESERECAESTHESEREMSBSTECSMEIEMIATTERDNMRORDDASTIAPFTEDARTHEUNDEXNUUFLTNIFCWEIHOWSTHECNSTSTIAUTLBGFOIDEDDNUFCERTAROLLHASTNIBMHERATMOSYAROLLBUNCWLETEDCORBBEOISLOTEITHEECWEINIDNCATAORASSFEDOREXEUFTAVENIDEIMHAUHWIEYOUEDEVEIBUNWBNYTHEUNDEXTHOTATMOSTNPEGFOIDEDPBEVEIBLEGANRTNTHELOSTCORTHEDEYEOTNYTHEAUERAORDTHESFAUADENYPNFDAUUOWEIHOWSWINTEUTEDTHERARTHLEGANRYINCDASSNLFTANROGIAUNLOMOSLOIGELBLEYTTNHASNMRDEVAUESARTHEWINVARUETHNFGHATMOSCODEULEOITNHACTHOTTNIETFIRMNFLDCEOROTPESTDASGIOUEORDOTMNISTDEOTHAROPIEOKMATHTIODATANRTHERARTHIOASEDOREMSTORDOIDTHELEGANRMOSEXALEDARDASGIOUETNERDFIETHEHOIDORDDORGEINFSMNIKNYSFPDFARGTHEUOLEDNRAAARTHEPLEOKMALDEIRESSESNYUOLEDNRAOSFETNRAFSNIDEIEDTHERARTHTNCOIUHTNEPNIOUFCMHAUHTHEBMEIETNCOKETHEAIPOSEYNIIOADSARTNUOLEDNRAOTHEFRSWNKERIEOSNRYNITHEAIEXALEMOSTHOTARTELLAGERUEIEWNITSSFGGESTEDTHOTTHASMOSMHEIETHEBCAGHTYARDTHEAILNSTOQFALOTHNSEMHNMNFLDIEODNRCFSTYNLLNMCBYOATHYFLSLOVETAINMHNYOUESOGIOVETOSKWEIHOWSTHELOIGESTTNDOTEOSHETIOVELSTNUNRUEOLTHEYNFITHUHOWTEINYTHASTIOGAUTOLE'"
+       "'MHALEATASTIFETHOTSFETNRAFSSETFWTHEYAROLVOTTLEMATHVNFDAUUOOTMOTLARGSTIEETHEHODREATHEITHETINNWSRNITHETOUTAUOLOVALATBTNNPEIUNCETHEHFGERFCEIAUOLDASODPORTOGEYOUEDVBHASOICBMATHNFTTHESFWWNITNYOGIAUNLOORDHASTOLERTEDSUNFTSDEYEOTMNFLDHOPEVEERAREPATOVLEORDMATHATTHELNSSNYVIATORRAOHOPARGIEOLASEDTHOTTHEAUERAMEIEIEODARGHASUNCCFRAUOTANRSOGIAUNLOOIIORGEDTNWLORTYOLSEARYNICOTANROVNFTTHEWLORREDDASWNSATANRNYTINNWSSETTARGOTIOWARTNMHAUHTHEAUERAYELLATASSTALLFRULEOIHNMTHESOPOGETIAVESHODLEOIREDTNDEUAWHEINFIOICBSUNCCFRAUOTANRSOSTHEBHODRNMIATARGNYTHEAINMRHNMEPEIEPEIBOICBHOSATSTIOATNISORDATSEECSLAKELBTNCETHOTNRENICNIENYTHELNUOLTIAVESWENWLETFIREDSNCENRESHEODLNPENICNREBATASOLLTIEOUHEIBVFTTHASOUTTHIEOTEREDTNFRDEICARETHEECWAIESERTAIESBSTECNYSEUFIEUNCCFRAUOTANRTHEUOESOIUAWHEIWEIHOWSCNIETHORINODSHODEROVLEDTHEECWAIETNYFRUTANRSEUFIELBORDATSLNSSMOSODASOSTEITFIRARGTHOTLNSSARTNOPAUTNIBNRTHEVOTTLEYAELDDESEIPEDCNIEIEUNGRATANRTHORATIEUEAPEDWEIHOWSSFETNRAFSREPEIKREMNIWEIHOWSHEYOALEDTNFRDEISTORDTHEUFRRARGNYTHEWLORVFTEATHEIMOBDESWATETHEPAUTNIBOGIAUNLOORDTHERARTHLEGANRIECOAREDARDASGIOUENRHEOIARGTHASREMSULOFDAFSUOESOIOFGFSTFSGEICORAUFSREINECWEINIASSFEDOSEUIETWINULOCOTANRESTOVLASHARGOUAWHEISUHNNLTNDEPELNWOREMSBSTECNYACWEIAOLUAWHEISATMNIKEDARSEUIETYNINPEITMERTBBEOISDEPELNWARGREMCETHNDSTNSEUFIEUNCCFRAUOTANRSOUINSSTHEECWAIESTFDBARGTHEMNIKSNYTHEGIEEKSOGESEFULADORDHBWOTAOORDEPERNLDEIMNIKSYINCORUAERTVOVBLNRTHESUHNNLDEPELNWEDREMMOBSTNSOYEGFOIDUNCCFRAUOTANRSYINCINCESERECAESTHESEREMSBSTECSMEIEMIATTERDNMRORDDASTIAVFTEDARTHEUNDEXNUUFLTNIFCWEIHOWSTHECNSTSTIAUTLBGFOIDEDDNUFCERTAROLLHASTNIBMHERATMOSYAROLLBUNCWLETEDCORBBEOISLOTEITHEECWEINIDNCATAORASSFEDOREXEUFTAPENIDEIMHAUHWIEYOUEDEPEIBUNWBNYTHEUNDEXTHOTATMOSTNVEGFOIDEDVBEPEIBLEGANRTNTHELOSTCORTHEDEYEOTNYTHEAUERAORDTHESFAUADENYVNFDAUUOWEIHOWSWINTEUTEDTHERARTHLEGANRYINCDASSNLFTANROGIAUNLOMOSLOIGELBLEYTTNHASNMRDEPAUESARTHEWINPARUETHNFGHATMOSCODEULEOITNHACTHOTTNIETFIRMNFLDCEOROTVESTDASGIOUEORDOTMNISTDEOTHAROVIEOKMATHTIODATANRTHERARTHIOASEDOREMSTORDOIDTHELEGANRMOSEXALEDARDASGIOUETNERDFIETHEHOIDORDDORGEINFSMNIKNYSFVDFARGTHEUOLEDNRAAARTHEVLEOKMALDEIRESSESNYUOLEDNRAOSFETNRAFSNIDEIEDTHERARTHTNCOIUHTNEVNIOUFCMHAUHTHEBMEIETNCOKETHEAIVOSEYNIIOADSARTNUOLEDNRAOTHEFRSWNKERIEOSNRYNITHEAIEXALEMOSTHOTARTELLAGERUEIEWNITSSFGGESTEDTHOTTHASMOSMHEIETHEBCAGHTYARDTHEAILNSTOQFALOTHNSEMHNMNFLDIEODNRCFSTYNLLNMCBYOATHYFLSLOPETAINMHNYOUESOGIOPETOSKWEIHOWSTHELOIGESTTNDOTEOSHETIOPELSTNUNRUEOLTHEYNFITHUHOWTEINYTHASTIOGAUTOLE'"
       ]
      },
-     "execution_count": 82,
+     "execution_count": 190,
      "metadata": {},
      "output_type": "execute_result"
     }
   },
   {
    "cell_type": "code",
-   "execution_count": 83,
+   "execution_count": 192,
    "metadata": {},
    "outputs": [
     {
        "'while it is true that suetonius setup the final battle with boudicca at watling street he had neither the troops nor the tactical ability to overcome the huge numerical disadvantage faced by his army without the support of agricola and his talented scouts defeat would have been inevitable and with it the loss of britannia having realised that the iceni were reading his communications agricola arranged to plant false information about the planned disposition of troops setting a trap into which the iceni fell it is still unclear how the savage tribes had learned to decipher our army s communications as they had no writing of their own however every army has its traitors and it seems likely to me that one or more of the local tribespeople turned someones head love or money it is all treachery but this act threatened to undermine the empires entire system of secure communication the caesar cipher perhaps more than roads had enabled the empire to function securely and its loss was a disaster turning that loss into a victory on the battlefield deserved more recognition than it received perhaps suetonius never knew or perhaps he failed to understand the cunning of the plan but either way despite the victory agricola and the ninth legion remained in disgrace on hearing this news claudius caesar augustus germanicus nero emperor issued a secret proclamation establishing a cipher school to develop a new system of imperial ciphers it worked in secret for over twenty years developing new methods to secure communications across the empire studying the works of the greeks ages euclid and hypatia and even older works from ancient babylon the school developed new ways to safeguard communications from rome s enemies these new systems were written down and distributed in the codex occult orum perhaps the most strictly guarded document in all history when it was finally completed many years later the emperor domitian issued an executive order which prefaced every copy of the codex that it was to be guarded by every legion to the last man the defeat of the iceni and the suicide of boudicca perhaps protected the ninth legion from dissolution agricola was largely left to his own devices in the province though it was made clear to him that to return would mean at best disgrace and at worst death in a break with tradition the ninth raised a new standard the legion was exiled in disgrace to endure the hard and dangerous work of subduing the caledon ii in the bleak wildernesses of caledonia suetonius ordered the ninth to march toe bora cum which they were to make their base for raids into caledonia the unspoken reason for their exile was that intelligence reports suggested that this was where they might find their lost aquila those who would read on must follow my faithful slave tiro who faces a grave task perhaps the largest to date a she travels to conceal the fourth chapter of this tragic tale'"
       ]
      },
-     "execution_count": 83,
+     "execution_count": 192,
      "metadata": {},
      "output_type": "execute_result"
     }
     "tr['H'] = 'h'\n",
     "tr['S'] = 's'\n",
     "tr['M'] = 'w'\n",
-    "tr['P'] = 'b'\n",
+    "tr['P'] = 'v'\n",
     "tr['L'] = 'l'\n",
     "tr['Y'] = 'f'\n",
     "tr['R'] = 'n'\n",
     "tr['G'] = 'g'\n",
     "tr['F'] = 'u'\n",
     "tr['B'] = 'y'\n",
-    "tr['V'] = 'v'\n",
+    "tr['V'] = 'b'\n",
     "tr['N'] = 'o'\n",
     "tr['D'] = 'd'\n",
     "tr['U'] = 'c'\n",
   },
   {
    "cell_type": "code",
-   "execution_count": 84,
+   "execution_count": 193,
    "metadata": {},
    "outputs": [
     {
        "2922"
       ]
      },
-     "execution_count": 84,
+     "execution_count": 193,
      "metadata": {},
      "output_type": "execute_result"
     }
   },
   {
    "cell_type": "code",
-   "execution_count": 85,
+   "execution_count": 194,
    "metadata": {},
    "outputs": [
     {
        " 'M': 'w',\n",
        " 'N': 'o',\n",
        " 'O': 'a',\n",
-       " 'P': 'b',\n",
+       " 'P': 'v',\n",
        " 'Q': 'q',\n",
        " 'R': 'n',\n",
        " 'S': 's',\n",
        " 'T': 't',\n",
        " 'U': 'c',\n",
-       " 'V': 'v',\n",
+       " 'V': 'b',\n",
        " 'W': 'p',\n",
        " 'X': 'x',\n",
        " 'Y': 'f'}"
       ]
      },
-     "execution_count": 85,
+     "execution_count": 194,
      "metadata": {},
      "output_type": "execute_result"
     }
   },
   {
    "cell_type": "code",
-   "execution_count": 86,
+   "execution_count": 195,
    "metadata": {},
    "outputs": [
     {
        " 'xx': 'a'}"
       ]
      },
-     "execution_count": 86,
+     "execution_count": 195,
      "metadata": {},
      "output_type": "execute_result"
     }
   },
   {
    "cell_type": "code",
-   "execution_count": 87,
+   "execution_count": 196,
    "metadata": {},
    "outputs": [],
    "source": [
   },
   {
    "cell_type": "code",
-   "execution_count": 88,
+   "execution_count": 197,
    "metadata": {},
    "outputs": [
     {
   },
   {
    "cell_type": "code",
-   "execution_count": 90,
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "0 abcdefghijklmnopqrsutvwxyz -6521.8056961355105 mhaleauasuifeuhousfeunrafsseufwuheyarolpouulemauhpnfdattooumoulargsuieeuhehodreauheiuheuinnwsrniuheuotuatolopalaubunnveitnceuhehfgerfceiatoldasodvoruogeyotedpbhasoicbmauhnfuuhesfwwniunyogiatnloordhasuoleruedstnfusdeyeoumnfldhovepeerarevauopleordmauhauuhelnssnypiauorraohovargieolaseduhouuheaterameieieodarghastnccfratouanrsogiatnlooiiorgedunwloruyolsearynicouanropnfuuhewlorreddaswnsauanrnyuinnwsseuuargouiowarunmhathuheaterayellauassuallfrtleoihnmuhesovogeuiapeshodleoiredundetawheinfioicbstnccfratouanrsosuhebhodrnmiauargnyuheainmrhnmeveieveiboicbhosausuioaunisordauseecslakelbunceuhounrenicnienyuhelntoluiapeswenwleufiredsncenresheodlnvenicnrebauasolluieotheibpfuuhasotuuhieoueredunfrdeicareuheecwaieseruaiesbsuecnysetfietnccfratouanruhetoesoitawheiweihowscnieuhorinodshoderopleduheecwaieunyfrtuanrsetfielbordauslnssmosodasosueiufirarguhoulnssarunovatunibnruhepouuleyaelddeseivedcnieietngrauanruhorauieteavedweihowssfeunrafsreveikremniweihowsheyoaledunfrdeisuorduhetfrrargnyuhewlorpfueauheimobdeswaueuhevatunibogiatnloorduheraruhleganriecoaredardasgiotenrheoiarguhasremstlofdafstoesoiofgfsufsgeicoratfsreinecweiniassfedosetieuwintlocouanresuoplashargotawheisthnnlundevelnworemsbsuecnyacweiaoltawheisaumnikedarsetieuyninveiumerubbeoisdevelnwargremceuhndsunsetfietnccfratouanrsotinssuheecwaiesufdbarguhemniksnyuhegieeksogeseftladordhbwouaoordevernldeimniksyincortaerupopblnruhesthnnldevelnwedremmobsunsoyegfoidtnccfratouanrsyincinceserecaesuheseremsbsuecsmeiemiauuerdnmrorddasuiapfuedaruhetndexnttflunifcweihowsuhecnsusuiatulbgfoideddntfceruarollhasunibmheraumosyarollbtncwleuedcorbbeoisloueiuheecweinidncauaorassfedorexetfuavenideimhathwieyotedeveibtnwbnyuhetndexuhouaumosunpegfoidedpbeveibleganrunuhelosucoruhedeyeounyuheateraorduhesfatadenypnfdattoweihowswinuetueduheraruhleganryincdassnlfuanrogiatnlomosloigelbleyuunhasnmrdevatesaruhewinvarteuhnfghaumoscodetleoiunhacuhouunieufirmnfldceoroupesudasgioteordoumnisudeouharopieokmauhuiodauanruheraruhioasedoremsuordoiduheleganrmosexaledardasgioteunerdfieuhehoidorddorgeinfsmniknysfpdfarguhetolednraaaruhepleokmaldeiressesnytolednraosfeunrafsnideieduheraruhuncoithunepniotfcmhathuhebmeieuncokeuheaiposeyniioadsaruntolednraouhefrswnkerieosnryniuheaiexalemosuhouaruellagerteiewniussfggesueduhouuhasmosmheieuhebcaghuyarduheailnsuoqfalouhnsemhnmnfldieodnrcfsuynllnmcbyoauhyflsloveuainmhnyotesogioveuoskweihowsuheloigesuundoueosheuiovelsuntnrteoluheynfiuhthowueinyuhasuiogatuole\n",
-      "0 aocdefghijklmnbpqrstuvwxyz -6521.891540991668 mhaleatastifethbtsfetnrafssetfwtheyarblpbttlemathpnfdauubbtmbtlargstieethehbdreatheithetinnwsrnithetbutaublbpalatotnnveiuncethehfgerfceiaubldasbdvbrtbgeybuedpohasbicomathnftthesfwwnitnybgiaunlbbrdhastblertedsunftsdeyebtmnfldhbvepeerarevatbplebrdmathatthelnssnypiatbrrabhbvargieblasedthbttheauerameieiebdarghasunccfraubtanrsbgiaunlbbiibrgedtnwlbrtyblsearynicbtanrbpnftthewlbrreddaswnsatanrnytinnwssettargbtibwartnmhauhtheauerayellatasstallfrulebihnmthesbvbgetiapeshbdlebiredtndeuawheinfibicosunccfraubtanrsbstheohbdrnmiatargnytheainmrhnmeveieveiobicohbsatstibatnisbrdatseecslakelotncethbtnrenicnienythelnubltiapeswenwletfiredsncenreshebdlnvenicnreoatasblltiebuheiopftthasbutthiebteredtnfrdeicaretheecwaiesertaiesostecnyseufieunccfraubtanrtheubesbiuawheiweihbwscniethbrinbdshbderbpledtheecwaietnyfrutanrseufielobrdatslnssmbsbdasbsteitfirargthbtlnssartnbvautnionrthepbttleyaelddeseivedcnieieungratanrthbratieueavedweihbwssfetnrafsreveikremniweihbwsheybaledtnfrdeistbrdtheufrrargnythewlbrpfteatheimbodeswatethevautniobgiaunlbbrdtherarthleganriecbaredardasgibuenrhebiargthasremsulbfdafsubesbibfgfstfsgeicbraufsreinecweiniassfedbseuietwinulbcbtanrestbplashargbuawheisuhnnltndevelnwbremsostecnyacweiabluawheisatmnikedarseuietyninveitmertooebisdevelnwargremcethndstnseufieunccfraubtanrsbuinsstheecwaiestfdoargthemniksnythegieeksbgesefuladbrdhowbtabbrdevernldeimniksyincbruaertpbpolnrthesuhnnldevelnwedremmbostnsbyegfbidunccfraubtanrsyincinceserecaestheseremsostecsmeiemiatterdnmrbrddastiapftedartheundexnuufltnifcweihbwsthecnststiautlogfbideddnufcertarbllhastniomheratmbsyarbllouncwletedcbrooebislbteitheecweinidncatabrassfedbrexeuftavenideimhauhwieybuedeveiounwonytheundexthbtatmbstnpegfbidedpoeveioleganrtnthelbstcbrthedeyebtnytheauerabrdthesfauadenypnfdauubweihbwswinteutedtherarthleganryincdassnlftanrbgiaunlbmbslbigeloleyttnhasnmrdevauesarthewinvaruethnfghatmbscbdeulebitnhacthbttnietfirmnfldcebrbtpestdasgibuebrdbtmnistdebtharbpiebkmathtibdatanrtherarthibasedbremstbrdbidtheleganrmbsexaledardasgibuetnerdfiethehbidbrddbrgeinfsmniknysfpdfargtheublednraaartheplebkmaldeiressesnyublednrabsfetnrafsnideiedtherarthtncbiuhtnepnibufcmhauhtheomeietncbketheaipbseyniibadsartnublednrabthefrswnkeriebsnrynitheaiexalembsthbtartellagerueiewnitssfggestedthbtthasmbsmheietheocaghtyardtheailnstbqfalbthnsemhnmnfldiebdnrcfstynllnmcoybathyflslbvetainmhnybuesbgibvetbskweihbwsthelbigesttndbtebshetibvelstnunruebltheynfithuhbwteinythastibgautble\n",
-      "0 abcdefghijklmnopqwstuvrxyz -6436.025956653204 mhaleatastifethotsfetnwafssetfrtheyawolpottlemathpnfdauuootmotlawgstieethehodweatheithetinnrswnithetoutauolopalatbtnnveiuncethehfgewfceiauoldasodvowtogeyouedpbhasoicbmathnftthesfrrnitnyogiaunloowdhastolewtedsunftsdeyeotmnfldhovepeewawevatopleowdmathatthelnssnypiatowwaohovawgieolasedthottheauewameieieodawghasunccfwauotanwsogiaunlooiiowgedtnrlowtyolseawynicotanwopnfttherlowweddasrnsatanwnytinnrssettawgotiorawtnmhauhtheauewayellatasstallfwuleoihnmthesovogetiapeshodleoiwedtndeuarheinfioicbsunccfwauotanwsosthebhodwnmiatawgnytheainmwhnmeveieveiboicbhosatstioatnisowdatseecslakelbtncethotnwenicnienythelnuoltiapesrenrletfiwedsncenwesheodlnvenicnwebatasolltieouheibpftthasoutthieotewedtnfwdeicawetheecraiesewtaiesbstecnyseufieunccfwauotanwtheuoesoiuarheireihorscniethowinodshodewopledtheecraietnyfwutanwseufielbowdatslnssmosodasosteitfiwawgthotlnssawtnovautnibnwthepottleyaelddeseivedcnieieungwatanwthowatieueavedreihorssfetnwafsweveikwemnireihorsheyoaledtnfwdeistowdtheufwwawgnytherlowpfteatheimobdesratethevautnibogiaunloowdthewawthleganwiecoawedawdasgiouenwheoiawgthaswemsulofdafsuoesoiofgfstfsgeicowaufsweinecreiniassfedoseuietrinulocotanwestoplashawgouarheisuhnnltndevelnrowemsbstecnyacreiaoluarheisatmnikedawseuietyninveitmewtbbeoisdevelnrawgwemcethndstnseufieunccfwauotanwsouinsstheecraiestfdbawgthemniksnythegieeksogesefuladowdhbrotaoowdevewnldeimniksyincowuaewtpopblnwthesuhnnldevelnredwemmobstnsoyegfoidunccfwauotanwsyincincesewecaesthesewemsbstecsmeiemiattewdnmwowddastiapftedawtheundexnuufltnifcreihorsthecnststiautlbgfoideddnufcewtawollhastnibmhewatmosyawollbuncrletedcowbbeoisloteitheecreinidncataowassfedowexeuftavenideimhauhrieyouedeveibunrbnytheundexthotatmostnpegfoidedpbeveibleganwtnthelostcowthedeyeotnytheauewaowdthesfauadenypnfdauuoreihorsrinteutedthewawthleganwyincdassnlftanwogiaunlomosloigelbleyttnhasnmwdevauesawtherinvawuethnfghatmoscodeuleoitnhacthottnietfiwmnfldceowotpestdasgioueowdotmnistdeothawopieokmathtiodatanwthewawthioasedowemstowdoidtheleganwmosexaledawdasgiouetnewdfiethehoidowddowgeinfsmniknysfpdfawgtheuolednwaaawthepleokmaldeiwessesnyuolednwaosfetnwafsnideiedthewawthtncoiuhtnepnioufcmhauhthebmeietncoketheaiposeyniioadsawtnuolednwaothefwsrnkewieosnwynitheaiexalemosthotawtellagewueiernitssfggestedthotthasmosmheiethebcaghtyawdtheailnstoqfalothnsemhnmnfldieodnwcfstynllnmcbyoathyflslovetainmhnyouesogiovetoskreihorstheloigesttndoteoshetiovelstnunwueoltheynfithuhorteinythastiogautole\n",
-      "0 abndefghijklmcopqrstuvwxyz -6281.062482373658 mhaleatastifethotsfetcrafssetfwtheyarolpottlemathpcfdauuootmotlargstieethehodreatheitheticcwsrcithetoutauolopalatbtccveiucnethehfgerfneiauoldasodvortogeyouedpbhasoinbmathcftthesfwwcitcyogiaucloordhastolertedsucftsdeyeotmcfldhovepeerarevatopleordmathatthelcsscypiatorraohovargieolasedthottheauerameieieodarghasucnnfrauotacrsogiauclooiiorgedtcwlortyolsearycinotacropcftthewlorreddaswcsatacrcyticcwssettargotiowartcmhauhtheauerayellatasstallfruleoihcmthesovogetiapeshodleoiredtcdeuawheicfioinbsucnnfrauotacrsosthebhodrcmiatargcytheaicmrhcmeveieveiboinbhosatstioatcisordatseenslakelbtcnethotcrecinciecythelcuoltiapeswecwletfiredscnecresheodlcvecincrebatasolltieouheibpftthasoutthieoteredtcfrdeinaretheenwaiesertaiesbstencyseufieucnnfrauotacrtheuoesoiuawheiweihowsnciethoricodshoderopledtheenwaietcyfrutacrseufielbordatslcssmosodasosteitfirargthotlcssartcovautcibcrthepottleyaelddeseivedncieieucgratacrthoratieueavedweihowssfetcrafsreveikremciweihowsheyoaledtcfrdeistordtheufrrargcythewlorpfteatheimobdeswatethevautcibogiaucloordtherarthlegacrienoaredardasgiouecrheoiargthasremsulofdafsuoesoiofgfstfsgeinoraufsreicenweiciassfedoseuietwiculonotacrestoplashargouawheisuhccltcdevelcworemsbstencyanweiaoluawheisatmcikedarseuietycicveitmertbbeoisdevelcwargremnethcdstcseufieucnnfrauotacrsouicsstheenwaiestfdbargthemcikscythegieeksogesefuladordhbwotaoordevercldeimciksyicnoruaertpopblcrthesuhccldevelcwedremmobstcsoyegfoiducnnfrauotacrsyicnicneserenaestheseremsbstensmeiemiatterdcmrorddastiapftedartheucdexcuufltcifnweihowsthencststiautlbgfoideddcufnertarollhastcibmheratmosyarollbucnwletednorbbeoisloteitheenweicidcnataorassfedorexeuftavecideimhauhwieyouedeveibucwbcytheucdexthotatmostcpegfoidedpbeveiblegacrtcthelostnorthedeyeotcytheaueraordthesfauadecypcfdauuoweihowswicteutedtherarthlegacryicndassclftacrogiauclomosloigelbleyttchascmrdevauesarthewicvaruethcfghatmosnodeuleoitchanthottcietfirmcfldneorotpestdasgioueordotmcistdeotharopieokmathtiodatacrtherarthioasedoremstordoidthelegacrmosexaledardasgiouetcerdfiethehoidorddorgeicfsmcikcysfpdfargtheuoledcraaarthepleokmaldeiressescyuoledcraosfetcrafscideiedtherarthtcnoiuhtcepcioufnmhauhthebmeietcnoketheaiposeyciioadsartcuoledcraothefrswckerieoscrycitheaiexalemosthotartellagerueiewcitssfggestedthotthasmosmheiethebnaghtyardtheailcstoqfalothcsemhcmcfldieodcrnfstycllcmnbyoathyflslovetaicmhcyouesogiovetoskweihowstheloigesttcdoteoshetiovelstcucrueoltheycfithuhowteicythastiogautole\n"
-     ]
-    },
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "0 abmdefghijklcnopqrstuvwxyz -6227.81870975455 chaleatastifethotsfetnrafssetfwtheyarolpottlecathpnfdauuootcotlargstieethehodreatheithetinnwsrnithetoutauolopalatbtnnveiunmethehfgerfmeiauoldasodvortogeyouedpbhasoimbcathnftthesfwwnitnyogiaunloordhastolertedsunftsdeyeotcnfldhovepeerarevatopleordcathatthelnssnypiatorraohovargieolasedthottheaueraceieieodarghasunmmfrauotanrsogiaunlooiiorgedtnwlortyolsearynimotanropnftthewlorreddaswnsatanrnytinnwssettargotiowartnchauhtheauerayellatasstallfruleoihncthesovogetiapeshodleoiredtndeuawheinfioimbsunmmfrauotanrsosthebhodrnciatargnytheaincrhnceveieveiboimbhosatstioatnisordatseemslakelbtnmethotnrenimnienythelnuoltiapeswenwletfiredsnmenresheodlnvenimnrebatasolltieouheibpftthasoutthieoteredtnfrdeimaretheemwaiesertaiesbstemnyseufieunmmfrauotanrtheuoesoiuawheiweihowsmniethorinodshoderopledtheemwaietnyfrutanrseufielbordatslnsscosodasosteitfirargthotlnssartnovautnibnrthepottleyaelddeseivedmnieieungratanrthoratieueavedweihowssfetnrafsreveikrecniweihowsheyoaledtnfrdeistordtheufrrargnythewlorpfteatheicobdeswatethevautnibogiaunloordtherarthleganriemoaredardasgiouenrheoiargthasrecsulofdafsuoesoiofgfstfsgeimoraufsreinemweiniassfedoseuietwinulomotanrestoplashargouawheisuhnnltndevelnworecsbstemnyamweiaoluawheisatcnikedarseuietyninveitcertbbeoisdevelnwargrecmethndstnseufieunmmfrauotanrsouinsstheemwaiestfdbargthecniksnythegieeksogesefuladordhbwotaoordevernldeicniksyinmoruaertpopblnrthesuhnnldevelnwedreccobstnsoyegfoidunmmfrauotanrsyinminmeseremaestheserecsbstemsceieciatterdncrorddastiapftedartheundexnuufltnifmweihowsthemnststiautlbgfoideddnufmertarollhastnibcheratcosyarollbunmwletedmorbbeoisloteitheemweinidnmataorassfedorexeuftavenideichauhwieyouedeveibunwbnytheundexthotatcostnpegfoidedpbeveibleganrtnthelostmorthedeyeotnytheaueraordthesfauadenypnfdauuoweihowswinteutedtherarthleganryinmdassnlftanrogiaunlocosloigelbleyttnhasncrdevauesarthewinvaruethnfghatcosmodeuleoitnhamthottnietfircnfldmeorotpestdasgioueordotcnistdeotharopieokcathtiodatanrtherarthioasedorecstordoidtheleganrcosexaledardasgiouetnerdfiethehoidorddorgeinfscniknysfpdfargtheuolednraaarthepleokcaldeiressesnyuolednraosfetnrafsnideiedtherarthtnmoiuhtnepnioufmchauhthebceietnmoketheaiposeyniioadsartnuolednraothefrswnkerieosnrynitheaiexalecosthotartellagerueiewnitssfggestedthotthascoscheiethebmaghtyardtheailnstoqfalothnsechncnfldieodnrmfstynllncmbyoathyflslovetainchnyouesogiovetoskweihowstheloigesttndoteoshetiovelstnunrueoltheynfithuhowteinythastiogautole\n",
-      "0 abfdecghijklmnopqrstuvwxyz -6237.213026406104 mhaleatasticethotscetnracssetcwtheyarolpottlemathpncdauuootmotlargstieethehodreatheithetinnwsrnithetoutauolopalatbtnnveiunfethehcgercfeiauoldasodvortogeyouedpbhasoifbmathnctthescwwnitnyogiaunloordhastolertedsunctsdeyeotmncldhovepeerarevatopleordmathatthelnssnypiatorraohovargieolasedthottheauerameieieodarghasunffcrauotanrsogiaunlooiiorgedtnwlortyolsearynifotanropnctthewlorreddaswnsatanrnytinnwssettargotiowartnmhauhtheauerayellatasstallcruleoihnmthesovogetiapeshodleoiredtndeuawheincioifbsunffcrauotanrsosthebhodrnmiatargnytheainmrhnmeveieveiboifbhosatstioatnisordatseefslakelbtnfethotnrenifnienythelnuoltiapeswenwletciredsnfenresheodlnvenifnrebatasolltieouheibpctthasoutthieoteredtncrdeifaretheefwaiesertaiesbstefnyseucieunffcrauotanrtheuoesoiuawheiweihowsfniethorinodshoderopledtheefwaietnycrutanrseucielbordatslnssmosodasosteitcirargthotlnssartnovautnibnrthepottleyaelddeseivedfnieieungratanrthoratieueavedweihowsscetnracsreveikremniweihowsheyoaledtncrdeistordtheucrrargnythewlorpcteatheimobdeswatethevautnibogiaunloordtherarthleganriefoaredardasgiouenrheoiargthasremsulocdacsuoesoiocgcstcsgeiforaucsreinefweiniasscedoseuietwinulofotanrestoplashargouawheisuhnnltndevelnworemsbstefnyafweiaoluawheisatmnikedarseuietyninveitmertbbeoisdevelnwargremfethndstnseucieunffcrauotanrsouinsstheefwaiestcdbargthemniksnythegieeksogeseculadordhbwotaoordevernldeimniksyinforuaertpopblnrthesuhnnldevelnwedremmobstnsoyegcoidunffcrauotanrsyinfinfeserefaestheseremsbstefsmeiemiatterdnmrorddastiapctedartheundexnuucltnicfweihowsthefnststiautlbgcoideddnucfertarollhastnibmheratmosyarollbunfwletedforbbeoisloteitheefweinidnfataorasscedorexeuctavenideimhauhwieyouedeveibunwbnytheundexthotatmostnpegcoidedpbeveibleganrtnthelostforthedeyeotnytheaueraordthescauadenypncdauuoweihowswinteutedtherarthleganryinfdassnlctanrogiaunlomosloigelbleyttnhasnmrdevauesarthewinvaruethncghatmosfodeuleoitnhafthottnietcirmncldfeorotpestdasgioueordotmnistdeotharopieokmathtiodatanrtherarthioasedoremstordoidtheleganrmosexaledardasgiouetnerdciethehoidorddorgeincsmniknyscpdcargtheuolednraaarthepleokmaldeiressesnyuolednraoscetnracsnideiedtherarthtnfoiuhtnepnioucfmhauhthebmeietnfoketheaiposeyniioadsartnuolednraothecrswnkerieosnrynitheaiexalemosthotartellagerueiewnitsscggestedthotthasmosmheiethebfaghtyardtheailnstoqcalothnsemhnmncldieodnrfcstynllnmfbyoathyclslovetainmhnyouesogiovetoskweihowstheloigesttndoteoshetiovelstnunrueoltheyncithuhowteinythastiogautole\n",
-      "0 abcxefghijklmnopqrstuvwdyz -6488.945354263256 mhaleatastifethotsfetnrafssetfwtheyarolpottlemathpnfxauuootmotlargstieethehoxreatheithetinnwsrnithetoutauolopalatbtnnveiuncethehfgerfceiauolxasoxvortogeyouexpbhasoicbmathnftthesfwwnitnyogiaunloorxhastolertexsunftsxeyeotmnflxhovepeerarevatopleorxmathatthelnssnypiatorraohovargieolasexthottheauerameieieoxarghasunccfrauotanrsogiaunlooiiorgextnwlortyolsearynicotanropnftthewlorrexxaswnsatanrnytinnwssettargotiowartnmhauhtheauerayellatasstallfruleoihnmthesovogetiapeshoxleoirextnxeuawheinfioicbsunccfrauotanrsosthebhoxrnmiatargnytheainmrhnmeveieveiboicbhosatstioatnisorxatseecslakelbtncethotnrenicnienythelnuoltiapeswenwletfirexsncenresheoxlnvenicnrebatasolltieouheibpftthasoutthieoterextnfrxeicaretheecwaiesertaiesbstecnyseufieunccfrauotanrtheuoesoiuawheiweihowscniethorinoxshoxeroplextheecwaietnyfrutanrseufielborxatslnssmosoxasosteitfirargthotlnssartnovautnibnrthepottleyaelxxeseivexcnieieungratanrthoratieueavexweihowssfetnrafsreveikremniweihowsheyoalextnfrxeistorxtheufrrargnythewlorpfteatheimobxeswatethevautnibogiaunloorxtherarthleganriecoarexarxasgiouenrheoiargthasremsulofxafsuoesoiofgfstfsgeicoraufsreinecweiniassfexoseuietwinulocotanrestoplashargouawheisuhnnltnxevelnworemsbstecnyacweiaoluawheisatmnikexarseuietyninveitmertbbeoisxevelnwargremcethnxstnseufieunccfrauotanrsouinsstheecwaiestfxbargthemniksnythegieeksogesefulaxorxhbwotaoorxevernlxeimniksyincoruaertpopblnrthesuhnnlxevelnwexremmobstnsoyegfoixunccfrauotanrsyincinceserecaestheseremsbstecsmeiemiatterxnmrorxxastiapftexartheunxednuufltnifcweihowsthecnststiautlbgfoixexxnufcertarollhastnibmheratmosyarollbuncwletexcorbbeoisloteitheecweinixncataorassfexoredeuftavenixeimhauhwieyouexeveibunwbnytheunxedthotatmostnpegfoixexpbeveibleganrtnthelostcorthexeyeotnytheaueraorxthesfauaxenypnfxauuoweihowswinteutextherarthleganryincxassnlftanrogiaunlomosloigelbleyttnhasnmrxevauesarthewinvaruethnfghatmoscoxeuleoitnhacthottnietfirmnflxceorotpestxasgioueorxotmnistxeotharopieokmathtioxatanrtherarthioasexoremstorxoixtheleganrmosedalexarxasgiouetnerxfiethehoixorxxorgeinfsmniknysfpxfargtheuolexnraaarthepleokmalxeiressesnyuolexnraosfetnrafsnixeiextherarthtncoiuhtnepnioufcmhauhthebmeietncoketheaiposeyniioaxsartnuolexnraothefrswnkerieosnrynitheaiedalemosthotartellagerueiewnitssfggestexthotthasmosmheiethebcaghtyarxtheailnstoqfalothnsemhnmnflxieoxnrcfstynllnmcbyoathyflslovetainmhnyouesogiovetoskweihowstheloigesttnxoteoshetiovelstnunrueoltheynfithuhowteinythastiogautole\n",
-      "0 abcdefghijklrnopqmstuvwxyz -6349.496693126095 rhaleatastifethotsfetnmafssetfwtheyamolpottlerathpnfdauuootrotlamgstieethehodmeatheithetinnwsmnithetoutauolopalatbtnnveiuncethehfgemfceiauoldasodvomtogeyouedpbhasoicbrathnftthesfwwnitnyogiaunloomdhastolemtedsunftsdeyeotrnfldhovepeemamevatopleomdrathatthelnssnypiatommaohovamgieolasedthottheauemareieieodamghasunccfmauotanmsogiaunlooiiomgedtnwlomtyolseamynicotanmopnftthewlommeddaswnsatanmnytinnwssettamgotiowamtnrhauhtheauemayellatasstallfmuleoihnrthesovogetiapeshodleoimedtndeuawheinfioicbsunccfmauotanmsosthebhodmnriatamgnytheainrmhnreveieveiboicbhosatstioatnisomdatseecslakelbtncethotnmenicnienythelnuoltiapeswenwletfimedsncenmesheodlnvenicnmebatasolltieouheibpftthasoutthieotemedtnfmdeicametheecwaiesemtaiesbstecnyseufieunccfmauotanmtheuoesoiuawheiweihowscniethominodshodemopledtheecwaietnyfmutanmseufielbomdatslnssrosodasosteitfimamgthotlnssamtnovautnibnmthepottleyaelddeseivedcnieieungmatanmthomatieueavedweihowssfetnmafsmeveikmerniweihowsheyoaledtnfmdeistomdtheufmmamgnythewlompfteatheirobdeswatethevautnibogiaunloomdthemamthleganmiecoamedamdasgiouenmheoiamgthasmersulofdafsuoesoiofgfstfsgeicomaufsmeinecweiniassfedoseuietwinulocotanmestoplashamgouawheisuhnnltndevelnwomersbstecnyacweiaoluawheisatrnikedamseuietyninveitremtbbeoisdevelnwamgmercethndstnseufieunccfmauotanmsouinsstheecwaiestfdbamgtherniksnythegieeksogesefuladomdhbwotaoomdevemnldeirniksyincomuaemtpopblnmthesuhnnldevelnwedmerrobstnsoyegfoidunccfmauotanmsyincincesemecaesthesemersbstecsreieriattemdnrmomddastiapftedamtheundexnuufltnifcweihowsthecnststiautlbgfoideddnufcemtamollhastnibrhematrosyamollbuncwletedcombbeoisloteitheecweinidncataomassfedomexeuftavenideirhauhwieyouedeveibunwbnytheundexthotatrostnpegfoidedpbeveibleganmtnthelostcomthedeyeotnytheauemaomdthesfauadenypnfdauuoweihowswinteutedthemamthleganmyincdassnlftanmogiaunlorosloigelbleyttnhasnrmdevauesamthewinvamuethnfghatroscodeuleoitnhacthottnietfimrnfldceomotpestdasgioueomdotrnistdeothamopieokrathtiodatanmthemamthioasedomerstomdoidtheleganmrosexaledamdasgiouetnemdfiethehoidomddomgeinfsrniknysfpdfamgtheuolednmaaamthepleokraldeimessesnyuolednmaosfetnmafsnideiedthemamthtncoiuhtnepnioufcrhauhthebreietncoketheaiposeyniioadsamtnuolednmaothefmswnkemieosnmynitheaiexalerosthotamtellagemueiewnitssfggestedthotthasrosrheiethebcaghtyamdtheailnstoqfalothnserhnrnfldieodnmcfstynllnrcbyoathyflslovetainrhnyouesogiovetoskweihowstheloigesttndoteoshetiovelstnunmueoltheynfithuhowteinythastiogautole\n"
-     ]
-    },
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "2 abcdefghnjylmiopqwstuvrxkz -6332.700651780452 mhaleatastnfethotsfetiwafssetfrthekawolpottlemathpifdauuootmotlawgstneethehodweathenthetniirswinthetoutauolopalatbtiivenuicethehfgewfcenauoldasodvowtogekouedpbhasoncbmathiftthesfrrintikognauiloowdhastolewtedsuiftsdekeotmifldhovepeewawevatopleowdmathatthelissikpnatowwaohovawgneolasedthottheauewameneneodawghasuiccfwauotaiwsognauiloonnowgedtirlowtkolseawkincotaiwopifttherlowweddasrisataiwiktniirssettawgotnorawtimhauhtheauewakellatasstallfwuleonhimthesovogetnapeshodleonwedtideuarhenifnoncbsuiccfwauotaiwsosthebhodwimnatawgiktheanimwhimevenevenboncbhosatstnoatinsowdatseecslayelbticethotiweincineiktheliuoltnapesreirletfnwedsiceiwesheodliveinciwebatasolltneouhenbpftthasoutthneotewedtifwdencawetheecranesewtanesbstecikseufneuiccfwauotaiwtheuoesonuarhenrenhorscinethowniodshodewopledtheecranetikfwutaiwseufnelbowdatslissmosodasostentfnwawgthotlissawtiovautinbiwthepottlekaelddesenvedcineneuigwataiwthowatneueavedrenhorssfetiwafswevenyweminrenhorshekoaledtifwdenstowdtheufwwawgiktherlowpfteathenmobdesratethevautinbognauiloowdthewawthlegaiwnecoawedawdasgnoueiwheonawgthaswemsulofdafsuoesonofgfstfsgencowaufsweniecreninassfedoseunetrniulocotaiwestoplashawgouarhensuhiiltidevelirowemsbstecikacrenaoluarhensatminyedawseunetkiniventmewtbbeonsdevelirawgwemcethidstiseufneuiccfwauotaiwsounisstheecranestfdbawgtheminysikthegneeysogesefuladowdhbrotaoowdevewildenminysknicowuaewtpopbliwthesuhiildeveliredwemmobstisokegfonduiccfwauotaiwsknicnicesewecaesthesewemsbstecsmenemnattewdimwowddastnapftedawtheuidexiuufltinfcrenhorstheciststnautlbgfondeddiufcewtawollhastinbmhewatmoskawollbuicrletedcowbbeonslotentheecrenindicataowassfedowexeuftaveindenmhauhrnekouedevenbuirbiktheuidexthotatmostipegfondedpbevenblegaiwtithelostcowthedekeotiktheauewaowdthesfauadeikpifdauuorenhorsrniteutedthewawthlegaiwknicdassilftaiwognauilomoslongelblekttihasimwdevauesawthernivawuethifghatmoscodeuleontihacthottinetfnwmifldceowotpestdasgnoueowdotminstdeothawopneoymathtnodataiwthewawthnoasedowemstowdondthelegaiwmosexaledawdasgnouetiewdfnethehondowddowgenifsminyiksfpdfawgtheuolediwaaawthepleoymaldenwessesikuolediwaosfetiwafsindenedthewawthticonuhtiepinoufcmhauhthebmeneticoyetheanposekinnoadsawtiuolediwaothefwsriyewneosiwkintheanexalemosthotawtellagewuenerintssfggestedthotthasmosmhenethebcaghtkawdtheanlistoqfalothisemhimifldneodiwcfstkillimcbkoathkflslovetanimhikouesognovetosyrenhorsthelongesttidoteoshetnovelstiuiwueolthekifnthuhortenikthastnogautole\n",
-      "0 awcdefghijklmnopqrstuvbxyz -6259.828883380676 mhaleatastifethotsfetnrafssetfbtheyarolpottlemathpnfdauuootmotlargstieethehodreatheithetinnbsrnithetoutauolopalatwtnnveiuncethehfgerfceiauoldasodvortogeyouedpwhasoicwmathnftthesfbbnitnyogiaunloordhastolertedsunftsdeyeotmnfldhovepeerarevatopleordmathatthelnssnypiatorraohovargieolasedthottheauerameieieodarghasunccfrauotanrsogiaunlooiiorgedtnblortyolsearynicotanropnfttheblorreddasbnsatanrnytinnbssettargotiobartnmhauhtheauerayellatasstallfruleoihnmthesovogetiapeshodleoiredtndeuabheinfioicwsunccfrauotanrsosthewhodrnmiatargnytheainmrhnmeveieveiwoicwhosatstioatnisordatseecslakelwtncethotnrenicnienythelnuoltiapesbenbletfiredsncenresheodlnvenicnrewatasolltieouheiwpftthasoutthieoteredtnfrdeicaretheecbaiesertaieswstecnyseufieunccfrauotanrtheuoesoiuabheibeihobscniethorinodshoderopledtheecbaietnyfrutanrseufielwordatslnssmosodasosteitfirargthotlnssartnovautniwnrthepottleyaelddeseivedcnieieungratanrthoratieueavedbeihobssfetnrafsreveikremnibeihobsheyoaledtnfrdeistordtheufrrargnytheblorpfteatheimowdesbatethevautniwogiaunloordtherarthleganriecoaredardasgiouenrheoiargthasremsulofdafsuoesoiofgfstfsgeicoraufsreinecbeiniassfedoseuietbinulocotanrestoplashargouabheisuhnnltndevelnboremswstecnyacbeiaoluabheisatmnikedarseuietyninveitmertwweoisdevelnbargremcethndstnseufieunccfrauotanrsouinsstheecbaiestfdwargthemniksnythegieeksogesefuladordhwbotaoordevernldeimniksyincoruaertpopwlnrthesuhnnldevelnbedremmowstnsoyegfoidunccfrauotanrsyincinceserecaestheseremswstecsmeiemiatterdnmrorddastiapftedartheundexnuufltnifcbeihobsthecnststiautlwgfoideddnufcertarollhastniwmheratmosyarollwuncbletedcorwweoisloteitheecbeinidncataorassfedorexeuftavenideimhauhbieyouedeveiwunbwnytheundexthotatmostnpegfoidedpweveiwleganrtnthelostcorthedeyeotnytheaueraordthesfauadenypnfdauuobeihobsbinteutedtherarthleganryincdassnlftanrogiaunlomosloigelwleyttnhasnmrdevauesarthebinvaruethnfghatmoscodeuleoitnhacthottnietfirmnfldceorotpestdasgioueordotmnistdeotharopieokmathtiodatanrtherarthioasedoremstordoidtheleganrmosexaledardasgiouetnerdfiethehoidorddorgeinfsmniknysfpdfargtheuolednraaarthepleokmaldeiressesnyuolednraosfetnrafsnideiedtherarthtncoiuhtnepnioufcmhauhthewmeietncoketheaiposeyniioadsartnuolednraothefrsbnkerieosnrynitheaiexalemosthotartellagerueiebnitssfggestedthotthasmosmheiethewcaghtyardtheailnstoqfalothnsemhnmnfldieodnrcfstynllnmcwyoathyflslovetainmhnyouesogiovetoskbeihobstheloigesttndoteoshetiovelstnunrueoltheynfithuhobteinythastiogautole\n",
-      "0 abtdefghijklmnopqrscuvwxyz -6404.904935483241 mhaleacascifechocsfecnrafssecfwcheyarolpocclemachpnfdauuoocmoclargscieechehodreacheichecinnwsrnichecoucauolopalacbcnnveiuntechehfgerfteiauoldasodvorcogeyouedpbhasoitbmachnfcchesfwwnicnyogiaunloordhascolercedsunfcsdeyeocmnfldhovepeerarevacopleordmachacchelnssnypiacorraohovargieolasedchoccheauerameieieodarghasunttfrauocanrsogiaunlooiiorgedcnwlorcyolsearynitocanropnfcchewlorreddaswnsacanrnycinnwsseccargociowarcnmhauhcheauerayellacasscallfruleoihnmchesovogeciapeshodleoiredcndeuawheinfioitbsunttfrauocanrsoschebhodrnmiacargnycheainmrhnmeveieveiboitbhosacscioacnisordacseetslakelbcntechocnrenitnienychelnuolciapeswenwlecfiredsntenresheodlnvenitnrebacasollcieouheibpfcchasoucchieoceredcnfrdeitarecheetwaiesercaiesbscetnyseufieunttfrauocanrcheuoesoiuawheiweihowstniechorinodshoderopledcheetwaiecnyfrucanrseufielbordacslnssmosodasosceicfirargchoclnssarcnovaucnibnrchepoccleyaelddeseivedtnieieungracanrchoracieueavedweihowssfecnrafsreveikremniweihowsheyoaledcnfrdeiscordcheufrrargnychewlorpfceacheimobdeswacechevaucnibogiaunloordcherarchleganrietoaredardasgiouenrheoiargchasremsulofdafsuoesoiofgfscfsgeitoraufsreinetweiniassfedoseuiecwinulotocanrescoplashargouawheisuhnnlcndevelnworemsbscetnyatweiaoluawheisacmnikedarseuiecyninveicmercbbeoisdevelnwargremtechndscnseufieunttfrauocanrsouinsscheetwaiescfdbargchemniksnychegieeksogesefuladordhbwocaoordevernldeimniksyintoruaercpopblnrchesuhnnldevelnwedremmobscnsoyegfoidunttfrauocanrsyintinteseretaescheseremsbscetsmeiemiaccerdnmrorddasciapfcedarcheundexnuuflcniftweihowschetnscsciauclbgfoideddnuftercarollhascnibmheracmosyarollbuntwlecedtorbbeoisloceicheetweinidntacaorassfedorexeufcavenideimhauhwieyouedeveibunwbnycheundexchocacmoscnpegfoidedpbeveibleganrcnchelosctorchedeyeocnycheaueraordchesfauadenypnfdauuoweihowswinceucedcherarchleganryintdassnlfcanrogiaunlomosloigelbleyccnhasnmrdevauesarchewinvaruechnfghacmostodeuleoicnhatchoccniecfirmnfldteorocpescdasgioueordocmniscdeocharopieokmachciodacanrcherarchioasedoremscordoidcheleganrmosexaledardasgiouecnerdfiechehoidorddorgeinfsmniknysfpdfargcheuolednraaarchepleokmaldeiressesnyuolednraosfecnrafsnideiedcherarchcntoiuhcnepniouftmhauhchebmeiecntokecheaiposeyniioadsarcnuolednraochefrswnkerieosnrynicheaiexalemoschocarcellagerueiewnicssfggescedchocchasmosmheiechebtaghcyardcheailnscoqfalochnsemhnmnfldieodnrtfscynllnmtbyoachyflslovecainmhnyouesogiovecoskweihowscheloigesccndoceosheciovelscnunrueolcheynfichuhowceinychasciogaucole\n",
-      "1 awcdefphijklmnogqrstuvbxyz -6257.505870319161 mhaleatastifethotsfetnrafssetfbtheyarolgottlemathgnfdauuootmotlarpstieethehodreatheithetinnbsrnithetoutauologalatwtnnveiuncethehfperfceiauoldasodvortopeyouedgwhasoicwmathnftthesfbbnitnyopiaunloordhastolertedsunftsdeyeotmnfldhovegeerarevatogleordmathatthelnssnygiatorraohovarpieolasedthottheauerameieieodarphasunccfrauotanrsopiaunlooiiorpedtnblortyolsearynicotanrognfttheblorreddasbnsatanrnytinnbssettarpotiobartnmhauhtheauerayellatasstallfruleoihnmthesovopetiageshodleoiredtndeuabheinfioicwsunccfrauotanrsosthewhodrnmiatarpnytheainmrhnmeveieveiwoicwhosatstioatnisordatseecslakelwtncethotnrenicnienythelnuoltiagesbenbletfiredsncenresheodlnvenicnrewatasolltieouheiwgftthasoutthieoteredtnfrdeicaretheecbaiesertaieswstecnyseufieunccfrauotanrtheuoesoiuabheibeihobscniethorinodshoderogledtheecbaietnyfrutanrseufielwordatslnssmosodasosteitfirarpthotlnssartnovautniwnrthegottleyaelddeseivedcnieieunpratanrthoratieueavedbeihobssfetnrafsreveikremnibeihobsheyoaledtnfrdeistordtheufrrarpnytheblorgfteatheimowdesbatethevautniwopiaunloordtherarthlepanriecoaredardaspiouenrheoiarpthasremsulofdafsuoesoiofpfstfspeicoraufsreinecbeiniassfedoseuietbinulocotanrestoglasharpouabheisuhnnltndevelnboremswstecnyacbeiaoluabheisatmnikedarseuietyninveitmertwweoisdevelnbarpremcethndstnseufieunccfrauotanrsouinsstheecbaiestfdwarpthemniksnythepieeksopesefuladordhwbotaoordevernldeimniksyincoruaertgogwlnrthesuhnnldevelnbedremmowstnsoyepfoidunccfrauotanrsyincinceserecaestheseremswstecsmeiemiatterdnmrorddastiagftedartheundexnuufltnifcbeihobsthecnststiautlwpfoideddnufcertarollhastniwmheratmosyarollwuncbletedcorwweoisloteitheecbeinidncataorassfedorexeuftavenideimhauhbieyouedeveiwunbwnytheundexthotatmostngepfoidedgweveiwlepanrtnthelostcorthedeyeotnytheaueraordthesfauadenygnfdauuobeihobsbinteutedtherarthlepanryincdassnlftanropiaunlomosloipelwleyttnhasnmrdevauesarthebinvaruethnfphatmoscodeuleoitnhacthottnietfirmnfldceorotgestdaspioueordotmnistdeotharogieokmathtiodatanrtherarthioasedoremstordoidthelepanrmosexaledardaspiouetnerdfiethehoidorddorpeinfsmniknysfgdfarptheuolednraaarthegleokmaldeiressesnyuolednraosfetnrafsnideiedtherarthtncoiuhtnegnioufcmhauhthewmeietncoketheaigoseyniioadsartnuolednraothefrsbnkerieosnrynitheaiexalemosthotartellaperueiebnitssfppestedthotthasmosmheiethewcaphtyardtheailnstoqfalothnsemhnmnfldieodnrcfstynllnmcwyoathyflslovetainmhnyouesopiovetoskbeihobstheloipesttndoteoshetiovelstnunrueoltheynfithuhobteinythastiopautole\n"
-     ]
-    },
-    {
-     "data": {
-      "text/plain": [
-       "('abmdefghijklcnopqrstuvwxyz', -6227.81870975455)"
-      ]
-     },
-     "execution_count": 90,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "# monoalphabetic_break_hillclimbing_mp(scbpt, alphabet=string.ascii_lowercase, fitness=Pbigrams)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 93,
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "'MHALEATASTIFETHOTSFETNRAFSSETFWTHEYAROLPOTTLEMATHPNFDAUUOOTMOTLARGSTIEETHEHODREATHEITHETINNWSRNITHETOUTAUOLOPALATBTNNVEIUNCETHEHFGERFCEIAUOLDASODVORTOGEYOUEDPBHASOICBMATHNFTTHESFWWNITNYOGIAUNLOORDHASTOLERTEDSUNFTSDEYEOTMNFLDHOVEPEERAREVATOPLEORDMATHATTHELNSSNYPIATORRAOHOVARGIEOLASEDTHOTTHEAUERAMEIEIEODARGHASUNCCFRAUOTANRSOGIAUNLOOIIORGEDTNWLORTYOLSEARYNICOTANROPNFTTHEWLORREDDASWNSATANRNYTINNWSSETTARGOTIOWARTNMHAUHTHEAUERAYELLATASSTALLFRULEOIHNMTHESOVOGETIAPESHODLEOIREDTNDEUAWHEINFIOICBSUNCCFRAUOTANRSOSTHEBHODRNMIATARGNYTHEAINMRHNMEVEIEVEIBOICBHOSATSTIOATNISORDATSEECSLAKELBTNCETHOTNRENICNIENYTHELNUOLTIAPESWENWLETFIREDSNCENRESHEODLNVENICNREBATASOLLTIEOUHEIBPFTTHASOUTTHIEOTEREDTNFRDEICARETHEECWAIESERTAIESBSTECNYSEUFIEUNCCFRAUOTANRTHEUOESOIUAWHEIWEIHOWSCNIETHORINODSHODEROPLEDTHEECWAIETNYFRUTANRSEUFIELBORDATSLNSSMOSODASOSTEITFIRARGTHOTLNSSARTNOVAUTNIBNRTHEPOTTLEYAELDDESEIVEDCNIEIEUNGRATANRTHORATIEUEAVEDWEIHOWSSFETNRAFSREVEIKREMNIWEIHOWSHEYOALEDTNFRDEISTORDTHEUFRRARGNYTHEWLORPFTEATHEIMOBDESWATETHEVAUTNIBOGIAUNLOORDTHERARTHLEGANRIECOAREDARDASGIOUENRHEOIARGTHASREMSULOFDAFSUOESOIOFGFSTFSGEICORAUFSREINECWEINIASSFEDOSEUIETWINULOCOTANRESTOPLASHARGOUAWHEISUHNNLTNDEVELNWOREMSBSTECNYACWEIAOLUAWHEISATMNIKEDARSEUIETYNINVEITMERTBBEOISDEVELNWARGREMCETHNDSTNSEUFIEUNCCFRAUOTANRSOUINSSTHEECWAIESTFDBARGTHEMNIKSNYTHEGIEEKSOGESEFULADORDHBWOTAOORDEVERNLDEIMNIKSYINCORUAERTPOPBLNRTHESUHNNLDEVELNWEDREMMOBSTNSOYEGFOIDUNCCFRAUOTANRSYINCINCESERECAESTHESEREMSBSTECSMEIEMIATTERDNMRORDDASTIAPFTEDARTHEUNDEXNUUFLTNIFCWEIHOWSTHECNSTSTIAUTLBGFOIDEDDNUFCERTAROLLHASTNIBMHERATMOSYAROLLBUNCWLETEDCORBBEOISLOTEITHEECWEINIDNCATAORASSFEDOREXEUFTAVENIDEIMHAUHWIEYOUEDEVEIBUNWBNYTHEUNDEXTHOTATMOSTNPEGFOIDEDPBEVEIBLEGANRTNTHELOSTCORTHEDEYEOTNYTHEAUERAORDTHESFAUADENYPNFDAUUOWEIHOWSWINTEUTEDTHERARTHLEGANRYINCDASSNLFTANROGIAUNLOMOSLOIGELBLEYTTNHASNMRDEVAUESARTHEWINVARUETHNFGHATMOSCODEULEOITNHACTHOTTNIETFIRMNFLDCEOROTPESTDASGIOUEORDOTMNISTDEOTHAROPIEOKMATHTIODATANRTHERARTHIOASEDOREMSTORDOIDTHELEGANRMOSEXALEDARDASGIOUETNERDFIETHEHOIDORDDORGEINFSMNIKNYSFPDFARGTHEUOLEDNRAAARTHEPLEOKMALDEIRESSESNYUOLEDNRAOSFETNRAFSNIDEIEDTHERARTHTNCOIUHTNEPNIOUFCMHAUHTHEBMEIETNCOKETHEAIPOSEYNIIOADSARTNUOLEDNRAOTHEFRSWNKERIEOSNRYNITHEAIEXALEMOSTHOTARTELLAGERUEIEWNITSSFGGESTEDTHOTTHASMOSMHEIETHEBCAGHTYARDTHEAILNSTOQFALOTHNSEMHNMNFLDIEODNRCFSTYNLLNMCBYOATHYFLSLOVETAINMHNYOUESOGIOVETOSKWEIHOWSTHELOIGESTTNDOTEOSHETIOVELSTNUNRUEOLTHEYNFITHUHOWTEINYTHASTIOGAUTOLE'"
-      ]
-     },
-     "execution_count": 93,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "trhc = ''.maketrans('abmdefghijklcnopqrstuvwxyz', string.ascii_lowercase)\n",
-    "scbpt.translate(trhc)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 117,
+   "execution_count": 198,
    "metadata": {},
    "outputs": [],
    "source": [
-    "def polybius_grid(keyword, column_order, row_order, letters_to_merge=None,\n",
-    "                  wrap_alphabet=KeywordWrapAlphabet.from_a):\n",
-    "    alphabet = keyword_cipher_alphabet_of(keyword, wrap_alphabet=wrap_alphabet)\n",
-    "    if letters_to_merge is None: \n",
-    "        letters_to_merge = {'j': 'i'}\n",
-    "    grid = {l: k \n",
-    "            for k, l in zip([(c, r) for c in column_order for r in row_order],\n",
-    "                [l for l in alphabet if l not in letters_to_merge])}\n",
-    "    for l in letters_to_merge:\n",
-    "        grid[l] = grid[letters_to_merge[l]]\n",
-    "    return grid        "
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 118,
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "{'a': ('x', 'x'),\n",
-       " 'b': ('x', 'l'),\n",
-       " 'c': ('x', 'c'),\n",
-       " 'd': ('x', 'd'),\n",
-       " 'e': ('x', 'm'),\n",
-       " 'f': ('l', 'x'),\n",
-       " 'g': ('l', 'l'),\n",
-       " 'h': ('l', 'c'),\n",
-       " 'i': ('l', 'd'),\n",
-       " 'j': ('l', 'd'),\n",
-       " 'k': ('l', 'm'),\n",
-       " 'l': ('c', 'x'),\n",
-       " 'm': ('c', 'l'),\n",
-       " 'n': ('c', 'c'),\n",
-       " 'o': ('c', 'd'),\n",
-       " 'p': ('c', 'm'),\n",
-       " 'q': ('d', 'x'),\n",
-       " 'r': ('d', 'l'),\n",
-       " 's': ('d', 'c'),\n",
-       " 't': ('d', 'd'),\n",
-       " 'u': ('d', 'm'),\n",
-       " 'v': ('m', 'x'),\n",
-       " 'w': ('m', 'l'),\n",
-       " 'x': ('m', 'c'),\n",
-       " 'y': ('m', 'd'),\n",
-       " 'z': ('m', 'm')}"
-      ]
-     },
-     "execution_count": 118,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "polybius_grid('', order, order)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 142,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "def polybius_reverse_grid(keyword, column_order, row_order, letters_to_merge=None,\n",
-    "                  wrap_alphabet=KeywordWrapAlphabet.from_a):\n",
-    "    alphabet = keyword_cipher_alphabet_of(keyword, wrap_alphabet=wrap_alphabet)\n",
-    "    if letters_to_merge is None: \n",
-    "        letters_to_merge = {'j': 'i'}\n",
-    "    grid = {k: l \n",
-    "            for k, l in zip([(c, r) for c in column_order for r in row_order],\n",
-    "                [l for l in alphabet if l not in letters_to_merge])}\n",
-    "#     for l in letters_to_merge:\n",
-    "#         for r, c in grid:\n",
-    "#             if grid[r, c] == letters_to_merge[l]:\n",
-    "#                 grid[l] = grid[r, c]\n",
-    "    return grid        "
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 112,
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "{('c', 'c'): 'n',\n",
-       " ('c', 'd'): 'o',\n",
-       " ('c', 'l'): 'm',\n",
-       " ('c', 'm'): 'p',\n",
-       " ('c', 'x'): 'l',\n",
-       " ('d', 'c'): 's',\n",
-       " ('d', 'd'): 't',\n",
-       " ('d', 'l'): 'r',\n",
-       " ('d', 'm'): 'u',\n",
-       " ('d', 'x'): 'q',\n",
-       " ('l', 'c'): 'h',\n",
-       " ('l', 'd'): 'i',\n",
-       " ('l', 'l'): 'g',\n",
-       " ('l', 'm'): 'k',\n",
-       " ('l', 'x'): 'f',\n",
-       " ('m', 'c'): 'x',\n",
-       " ('m', 'd'): 'y',\n",
-       " ('m', 'l'): 'w',\n",
-       " ('m', 'm'): 'z',\n",
-       " ('m', 'x'): 'v',\n",
-       " ('x', 'c'): 'c',\n",
-       " ('x', 'd'): 'd',\n",
-       " ('x', 'l'): 'b',\n",
-       " ('x', 'm'): 'e',\n",
-       " ('x', 'x'): 'a'}"
-      ]
-     },
-     "execution_count": 112,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "reverse_polybius_grid('', order, order)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 119,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "def polybius_encipher_letter(l, grid):\n",
-    "    if l in grid:\n",
-    "        return grid[l]\n",
-    "    else:\n",
-    "        return l"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 126,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "def polybius_flatten(pair, column_first):\n",
-    "    if column_first:\n",
-    "        return str(pair[1]) + str(pair[0])\n",
-    "    else:\n",
-    "        return str(pair[0]) + str(pair[1])"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 135,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "def polybius_encipher(message, keyword, column_order, row_order, \n",
-    "                      column_first=False,\n",
-    "                      letters_to_merge=None, wrap_alphabet=KeywordWrapAlphabet.from_a):    \n",
-    "    grid = polybius_grid(keyword, column_order, row_order, letters_to_merge, wrap_alphabet)\n",
-    "    return cat(polybius_flatten(grid[l], column_first)\n",
-    "               for l in message\n",
-    "               if l in grid)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 136,
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "'mllcldcxxmldddlddcdddldmxm'"
-      ]
-     },
-     "execution_count": 136,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "polybius_encipher('while it is true', '', order, order)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 133,
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "'mllcldcxxmldddlddcdddldmxmddlcxxdddcdmxmddcdcclddmdcdcxmdddmcmddlcxmlxldccxxcxxlxxdd'"
-      ]
-     },
-     "execution_count": 133,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "sanitise(cb[:100])"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 161,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "def polybius_decipher(message, keyword, column_order, row_order, \n",
-    "                      column_first=False,\n",
-    "                      letters_to_merge=None, wrap_alphabet=KeywordWrapAlphabet.from_a):    \n",
-    "    grid = polybius_reverse_grid(keyword, column_order, row_order, letters_to_merge, wrap_alphabet)\n",
-    "    if column_first:\n",
-    "        pairs = [(p[1], p[0]) for p in chunks(message, 2)]\n",
-    "    else:\n",
-    "        pairs = [(p[0], p[1]) for p in chunks(message, 2)]\n",
-    "    return cat(grid[p] for p in pairs if p in grid)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 155,
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "'whileitistrue'"
-      ]
-     },
-     "execution_count": 155,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "polybius_decipher('mllcldcxxmldddlddcdddldmxm', '', order, order)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 158,
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "'lmcldlxcmxdldddlcdddldmdmx'"
-      ]
-     },
-     "execution_count": 158,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "polybius_encipher('whileitistrue', '', order, order, column_first=True)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 159,
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "'kmrcvrtrotiyv'"
-      ]
-     },
-     "execution_count": 159,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "polybius_decipher('lmcldlxcmxdldddlcdddldmdmx', '', order, order)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 162,
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "'whileitistrue'"
-      ]
-     },
-     "execution_count": 162,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "polybius_decipher('lmcldlxcmxdldddlcdddldmdmx', '', order, order, column_first=True)"
+    "# monoalphabetic_break_hillclimbing_mp(scbpt, alphabet=string.ascii_lowercase, fitness=Pbigrams)"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 167,
+   "execution_count": 199,
    "metadata": {},
    "outputs": [
     {
      "data": {
       "text/plain": [
-       "'52232431152444244344424515'"
+       "'while it is true that suetonius setup the final battle with boudicca at watling street he had neither the troops nor the tactical ability to overcome the huge numerical disadvantage faced by his army without the support of agricola and his talented scouts defeat would have been inevitable and with it the loss of britannia having realised that the iceni were reading his communications agricola arranged to plant false information about the planned disposition of troops setting a trap into which the iceni fell it is still unclear how the savage tribes had learned to decipher our army s communications as they had no writing of their own however every army has its traitors and it seems likely to me that one or more of the local tribespeople turned someones head love or money it is all treachery but this act threatened to undermine the empires entire system of secure communication the caesar cipher perhaps more than roads had enabled the empire to function securely and its loss was a disaster turning that loss into a victory on the battlefield deserved more recognition than it received perhaps suetonius never knew or perhaps he failed to understand the cunning of the plan but either way despite the victory agricola and the ninth legion remained in disgrace on hearing this news claudius caesar augustus germanicus nero emperor issued a secret proclamation establishing a cipher school to develop a new system of imperial ciphers it worked in secret for over twenty years developing new methods to secure communications across the empire studying the works of the greeks ages euclid and hypatia and even older works from ancient babylon the school developed new ways to safeguard communications from rome s enemies these new systems were written down and distributed in the codex occult orum perhaps the most strictly guarded document in all history when it was finally completed many years later the emperor domitian issued an executive order which prefaced every copy of the codex that it was to be guarded by every legion to the last man the defeat of the iceni and the suicide of boudicca perhaps protected the ninth legion from dissolution agricola was largely left to his own devices in the province though it was made clear to him that to return would mean at best disgrace and at worst death in a break with tradition the ninth raised a new standard the legion was exiled in disgrace to endure the hard and dangerous work of subduing the caledon ii in the bleak wildernesses of caledonia suetonius ordered the ninth to march toe bora cum which they were to make their base for raids into caledonia the unspoken reason for their exile was that intelligence reports suggested that this was where they might find their lost aquila those who would read on must follow my faithful slave tiro who faces a grave task perhaps the largest to date a she travels to conceal the fourth chapter of this tragic tale'"
       ]
      },
-     "execution_count": 167,
+     "execution_count": 199,
      "metadata": {},
      "output_type": "execute_result"
     }
    ],
    "source": [
-    "polybius_encipher('while it is true', '', '12345', '12345')"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 181,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "from multiprocessing import Pool\n",
-    "import itertools\n",
-    "\n",
-    "def polybius_break_mp(message, column_order, row_order,\n",
-    "                      letters_to_merge=None,\n",
-    "                      wordlist=keywords, fitness=Pletters,\n",
-    "                      number_of_solutions=1, chunksize=500):\n",
-    "    \"\"\"Breaks a keyword substitution cipher using a dictionary and\n",
-    "    frequency analysis\n",
-    "\n",
-    "    >>> keyword_break_mp(keyword_encipher('this is a test message for the ' \\\n",
-    "          'keyword decipherment', 'elephant', KeywordWrapAlphabet.from_last), \\\n",
-    "          wordlist=['cat', 'elephant', 'kangaroo']) # doctest: +ELLIPSIS\n",
-    "    (('elephant', <KeywordWrapAlphabet.from_last: 2>), -52.834575011...)\n",
-    "    >>> keyword_break_mp(keyword_encipher('this is a test message for the ' \\\n",
-    "          'keyword decipherment', 'elephant', KeywordWrapAlphabet.from_last), \\\n",
-    "          wordlist=['cat', 'elephant', 'kangaroo'], \\\n",
-    "          number_of_solutions=2) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE\n",
-    "    [(('elephant', <KeywordWrapAlphabet.from_last: 2>), -52.834575011...), \n",
-    "    (('elephant', <KeywordWrapAlphabet.from_largest: 3>), -52.834575011...)]\n",
-    "    \"\"\"\n",
-    "    if letters_to_merge is None: \n",
-    "        letters_to_merge = {'j': 'i'}\n",
-    "    with Pool() as pool:\n",
-    "        helper_args = [(message, word, wrap, \n",
-    "                        cs, rs, column_first, \n",
-    "                        letters_to_merge, \n",
-    "                        fitness)\n",
-    "                       for word in wordlist\n",
-    "                       for wrap in KeywordWrapAlphabet\n",
-    "                       for cs in itertools.permutations(column_order)\n",
-    "                       for rs in itertools.permutations(row_order)\n",
-    "                       for column_first in [False, True]]\n",
-    "        # Gotcha: the helper function here needs to be defined at the top level\n",
-    "        #   (limitation of Pool.starmap)\n",
-    "        breaks = pool.starmap(polybius_break_worker, helper_args, chunksize)\n",
-    "        if number_of_solutions == 1:\n",
-    "            return max(breaks, key=lambda k: k[1])\n",
-    "        else:\n",
-    "            return sorted(breaks, key=lambda k: k[1], reverse=True)[:number_of_solutions]\n",
-    "\n",
-    "def polybius_break_worker(message, keyword, wrap_alphabet, \n",
-    "                          column_order, row_order, column_first, \n",
-    "                          letters_to_merge, \n",
-    "                          fitness):\n",
-    "    plaintext = polybius_decipher(message, keyword, \n",
-    "                                  column_order, row_order, \n",
-    "                                  column_first=column_first,\n",
-    "                                  letters_to_merge=letters_to_merge, \n",
-    "                                  wrap_alphabet=wrap_alphabet)\n",
-    "    fit = fitness(plaintext)\n",
-    "    logger.debug('Keyword break attempt using key {0} (wrap={1}) gives fit of '\n",
-    "                 '{2} and decrypt starting: {3}'.format(keyword, \n",
-    "                     wrap_alphabet, fit, sanitise(plaintext)[:50]))\n",
-    "    return (keyword, wrap_alphabet, column_order, row_order, column_first), fit"
+    "wcat(segment(polybius_decipher(scb, '', order, order)))"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "%time polybius_break_mp(sanitise(cb), order, order)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 183,
+   "execution_count": 200,
    "metadata": {},
    "outputs": [
     {
      "data": {
       "text/plain": [
-       "'xlcdm'"
+       "True"
       ]
      },
-     "execution_count": 183,
+     "execution_count": 200,
      "metadata": {},
      "output_type": "execute_result"
     }
    ],
    "source": [
-    "order"
+    "scbpt.translate(tt) == polybius_decipher(scb, '', order, order)"
    ]
   },
   {
diff --git a/2017/polybius-development.ipynb b/2017/polybius-development.ipynb
new file mode 100644 (file)
index 0000000..eb81437
--- /dev/null
@@ -0,0 +1,1086 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import os,sys,inspect, collections\n",
+    "currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))\n",
+    "parentdir = os.path.dirname(currentdir)\n",
+    "sys.path.insert(0,parentdir) \n",
+    "\n",
+    "import matplotlib.pyplot as plt\n",
+    "%matplotlib inline\n",
+    "\n",
+    "from cipherbreak import *\n",
+    "\n",
+    "ca = open('3a.ciphertext').read()\n",
+    "cb = open('3b.ciphertext').read()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "'MHALEATASTIFETHOTSFETNRAFSSETFWTHEYAROLPOTTLEMATHPNFDAUUOOTMOTLARGSTIEETHEHODREATHEITHETINNWSRNITHETOUTAUOLOPALATBTNNVEIUNCETHEHFGERFCEIAUOLDASODVORTOGEYOUEDPBHASOICBMATHNFTTHESFWWNITNYOGIAUNLOORDHASTOLERTEDSUNFTSDEYEOTMNFLDHOVEPEERAREVATOPLEORDMATHATTHELNSSNYPIATORRAOHOVARGIEOLASEDTHOTTHEAUERAMEIEIEODARGHASUNCCFRAUOTANRSOGIAUNLOOIIORGEDTNWLORTYOLSEARYNICOTANROPNFTTHEWLORREDDASWNSATANRNYTINNWSSETTARGOTIOWARTNMHAUHTHEAUERAYELLATASSTALLFRULEOIHNMTHESOVOGETIAPESHODLEOIREDTNDEUAWHEINFIOICBSUNCCFRAUOTANRSOSTHEBHODRNMIATARGNYTHEAINMRHNMEVEIEVEIBOICBHOSATSTIOATNISORDATSEECSLAKELBTNCETHOTNRENICNIENYTHELNUOLTIAPESWENWLETFIREDSNCENRESHEODLNVENICNREBATASOLLTIEOUHEIBPFTTHASOUTTHIEOTEREDTNFRDEICARETHEECWAIESERTAIESBSTECNYSEUFIEUNCCFRAUOTANRTHEUOESOIUAWHEIWEIHOWSCNIETHORINODSHODEROPLEDTHEECWAIETNYFRUTANRSEUFIELBORDATSLNSSMOSODASOSTEITFIRARGTHOTLNSSARTNOVAUTNIBNRTHEPOTTLEYAELDDESEIVEDCNIEIEUNGRATANRTHORATIEUEAVEDWEIHOWSSFETNRAFSREVEIKREMNIWEIHOWSHEYOALEDTNFRDEISTORDTHEUFRRARGNYTHEWLORPFTEATHEIMOBDESWATETHEVAUTNIBOGIAUNLOORDTHERARTHLEGANRIECOAREDARDASGIOUENRHEOIARGTHASREMSULOFDAFSUOESOIOFGFSTFSGEICORAUFSREINECWEINIASSFEDOSEUIETWINULOCOTANRESTOPLASHARGOUAWHEISUHNNLTNDEVELNWOREMSBSTECNYACWEIAOLUAWHEISATMNIKEDARSEUIETYNINVEITMERTBBEOISDEVELNWARGREMCETHNDSTNSEUFIEUNCCFRAUOTANRSOUINSSTHEECWAIESTFDBARGTHEMNIKSNYTHEGIEEKSOGESEFULADORDHBWOTAOORDEVERNLDEIMNIKSYINCORUAERTPOPBLNRTHESUHNNLDEVELNWEDREMMOBSTNSOYEGFOIDUNCCFRAUOTANRSYINCINCESERECAESTHESEREMSBSTECSMEIEMIATTERDNMRORDDASTIAPFTEDARTHEUNDEXNUUFLTNIFCWEIHOWSTHECNSTSTIAUTLBGFOIDEDDNUFCERTAROLLHASTNIBMHERATMOSYAROLLBUNCWLETEDCORBBEOISLOTEITHEECWEINIDNCATAORASSFEDOREXEUFTAVENIDEIMHAUHWIEYOUEDEVEIBUNWBNYTHEUNDEXTHOTATMOSTNPEGFOIDEDPBEVEIBLEGANRTNTHELOSTCORTHEDEYEOTNYTHEAUERAORDTHESFAUADENYPNFDAUUOWEIHOWSWINTEUTEDTHERARTHLEGANRYINCDASSNLFTANROGIAUNLOMOSLOIGELBLEYTTNHASNMRDEVAUESARTHEWINVARUETHNFGHATMOSCODEULEOITNHACTHOTTNIETFIRMNFLDCEOROTPESTDASGIOUEORDOTMNISTDEOTHAROPIEOKMATHTIODATANRTHERARTHIOASEDOREMSTORDOIDTHELEGANRMOSEXALEDARDASGIOUETNERDFIETHEHOIDORDDORGEINFSMNIKNYSFPDFARGTHEUOLEDNRAAARTHEPLEOKMALDEIRESSESNYUOLEDNRAOSFETNRAFSNIDEIEDTHERARTHTNCOIUHTNEPNIOUFCMHAUHTHEBMEIETNCOKETHEAIPOSEYNIIOADSARTNUOLEDNRAOTHEFRSWNKERIEOSNRYNITHEAIEXALEMOSTHOTARTELLAGERUEIEWNITSSFGGESTEDTHOTTHASMOSMHEIETHEBCAGHTYARDTHEAILNSTOQFALOTHNSEMHNMNFLDIEODNRCFSTYNLLNMCBYOATHYFLSLOVETAINMHNYOUESOGIOVETOSKWEIHOWSTHELOIGESTTNDOTEOSHETIOVELSTNUNRUEOLTHEYNFITHUHOWTEINYTHASTIOGAUTOLE'"
+      ]
+     },
+     "execution_count": 6,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "scb = sanitise(cb)\n",
+    "scbp = chunks(scb, 2)\n",
+    "order = 'xlcdm'\n",
+    "ltrs = 'etoanisrhdlufcmwgybpvkxqz'\n",
+    "prs =  [p[0] for p in collections.Counter(chunks(sanitise(cb), 2)).most_common()]\n",
+    "trans = {pr[1]: pr[0].upper() for pr in zip(ltrs, prs)}\n",
+    "scbpt = cat(trans[p] for p in scbp)\n",
+    "scbpt"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 7,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "'MHALEATASTIFETHOTSFETNRAFSSETFWTHEYAROLPOTTLEMATHPNFDAUUOOTMOTLARGSTIEETHEHODREATHEITHETINNWSRNITHETOUTAUOLOPALATBTNNVEIUNCETHEHFGERFCEIAUOLDASODVORTOGEYOUEDPBHASOICBMATHNFTTHESFWWNITNYOGIAUNLOORDHASTOLERTEDSUNFTSDEYEOTMNFLDHOVEPEERAREVATOPLEORDMATHATTHELNSSNYPIATORRAOHOVARGIEOLASEDTHOTTHEAUERAMEIEIEODARGHASUNCCFRAUOTANRSOGIAUNLOOIIORGEDTNWLORTYOLSEARYNICOTANROPNFTTHEWLORREDDASWNSATANRNYTINNWSSETTARGOTIOWARTNMHAUHTHEAUERAYELLATASSTALLFRULEOIHNMTHESOVOGETIAPESHODLEOIREDTNDEUAWHEINFIOICBSUNCCFRAUOTANRSOSTHEBHODRNMIATARGNYTHEAINMRHNMEVEIEVEIBOICBHOSATSTIOATNISORDATSEECSLAKELBTNCETHOTNRENICNIENYTHELNUOLTIAPESWENWLETFIREDSNCENRESHEODLNVENICNREBATASOLLTIEOUHEIBPFTTHASOUTTHIEOTEREDTNFRDEICARETHEECWAIESERTAIESBSTECNYSEUFIEUNCCFRAUOTANRTHEUOESOIUAWHEIWEIHOWSCNIETHORINODSHODEROPLEDTHEECWAIETNYFRUTANRSEUFIELBORDATSLNSSMOSODASOSTEITFIRARGTHOTLNSSARTNOVAUTNIBNRTHEPOTTLEYAELDDESEIVEDCNIEIEUNGRATANRTHORATIEUEAVEDWEIHOWSSFETNRAFSREVEIKREMNIWEIHOWSHEYOALEDTNFRDEISTORDTHEUFRRARGNYTHEWLORPFTEATHEIMOBDESWATETHEVAUTNIBOGIAUNLOORDTHERARTHLEGANRIECOAREDARDASGIOUENRHEOIARGTHASREMSULOFDAFSUOESOIOFGFSTFSGEICORAUFSREINECWEINIASSFEDOSEUIETWINULOCOTANRESTOPLASHARGOUAWHEISUHNNLTNDEVELNWOREMSBSTECNYACWEIAOLUAWHEISATMNIKEDARSEUIETYNINVEITMERTBBEOISDEVELNWARGREMCETHNDSTNSEUFIEUNCCFRAUOTANRSOUINSSTHEECWAIESTFDBARGTHEMNIKSNYTHEGIEEKSOGESEFULADORDHBWOTAOORDEVERNLDEIMNIKSYINCORUAERTPOPBLNRTHESUHNNLDEVELNWEDREMMOBSTNSOYEGFOIDUNCCFRAUOTANRSYINCINCESERECAESTHESEREMSBSTECSMEIEMIATTERDNMRORDDASTIAPFTEDARTHEUNDEXNUUFLTNIFCWEIHOWSTHECNSTSTIAUTLBGFOIDEDDNUFCERTAROLLHASTNIBMHERATMOSYAROLLBUNCWLETEDCORBBEOISLOTEITHEECWEINIDNCATAORASSFEDOREXEUFTAVENIDEIMHAUHWIEYOUEDEVEIBUNWBNYTHEUNDEXTHOTATMOSTNPEGFOIDEDPBEVEIBLEGANRTNTHELOSTCORTHEDEYEOTNYTHEAUERAORDTHESFAUADENYPNFDAUUOWEIHOWSWINTEUTEDTHERARTHLEGANRYINCDASSNLFTANROGIAUNLOMOSLOIGELBLEYTTNHASNMRDEVAUESARTHEWINVARUETHNFGHATMOSCODEULEOITNHACTHOTTNIETFIRMNFLDCEOROTPESTDASGIOUEORDOTMNISTDEOTHAROPIEOKMATHTIODATANRTHERARTHIOASEDOREMSTORDOIDTHELEGANRMOSEXALEDARDASGIOUETNERDFIETHEHOIDORDDORGEINFSMNIKNYSFPDFARGTHEUOLEDNRAAARTHEPLEOKMALDEIRESSESNYUOLEDNRAOSFETNRAFSNIDEIEDTHERARTHTNCOIUHTNEPNIOUFCMHAUHTHEBMEIETNCOKETHEAIPOSEYNIIOADSARTNUOLEDNRAOTHEFRSWNKERIEOSNRYNITHEAIEXALEMOSTHOTARTELLAGERUEIEWNITSSFGGESTEDTHOTTHASMOSMHEIETHEBCAGHTYARDTHEAILNSTOQFALOTHNSEMHNMNFLDIEODNRCFSTYNLLNMCBYOATHYFLSLOVETAINMHNYOUESOGIOVETOSKWEIHOWSTHELOIGESTTNDOTEOSHETIOVELSTNUNRUEOLTHEYNFITHUHOWTEINYTHASTIOGAUTOLE'"
+      ]
+     },
+     "execution_count": 7,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "trhc = ''.maketrans('abmdefghijklcnopqrstuvwxyz', string.ascii_lowercase)\n",
+    "scbpt.translate(trhc)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 8,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def polybius_grid(keyword, column_order, row_order, letters_to_merge=None,\n",
+    "                  wrap_alphabet=KeywordWrapAlphabet.from_a):\n",
+    "    alphabet = keyword_cipher_alphabet_of(keyword, wrap_alphabet=wrap_alphabet)\n",
+    "    if letters_to_merge is None: \n",
+    "        letters_to_merge = {'j': 'i'}\n",
+    "    grid = {l: k \n",
+    "            for k, l in zip([(c, r) for c in column_order for r in row_order],\n",
+    "                [l for l in alphabet if l not in letters_to_merge])}\n",
+    "    for l in letters_to_merge:\n",
+    "        grid[l] = grid[letters_to_merge[l]]\n",
+    "    return grid        "
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "{'a': ('x', 'x'),\n",
+       " 'b': ('x', 'l'),\n",
+       " 'c': ('x', 'c'),\n",
+       " 'd': ('x', 'd'),\n",
+       " 'e': ('x', 'm'),\n",
+       " 'f': ('l', 'x'),\n",
+       " 'g': ('l', 'l'),\n",
+       " 'h': ('l', 'c'),\n",
+       " 'i': ('l', 'd'),\n",
+       " 'j': ('l', 'd'),\n",
+       " 'k': ('l', 'm'),\n",
+       " 'l': ('c', 'x'),\n",
+       " 'm': ('c', 'l'),\n",
+       " 'n': ('c', 'c'),\n",
+       " 'o': ('c', 'd'),\n",
+       " 'p': ('c', 'm'),\n",
+       " 'q': ('d', 'x'),\n",
+       " 'r': ('d', 'l'),\n",
+       " 's': ('d', 'c'),\n",
+       " 't': ('d', 'd'),\n",
+       " 'u': ('d', 'm'),\n",
+       " 'v': ('m', 'x'),\n",
+       " 'w': ('m', 'l'),\n",
+       " 'x': ('m', 'c'),\n",
+       " 'y': ('m', 'd'),\n",
+       " 'z': ('m', 'm')}"
+      ]
+     },
+     "execution_count": 9,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "polybius_grid('', order, order)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 10,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "{'a': ('a', 'a'),\n",
+       " 'b': ('a', 'b'),\n",
+       " 'c': ('a', 'c'),\n",
+       " 'd': ('a', 'd'),\n",
+       " 'e': ('a', 'e'),\n",
+       " 'f': ('b', 'a'),\n",
+       " 'g': ('b', 'b'),\n",
+       " 'h': ('b', 'c'),\n",
+       " 'i': ('b', 'd'),\n",
+       " 'j': ('b', 'd'),\n",
+       " 'k': ('b', 'e'),\n",
+       " 'l': ('c', 'a'),\n",
+       " 'm': ('c', 'b'),\n",
+       " 'n': ('c', 'c'),\n",
+       " 'o': ('c', 'd'),\n",
+       " 'p': ('c', 'e'),\n",
+       " 'q': ('d', 'a'),\n",
+       " 'r': ('d', 'b'),\n",
+       " 's': ('d', 'c'),\n",
+       " 't': ('d', 'd'),\n",
+       " 'u': ('d', 'e'),\n",
+       " 'v': ('e', 'a'),\n",
+       " 'w': ('e', 'b'),\n",
+       " 'x': ('e', 'c'),\n",
+       " 'y': ('e', 'd'),\n",
+       " 'z': ('e', 'e')}"
+      ]
+     },
+     "execution_count": 10,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "polybius_grid('a', 'abcde', 'abcde')"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 11,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "True"
+      ]
+     },
+     "execution_count": 11,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "polybius_grid('elephant', 'abcde', 'abcde')['b'] == ('b', 'c')"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 12,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "True"
+      ]
+     },
+     "execution_count": 12,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "polybius_grid('elephant', 'abcde', 'abcde')['e'] == ('a', 'a')"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 13,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def polybius_reverse_grid(keyword, column_order, row_order, letters_to_merge=None,\n",
+    "                  wrap_alphabet=KeywordWrapAlphabet.from_a):\n",
+    "    alphabet = keyword_cipher_alphabet_of(keyword, wrap_alphabet=wrap_alphabet)\n",
+    "    if letters_to_merge is None: \n",
+    "        letters_to_merge = {'j': 'i'}\n",
+    "    grid = {k: l \n",
+    "            for k, l in zip([(c, r) for c in column_order for r in row_order],\n",
+    "                [l for l in alphabet if l not in letters_to_merge])}\n",
+    "#     for l in letters_to_merge:\n",
+    "#         for r, c in grid:\n",
+    "#             if grid[r, c] == letters_to_merge[l]:\n",
+    "#                 grid[l] = grid[r, c]\n",
+    "    return grid        "
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 14,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "{('c', 'c'): 'n',\n",
+       " ('c', 'd'): 'o',\n",
+       " ('c', 'l'): 'm',\n",
+       " ('c', 'm'): 'p',\n",
+       " ('c', 'x'): 'l',\n",
+       " ('d', 'c'): 's',\n",
+       " ('d', 'd'): 't',\n",
+       " ('d', 'l'): 'r',\n",
+       " ('d', 'm'): 'u',\n",
+       " ('d', 'x'): 'q',\n",
+       " ('l', 'c'): 'h',\n",
+       " ('l', 'd'): 'i',\n",
+       " ('l', 'l'): 'g',\n",
+       " ('l', 'm'): 'k',\n",
+       " ('l', 'x'): 'f',\n",
+       " ('m', 'c'): 'x',\n",
+       " ('m', 'd'): 'y',\n",
+       " ('m', 'l'): 'w',\n",
+       " ('m', 'm'): 'z',\n",
+       " ('m', 'x'): 'v',\n",
+       " ('x', 'c'): 'c',\n",
+       " ('x', 'd'): 'd',\n",
+       " ('x', 'l'): 'b',\n",
+       " ('x', 'm'): 'e',\n",
+       " ('x', 'x'): 'a'}"
+      ]
+     },
+     "execution_count": 14,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "polybius_reverse_grid('', order, order)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 15,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def polybius_flatten(pair, column_first):\n",
+    "    if column_first:\n",
+    "        return str(pair[1]) + str(pair[0])\n",
+    "    else:\n",
+    "        return str(pair[0]) + str(pair[1])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 16,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def polybius_encipher(message, keyword, column_order, row_order, \n",
+    "                      column_first=False,\n",
+    "                      letters_to_merge=None, wrap_alphabet=KeywordWrapAlphabet.from_a):    \n",
+    "    grid = polybius_grid(keyword, column_order, row_order, letters_to_merge, wrap_alphabet)\n",
+    "    return cat(polybius_flatten(grid[l], column_first)\n",
+    "               for l in message\n",
+    "               if l in grid)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 17,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "'mllcldcxxmldddlddcdddldmxm'"
+      ]
+     },
+     "execution_count": 17,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "polybius_encipher('while it is true', '', order, order)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 18,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "'mllcldcxxmldddlddcdddldmxmddlcxxdddcdmxmddcdcclddmdcdcxmdddmcmddlcxmlxldccxxcxxlxxdd'"
+      ]
+     },
+     "execution_count": 18,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "sanitise(cb[:100])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 19,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def polybius_decipher(message, keyword, column_order, row_order, \n",
+    "                      column_first=False,\n",
+    "                      letters_to_merge=None, wrap_alphabet=KeywordWrapAlphabet.from_a):    \n",
+    "    grid = polybius_reverse_grid(keyword, column_order, row_order, letters_to_merge, wrap_alphabet)\n",
+    "    column_index_type = type(column_order[0])\n",
+    "    row_index_type = type(row_order[0])\n",
+    "    if column_first:\n",
+    "        pairs = [(column_index_type(p[1]), row_index_type(p[0])) for p in chunks(message, 2)]\n",
+    "    else:\n",
+    "        pairs = [(row_index_type(p[0]), column_index_type(p[1])) for p in chunks(message, 2)]\n",
+    "    return cat(grid[p] for p in pairs if p in grid)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 20,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "'whileitistrue'"
+      ]
+     },
+     "execution_count": 20,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "polybius_decipher('mllcldcxxmldddlddcdddldmxm', '', order, order)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 21,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "'lmcldlxcmxdldddlcdddldmdmx'"
+      ]
+     },
+     "execution_count": 21,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "polybius_encipher('whileitistrue', '', order, order, column_first=True)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 22,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "'kmrcvrtrotiyv'"
+      ]
+     },
+     "execution_count": 22,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "polybius_decipher('lmcldlxcmxdldddlcdddldmdmx', '', order, order)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 23,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "'whileitistrue'"
+      ]
+     },
+     "execution_count": 23,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "polybius_decipher('lmcldlxcmxdldddlcdddldmdmx', '', order, order, column_first=True)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 24,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "'52232431152444244344424515'"
+      ]
+     },
+     "execution_count": 24,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "polybius_encipher('while it is true', '', '12345', '12345')"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 25,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "'52232431152444244344424515'"
+      ]
+     },
+     "execution_count": 25,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "polybius_encipher('while it is true', '', [1, 2, 3, 4, 5], [1, 2, 3, 4, 5])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 26,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "'whileitistrue'"
+      ]
+     },
+     "execution_count": 26,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "polybius_decipher('52232431152444244344424515', '', [1, 2, 3, 4, 5], [1, 2, 3, 4, 5])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 27,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from multiprocessing import Pool\n",
+    "\n",
+    "def polybius_break_mp(message, column_labels, row_labels,\n",
+    "                      letters_to_merge=None,\n",
+    "                      wordlist=keywords, fitness=Pletters,\n",
+    "                      number_of_solutions=1, chunksize=500):\n",
+    "    \"\"\"Breaks a Polybius substitution cipher using a dictionary and\n",
+    "    frequency analysis\n",
+    "\n",
+    "    >>> polybius_break_mp(polybius_encipher('this is a test message for the ' \\\n",
+    "          'polybius decipherment', 'elephant', 'abcde', 'abcde'), \\\n",
+    "          'abcde', 'abcde', \\\n",
+    "          wordlist=['cat', 'elephant', 'kangaroo']) # doctest: +ELLIPSIS\n",
+    "    (('elephant', <KeywordWrapAlphabet.from_a: 1>, 'abcde', 'abcde', False), \\\n",
+    "    -54.5397...)\n",
+    "    >>> polybius_break_mp(polybius_encipher('this is a test message for the ' \\\n",
+    "          'polybius decipherment', 'elephant', 'abcde', 'abcde', column_first=True), \\\n",
+    "          'abcde', 'abcde', \\\n",
+    "          wordlist=['cat', 'elephant', 'kangaroo']) # doctest: +ELLIPSIS\n",
+    "    (('elephant', <KeywordWrapAlphabet.from_a: 1>, 'abcde', 'abcde', True), \\\n",
+    "    -54.5397...)\n",
+    "    >>> polybius_break_mp(polybius_encipher('this is a test message for the ' \\\n",
+    "          'polybius decipherment', 'elephant', 'abcde', 'abcde', column_first=False), \\\n",
+    "          'abcde', 'abcde', \\\n",
+    "          wordlist=['cat', 'elephant', 'kangaroo']) # doctest: +ELLIPSIS\n",
+    "    (('elephant', <KeywordWrapAlphabet.from_a: 1>, 'abcde', 'abcde', False), \\\n",
+    "    -54.5397...)\n",
+    "    >>> polybius_break_mp(polybius_encipher('this is a test message for the ' \\\n",
+    "          'polybius decipherment', 'elephant', 'abcde', 'pqrst', column_first=True), \\\n",
+    "          'abcde', 'pqrst', \\\n",
+    "          wordlist=['cat', 'elephant', 'kangaroo']) # doctest: +ELLIPSIS\n",
+    "    (('elephant', <KeywordWrapAlphabet.from_a: 1>, 'abcde', 'pqrst', True), \\\n",
+    "    -54.5397...)\n",
+    "    \"\"\"\n",
+    "    if letters_to_merge is None: \n",
+    "        letters_to_m53880erge = {'j': 'i'}\n",
+    "    with Pool() as pool:\n",
+    "        helper_args = [(message, word, wrap, \n",
+    "                        column_labels, row_labels, column_first, \n",
+    "                        letters_to_merge, \n",
+    "                        fitness)\n",
+    "                       for word in wordlist\n",
+    "                       for wrap in KeywordWrapAlphabet\n",
+    "                       for column_first in [False, True]]\n",
+    "        # Gotcha: the helper function here needs to be defined at the top level\n",
+    "        #   (limitation of Pool.starmap)\n",
+    "        breaks = pool.starmap(polybius_break_worker, helper_args, chunksize)\n",
+    "        if number_of_solutions == 1:\n",
+    "            return max(breaks, key=lambda k: k[1])\n",
+    "        else:\n",
+    "            return sorted(breaks, key=lambda k: k[1], reverse=True)[:number_of_solutions]\n",
+    "\n",
+    "def polybius_break_worker(message, keyword, wrap_alphabet, \n",
+    "                          column_order, row_order, column_first, \n",
+    "                          letters_to_merge, \n",
+    "                          fitness):\n",
+    "    plaintext = polybius_decipher(message, keyword, \n",
+    "                                  column_order, row_order, \n",
+    "                                  column_first=column_first,\n",
+    "                                  letters_to_merge=letters_to_merge, \n",
+    "                                  wrap_alphabet=wrap_alphabet)\n",
+    "    if plaintext:\n",
+    "        fit = fitness(plaintext)\n",
+    "    else:\n",
+    "        fit = float('-inf')\n",
+    "    logger.debug('Polybius break attempt using key {0} (wrap={1}, merging {2}), '\n",
+    "                 'columns as {3}, rows as {4} (column_first={5}) '\n",
+    "                 'gives fit of {6} and decrypt starting: '\n",
+    "                 '{7}'.format(keyword, wrap_alphabet, letters_to_merge,\n",
+    "                              column_order, row_order, column_first,\n",
+    "                              fit, sanitise(plaintext)[:50]))\n",
+    "    return (keyword, wrap_alphabet, column_order, row_order, column_first), fit"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 28,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "CPU times: user 2.67 s, sys: 308 ms, total: 2.98 s\n",
+      "Wall time: 3min 58s\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "(('a', <KeywordWrapAlphabet.from_a: 1>, 'xlcdm', 'xlcdm', False),\n",
+       " -3018.8648333417113)"
+      ]
+     },
+     "execution_count": 28,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "%time polybius_break_mp(sanitise(cb), order, order)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 29,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "(('elephant',\n",
+       "  <KeywordWrapAlphabet.from_a: 1>,\n",
+       "  [1, 2, 3, 4, 5],\n",
+       "  [1, 2, 3, 4, 5],\n",
+       "  True),\n",
+       " -54.53880323982303)"
+      ]
+     },
+     "execution_count": 29,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "polybius_break_mp(polybius_encipher('this is a test message for the ' \\\n",
+    "          'polybius decipherment', 'elephant', \\\n",
+    "          [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], \\\n",
+    "          KeywordWrapAlphabet.from_last), \\\n",
+    "          [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], \\\n",
+    "          wordlist=['cat', 'elephant', 'kangaroo'])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 30,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "'2214445544551522115522511155551543114252542214111352123234442355411135441314115451112122'"
+      ]
+     },
+     "execution_count": 30,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "polybius_encipher('this is a test message for the ' \\\n",
+    "          'polybius decipherment', 'elephant', \\\n",
+    "          [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], \\\n",
+    "          wrap_alphabet=KeywordWrapAlphabet.from_last)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 31,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "(('elephant',\n",
+       "  <KeywordWrapAlphabet.from_last: 2>,\n",
+       "  [1, 2, 3, 4, 5],\n",
+       "  [1, 2, 3, 4, 5],\n",
+       "  False),\n",
+       " -54.53880323982303)"
+      ]
+     },
+     "execution_count": 31,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "polybius_break_mp('2214445544551522115522511155551543114252542214111352123234442355411135441314115451112122', \\\n",
+    "          [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], \\\n",
+    "          wordlist=['cat', 'elephant', 'kangaroo'])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 32,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "'thisisatestmessageforthepolybiusdecipherment'"
+      ]
+     },
+     "execution_count": 32,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "polybius_decipher('2214445544551522115522511155551543114252542214111352123234442355411135441314115451112122', \\\n",
+    "                  'elephant',\n",
+    "          [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], \\\n",
+    "          wrap_alphabet=KeywordWrapAlphabet.from_last)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 33,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "{(1, 1): 'e',\n",
+       " (1, 2): 'l',\n",
+       " (1, 3): 'p',\n",
+       " (1, 4): 'h',\n",
+       " (1, 5): 'a',\n",
+       " (2, 1): 'n',\n",
+       " (2, 2): 't',\n",
+       " (2, 3): 'b',\n",
+       " (2, 4): 'c',\n",
+       " (2, 5): 'd',\n",
+       " (3, 1): 'f',\n",
+       " (3, 2): 'g',\n",
+       " (3, 3): 'i',\n",
+       " (3, 4): 'k',\n",
+       " (3, 5): 'm',\n",
+       " (4, 1): 'o',\n",
+       " (4, 2): 'q',\n",
+       " (4, 3): 'r',\n",
+       " (4, 4): 's',\n",
+       " (4, 5): 'u',\n",
+       " (5, 1): 'v',\n",
+       " (5, 2): 'w',\n",
+       " (5, 3): 'x',\n",
+       " (5, 4): 'y',\n",
+       " (5, 5): 'z'}"
+      ]
+     },
+     "execution_count": 33,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "polybius_reverse_grid('elephant', [1, 2, 3, 4, 5], [1, 2, 3, 4, 5] )"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 34,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "4"
+      ]
+     },
+     "execution_count": 34,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "type(2)('4')"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 35,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "'bbadccddccddaebbaaddbbceaaddddaecbaacadadcbbadaaacdaabedbcccdeddbeaabdccacadaadcceaababb'"
+      ]
+     },
+     "execution_count": 35,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "polybius_encipher('this is a test message for the ' \\\n",
+    "          'polybius decipherment', 'elephant', 'abcde', 'abcde', column_first=False)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 36,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "'bbdaccddccddeabbaaddbbecaaddddeabcaaacadcdbbdaaacaadbadecbccedddebaadbcccadaaacdecaaabbb'"
+      ]
+     },
+     "execution_count": 36,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "polybius_encipher('this is a test message for the ' \\\n",
+    "          'polybius decipherment', 'elephant', 'abcde', 'abcde', column_first=True)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 37,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "'toisisvtestxessvbephktoefhnugiysweqifoekxelt'"
+      ]
+     },
+     "execution_count": 37,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "polybius_decipher('bbdaccddccddeabbaaddbbecaaddddeabcaaacadcdbbdaaacaadbadecbccedddebaadbcccadaaacdecaaabbb', 'elephant', 'abcde', 'abcde', column_first=False)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 38,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "'thisisatestmessageforthepolybiusdecipherment'"
+      ]
+     },
+     "execution_count": 38,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "polybius_decipher('bbdaccddccddeabbaaddbbecaaddddeabcaaacadcdbbdaaacaadbadecbccedddebaadbcccadaaacdecaaabbb', 'elephant', 'abcde', 'abcde', column_first=True)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 39,
+   "metadata": {
+    "scrolled": true
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "(('elephant', <KeywordWrapAlphabet.from_a: 1>, 'abcde', 'abcde', False),\n",
+       " -54.53880323982303)"
+      ]
+     },
+     "execution_count": 39,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "polybius_break_mp(polybius_encipher('this is a test message for the ' \\\n",
+    "          'polybius decipherment', 'elephant', 'abcde', 'abcde'), \\\n",
+    "          'abcde', 'abcde',\n",
+    "          wordlist=['cat', 'elephant', 'kangaroo'])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 40,
+   "metadata": {
+    "scrolled": true
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "(('elephant', <KeywordWrapAlphabet.from_a: 1>, 'abcde', 'abcde', True),\n",
+       " -54.53880323982303)"
+      ]
+     },
+     "execution_count": 40,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "polybius_break_mp(polybius_encipher('this is a test message for the ' \\\n",
+    "          'polybius decipherment', 'elephant', 'abcde', 'abcde', column_first=True), \\\n",
+    "          'abcde', 'abcde',\n",
+    "          wordlist=['cat', 'elephant', 'kangaroo'])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 41,
+   "metadata": {
+    "scrolled": true
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "(('elephant', <KeywordWrapAlphabet.from_a: 1>, 'abcde', 'abcde', False),\n",
+       " -54.53880323982303)"
+      ]
+     },
+     "execution_count": 41,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "polybius_break_mp(polybius_encipher('this is a test message for the ' \\\n",
+    "          'polybius decipherment', 'elephant', 'abcde', 'abcde', column_first=False), \\\n",
+    "          'abcde', 'abcde',\n",
+    "          wordlist=['cat', 'elephant', 'kangaroo'])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 42,
+   "metadata": {
+    "scrolled": true
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "(('elephant', <KeywordWrapAlphabet.from_a: 1>, 'abcde', 'pqrst', True),\n",
+       " -54.53880323982303)"
+      ]
+     },
+     "execution_count": 42,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "polybius_break_mp(polybius_encipher('this is a test message for the ' \\\n",
+    "          'polybius decipherment', 'elephant', 'abcde', 'pqrst', column_first=True), \\\n",
+    "          'abcde', 'pqrst',\n",
+    "          wordlist=['cat', 'elephant', 'kangaroo'])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 43,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "'bwaycxdycxdyazbwavdybwczavdydyazcwavcvdvdxbwayavaxdvaweybxcxdzdybzavbycxaxayavdxczavbvbw'"
+      ]
+     },
+     "execution_count": 43,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "polybius_encipher('this is a test message for the ' \\\n",
+    "          'polybius decipherment', 'elephant', 'abcde', 'vwxyz')"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 44,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "'thisisatestmessageforthepolybiusdecipherment'"
+      ]
+     },
+     "execution_count": 44,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "polybius_decipher('bwaycxdycxdyazbwavdybwczavdydyazcwavcvdvdxbwayavaxdvaweybxcxdzdybzavbycxaxayavdxczavbvbw', \n",
+    "                  'elephant', 'abcde', 'vwxyz')"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 45,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "logger.setLevel(logging.DEBUG)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 46,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "(('elephant', <KeywordWrapAlphabet.from_a: 1>, 'abcde', 'vwxyz', False),\n",
+       " -54.53880323982303)"
+      ]
+     },
+     "execution_count": 46,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "polybius_break_mp('bwaycxdycxdyazbwavdybwczavdydyazcwavcvdvdxbwayavaxdvaweybxcxdzdybzavbycxaxayavdxczavbvbw', \\\n",
+    "          'abcde', 'vwxyz',\n",
+    "          wordlist=['cat', 'elephant', 'kangaroo'])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 47,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "logger.debug('test')"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "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.5.3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
index 1c71b5c205567807332accf8d3fbada74b1c0c28..4c3f14b00ab3c8ce8c0f37aae144d9470bdb8d35 100644 (file)
--- a/cipher.py
+++ b/cipher.py
@@ -399,6 +399,110 @@ beaufort_encipher=vigenere_decipher
 beaufort_decipher=vigenere_encipher
 
 
+def polybius_grid(keyword, column_order, row_order, letters_to_merge=None,
+                  wrap_alphabet=KeywordWrapAlphabet.from_a):
+    """Grid for a Polybius cipher, using a keyword to rearrange the
+    alphabet.
+
+
+    >>> polybius_grid('a', 'abcde', 'abcde')['x'] == ('e', 'c')
+    True
+    >>> polybius_grid('elephant', 'abcde', 'abcde')['e'] == ('a', 'a')
+    True
+    >>> polybius_grid('elephant', 'abcde', 'abcde')['b'] == ('b', 'c')
+    True
+    """
+    alphabet = keyword_cipher_alphabet_of(keyword, wrap_alphabet=wrap_alphabet)
+    if letters_to_merge is None: 
+        letters_to_merge = {'j': 'i'}
+    grid = {l: k 
+            for k, l in zip([(c, r) for c in column_order for r in row_order],
+                [l for l in alphabet if l not in letters_to_merge])}
+    for l in letters_to_merge:
+        grid[l] = grid[letters_to_merge[l]]
+    return grid
+
+def polybius_reverse_grid(keyword, column_order, row_order, letters_to_merge=None,
+                  wrap_alphabet=KeywordWrapAlphabet.from_a):
+    """Grid for decrypting using a Polybius cipher, using a keyword to 
+    rearrange the alphabet.
+
+    >>> polybius_reverse_grid('a', 'abcde', 'abcde')['e', 'c'] == 'x'
+    True
+    >>> polybius_reverse_grid('elephant', 'abcde', 'abcde')['a', 'a'] == 'e'
+    True
+    >>> polybius_reverse_grid('elephant', 'abcde', 'abcde')['b', 'c'] == 'b'
+    True
+    """
+    alphabet = keyword_cipher_alphabet_of(keyword, wrap_alphabet=wrap_alphabet)
+    if letters_to_merge is None: 
+        letters_to_merge = {'j': 'i'}
+    grid = {k: l 
+            for k, l in zip([(c, r) for c in column_order for r in row_order],
+                [l for l in alphabet if l not in letters_to_merge])}
+    return grid  
+
+
+def polybius_flatten(pair, column_first):
+    """Convert a series of pairs into a single list of characters"""
+    if column_first:
+        return str(pair[1]) + str(pair[0])
+    else:
+        return str(pair[0]) + str(pair[1])
+
+def polybius_encipher(message, keyword, column_order, row_order, 
+                      column_first=False,
+                      letters_to_merge=None, wrap_alphabet=KeywordWrapAlphabet.from_a): 
+    """Encipher a message with Polybius cipher, using a keyword to rearrange
+    the alphabet
+
+
+    >>> polybius_encipher('this is a test message for the ' \
+          'polybius decipherment', 'elephant', \
+          [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], \
+          wrap_alphabet=KeywordWrapAlphabet.from_last)
+    '2214445544551522115522511155551543114252542214111352123234442355411135441314115451112122'
+    >>> polybius_encipher('this is a test message for the ' \
+          'polybius decipherment', 'elephant', 'abcde', 'abcde', \
+          column_first=False)
+    'bbadccddccddaebbaaddbbceaaddddaecbaacadadcbbadaaacdaabedbcccdeddbeaabdccacadaadcceaababb'
+    >>> polybius_encipher('this is a test message for the ' \
+          'polybius decipherment', 'elephant', 'abcde', 'abcde', \
+          column_first=True)
+    'bbdaccddccddeabbaaddbbecaaddddeabcaaacadcdbbdaaacaadbadecbccedddebaadbcccadaaacdecaaabbb'
+    """
+    grid = polybius_grid(keyword, column_order, row_order, letters_to_merge, wrap_alphabet)
+    return cat(polybius_flatten(grid[l], column_first)
+               for l in message
+               if l in grid)
+
+
+def polybius_decipher(message, keyword, column_order, row_order, 
+                      column_first=False,
+                      letters_to_merge=None, wrap_alphabet=KeywordWrapAlphabet.from_a):    
+    """Decipher a message with a Polybius cipher, using a keyword to rearrange
+    the alphabet
+
+    >>> polybius_decipher('bbdaccddccddeabbaaddbbecaaddddeabcaaacadcdbbdaaaca'\
+    'adbadecbccedddebaadbcccadaaacdecaaabbb', 'elephant', 'abcde', 'abcde', \
+    column_first=False)
+    'toisisvtestxessvbephktoefhnugiysweqifoekxelt'
+
+    >>> polybius_decipher('bbdaccddccddeabbaaddbbecaaddddeabcaaacadcdbbdaaaca'\
+    'adbadecbccedddebaadbcccadaaacdecaaabbb', 'elephant', 'abcde', 'abcde', \
+    column_first=True)
+    'thisisatestmessageforthepolybiusdecipherment'
+    """
+    grid = polybius_reverse_grid(keyword, column_order, row_order, letters_to_merge, wrap_alphabet)
+    column_index_type = type(column_order[0])
+    row_index_type = type(row_order[0])
+    if column_first:
+        pairs = [(column_index_type(p[1]), row_index_type(p[0])) for p in chunks(message, 2)]
+    else:
+        pairs = [(row_index_type(p[0]), column_index_type(p[1])) for p in chunks(message, 2)]
+    return cat(grid[p] for p in pairs if p in grid)
+
+
 def transpositions_of(keyword):
     """Finds the transpostions given by a keyword. For instance, the keyword
     'clever' rearranges to 'celrv', so the first column (0) stays first, the
index f1ab58e50a4f293368ef9d5382aef9dabf590420..669d1330b898e888890f4cdd6e74f41cac98dcc4 100644 (file)
@@ -266,7 +266,7 @@ def vigenere_keyword_break_mp(message, wordlist=keywords, fitness=Pletters,
     >>> vigenere_keyword_break_mp(vigenere_encipher(sanitise('this is a test ' \
              'message for the vigenere decipherment'), 'cat'), \
              wordlist=['cat', 'elephant', 'kangaroo']) # doctest: +ELLIPSIS
-    ('cat', -52.947271216...)
+    ('cat', -52.9472712...)
     """
     with Pool() as pool:
         helper_args = [(message, word, fitness)
@@ -297,7 +297,7 @@ def vigenere_frequency_break(message, max_key_length=20, fitness=Pletters):
             "certain that the theft has been discovered and that I will " \
             "be caught. The SS officer visits less often now that he is " \
             "sure"), 'florence')) # doctest: +ELLIPSIS
-    ('florence', -307.5473096791...)
+    ('florence', -307.5473096...)
     """
     def worker(message, key_length, fitness):
         splits = every_nth(sanitised_message, key_length)
@@ -336,6 +336,78 @@ def beaufort_frequency_break(message, max_key_length=20, fitness=Pletters):
     return max(results, key=lambda k: k[1])
 
 
+def polybius_break_mp(message, column_labels, row_labels,
+                      letters_to_merge=None,
+                      wordlist=keywords, fitness=Pletters,
+                      number_of_solutions=1, chunksize=500):
+    """Breaks a Polybius substitution cipher using a dictionary and
+    frequency analysis
+
+    >>> polybius_break_mp(polybius_encipher('this is a test message for the ' \
+          'polybius decipherment', 'elephant', 'abcde', 'abcde'), \
+          'abcde', 'abcde', \
+          wordlist=['cat', 'elephant', 'kangaroo']) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
+    (('elephant', <KeywordWrapAlphabet.from_a: 1>, 'abcde', 'abcde', False), \
+    -54.53880...)
+    >>> polybius_break_mp(polybius_encipher('this is a test message for the ' \
+          'polybius decipherment', 'elephant', 'abcde', 'abcde', column_first=True), \
+          'abcde', 'abcde', \
+          wordlist=['cat', 'elephant', 'kangaroo']) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
+    (('elephant', <KeywordWrapAlphabet.from_a: 1>, 'abcde', 'abcde', True), \
+    -54.53880...)
+    >>> polybius_break_mp(polybius_encipher('this is a test message for the ' \
+          'polybius decipherment', 'elephant', 'abcde', 'abcde', column_first=False), \
+          'abcde', 'abcde', \
+          wordlist=['cat', 'elephant', 'kangaroo']) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
+    (('elephant', <KeywordWrapAlphabet.from_a: 1>, 'abcde', 'abcde', False), \
+    -54.53880...)
+    >>> polybius_break_mp(polybius_encipher('this is a test message for the ' \
+          'polybius decipherment', 'elephant', 'abcde', 'pqrst', column_first=True), \
+          'abcde', 'pqrst', \
+          wordlist=['cat', 'elephant', 'kangaroo']) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
+    (('elephant', <KeywordWrapAlphabet.from_a: 1>, 'abcde', 'pqrst', True), \
+    -54.53880...)
+    """
+    if letters_to_merge is None: 
+        letters_to_merge = {'j': 'i'}
+    with Pool() as pool:
+        helper_args = [(message, word, wrap, 
+                        column_labels, row_labels, column_first, 
+                        letters_to_merge, 
+                        fitness)
+                       for word in wordlist
+                       for wrap in KeywordWrapAlphabet
+                       for column_first in [False, True]]
+        # Gotcha: the helper function here needs to be defined at the top level
+        #   (limitation of Pool.starmap)
+        breaks = pool.starmap(polybius_break_worker, helper_args, chunksize)
+        if number_of_solutions == 1:
+            return max(breaks, key=lambda k: k[1])
+        else:
+            return sorted(breaks, key=lambda k: k[1], reverse=True)[:number_of_solutions]
+
+def polybius_break_worker(message, keyword, wrap_alphabet, 
+                          column_order, row_order, column_first, 
+                          letters_to_merge, 
+                          fitness):
+    plaintext = polybius_decipher(message, keyword, 
+                                  column_order, row_order, 
+                                  column_first=column_first,
+                                  letters_to_merge=letters_to_merge, 
+                                  wrap_alphabet=wrap_alphabet)
+    if plaintext:
+        fit = fitness(plaintext)
+    else:
+        fit = float('-inf')
+    logger.debug('Polybius break attempt using key {0} (wrap={1}, merging {2}), '
+                 'columns as {3}, rows as {4} (column_first={5}) '
+                 'gives fit of {6} and decrypt starting: '
+                 '{7}'.format(keyword, wrap_alphabet, letters_to_merge,
+                              column_order, row_order, column_first,
+                              fit, sanitise(plaintext)[:50]))
+    return (keyword, wrap_alphabet, column_order, row_order, column_first), fit
+
+
 def column_transposition_break_mp(message, translist=transpositions,
                                   fitness=Pbigrams, chunksize=500):
     """Breaks a column transposition cipher using a dictionary and