+ "source": [
+ "def index_of_coincidence(text):\n",
+ " stext = sanitise(text)\n",
+ " counts = collections.Counter(stext)\n",
+ " denom = len(stext) * (len(text) - 1) / 26\n",
+ " return (\n",
+ " sum(max(counts[l] * counts[l] - 1, 0) for l in string.ascii_lowercase) \n",
+ " / \n",
+ " denom\n",
+ " )"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 48,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "1.0739632382990911"
+ ]
+ },
+ "execution_count": 48,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "index_of_coincidence(scb)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 49,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "1.7196234250491917"
+ ]
+ },
+ "execution_count": 49,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "index_of_coincidence(pa)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 50,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "1 1.0739632382990911\n",
+ "2 1.0847309682648545\n",
+ "3 1.1901326088129982\n",
+ "4 1.0944337274794889\n",
+ "5 1.3385608752635192\n",
+ "6 1.2148423611711687\n",
+ "7 1.1475045557348746\n",
+ "8 1.126018862329491\n",
+ "9 1.246033937674865\n",
+ "10 1.387327185449383\n",
+ "11 1.1687618776366193\n",
+ "12 1.2613508799467053\n",
+ "13 1.1901044676035653\n",
+ "14 1.1988503247011726\n",
+ "15 1.8423114658282445\n",
+ "16 1.1835776099617201\n",
+ "17 1.229047094202011\n",
+ "18 1.3067952909636624\n",
+ "19 1.2593538199678038\n"
+ ]
+ }
+ ],
+ "source": [
+ "for i in range(1, 20):\n",
+ " print(i, sum(index_of_coincidence(section) for section in every_nth(scb, i)) / i)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 51,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "[(1, 1.0739632382990911),\n",
+ " (2, 1.0847309682648545),\n",
+ " (4, 1.0944337274794889),\n",
+ " (8, 1.126018862329491),\n",
+ " (7, 1.1475045557348746),\n",
+ " (11, 1.1687618776366193),\n",
+ " (16, 1.1835776099617201),\n",
+ " (13, 1.1901044676035653),\n",
+ " (3, 1.1901326088129982),\n",
+ " (14, 1.1988503247011726),\n",
+ " (6, 1.2148423611711687),\n",
+ " (17, 1.229047094202011),\n",
+ " (9, 1.246033937674865),\n",
+ " (19, 1.2593538199678038),\n",
+ " (12, 1.2613508799467053),\n",
+ " (18, 1.3067952909636624),\n",
+ " (5, 1.3385608752635192),\n",
+ " (10, 1.387327185449383),\n",
+ " (15, 1.8423114658282445)]"
+ ]
+ },
+ "execution_count": 51,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "ics = [(i, sum(index_of_coincidence(section) for section in every_nth(scb, i)) / i)\n",
+ " for i in range(1, 20)]\n",
+ "sorted(ics, key=lambda p: p[1])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 52,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "'etgyasrlhmbcnikuodwfqvjpxz'"
+ ]
+ },
+ "execution_count": 52,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "ctls = [p[0] for p in collections.Counter(sanitise(vb5)).most_common()]\n",
+ "cat(ctls)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 53,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "{'a': 'i',\n",
+ " 'b': 'l',\n",
+ " 'c': 'u',\n",
+ " 'd': 'g',\n",
+ " 'e': 'e',\n",
+ " 'f': 'b',\n",
+ " 'g': 'o',\n",
+ " 'h': 'r',\n",
+ " 'i': 'w',\n",
+ " 'j': 'x',\n",
+ " 'k': 'y',\n",
+ " 'l': 's',\n",
+ " 'm': 'd',\n",
+ " 'n': 'm',\n",
+ " 'o': 'f',\n",
+ " 'p': 'j',\n",
+ " 'q': 'v',\n",
+ " 'r': 'h',\n",
+ " 's': 'n',\n",
+ " 't': 't',\n",
+ " 'u': 'c',\n",
+ " 'v': 'k',\n",
+ " 'w': 'p',\n",
+ " 'x': 'q',\n",
+ " 'y': 'a',\n",
+ " 'z': 'z'}"
+ ]
+ },
+ "execution_count": 53,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "trans = {pr[1]: pr[0] for pr in zip(ltrs, ctls)}\n",
+ "trans"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 54,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Pfwq spkxhbm nuc jvymakoeeun xh etfv nnhcpm en hpj dlolfslvlj punapbcol dbx aqfrtsf bu Lcxgnlnmh rnk\n",
+ "dsopcdyf hu Hqte gr wtt odh gz vgd oead, nv iclyerk bj dce Mqftcfq. Or lphnek sol rfuy wrshvzhe mae\n",
+ "et uywrk tld gp Ggnyjxta hu bqdc Adcytkra Eyimepoi pae Hovbaaoy Kaneabd, lxb gor Wtuu reds ctkwqgyi\n",
+ "opq, sexniufkvh hcp rxii yi oho Erfrz hdtathse e twxerv, epxrn yaym jf t irr lqoah agnbflyl zstz dce\n",
+ "Hbuzz. Smuktrro mcygvykt gg jeahnk cwl eirtyf cohkftv yi oho Mpcaho hu a ypeh ib ercjaad, dce\n",
+ "Mqftcfq lcahdq ctx wtt bmgs wl tekes gbvxcmxsb wppasajl gsv, br wqe ylndswll ib orqr, cp cri aonicol\n",
+ "geaowakah gskicmgbwny. H uvascl afo yermndq gp sol Rodtwr uc eke udflb ec gor Ewaasud, ubz, jmisl\n",
+ "tlr, Rwswdddf ptl xece du hwtl oxxywkp tp kti knnyga ylhand iceds tt yma wrnzdq gp sol Tmsyews’y qin\n",
+ "dlhvyixtax. Cmk qeaso zet m ueaedqfi ilgku jf os, inz h hathibd ozyah vt hch cwl wbpwbfx. Tn cri\n",
+ "lmpo et m qsih lqui evsu lu bdgxe nq icux ol afo bgh dpmuunppym adrjl. Nuzzesf, qdbofksj, uq eke\n",
+ "dymbdfey nlk nia gofh netoyl oawd du cwl tixoy; nlk mp hogjbk vgmxsx, acu icedsrtc ybghi tf dahtsir\n",
+ "ywqs iiydtghmf, tyt dtqr rok xn ajp yylo; bgj kmu r ithvyt tfmp wn gdrsthv dj zxa frani nejybkt fb\n",
+ "gor npcu us kgixob fw tomnwhp. Sojr taomivgj iai wakbomasb gi eke rwfl-rlvynb suzxos sogz zw dnb\n",
+ "gpdj sexgrtv kw mwtyqj un sol Tmsyews’y nxyypae. Ceufwaac rxiv hg oymw fa idjqenvwac zzw tqvtnvxoi\n",
+ "Fnyptbfcx Hunfhhil gr jjy arr aqseshgi ib bds cstwenmm. Ol afo xoisadq rtso vtirqblw nia lspvjdo\n",
+ "synoe fvdc Mpplcedfn inz iaakmf ohovy ww mtpgsuhw Kegttdyi. Me iai yaym jjya hcoe dce jgvr ec gor\n",
+ "Mtuaz rpl ithwtprk. Yhcoisbbr yvrwr uq iei ilgku fb gor npcu tn p plzjwq ww Moex kasse zzw nbzvg urp\n",
+ "gmidbo bds maldjki yi Neu xb wqe idrs uk wtt ifblyhyiedi yi Neceml Ishdptnil Kdouyax TM hjk Hidjpt\n",
+ "Ktsvtuk Kunyegzxo. “Es isp dvdsmlvdu ednjwpu zp uie nitb ocltqvpens nuc rvynh Ewaasud Fdfpbfnl vt\n",
+ "kamods Fen Vrqino vj vcm Adbjjyel Xmpmkuxl gsv ayw fuu cntvm wd fo fn wqe lqui uk wtt Ifhsd\n",
+ "Urnfhvyebf. Ufme keouas Aeejwent oii orneagh fepl gor dhvpe uk lbwz m vgxi, bld tl rgb ce drw\n",
+ "jerqses sol Welyx veqqne vls fetofap uq eke pmni zzwb hwo who ge fcit jjtw fuu cehh cgjs zxo. Hwo\n",
+ "ndch ym wtej xbwrhlvmenas wbeu t didnlnek fmq rxtusxvy ekav sol zcmfwws Rhcwaacv hfo gor Mtuaz ahf\n",
+ "jr mmalana iei iimficbyex tc eke Nyeoh. Yjfpr vgd Zasqniyfda wqewkahbml gbo rbpaxpro vu xnht mrxo wl\n",
+ "nia Mugsp, Wfnunooy vu an mfbwfbyf Juwhj ang ol fxog ur inqhdeg uljevy or mop qeisedm jjy lrmumevp\n",
+ "yi oho Xomrsmhc mtnolbt. Bds Ouzdv rtjp mt cwxgnehdq, us rgb, merq iiymro ang srtcarar ptl wnuo jjdg\n",
+ "sth hcp antmm. Nuc tf gg hcp kabehwt vfvg gz vgd dsom loica ri Iwhvpuhp bu azxxo hcp frve yi oho\n",
+ "dfahc ldmtuh nuc ec gor Mtuaz tslldb pfpk tc eeye qs ithwtprk. Updth sols pb ebkh bd moadflc sa gor\n",
+ "acneens Uglunraaac Sxkkmlv ib Pte.\n"
+ ]
+ }
+ ],
+ "source": [
+ "trans = {\n",
+ " 'a': 'i',\n",
+ " 'b': 'c',\n",
+ " 'c': 'r',\n",
+ " 'd': 'h',\n",
+ " 'e': 'a',\n",
+ " 'f': 'd',\n",
+ " 'g': 'e',\n",
+ " 'h': 'v',\n",
+ " 'i': 't',\n",
+ " 'j': 'z',\n",
+ " 'k': 'y',\n",
+ " 'l': 'g',\n",
+ " 'm': 'f',\n",
+ " 'n': 'l',\n",
+ " 'o': 'o',\n",
+ " 'p': 'j',\n",
+ " 'q': 'p',\n",
+ " 'r': 'n',\n",
+ " 's': 'm',\n",
+ " 't': 's',\n",
+ " 'u': 'w',\n",
+ " 'v': 'b',\n",
+ " 'w': 'x',\n",
+ " 'x': 'q',\n",
+ " 'y': 'u',\n",
+ " 'z': 'k'}\n",
+ "\n",
+ "tt = ''.maketrans(trans)\n",
+ "print(lcat(tpack(repunctuate(vb3.translate(tt), pub).split())))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 55,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "149.2"
+ ]
+ },
+ "execution_count": 55,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "len(scb)/15"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 56,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "[((7, 20, True), -192.8137923614311),\n",
+ " ((7, 0, True), -180.2911386965482),\n",
+ " ((7, 11, True), -183.95343106989503),\n",
+ " ((7, 2, True), -181.0350604256714),\n",
+ " ((7, 22, True), -192.14210180827317),\n",
+ " ((7, 8, True), -189.97699806864554),\n",
+ " ((7, 5, True), -183.3749838057261),\n",
+ " ((7, 20, True), -196.06251170079523),\n",
+ " ((7, 24, True), -192.12634724908915),\n",
+ " ((7, 6, True), -186.7317665812622),\n",
+ " ((7, 8, True), -186.75777446041903),\n",
+ " ((7, 11, True), -188.32887282416843),\n",
+ " ((7, 13, True), -193.76214231086124),\n",
+ " ((7, 24, True), -190.6840821835727),\n",
+ " ((7, 6, True), -188.8811192976835)]"
+ ]
+ },
+ "execution_count": 56,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "[affine_break(section) for section in every_nth(scb, 15)]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 57,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "[(7, 20, True),\n",
+ " (7, 0, True),\n",
+ " (7, 11, True),\n",
+ " (7, 2, True),\n",
+ " (7, 22, True),\n",
+ " (7, 8, True),\n",
+ " (7, 5, True),\n",
+ " (7, 20, True),\n",
+ " (7, 24, True),\n",
+ " (7, 6, True),\n",
+ " (7, 8, True),\n",
+ " (7, 11, True),\n",
+ " (7, 13, True),\n",
+ " (7, 24, True),\n",
+ " (7, 6, True)]"
+ ]
+ },
+ "execution_count": 57,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "akeys = [affine_break(section)[0] for section in every_nth(scb, 15)]\n",
+ "akeys"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 58,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "'withresolveandtrepidationmydearfatherinlawobedientlyconcludedhisaffairsinbritanniaandreturnedtoromebytheendoftheyearasorderedbytheemperorhefearedthesameterribleendasmetedoutbydomitiantobotharulenusrusitcusandhernniusseneciobutthegodsweregenerousandremarkablythelossofthecodexremainedasecretknownonlytoafewloyalcomradesfromtheninthpubliclychoosingtoregardhisdoggedpursuitoftheaquilaasamarkofcouragetheemperorsparedhimthefateofotherdisgracedgeneralsandtothesurpriseofsomehewasawardedtriumphaldecorationsastatuewaserectedbythesenateontheorderoftheemperorbuttakenillagricolawassenttolivequietlyonhisfamilyestatewherehewastendedbytheemperorsownphysicianshisdeathwasagrievousshocktomeandapainfuleventtoallhisfriendsitwasfeltasareallossevenbythosetowhomhewasnotpersonallyknownnumbersmoreoverofthepopulaceandthebusymassescametohishouseandinpublicplacesandwhereverknotsoftalkerswereassembledhisnamewasonalllipsnordidasinglesoulonhearingofhisdeathrejoiceatthenewsorforgetitquicklythissympathywasincreasedbythewidespreadrumourthathehadbeenremovedbypoisonontheemperorscommanddomitianlostnotimeinappointingtheambitioussalustiuslucullusasthenewgovernoroftheprovincehewaschargedwithsecuringthefragilepeacewithcaledoniaandheadedtheretoconfrontcalgacusitwasonlythenthatthelossofthecodexwasrevealedsalustiuswroteofhisshockatthenewsinalettertocatodatedthefifthdaybeforethekalendsofmayintheyearoftheconsulshipsofmarcusarrecinusclemensiiandluciusbaebiushonoratusweareexpresslychargedbyourmostmunificentandgreatemperordomitiantosecurepaxromanainthenorthernkingdomsandyetyouwritetomeofthelossofthecodexoccultorumyourleaderagricolahasalreadypaidthepriceofsuchalossbutifyoudonotrecoverthecodexbeforethepassingoftheyearthenyoumaybesurethatyouwilljoinhimyoutellmethatintelligencefromacapturedspysuggeststhatthetraitorcalgacushasthecodexandismassinghissupportersinthenorthwhilethebarbariansthemselvesareunlikelytomakemuchofthecodexcalgacusisaneducatedromanandhemustbestoppedbeforehecandestroythesecurityoftheimperialciphersthecodexwillberecoveredoryouyourfamilyandeveryoneyouknowwillpaythepriceandsototheseventhpartofthetruestoryofagricolainwhichthefateoftheninthlegionandofthecodexitselfwillintimeberevealeduntilthenitwillbeguardedbytheancientbabyloniangoddessofwar'"
+ ]
+ },
+ "execution_count": 58,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "spb = combine_every_nth(affine_decipher(section, key[0], key[1], key[2]) \n",
+ " for key, section in zip(akeys, every_nth(scb, 15)))\n",
+ "spb"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 59,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "With resolve and trepidation my dear father in law obediently concluded his affairs in Britannia and\n",
+ "returned to Rome by the end of the year, as ordered by the Emperor. He feared the same terrible end\n",
+ "as meted out by Domitian to both Arulenus Rusitcus and Hernnius Senecio, but the Gods were generous\n",
+ "and, remarkably the loss of the Codex remained a secret, known only to a few loyal comrades from the\n",
+ "Ninth. Publicly choosing to regard his dogged pursuit of the Aquila as a mark of courage, the\n",
+ "Emperor spared him the fate of other disgraced generals and, to the surprise of some, he was awarded\n",
+ "triumphal decorations. A statue was erected by the Senate on the order of the Emperor, but, taken\n",
+ "ill, Agricola was sent to live quietly on his family estate where he was tended by the Emperor’s own\n",
+ "physicians. His death was a grievous shock to me, and a painful event to all his friends. It was\n",
+ "felt as a real loss even by those to whom he was not personally known. Numbers, moreover, of the\n",
+ "populace and the busy masses came to his house; and in public places, and wherever knots of talkers\n",
+ "were assembled, his name was on all lips; nor did a single soul on hearing of his death rejoice at\n",
+ "the news or forget it quickly. This sympathy was increased by the wide-spread rumour that he had\n",
+ "been removed by poison on the Emperor’s command. Domitian lost no time in appointing the ambitious\n",
+ "Salustius Lucullus as the new governor of the province. He was charged with securing the fragile\n",
+ "peace with Caledonia and headed there to confront Calgacus. It was only then that the loss of the\n",
+ "Codex was revealed. Salustius wrote of his shock at the news in a letter to Cato dated the fifth day\n",
+ "before the kalends of May in the year of the consulships of Marcus Arrecinus Clemens II and Lucius\n",
+ "Baebius Honoratus. “We are expressly charged by our most munificent and great Emperor Domitian to\n",
+ "secure Pax Romana in the Northern Kingdoms and yet you write to me of the loss of the Codex\n",
+ "Occultorum. Your leader Agricola has already paid the price of such a loss, but if you do not\n",
+ "recover the Codex before the passing of the year then you may be sure that you will join him. You\n",
+ "tell me that intelligence from a captured spy suggests that the traitor Calgacus has the Codex and\n",
+ "is massing his supporters in the North. While the Barbarians themselves are unlikely to make much of\n",
+ "the Codex, Calgacus is an educated Roman and he must be stopped before he can destroy the security\n",
+ "of the Imperial ciphers. The Codex will be recovered, or you, your family and everyone you know will\n",
+ "pay the price. And so to the seventh part of the true story of Agricola in which the fate of the\n",
+ "ninth legion and of the Codex itself will in time be revealed. Until then it will be guarded by the\n",
+ "ancient Babylonian Goddess of War.\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(lcat(tpack(repunctuate(spb, pub).split())))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 60,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "'ualcwifuygilnyg'"
+ ]
+ },
+ "execution_count": 60,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "cat(unpos(k[1]) for k in akeys)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 61,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "'h'"
+ ]
+ },
+ "execution_count": 61,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "unpos(7)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 62,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "2793"
+ ]
+ },
+ "execution_count": 62,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "open('6b.plaintext', 'w').write(lcat(tpack(repunctuate(spb, pub).split())))"
+ ]