Skip to content

Instantly share code, notes, and snippets.

@subsr97
Created April 22, 2019 13:00
Show Gist options
  • Save subsr97/052f0f121e7a1df2db0005ac703f887b to your computer and use it in GitHub Desktop.
Save subsr97/052f0f121e7a1df2db0005ac703f887b to your computer and use it in GitHub Desktop.
Python implementation of common Cryptographic functions
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Cryptography Exercises\n",
"\n",
"\n",
"1. [Caesar Cipher](#Caesar-Cipher)\n",
"2. [Playfair Cipher](#Playfair-Cipher)\n",
"3. [Hill Cipher](#Hill-Cipher)\n",
"4. [Vigenere Cipher](#Vigenere-Cipher)\n",
"5. Transposition Techniques\n",
" * [Rail Fence Cipher](#Rail-Fence-Cipher)\n",
" * [Simple Transpositional Technique](#Simple-Transpositional-Technique)\n",
"6. [RSA](#RSA)\n",
"7. [MD5](#MD5)\n",
"8. [SHA](#SHA)\n",
"9. [Diffie Hellman](#Diffie-Hellman)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Caesar Cipher"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'a': 0, 'b': 1, 'c': 2, 'd': 3, 'e': 4, 'f': 5, 'g': 6, 'h': 7, 'i': 8, 'j': 9, 'k': 10, 'l': 11, 'm': 12, 'n': 13, 'o': 14, 'p': 15, 'q': 16, 'r': 17, 's': 18, 't': 19, 'u': 20, 'v': 21, 'w': 22, 'x': 23, 'y': 24, 'z': 25}\n",
"\n",
"{0: 'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e', 5: 'f', 6: 'g', 7: 'h', 8: 'i', 9: 'j', 10: 'k', 11: 'l', 12: 'm', 13: 'n', 14: 'o', 15: 'p', 16: 'q', 17: 'r', 18: 's', 19: 't', 20: 'u', 21: 'v', 22: 'w', 23: 'x', 24: 'y', 25: 'z'}\n"
]
}
],
"source": [
"alphabets = \"abcdefghijklmnopqrstuvwxyz\"\n",
"alphabetToNumericDict = {alphabets[x]:x for x in range(26)}\n",
"numericToAlphabetDict = {x:alphabets[x] for x in range(26)}\n",
"\n",
"print(alphabetToNumericDict)\n",
"print()\n",
"print(numericToAlphabetDict)"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Message: Hello world!\n",
"Secret Key: 5\n",
"Encrypted Message: MJQQT BTWQI!\n",
"Decrypted Message: hello world!\n"
]
}
],
"source": [
"def caesar_encrypt(plainText, key):\n",
" plainText = plainText.lower()\n",
" key %= 26\n",
" cipherText = \"\"\n",
" \n",
" for letter in plainText:\n",
" if not letter.isalpha():\n",
" cipherText += letter\n",
" continue\n",
" cipherText += numericToAlphabetDict[(alphabetToNumericDict[letter]+key) % 26]\n",
" \n",
" return cipherText.upper()\n",
"\n",
"def caesar_decrypt(cipherText, key):\n",
" cipherText = cipherText.lower()\n",
" key %= 26\n",
" plainText = \"\"\n",
" \n",
" for letter in cipherText:\n",
" if not letter.isalpha():\n",
" plainText += letter\n",
" continue\n",
" plainText += numericToAlphabetDict[(alphabetToNumericDict[letter]-key) % 26]\n",
" \n",
" return plainText\n",
"\n",
"message = \"Hello world!\"\n",
"secretKey = 5\n",
"\n",
"print(\"Message:\", message)\n",
"print(\"Secret Key:\", secretKey)\n",
"\n",
"encryptedMessage = caesar_encrypt(message, secretKey)\n",
"print(\"Encrypted Message:\", encryptedMessage)\n",
"\n",
"decryptedMessage = caesar_decrypt(encryptedMessage, secretKey)\n",
"print(\"Decrypted Message:\", decryptedMessage)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Playfair Cipher"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"P L A Y F \n",
"I R E X M \n",
"B C D G H \n",
"K N O Q S \n",
"T U V W Z \n",
"\n",
"Message: Hide the gold in the tree stump\n",
"Secret Key: playfair example\n",
"Encrypted Message: BMODZBXDNABEKUDMUIXMMOUVIF\n"
]
}
],
"source": [
"def findIndices(letter, matrix):\n",
" for i in range(5):\n",
" for j in range(5):\n",
" if matrix[i][j] == letter:\n",
" return (i,j)\n",
" \n",
"def playfair_encrypt(message, key):\n",
" # Removing spaces from the message and key and assuming I and J to be replaceable\n",
" message = message.upper().replace(' ','').replace('J','I')\n",
" key = key.upper().replace(' ','').replace('J','I')\n",
" \n",
" parsedMessage = message[0]\n",
" \n",
" # Forming digrams from the message\n",
" for letter in message[1:]:\n",
" if not letter.isalpha() or parsedMessage[-1] != letter:\n",
" parsedMessage += letter\n",
" else:\n",
" parsedMessage += \"X\" + letter\n",
" \n",
" # Adding an 'X' if the last digram doesn't have a pair\n",
" if len(parsedMessage)%2:\n",
" parsedMessage += \"X\"\n",
" \n",
" digrams = [(parsedMessage[x],parsedMessage[x+1]) for x in range(0,len(parsedMessage),2)]\n",
" # print(parsedMessage)\n",
" \n",
" # Assuming I and J to be replaceable\n",
" remaininingAlphabets = list(\"ABCDEFGHIKLMNOPQRSTUVWXYZ\")\n",
" \n",
" matrix = [[\"\" for _ in range(5)] for _ in range(5)]\n",
" \n",
" keyIndex = 0\n",
" \n",
" for i in range(5):\n",
" for j in range(5):\n",
" flag = 0\n",
" if keyIndex < len(key):\n",
" if key[keyIndex] in remaininingAlphabets:\n",
" matrix[i][j] = key[keyIndex]\n",
" flag = 1\n",
" remaininingAlphabets.remove(key[keyIndex])\n",
" else:\n",
" while keyIndex < len(key) and key[keyIndex] not in remaininingAlphabets:\n",
" keyIndex += 1\n",
" if keyIndex < len(key):\n",
" matrix[i][j] = key[keyIndex]\n",
" flag = 1\n",
" remaininingAlphabets.remove(key[keyIndex])\n",
" keyIndex += 1\n",
" if flag == 0:\n",
" matrix[i][j] = remaininingAlphabets.pop(0)\n",
" else:\n",
" matrix[i][j] = remaininingAlphabets.pop(0)\n",
" \n",
" for i in range(5):\n",
" for j in range(5):\n",
" print(matrix[i][j], end=\" \")\n",
" print()\n",
" print()\n",
" \n",
" encryptedMessage = \"\"\n",
" \n",
" for digram in digrams:\n",
" \n",
" firstIndex = findIndices(digram[0], matrix)\n",
" secondIndex = findIndices(digram[1], matrix)\n",
" \n",
" # Same row\n",
" if firstIndex[0] == secondIndex[0]:\n",
" encryptedMessage += matrix[firstIndex[0]][(firstIndex[1]+1)%5] + matrix[secondIndex[0]][(secondIndex[1]+1)%5]\n",
" # Same column\n",
" elif firstIndex[1] == secondIndex[1]:\n",
" encryptedMessage += matrix[(firstIndex[0]+1)%5][firstIndex[1]] + matrix[(secondIndex[0]+1)%5][secondIndex[1]]\n",
" # Forms a rectangle\n",
" else:\n",
" encryptedMessage += matrix[firstIndex[0]][secondIndex[1]] + matrix[secondIndex[0]][firstIndex[1]]\n",
" \n",
" return encryptedMessage\n",
"\n",
"message = \"Hide the gold in the tree stump\"\n",
"secretKey = \"playfair example\"\n",
"\n",
"encryptedMessage = playfair_encrypt(message, secretKey)\n",
"\n",
"print(\"Message:\", message)\n",
"print(\"Secret Key:\", secretKey)\n",
"print(\"Encrypted Message:\", encryptedMessage)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Hill Cipher"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Message: act\n",
"Secret Key: [[6, 24, 1], [13, 16, 10], [20, 17, 15]]\n",
"Message vectors: [[0, 2, 19]]\n",
"Encrypted vectors: [[15], [14], [7]]\n",
"Encrypted Message: POH\n"
]
}
],
"source": [
"def hill_encrypt(message, key):\n",
" message.lower().replace(' ','')\n",
" dimension = len(key)\n",
" \n",
" if len(message) % dimension != 0:\n",
" message += \"X\"*(len(message)%dimension)\n",
" \n",
" messageVector = [alphabetToNumericDict[letter] for letter in message]\n",
" \n",
" vectors = []\n",
" for i in range(0, len(messageVector), dimension):\n",
" vectors.append(messageVector[i:i+dimension])\n",
" \n",
" print(\"Message vectors:\", vectors)\n",
" \n",
" encryptedVectors = []\n",
" \n",
" for vector in vectors:\n",
" for row in key:\n",
" encryptedVectors.append([sum([row[i]*vector[i] for i in range(dimension)])%26])\n",
" \n",
" print(\"Encrypted vectors:\", encryptedVectors)\n",
" \n",
" encryptedMessage = \"\"\n",
" \n",
" for vector in encryptedVectors:\n",
" for value in vector:\n",
" encryptedMessage += numericToAlphabetDict[value]\n",
" \n",
" return encryptedMessage.upper()\n",
"\n",
"message = \"act\"\n",
"secretKey = [\n",
" [6, 24, 1 ],\n",
" [13, 16, 10],\n",
" [20, 17, 15],\n",
"]\n",
"\n",
"print(\"Message:\", message)\n",
"print(\"Secret Key:\", secretKey)\n",
"\n",
"encryptedMessage = hill_encrypt(message, secretKey)\n",
"\n",
"print(\"Encrypted Message:\", encryptedMessage)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Vigenere Cipher"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"vigenereSquare = [list(\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\")]\n",
"\n",
"for _ in range(25):\n",
" vigenereSquare.append(vigenereSquare[-1][1:]+[vigenereSquare[-1][0]])\n"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Message: attack at dawn\n",
"Secret Key: lemon\n",
"Encrypted Message: LXFOPV EF RNHR\n",
"Decrypted Message: attack at dawn\n"
]
}
],
"source": [
"def vigenere_encrypt(plainText, key):\n",
" plainText = plainText.lower()\n",
" key = key.lower()\n",
" keyIndex = 0\n",
" cipherText = \"\"\n",
" \n",
" for letter in plainText:\n",
" if not letter.isalpha():\n",
" cipherText += letter\n",
" continue\n",
" row = alphabetToNumericDict[key[keyIndex]]\n",
" cipherText += vigenereSquare[row][alphabetToNumericDict[letter]]\n",
" \n",
" keyIndex += 1\n",
" if keyIndex >= len(key):\n",
" keyIndex = 0\n",
" \n",
" return cipherText.upper()\n",
"\n",
"def vigenere_decrypt(cipherText, key):\n",
" cipherText = cipherText.lower()\n",
" key = key.lower()\n",
" keyIndex = 0\n",
" plainText = \"\"\n",
" \n",
" for letter in cipherText:\n",
" if not letter.isalpha():\n",
" plainText += letter\n",
" continue\n",
" row = alphabetToNumericDict[key[keyIndex]]\n",
" plainText += numericToAlphabetDict[vigenereSquare[row].index(letter.upper())]\n",
" \n",
" keyIndex += 1\n",
" if keyIndex >= len(key):\n",
" keyIndex = 0\n",
" \n",
" return plainText\n",
"\n",
"message = \"attack at dawn\"\n",
"secretKey = \"lemon\"\n",
"encryptedMessage = vigenere_encrypt(message, secretKey)\n",
"decryptedMessage = vigenere_decrypt(encryptedMessage, secretKey)\n",
"\n",
"print(\"Message:\", message)\n",
"print(\"Secret Key:\", secretKey)\n",
"print(\"Encrypted Message:\", encryptedMessage)\n",
"print(\"Decrypted Message:\", decryptedMessage)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Rail Fence Cipher"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Message: we are discovered flee at once\n",
"Depth: 3\n",
"Encrypted Message: WECRLTEERDSOEEFEAOCAIVDEN\n"
]
}
],
"source": [
"def railfence_encrypt(plainText, depth):\n",
" plainText = plainText.lower().replace(' ','')\n",
" railfence = [[] for _ in range(depth)]\n",
" currentRow = 0\n",
" currentIndex = 0\n",
" difference = 1\n",
" \n",
" while currentIndex < len(plainText):\n",
" railfence[currentRow].append(plainText[currentIndex])\n",
" currentIndex += 1\n",
" \n",
" if currentRow == 0:\n",
" difference = 1\n",
" elif currentRow == depth-1:\n",
" difference = -1\n",
" \n",
" currentRow += difference \n",
" \n",
" cipherText = \"\"\n",
" \n",
" for row in railfence:\n",
" for letter in row:\n",
" cipherText += letter\n",
" \n",
" return cipherText.upper()\n",
"\n",
"message = \"we are discovered flee at once\"\n",
"depth = 3\n",
"\n",
"encryptedMessage = railfence_encrypt(message, depth)\n",
"\n",
"print(\"Message:\", message)\n",
"print(\"Depth:\", depth)\n",
"print(\"Encrypted Message:\", encryptedMessage)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Simple Transpositional Technique"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Message: attack postponed until two am\n",
"Key: [4, 3, 1, 2, 5, 6, 7]\n",
"Encrypted Message: TTNAAPTMTSUOAODWCOIXKNLXPETX\n"
]
}
],
"source": [
"def transposition_encrypt(plainText, key):\n",
" \n",
" plainText = plainText.lower().replace(' ','')\n",
" \n",
" matrix = [[] for _ in range(len(key))]\n",
" plainTextList = list(plainText)\n",
" \n",
" while len(plainTextList):\n",
" for column in matrix:\n",
" if len(plainTextList):\n",
" column.append(plainTextList.pop(0))\n",
" else:\n",
" column.append(\"X\")\n",
" \n",
" cipherText = \"\"\n",
" \n",
" for i in range(1,len(key)+1):\n",
" cipherText += \"\".join(matrix[key.index(i)])\n",
" \n",
" return cipherText.upper()\n",
" \n",
" \n",
"message = \"attack postponed until two am\"\n",
"key = [4, 3, 1, 2, 5, 6, 7]\n",
"\n",
"encryptedMessage = transposition_encrypt(message, key)\n",
"\n",
"print(\"Message:\", message)\n",
"print(\"Key:\", key)\n",
"print(\"Encrypted Message:\", encryptedMessage)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### RSA"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"p: 71\n",
"q: 373\n",
"n: 26483\n",
"Totient: 13020\n",
"e: 11047\n",
"d: 10123\n",
"Message: 1997\n",
"Encrypted message: 7045\n",
"Decrypted message: 1997\n"
]
}
],
"source": [
"import math\n",
"import random\n",
"\n",
"def isPrime(n):\n",
" if n < 2:\n",
" return False\n",
" \n",
" for num in range(2,int(math.sqrt(n))+1):\n",
" if n%num == 0:\n",
" return False\n",
" \n",
" return True\n",
"\n",
"def generateRandomPrime(start, stop):\n",
" randomNum = random.randint(start, stop)\n",
" \n",
" while not isPrime(randomNum):\n",
" randomNum = random.randint(start, stop)\n",
" \n",
" return randomNum\n",
"\n",
"def findLCM(x, y):\n",
" lcm = max(x,y)\n",
" \n",
" while (lcm%x != 0) or (lcm%y != 0):\n",
" lcm += 1\n",
" \n",
" return lcm\n",
"\n",
"def modularMultiplicativeInverse(e, totient):\n",
" e = e%totient\n",
" \n",
" for num in range(1, totient):\n",
" if (e*num)%totient == 1:\n",
" return num\n",
" \n",
" return 1\n",
"\n",
"def rsa_encrypt(m, e, n):\n",
" return (m**e) % n\n",
"\n",
"def rsa_decrypt(m, d, n):\n",
" return (m**d) % n\n",
"\n",
"p = generateRandomPrime(1,1000)\n",
"q = generateRandomPrime(1,1000)\n",
"\n",
"n = p*q\n",
"\n",
"\n",
"totient = findLCM(p-1, q-1)\n",
"\n",
"e = generateRandomPrime(1, totient)\n",
"d = modularMultiplicativeInverse(e, totient)\n",
"\n",
"message = 1997\n",
"\n",
"encryptedMessage = rsa_encrypt(message, e, n)\n",
"\n",
"decryptedMessage = rsa_decrypt(encryptedMessage, d, n)\n",
"\n",
"print(\"p:\", p)\n",
"print(\"q:\", q)\n",
"print(\"n:\", n)\n",
"print(\"Totient:\", totient)\n",
"print(\"e:\", e)\n",
"print(\"d:\", d)\n",
"print(\"Message:\", message)\n",
"print(\"Encrypted message:\", encryptedMessage)\n",
"print(\"Decrypted message:\", decryptedMessage)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### MD5"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"64ef07ce3e4b420c334227eecb3b3f4c\n",
"[Errno 2] No such file or directory: 'Harry_Potter.txt'\n"
]
}
],
"source": [
"import hashlib\n",
"\n",
"def md5(message):\n",
" m = hashlib.md5()\n",
" m.update(str.encode(message))\n",
" return m.hexdigest()\n",
"\n",
"def md5_file(filename):\n",
" try:\n",
" with open(filename) as f:\n",
" contents = f.read()\n",
" except Exception as e:\n",
" return e\n",
" m = hashlib.md5()\n",
" m.update(str.encode(contents))\n",
" return m.hexdigest()\n",
" \n",
"\n",
"print(md5(\"Cryptography\"))\n",
"print(md5_file(\"Harry_Potter.txt\"))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### SHA"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"d3486ae9136e7856bc42212385ea797094475802\n",
"7e81ebe9e604a0c97fef0e4cfe71f9ba0ecba13332bde953ad1c66e4\n",
"c0535e4be2b79ffd93291305436bf889314e4a3faec05ecffcbb7df31ad9e51a\n",
"86255fa2c36e4b30969eae17dc34c772cbebdfc58b58403900be87614eb1a34b8780263f255eb5e65ca9bbb8641cccfe\n",
"95decc72f0a50ae4d9d5378e1b2252587cfc71977e43292c8f1b84648248509f1bc18bc6f0b0d0b8606a643eff61d611ae84e6fbd4a2683165706bd6fd48b334\n"
]
}
],
"source": [
"import hashlib\n",
"\n",
"def sha1(message):\n",
" s = hashlib.sha1()\n",
" s.update(str.encode(message))\n",
" return s.hexdigest()\n",
"\n",
"def sha224(message):\n",
" s = hashlib.sha224()\n",
" s.update(str.encode(message))\n",
" return s.hexdigest()\n",
"\n",
"def sha256(message):\n",
" s = hashlib.sha256()\n",
" s.update(str.encode(message))\n",
" return s.hexdigest()\n",
"\n",
"def sha384(message):\n",
" s = hashlib.sha384()\n",
" s.update(str.encode(message))\n",
" return s.hexdigest()\n",
"\n",
"def sha512(message):\n",
" s = hashlib.sha3_512()\n",
" s.update(str.encode(message))\n",
" return s.hexdigest()\n",
"\n",
"print(sha1(\"Hello world!\"))\n",
"print(sha224(\"Hello world!\"))\n",
"print(sha256(\"Hello world!\"))\n",
"print(sha384(\"Hello world!\"))\n",
"print(sha512(\"Hello world!\"))"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"SHA-1 : 40\n",
"SHA-224: 56\n",
"SHA-256: 64\n",
"SHA-384: 96\n",
"SHA-512: 128\n"
]
}
],
"source": [
"print(\"SHA-1 :\", len(sha1(\"Hello world!\")))\n",
"print(\"SHA-224:\", len(sha224(\"Hello world!\")))\n",
"print(\"SHA-256:\", len(sha256(\"Hello world!\")))\n",
"print(\"SHA-384:\", len(sha384(\"Hello world!\")))\n",
"print(\"SHA-512:\", len(sha512(\"Hello world!\")))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Diffie-Hellman"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"p: 23\n",
"g: 5\n",
"a: 54\n",
"A: 9\n",
"b: 68\n",
"B: 2\n",
"sa: 12\n",
"sb: 12\n"
]
}
],
"source": [
"\"\"\"\n",
"p - Prime\n",
"g - Primitive Root modulo p\n",
"p and g are shared between Alice and Bob\n",
"\"\"\"\n",
"\n",
"p = 23\n",
"g = 5\n",
"\n",
"# a is Alice's secret integer\n",
"a = random.randint(1, 100)\n",
"\n",
"# Alice shares A with Bob\n",
"A = (g**a)%p\n",
"\n",
"# b is Bob's secret integer\n",
"b = random.randint(1, 100)\n",
"\n",
"# Bob shares B with Alice\n",
"B = (g**b)%p\n",
"\n",
"sa = (B**a)%p\n",
"sb = (A**b)%p\n",
"\n",
"# sa and sb is the shared secret between Alice and Bob\n",
"assert sa == sb\n",
"\n",
"print(\"p:\", p)\n",
"print(\"g:\", g)\n",
"print(\"a:\", a)\n",
"print(\"A:\", A)\n",
"print(\"b:\", b)\n",
"print(\"B:\", B)\n",
"print(\"sa:\", sa)\n",
"print(\"sb:\", sb)"
]
}
],
"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.6.5"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment