{
 "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
}