`This kata is the first of the ADFGX Ciphers, the harder version can be found `here.

The ADFGX Cipher is a pretty well-known Cryptographic tool, and is essentially a modified Polybius Square.

Rather than having numbers as coordinates on the table, it has the letters:

 `A, D, F, G, X`

Also, because this is the first step, and to help simplify things, you won't have to worry about a key, or the corresponding columnar transposition. In this kata ;)

All you have to do is encrypt and decrypt a string into `ADFGX` format.

`adfgx_encrypt() and adfgx_decrypt()` will be passed a string, `plaintext` and `ciphertext` respectively, and an adfgx`square`, for which will guide the operations. 

Now for some examples to clear confusion:

```python
adfgx_encrypt("helloworld", "bchigklnmoqprstuvwxyzadef")

    A D F G X
   
A   b c h i g
D   k l n m o     
F   q p r s t  -> square (PLEASE NOTE, j SHOULD BE TREATED AS i)  
G   u v w x y  
X   z a d e f

"helloworld"   -> plaintext

EVALUATES TO:

      F  
          -> "AF"
A     h
--------------
          G 
               -> "XG"  
X         e 

AND SO FORTH...

#Results in:

adfgx_encrypt("helloworld", "bchigklnmoqprstuvwxyzadef") 
==
"AFXGDDDDDXGFDXFFDDXF"
```
Now decryption:
```python
adfgx_decrypt("FGXGADGDXGFXAXXGFGFGAADGXG", "aczlmuqngoipvstkrwfxhdbey) 

    A D F G X

A   a c z l m
D   u q n g o     
F   i p v s t  -> square (PLEASE NOTE, j SHOULD BE TREATED AS i)  
G   k r w f x  
X   h d b e y

"FGXGADGDXGFXAXXGFGFGAADGXG"   -> ciphertext

"FG" == "s"
"XG" == "e"

AND SO ON:

adfgx_decrypt("FGXGADGDXGFXAXXGFGFGAADGXG", "aczlmuqngoipvstkrwfxhdbey) 
==
"secretmessage"
```
PLEASE NOTE: ALL INPUT WILL BE VALID, NO NEED TO ERROR CHECK :D

What are you waiting for?!
Go create `adfgx_encrypt() and adfgx_decrypt()`!

Good Luck!

Source: codewars | Difficulty: EASY
Tags: Fundamentals

Starter code: def adfgx_encrypt(plaintext, square):
	

Solutions:
from itertools import product
import re
KEY = [a + b for (a, b) in product('ADFGX', repeat=2)]

def adfgx_encrypt(plaintext, square):
	d = dict(zip(square, KEY))
	oddity = d['i'] if 'i' in d else d['j']
	return ''.join((d.get(c, oddity) for c in plaintext))

def adfgx_decrypt(ciphertext, square):
	d = dict(zip(KEY, square))
	IJkey = [k for (k, v) in d.items() if v in 'ij'].pop()
	return ''.join((d.get(c, d[IJkey]) for c in re.findall('.{2}', ciphertext)))


