+ "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": 46,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "'etgyasrlhmbcinkoudfwqvjpxz'"
+ ]
+ },
+ "execution_count": 46,
+ "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": 47,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "{'a': 'i',\n",
+ " 'b': 'l',\n",
+ " 'c': 'u',\n",
+ " 'd': 'g',\n",
+ " 'e': 'e',\n",
+ " 'f': 'p',\n",
+ " 'g': 'o',\n",
+ " 'h': 'r',\n",
+ " 'i': 'm',\n",
+ " 'j': 'x',\n",
+ " 'k': 'y',\n",
+ " 'l': 's',\n",
+ " 'm': 'd',\n",
+ " 'n': 'w',\n",
+ " 'o': 'c',\n",
+ " 'p': 'j',\n",
+ " 'q': 'v',\n",
+ " 'r': 'h',\n",
+ " 's': 'n',\n",
+ " 't': 't',\n",
+ " 'u': 'f',\n",
+ " 'v': 'k',\n",
+ " 'w': 'b',\n",
+ " 'x': 'q',\n",
+ " 'y': 'a',\n",
+ " 'z': 'z'}"
+ ]
+ },
+ "execution_count": 47,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "trans = {pr[1]: pr[0] for pr in zip(ltrs, ctls)}\n",
+ "trans"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 48,
+ "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": 49,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "149.2"
+ ]
+ },
+ "execution_count": 49,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "len(scb)/15"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 50,
+ "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": 50,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "[affine_break(section) for section in every_nth(scb, 15)]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 51,
+ "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": 51,
+ "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": 52,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "'withresolveandtrepidationmydearfatherinlawobedientlyconcludedhisaffairsinbritanniaandreturnedtoromebytheendoftheyearasorderedbytheemperorhefearedthesameterribleendasmetedoutbydomitiantobotharulenusrusitcusandhernniusseneciobutthegodsweregenerousandremarkablythelossofthecodexremainedasecretknownonlytoafewloyalcomradesfromtheninthpubliclychoosingtoregardhisdoggedpursuitoftheaquilaasamarkofcouragetheemperorsparedhimthefateofotherdisgracedgeneralsandtothesurpriseofsomehewasawardedtriumphaldecorationsastatuewaserectedbythesenateontheorderoftheemperorbuttakenillagricolawassenttolivequietlyonhisfamilyestatewherehewastendedbytheemperorsownphysicianshisdeathwasagrievousshocktomeandapainfuleventtoallhisfriendsitwasfeltasareallossevenbythosetowhomhewasnotpersonallyknownnumbersmoreoverofthepopulaceandthebusymassescametohishouseandinpublicplacesandwhereverknotsoftalkerswereassembledhisnamewasonalllipsnordidasinglesoulonhearingofhisdeathrejoiceatthenewsorforgetitquicklythissympathywasincreasedbythewidespreadrumourthathehadbeenremovedbypoisonontheemperorscommanddomitianlostnotimeinappointingtheambitioussalustiuslucullusasthenewgovernoroftheprovincehewaschargedwithsecuringthefragilepeacewithcaledoniaandheadedtheretoconfrontcalgacusitwasonlythenthatthelossofthecodexwasrevealedsalustiuswroteofhisshockatthenewsinalettertocatodatedthefifthdaybeforethekalendsofmayintheyearoftheconsulshipsofmarcusarrecinusclemensiiandluciusbaebiushonoratusweareexpresslychargedbyourmostmunificentandgreatemperordomitiantosecurepaxromanainthenorthernkingdomsandyetyouwritetomeofthelossofthecodexoccultorumyourleaderagricolahasalreadypaidthepriceofsuchalossbutifyoudonotrecoverthecodexbeforethepassingoftheyearthenyoumaybesurethatyouwilljoinhimyoutellmethatintelligencefromacapturedspysuggeststhatthetraitorcalgacushasthecodexandismassinghissupportersinthenorthwhilethebarbariansthemselvesareunlikelytomakemuchofthecodexcalgacusisaneducatedromanandhemustbestoppedbeforehecandestroythesecurityoftheimperialciphersthecodexwillberecoveredoryouyourfamilyandeveryoneyouknowwillpaythepriceandsototheseventhpartofthetruestoryofagricolainwhichthefateoftheninthlegionandofthecodexitselfwillintimeberevealeduntilthenitwillbeguardedbytheancientbabyloniangoddessofwar'"
+ ]
+ },
+ "execution_count": 52,
+ "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": 53,
+ "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": 54,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "'ualcwifuygilnyg'"
+ ]
+ },
+ "execution_count": 54,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "cat(unpos(k[1]) for k in akeys)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 55,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "'h'"
+ ]
+ },
+ "execution_count": 55,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "unpos(7)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 56,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "2793"
+ ]
+ },
+ "execution_count": 56,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "open('6b.plaintext', 'w').write(lcat(tpack(repunctuate(spb, pub).split())))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# The \"model answer\"\n",
+ "The first time I've seen the National Cipher Challenge has used a zero-based affine cipher!"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 57,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "'withresolveandtrepidationmydearfatherinlawobedientlyconcludedhisaffairsinbritanniaandreturnedtoromebytheendoftheyearasorderedbytheemperorhefearedthesameterribleendasmetedoutbydomitiantobotharulenusrusitcusandhernniusseneciobutthegodsweregenerousandremarkablythelossofthecodexremainedasecretknownonlytoafewloyalcomradesfromtheninthpubliclychoosingtoregardhisdoggedpursuitoftheaquilaasamarkofcouragetheemperorsparedhimthefateofotherdisgracedgeneralsandtothesurpriseofsomehewasawardedtriumphaldecorationsastatuewaserectedbythesenateontheorderoftheemperorbuttakenillagricolawassenttolivequietlyonhisfamilyestatewherehewastendedbytheemperorsownphysicianshisdeathwasagrievousshocktomeandapainfuleventtoallhisfriendsitwasfeltasareallossevenbythosetowhomhewasnotpersonallyknownnumbersmoreoverofthepopulaceandthebusymassescametohishouseandinpublicplacesandwhereverknotsoftalkerswereassembledhisnamewasonalllipsnordidasinglesoulonhearingofhisdeathrejoiceatthenewsorforgetitquicklythissympathywasincreasedbythewidespreadrumourthathehadbeenremovedbypoisonontheemperorscommanddomitianlostnotimeinappointingtheambitioussalustiuslucullusasthenewgovernoroftheprovincehewaschargedwithsecuringthefragilepeacewithcaledoniaandheadedtheretoconfrontcalgacusitwasonlythenthatthelossofthecodexwasrevealedsalustiuswroteofhisshockatthenewsinalettertocatodatedthefifthdaybeforethekalendsofmayintheyearoftheconsulshipsofmarcusarrecinusclemensiiandluciusbaebiushonoratusweareexpresslychargedbyourmostmunificentandgreatemperordomitiantosecurepaxromanainthenorthernkingdomsandyetyouwritetomeofthelossofthecodexoccultorumyourleaderagricolahasalreadypaidthepriceofsuchalossbutifyoudonotrecoverthecodexbeforethepassingoftheyearthenyoumaybesurethatyouwilljoinhimyoutellmethatintelligencefromacapturedspysuggeststhatthetraitorcalgacushasthecodexandismassinghissupportersinthenorthwhilethebarbariansthemselvesareunlikelytomakemuchofthecodexcalgacusisaneducatedromanandhemustbestoppedbeforehecandestroythesecurityoftheimperialciphersthecodexwillberecoveredoryouyourfamilyandeveryoneyouknowwillpaythepriceandsototheseventhpartofthetruestoryofagricolainwhichthefateoftheninthlegionandofthecodexitselfwillintimeberevealeduntilthenitwillbeguardedbytheancientbabyloniangoddessofwar'"
+ ]
+ },
+ "execution_count": 57,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "affine_decipher(vigenere_decipher(scb, sanitise('Agricolae mortem')), \n",
+ " multiplier=7, adder=0, one_based=False)"