Hill ciphers enciphering and deciphering done
authorNeil Smith <neil.git@njae.me.uk>
Sun, 30 Nov 2014 12:32:39 +0000 (12:32 +0000)
committerNeil Smith <neil.git@njae.me.uk>
Wed, 4 Oct 2017 08:08:13 +0000 (09:08 +0100)
cipher.py
hill-ciphers.ipynb [new file with mode: 0644]

index 6cce72543224f02468e8a7609b674d158d238127..cd75d2cc505f61d788ac190cc186810ff32f6b04 100644 (file)
--- a/cipher.py
+++ b/cipher.py
@@ -3,6 +3,9 @@ import collections
 import math
 from enum import Enum
 from itertools import zip_longest, cycle, chain
+import numpy as np
+from numpy import matrix
+from numpy import linalg
 from language_models import *
 
 
@@ -630,6 +633,44 @@ def railfence_decipher(message, height, fillvalue=''):
     return ''.join(c for r in zip_longest(*(down_rows + up_rows), fillvalue='') for c in r)
 
 
+def hill_encipher(matrix, message_letters, fillvalue='a'):
+    """Hill cipher
+
+    >>> hill_encipher(np.matrix([[7,8], [11,11]]), 'hellothere')
+    'drjiqzdrvx'
+    >>> hill_encipher(np.matrix([[6, 24, 1], [13, 16, 10], [20, 17, 15]]), \
+        'hello there')
+    'tfjflpznvyac'
+    """
+    n = len(matrix)
+    sanitised_message = sanitise(message_letters)
+    if len(sanitised_message) % n != 0:
+        padding = fillvalue[0] * (n - len(sanitised_message) % n)
+    else:
+        padding = ''
+    message = [ord(c) - ord('a') for c in sanitised_message + padding]
+    message_chunks = [message[i:i+n] for i in range(0, len(message), n)]
+    # message_chunks = chunks(message, len(matrix), fillvalue=None)
+    enciphered_chunks = [((matrix * np.matrix(c).T).T).tolist()[0] 
+            for c in message_chunks]
+    return ''.join([chr(int(round(l)) % 26 + ord('a')) 
+            for l in sum(enciphered_chunks, [])])
+
+
+def hill_decipher(matrix, message, fillvalue='a'):
+    """Hill cipher
+
+    >>> hill_decipher(np.matrix([[7,8], [11,11]]), 'drjiqzdrvx')
+    'hellothere'
+    >>> hill_decipher(np.matrix([[6, 24, 1], [13, 16, 10], [20, 17, 15]]), \
+        'tfjflpznvyac')
+    'hellothereaa'
+    """
+    adjoint = linalg.det(matrix)*linalg.inv(matrix)
+    inverse_determinant = modular_division_table[int(round(linalg.det(matrix))) % 26][1]
+    inverse_matrix = (inverse_determinant * adjoint) % 26
+    return hill_encipher(inverse_matrix, message, fillvalue)          
+
 class PocketEnigma(object):
     """A pocket enigma machine
     The wheel is internally represented as a 26-element list self.wheel_map, 
diff --git a/hill-ciphers.ipynb b/hill-ciphers.ipynb
new file mode 100644 (file)
index 0000000..909828a
--- /dev/null
@@ -0,0 +1,1392 @@
+{
+ "metadata": {
+  "name": "",
+  "signature": "sha256:0fe9165e4a65606dd93377df5e8d4ccf8671a4edf4adc8d5da4b33f83316516f"
+ },
+ "nbformat": 3,
+ "nbformat_minor": 0,
+ "worksheets": [
+  {
+   "cells": [
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "import matplotlib.pyplot as plt\n",
+      "import pandas as pd\n",
+      "import collections\n",
+      "import string\n",
+      "import numpy as np\n",
+      "from numpy import matrix\n",
+      "from numpy import linalg\n",
+      "%matplotlib inline\n",
+      "\n",
+      "from cipher import *\n",
+      "from cipherbreak import *\n",
+      "\n",
+      "c6a = open('2014/6a.ciphertext').read()\n",
+      "c6b = open('2014/6b.ciphertext').read()"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [],
+     "prompt_number": 54
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "key_a, score = railfence_break(sanitise(c6a))\n",
+      "key_a, score"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 3,
+       "text": [
+        "(3, -2314.997881051078)"
+       ]
+      }
+     ],
+     "prompt_number": 3
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "' '.join(segment(railfence_decipher(sanitise(c6a), key_a)))"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 4,
+       "text": [
+        "'mark the last message told usa lot the scuttling equipment is designed to pump water in and out of the vessel like a submarine dive control but clearly they werent planning to turn a container ship into a sub this ship is a largescale version of something i have seen in the caribbean drug runners use a similar technique to get below radar coverage for inshore runs sinking the vessel so that the deck remains just below the wave tops the fda pirates seem more interested in staying away from shore but getting close enough to track and record electronic communications without detection i am guessing this scuttling system is what they call nautilus in their log but i am still baffled by the references to seahorse the next page of the log looks harder to crack but the cipher clerk tells me it is a hill cipher and that they must have been in a hurry or have been enciphering by hand since they just used a two by two matrix actually we have been pretty lax with our security and i think the next message is end will use avi genere cipher given that we are using secure cables i dont think we have too much to worry about so i will keep the keyword short say three characters more later harry'"
+       ]
+      }
+     ],
+     "prompt_number": 4
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "c6bs = sanitise(c6b)\n",
+      "c6bs"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 7,
+       "text": [
+        "'hwssswxfewhhrfewpdrvttdhxbccleayphalnadhiehaoudrotwnrrvysabjlttbaytmelrkaidopthatlelrtwaamaneksvvzrvllatkcrjquicizgtoqcpnrrkttowandqehtqrvtbaydqealannohulanuzlwextlvjrvivhnohdqmgykaclmswrupdetfioftfelhzpxhaswftwprrsweiseohefpdrvttnvagdvswgoerbetnharvaeevtlltbmgaiatgelinmdawevhatterdhrznbnvoutnefoteveaehlaymhacglzeptvvdimworfisgtuzlwibeqohubtghamqornjnnrumqvjtxeltfovgawdaeevllgrtxibgtibevmpsaateoasevaeyqohameonncfuidoefafattemuimnflznbekofobrliaehhauihnnnwzaeevtlltpaalnanvtzlzuucptaelinanpaahewfthaosetaribnbnvhaevdhyytlmuxb'"
+       ]
+      }
+     ],
+     "prompt_number": 7
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "c6b_nums = [ord(c) - ord('a') for c in c6bs]\n",
+      "c6b_nums"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 17,
+       "text": [
+        "[7,\n",
+        " 22,\n",
+        " 18,\n",
+        " 18,\n",
+        " 18,\n",
+        " 22,\n",
+        " 23,\n",
+        " 5,\n",
+        " 4,\n",
+        " 22,\n",
+        " 7,\n",
+        " 7,\n",
+        " 17,\n",
+        " 5,\n",
+        " 4,\n",
+        " 22,\n",
+        " 15,\n",
+        " 3,\n",
+        " 17,\n",
+        " 21,\n",
+        " 19,\n",
+        " 19,\n",
+        " 3,\n",
+        " 7,\n",
+        " 23,\n",
+        " 1,\n",
+        " 2,\n",
+        " 2,\n",
+        " 11,\n",
+        " 4,\n",
+        " 0,\n",
+        " 24,\n",
+        " 15,\n",
+        " 7,\n",
+        " 0,\n",
+        " 11,\n",
+        " 13,\n",
+        " 0,\n",
+        " 3,\n",
+        " 7,\n",
+        " 8,\n",
+        " 4,\n",
+        " 7,\n",
+        " 0,\n",
+        " 14,\n",
+        " 20,\n",
+        " 3,\n",
+        " 17,\n",
+        " 14,\n",
+        " 19,\n",
+        " 22,\n",
+        " 13,\n",
+        " 17,\n",
+        " 17,\n",
+        " 21,\n",
+        " 24,\n",
+        " 18,\n",
+        " 0,\n",
+        " 1,\n",
+        " 9,\n",
+        " 11,\n",
+        " 19,\n",
+        " 19,\n",
+        " 1,\n",
+        " 0,\n",
+        " 24,\n",
+        " 19,\n",
+        " 12,\n",
+        " 4,\n",
+        " 11,\n",
+        " 17,\n",
+        " 10,\n",
+        " 0,\n",
+        " 8,\n",
+        " 3,\n",
+        " 14,\n",
+        " 15,\n",
+        " 19,\n",
+        " 7,\n",
+        " 0,\n",
+        " 19,\n",
+        " 11,\n",
+        " 4,\n",
+        " 11,\n",
+        " 17,\n",
+        " 19,\n",
+        " 22,\n",
+        " 0,\n",
+        " 0,\n",
+        " 12,\n",
+        " 0,\n",
+        " 13,\n",
+        " 4,\n",
+        " 10,\n",
+        " 18,\n",
+        " 21,\n",
+        " 21,\n",
+        " 25,\n",
+        " 17,\n",
+        " 21,\n",
+        " 11,\n",
+        " 11,\n",
+        " 0,\n",
+        " 19,\n",
+        " 10,\n",
+        " 2,\n",
+        " 17,\n",
+        " 9,\n",
+        " 16,\n",
+        " 20,\n",
+        " 8,\n",
+        " 2,\n",
+        " 8,\n",
+        " 25,\n",
+        " 6,\n",
+        " 19,\n",
+        " 14,\n",
+        " 16,\n",
+        " 2,\n",
+        " 15,\n",
+        " 13,\n",
+        " 17,\n",
+        " 17,\n",
+        " 10,\n",
+        " 19,\n",
+        " 19,\n",
+        " 14,\n",
+        " 22,\n",
+        " 0,\n",
+        " 13,\n",
+        " 3,\n",
+        " 16,\n",
+        " 4,\n",
+        " 7,\n",
+        " 19,\n",
+        " 16,\n",
+        " 17,\n",
+        " 21,\n",
+        " 19,\n",
+        " 1,\n",
+        " 0,\n",
+        " 24,\n",
+        " 3,\n",
+        " 16,\n",
+        " 4,\n",
+        " 0,\n",
+        " 11,\n",
+        " 0,\n",
+        " 13,\n",
+        " 13,\n",
+        " 14,\n",
+        " 7,\n",
+        " 20,\n",
+        " 11,\n",
+        " 0,\n",
+        " 13,\n",
+        " 20,\n",
+        " 25,\n",
+        " 11,\n",
+        " 22,\n",
+        " 4,\n",
+        " 23,\n",
+        " 19,\n",
+        " 11,\n",
+        " 21,\n",
+        " 9,\n",
+        " 17,\n",
+        " 21,\n",
+        " 8,\n",
+        " 21,\n",
+        " 7,\n",
+        " 13,\n",
+        " 14,\n",
+        " 7,\n",
+        " 3,\n",
+        " 16,\n",
+        " 12,\n",
+        " 6,\n",
+        " 24,\n",
+        " 10,\n",
+        " 0,\n",
+        " 2,\n",
+        " 11,\n",
+        " 12,\n",
+        " 18,\n",
+        " 22,\n",
+        " 17,\n",
+        " 20,\n",
+        " 15,\n",
+        " 3,\n",
+        " 4,\n",
+        " 19,\n",
+        " 5,\n",
+        " 8,\n",
+        " 14,\n",
+        " 5,\n",
+        " 19,\n",
+        " 5,\n",
+        " 4,\n",
+        " 11,\n",
+        " 7,\n",
+        " 25,\n",
+        " 15,\n",
+        " 23,\n",
+        " 7,\n",
+        " 0,\n",
+        " 18,\n",
+        " 22,\n",
+        " 5,\n",
+        " 19,\n",
+        " 22,\n",
+        " 15,\n",
+        " 17,\n",
+        " 17,\n",
+        " 18,\n",
+        " 22,\n",
+        " 4,\n",
+        " 8,\n",
+        " 18,\n",
+        " 4,\n",
+        " 14,\n",
+        " 7,\n",
+        " 4,\n",
+        " 5,\n",
+        " 15,\n",
+        " 3,\n",
+        " 17,\n",
+        " 21,\n",
+        " 19,\n",
+        " 19,\n",
+        " 13,\n",
+        " 21,\n",
+        " 0,\n",
+        " 6,\n",
+        " 3,\n",
+        " 21,\n",
+        " 18,\n",
+        " 22,\n",
+        " 6,\n",
+        " 14,\n",
+        " 4,\n",
+        " 17,\n",
+        " 1,\n",
+        " 4,\n",
+        " 19,\n",
+        " 13,\n",
+        " 7,\n",
+        " 0,\n",
+        " 17,\n",
+        " 21,\n",
+        " 0,\n",
+        " 4,\n",
+        " 4,\n",
+        " 21,\n",
+        " 19,\n",
+        " 11,\n",
+        " 11,\n",
+        " 19,\n",
+        " 1,\n",
+        " 12,\n",
+        " 6,\n",
+        " 0,\n",
+        " 8,\n",
+        " 0,\n",
+        " 19,\n",
+        " 6,\n",
+        " 4,\n",
+        " 11,\n",
+        " 8,\n",
+        " 13,\n",
+        " 12,\n",
+        " 3,\n",
+        " 0,\n",
+        " 22,\n",
+        " 4,\n",
+        " 21,\n",
+        " 7,\n",
+        " 0,\n",
+        " 19,\n",
+        " 19,\n",
+        " 4,\n",
+        " 17,\n",
+        " 3,\n",
+        " 7,\n",
+        " 17,\n",
+        " 25,\n",
+        " 13,\n",
+        " 1,\n",
+        " 13,\n",
+        " 21,\n",
+        " 14,\n",
+        " 20,\n",
+        " 19,\n",
+        " 13,\n",
+        " 4,\n",
+        " 5,\n",
+        " 14,\n",
+        " 19,\n",
+        " 4,\n",
+        " 21,\n",
+        " 4,\n",
+        " 0,\n",
+        " 4,\n",
+        " 7,\n",
+        " 11,\n",
+        " 0,\n",
+        " 24,\n",
+        " 12,\n",
+        " 7,\n",
+        " 0,\n",
+        " 2,\n",
+        " 6,\n",
+        " 11,\n",
+        " 25,\n",
+        " 4,\n",
+        " 15,\n",
+        " 19,\n",
+        " 21,\n",
+        " 21,\n",
+        " 3,\n",
+        " 8,\n",
+        " 12,\n",
+        " 22,\n",
+        " 14,\n",
+        " 17,\n",
+        " 5,\n",
+        " 8,\n",
+        " 18,\n",
+        " 6,\n",
+        " 19,\n",
+        " 20,\n",
+        " 25,\n",
+        " 11,\n",
+        " 22,\n",
+        " 8,\n",
+        " 1,\n",
+        " 4,\n",
+        " 16,\n",
+        " 14,\n",
+        " 7,\n",
+        " 20,\n",
+        " 1,\n",
+        " 19,\n",
+        " 6,\n",
+        " 7,\n",
+        " 0,\n",
+        " 12,\n",
+        " 16,\n",
+        " 14,\n",
+        " 17,\n",
+        " 13,\n",
+        " 9,\n",
+        " 13,\n",
+        " 13,\n",
+        " 17,\n",
+        " 20,\n",
+        " 12,\n",
+        " 16,\n",
+        " 21,\n",
+        " 9,\n",
+        " 19,\n",
+        " 23,\n",
+        " 4,\n",
+        " 11,\n",
+        " 19,\n",
+        " 5,\n",
+        " 14,\n",
+        " 21,\n",
+        " 6,\n",
+        " 0,\n",
+        " 22,\n",
+        " 3,\n",
+        " 0,\n",
+        " 4,\n",
+        " 4,\n",
+        " 21,\n",
+        " 11,\n",
+        " 11,\n",
+        " 6,\n",
+        " 17,\n",
+        " 19,\n",
+        " 23,\n",
+        " 8,\n",
+        " 1,\n",
+        " 6,\n",
+        " 19,\n",
+        " 8,\n",
+        " 1,\n",
+        " 4,\n",
+        " 21,\n",
+        " 12,\n",
+        " 15,\n",
+        " 18,\n",
+        " 0,\n",
+        " 0,\n",
+        " 19,\n",
+        " 4,\n",
+        " 14,\n",
+        " 0,\n",
+        " 18,\n",
+        " 4,\n",
+        " 21,\n",
+        " 0,\n",
+        " 4,\n",
+        " 24,\n",
+        " 16,\n",
+        " 14,\n",
+        " 7,\n",
+        " 0,\n",
+        " 12,\n",
+        " 4,\n",
+        " 14,\n",
+        " 13,\n",
+        " 13,\n",
+        " 2,\n",
+        " 5,\n",
+        " 20,\n",
+        " 8,\n",
+        " 3,\n",
+        " 14,\n",
+        " 4,\n",
+        " 5,\n",
+        " 0,\n",
+        " 5,\n",
+        " 0,\n",
+        " 19,\n",
+        " 19,\n",
+        " 4,\n",
+        " 12,\n",
+        " 20,\n",
+        " 8,\n",
+        " 12,\n",
+        " 13,\n",
+        " 5,\n",
+        " 11,\n",
+        " 25,\n",
+        " 13,\n",
+        " 1,\n",
+        " 4,\n",
+        " 10,\n",
+        " 14,\n",
+        " 5,\n",
+        " 14,\n",
+        " 1,\n",
+        " 17,\n",
+        " 11,\n",
+        " 8,\n",
+        " 0,\n",
+        " 4,\n",
+        " 7,\n",
+        " 7,\n",
+        " 0,\n",
+        " 20,\n",
+        " 8,\n",
+        " 7,\n",
+        " 13,\n",
+        " 13,\n",
+        " 13,\n",
+        " 22,\n",
+        " 25,\n",
+        " 0,\n",
+        " 4,\n",
+        " 4,\n",
+        " 21,\n",
+        " 19,\n",
+        " 11,\n",
+        " 11,\n",
+        " 19,\n",
+        " 15,\n",
+        " 0,\n",
+        " 0,\n",
+        " 11,\n",
+        " 13,\n",
+        " 0,\n",
+        " 13,\n",
+        " 21,\n",
+        " 19,\n",
+        " 25,\n",
+        " 11,\n",
+        " 25,\n",
+        " 20,\n",
+        " 20,\n",
+        " 2,\n",
+        " 15,\n",
+        " 19,\n",
+        " 0,\n",
+        " 4,\n",
+        " 11,\n",
+        " 8,\n",
+        " 13,\n",
+        " 0,\n",
+        " 13,\n",
+        " 15,\n",
+        " 0,\n",
+        " 0,\n",
+        " 7,\n",
+        " 4,\n",
+        " 22,\n",
+        " 5,\n",
+        " 19,\n",
+        " 7,\n",
+        " 0,\n",
+        " 14,\n",
+        " 18,\n",
+        " 4,\n",
+        " 19,\n",
+        " 0,\n",
+        " 17,\n",
+        " 8,\n",
+        " 1,\n",
+        " 13,\n",
+        " 1,\n",
+        " 13,\n",
+        " 21,\n",
+        " 7,\n",
+        " 0,\n",
+        " 4,\n",
+        " 21,\n",
+        " 3,\n",
+        " 7,\n",
+        " 24,\n",
+        " 24,\n",
+        " 19,\n",
+        " 11,\n",
+        " 12,\n",
+        " 20,\n",
+        " 23,\n",
+        " 1]"
+       ]
+      }
+     ],
+     "prompt_number": 17
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "m = np.matrix([[7,8], [11,11]])\n",
+      "m"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 106,
+       "text": [
+        "matrix([[ 7,  8],\n",
+        "        [11, 11]])"
+       ]
+      }
+     ],
+     "prompt_number": 106
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "np.linalg.det(m)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 107,
+       "text": [
+        "-11.000000000000002"
+       ]
+      }
+     ],
+     "prompt_number": 107
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "m.I"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 108,
+       "text": [
+        "matrix([[-1.        ,  0.72727273],\n",
+        "        [ 1.        , -0.63636364]])"
+       ]
+      }
+     ],
+     "prompt_number": 108
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "v = np.matrix([[7], [22]])\n",
+      "v"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 37,
+       "text": [
+        "matrix([[ 7],\n",
+        "        [22]])"
+       ]
+      }
+     ],
+     "prompt_number": 37
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "c = (m*v) % 26\n",
+      "c"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 46,
+       "text": [
+        "matrix([[9],\n",
+        "        [5]])"
+       ]
+      }
+     ],
+     "prompt_number": 46
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "np.linalg.solve(m, c) % 26"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 48,
+       "text": [
+        "matrix([[  7.],\n",
+        "        [ 22.]])"
+       ]
+      }
+     ],
+     "prompt_number": 48
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "m*v"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 40,
+       "text": [
+        "matrix([[ 87],\n",
+        "        [109]])"
+       ]
+      }
+     ],
+     "prompt_number": 40
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "(m*v)%26"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 41,
+       "text": [
+        "matrix([[9],\n",
+        "        [5]])"
+       ]
+      }
+     ],
+     "prompt_number": 41
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "np.linalg.solve(m, (m*v)%26)%26"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 42,
+       "text": [
+        "matrix([[  7.],\n",
+        "        [ 22.]])"
+       ]
+      }
+     ],
+     "prompt_number": 42
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "len(m)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 51,
+       "text": [
+        "2"
+       ]
+      }
+     ],
+     "prompt_number": 51
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "def hill_encipher(matrix, message_letters, fillvalue='a'):\n",
+      "    n = len(matrix)\n",
+      "    sanitised_message = sanitise(message_letters)\n",
+      "    if len(sanitised_message) % n != 0:\n",
+      "        padding = fillvalue[0] * (n - len(sanitised_message) % n)\n",
+      "    else:\n",
+      "        padding = ''\n",
+      "    message = [ord(c) - ord('a') for c in sanitised_message + padding]\n",
+      "    message_chunks = [message[i:i+n] for i in range(0, len(message), n)]\n",
+      "    # message_chunks = chunks(message, len(matrix), fillvalue=None)\n",
+      "    enciphered_chunks = [((matrix * np.matrix(c).T).T).tolist()[0] for c in message_chunks]\n",
+      "    return ''.join([chr(int(round(l)) % 26 + ord('a')) for l in sum(enciphered_chunks, [])])"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [],
+     "prompt_number": 181
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "hill_encipher(m, 'hellothere')"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 156,
+       "text": [
+        "'drjiqzdrvx'"
+       ]
+      }
+     ],
+     "prompt_number": 156
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "msg = [ord(c) - ord('a') for c in 'hellothere']\n",
+      "msg"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 68,
+       "text": [
+        "[7, 4, 11, 11, 14, 19, 7, 4, 17, 4]"
+       ]
+      }
+     ],
+     "prompt_number": 68
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "msgc = [msg[i:i+len(m)] for i in range(0, len(msg), len(m))]\n",
+      "msgc"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 112,
+       "text": [
+        "[[7, 11], [14, 25], [21, 14], [7, 11], [11, 15], [0, 0]]"
+       ]
+      }
+     ],
+     "prompt_number": 112
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "((m*np.matrix(msgc[0]).T).T % 26).tolist()[0]"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 87,
+       "text": [
+        "[7, 11]"
+       ]
+      }
+     ],
+     "prompt_number": 87
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "def hill_decipher(matrix, message, fillvalue='a'):\n",
+      "    adjugate = linalg.det(matrix)*linalg.inv(matrix)\n",
+      "    inverse_determinant = modular_division_table[int(round(linalg.det(matrix))) % 26][1]\n",
+      "    inverse_matrix = (inverse_determinant * adjugate) % 26\n",
+      "    return hill_encipher(inverse_matrix, message, fillvalue)                                                 "
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [],
+     "prompt_number": 195
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "hill_decipher(m, 'drjiqzdrvx')"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 161,
+       "text": [
+        "'hellothere'"
+       ]
+      }
+     ],
+     "prompt_number": 161
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "msg = [ord(c) - ord('a') for c in 'drjiqzdrvxaa']\n",
+      "msg"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 114,
+       "text": [
+        "[3, 17, 9, 8, 16, 25, 3, 17, 21, 23, 0, 0]"
+       ]
+      }
+     ],
+     "prompt_number": 114
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "msgc = [msg[i:i+len(m)] for i in range(0, len(msg), len(m))]\n",
+      "msgc"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 115,
+       "text": [
+        "[[3, 17], [9, 8], [16, 25], [3, 17], [21, 23], [0, 0]]"
+       ]
+      }
+     ],
+     "prompt_number": 115
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "(np.linalg.solve(m, np.matrix(msgc[0]).T).T % 26)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 116,
+       "text": [
+        "matrix([[  9.36363636,  18.18181818]])"
+       ]
+      }
+     ],
+     "prompt_number": 116
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "m_adj = linalg.det(m)*linalg.inv(m)\n",
+      "m_adj"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 142,
+       "text": [
+        "matrix([[ 11.,  -8.],\n",
+        "        [-11.,   7.]])"
+       ]
+      }
+     ],
+     "prompt_number": 142
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "modular_division_table[int(round(linalg.det(m))) % 26][1]"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 148,
+       "text": [
+        "7"
+       ]
+      }
+     ],
+     "prompt_number": 148
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "m_inv = (modular_division_table[int(round(linalg.det(m))) % 26][1] * m_adj) % 26\n",
+      "m_inv"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 150,
+       "text": [
+        "matrix([[ 25.,  22.],\n",
+        "        [  1.,  23.]])"
+       ]
+      }
+     ],
+     "prompt_number": 150
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "hill_encipher(m_inv, 'drjiqzdrvx')"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 157,
+       "text": [
+        "'hellothere'"
+       ]
+      }
+     ],
+     "prompt_number": 157
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "np.dot(m , 1/linalg.det(m) * mc)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 120,
+       "text": [
+        "matrix([[ 1.,  0.],\n",
+        "        [ 0.,  1.]])"
+       ]
+      }
+     ],
+     "prompt_number": 120
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "ml = np.matrix([[6, 24, 1], [13, 16, 10], [20, 17, 15]])\n",
+      "ml"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 122,
+       "text": [
+        "matrix([[ 6, 24,  1],\n",
+        "        [13, 16, 10],\n",
+        "        [20, 17, 15]])"
+       ]
+      }
+     ],
+     "prompt_number": 122
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "ml_adj = linalg.det(ml)*linalg.inv(ml) % 26\n",
+      "ml_adj"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 137,
+       "text": [
+        "matrix([[ 18.,  21.,  16.],\n",
+        "        [  5.,  18.,   5.],\n",
+        "        [  5.,  14.,  18.]])"
+       ]
+      }
+     ],
+     "prompt_number": 137
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "modular_division_table[int(linalg.det(ml) % 26)][1]"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 138,
+       "text": [
+        "25"
+       ]
+      }
+     ],
+     "prompt_number": 138
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "ml_inv = (modular_division_table[int(linalg.det(ml) % 26)][1] * ml_adj) % 26\n",
+      "ml_inv"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 139,
+       "text": [
+        "matrix([[  8.,   5.,  10.],\n",
+        "        [ 21.,   8.,  21.],\n",
+        "        [ 21.,  12.,   8.]])"
+       ]
+      }
+     ],
+     "prompt_number": 139
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "hill_encipher(ml, 'hello there')"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 193,
+       "text": [
+        "'tfjflpznvyac'"
+       ]
+      }
+     ],
+     "prompt_number": 193
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "hill_decipher(ml, 'tfjflpznvyac')"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 196,
+       "text": [
+        "'hellothereaa'"
+       ]
+      }
+     ],
+     "prompt_number": 196
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "hill_encipher(ml, 'act')"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 182,
+       "text": [
+        "'poh'"
+       ]
+      }
+     ],
+     "prompt_number": 182
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "hill_decipher(ml, 'poh')"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "[[  8.   5.  10.]\n",
+        " [ 21.   8.  21.]\n",
+        " [ 21.  12.   8.]]\n"
+       ]
+      },
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 192,
+       "text": [
+        "'act'"
+       ]
+      }
+     ],
+     "prompt_number": 192
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "[chr(int(round(i)) % 26 + ord('a')) for i in (ml_inv * np.matrix([ord(c) - ord('a') for c in 'poh']).T).T.tolist()[0]]"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 180,
+       "text": [
+        "['a', 'c', 't']"
+       ]
+      }
+     ],
+     "prompt_number": 180
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "hill_encipher(ml_inv, 'poh')"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 184,
+       "text": [
+        "'act'"
+       ]
+      }
+     ],
+     "prompt_number": 184
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    }
+   ],
+   "metadata": {}
+  }
+ ]
+}
\ No newline at end of file