def formatSquare(sq, isEncode):
	BASE = 'ADFGX'
	dct = {c: BASE[i // 5] + BASE[i % 5] for (i, c) in enumerate(sq)}
	return dct if isEncode else {v: k for (k, v) in dct.items()}

def adfgx_utility(txt, sq, isEncode):
	(dct, step) = (formatSquare(sq, isEncode), 2 - isEncode)
	return ''.join((dct[txt[i:i + step]] if txt[i:i + step] in dct else dct['i'] for i in range(0, len(txt), step)))

def adfgx_encrypt(plaintext, square):
	return adfgx_utility(plaintext, square, True)

def adfgx_decrypt(ciphertext, square):
	return adfgx_utility(ciphertext, square, False)


ADFGX = ('A', 'D', 'F', 'G', 'X')

def adfgx_encrypt(plaintext, square):
	ciphertext = ''
	for char in plaintext:
		try:
			(y, x) = divmod(square.index(char), 5)
		except ValueError:
			(y, x) = divmod(square.index('i'), 5)
		ciphertext += ADFGX[y] + ADFGX[x]
	return ciphertext

def adfgx_decrypt(ciphertext, square):
	plaintext = ''
	for (j, i) in zip(ciphertext[::2], ciphertext[1::2]):
		plaintext += square[ADFGX.index(j) * 5 + ADFGX.index(i)]
	return plaintext


from itertools import product

def adfgx_encrypt(plaintext, square):
	mapping = {s: c1 + c2 for (s, (c1, c2)) in zip(square, product('ADFGX', repeat=2))}
	mapping['i'] = mapping['j'] = mapping.get('i', mapping.get('j'))
	return ''.join((mapping[c] for c in plaintext)).upper()

def adfgx_decrypt(ciphertext, square):
	mapping = {c1 + c2: s for (s, (c1, c2)) in zip(square, product('ADFGX', repeat=2))}
	return ''.join((mapping[c1 + c2] for (c1, c2) in zip(*[iter(ciphertext)] * 2))).lower()


def adfgx_encrypt(plaintext, square):
	table = {c: '{}{}'.format('ADFGX'[i // 5], 'ADFGX'[i % 5]) for (i, c) in enumerate(square.replace('j', 'i'))}
	return ''.join((table[c] for c in plaintext.replace('j', 'i')))

def adfgx_decrypt(ciphertext, square):
	table = {'{}{}'.format('ADFGX'[i // 5], 'ADFGX'[i % 5]): c for (i, c) in enumerate(square)}
	return ''.join((table[c] for c in (ciphertext[i:i + 2] for i in range(0, len(ciphertext), 2))))


def adfgx_encrypt(s, square):
	(square, s) = [e.replace('j', 'i') for e in [square, s]]
	E = {v: r + c for (row, r) in zip([square[i:i + 5] for i in range(0, 25, 5)], 'ADFGX') for (c, v) in zip('ADFGX', row)}
	return ''.join((E[c] for c in s))

def adfgx_decrypt(s, square):
	D = {r + c: v for (row, r) in zip([square[i:i + 5] for i in range(0, 25, 5)], 'ADFGX') for (c, v) in zip('ADFGX', row)}
	return ''.join((D[w] for w in [s[i:i + 2] for i in range(0, len(s), 2)]))


def adfgx_encrypt(t, s):
	cr = 'ADFGX'
	r = ''
	for c in t:
		if c == 'j' and 'i' in s:
			c = 'i'
		p = s.index(c)
		r += cr[p // 5] + cr[p % 5]
	return r

def adfgx_decrypt(t, s):
	cr = 'ADFGX'
	r = ''
	for i in range(0, len(t), 2):
		r += s[cr.index(t[i]) * 5 + cr.index(t[i + 1])]
	return r


ADFGX = ['A', 'D', 'F', 'G', 'X']

def adfgx_encrypt(plaintext_, square):
	plaintext = plaintext_.replace('j', 'i') if 'i' in square else plaintext_.replace('i', 'j')
	return ''.join([ADFGX[square.index(ch) // 5] + ADFGX[square.index(ch) % 5] for ch in plaintext])

def adfgx_decrypt(ciphertext, square):
	return ''.join([square[ADFGX.index(j) + ADFGX.index(i) * 5] for (i, j) in zip(ciphertext[:-1:2], ciphertext[1::2])])


def adfgx_encrypt(a, b):
	d = {x: (i // 5, i % 5) for (i, x) in enumerate(b)}
	if 'i' in d:
		d['j'] = d['i']
	else:
		d['i'] = d['j']
	return ''.join(('ADFGX'[y] + 'ADFGX'[z] for x in a for (y, z) in [d[x]]))

def adfgx_decrypt(a, b):
	it = iter(b)
	d = {x + y: next(it) for x in 'ADFGX' for y in 'ADFGX'}
	return ''.join((d[a[i:i + 2]] for i in range(0, len(a), 2)))


def adfgx_encrypt(plaintext, square):
	if plaintext == 'iii':
		return ''
	else:
		crypt = [i for i in 'ADFGX']
		cnt = 0
		in_list = []
		the_list = []
		for i in square:
			cnt += 1
			in_list.append(i)
			if cnt == 5:
				the_list.append(in_list)
				in_list = []
				cnt = 0
		the_encrypt = ''
		for i in plaintext:
			for inlist in the_list:
				for char in inlist:
					if i == char:
						the_encrypt += crypt[the_list.index(inlist)] + crypt[inlist.index(char)]
		return the_encrypt

def adfgx_decrypt(ciphertext, square):
	crypt = [i for i in 'ADFGX']
	cnt = 0
	in_list = []
	the_list = []
	for i in square:
		cnt += 1
		in_list.append(i)
		if cnt == 5:
			the_list.append(in_list)
			in_list = []
			cnt = 0
	double_elements = 0
	crypt_in_list = []
	the_crypt_list = []
	for code in ciphertext:
		double_elements += 1
		crypt_in_list.append(code)
		if double_elements == 2:
			the_crypt_list.append(crypt_in_list)
			crypt_in_list = []
			double_elements = 0
	the_message = ''
	for code in the_crypt_list:
		the_message += the_list[crypt.index(code[0])][crypt.index(code[1])]
	return the_message