If you like cryptography and playing cards, have also a look at the kata [Card-Chameleon, a Cipher with Playing cards](http://www.codewars.com/kata/card-chameleon-a-cipher-with-playing-cards).



As a secret agent, you need a method to transmit a message to another secret agent. But an encrypted text written on a notebook will be suspicious if you get caught. A simple deck of playing cards, however, is everything but suspicious...

With a deck of 52 playing cards, there are `52!` different possible permutations to order it. And `52!` is equal to `80658175170943878571660636856403766975289505440883277824000000000000`. That's a number with [68 digits](https://www.wolframalpha.com/input/?i=52!)!

There are so many different possible permutations, we can affirm that if you shuffle the cards well and put them back together to form a deck, you are the first one in history to get this particular order. The number of possible permutations in a deck of cards is higher than the estimated number of atoms in planet Earth (which is a number with about [50 digits](https://www.wolframalpha.com/input/?i=number+of+atoms+in+earth)).

With a way to associate a permutation of the cards to a sequence of characters, we can hide a message in the deck by ordering it correctly.

---

# Correspondence between message and permutation

## Message

To compose our message, we will use an alphabet containing 27 characters: the space and the letters from A to Z. We give them the following values:

`" " = 0, A = 1, B = 2, ..., Z = 26`

We now have a [numeral system](https://en.wikipedia.org/wiki/Numeral_system) with a base equal to 27. We can compute a numeric value corresponding to any message:

`"A " = 27`
`"AA" = 28`
`"AB" = 29`
`"ABC" = 786`
etc.

## Permutation

Now we need a way to attribute a unique number to each of the possible [permutations](https://en.wikipedia.org/wiki/Permutation) of our deck of playing cards.

There are few methods to [enumerate permutations](https://en.wikipedia.org/wiki/Permutation#Algorithms_to_generate_permutations) and [assign a number](https://en.wikipedia.org/wiki/Permutation#Numbering_permutations) to each of them, we will use the [lexicographical order](https://en.wikipedia.org/wiki/Lexicographical_order). With three cards, A, B, and C, as an example, it gives:

`ABC = 0`
`ACB = 1`
`BAC = 2`
`BCA = 3`
`CAB = 4`
`CBA = 5`

So the first arrangement is ABC, and the last one is CBA. With our 52 playing cards – ranks sorted from the Ace to the King, and suits in alphabetical order (Clubs, Diamonds, Hearts, Spades) – the first arrangement (number `0`) is:



and the last one (number `52! - 1`) is:



To transmit a message, we will compute the permutation for which the unique number is the numeric value of the message.

---

# Your task

Write two functions:

* ```python
encode(message)
```
```if:java
Which takes a message as argument and returns a deck of playing cards ordered to hide the message (or `null` if the message contains invalid characters or has a numeric value greater than or equal to `52!`).
```
```if:python
Which takes a String containing a message, and returns an array of Strings representing a deck of playing cards ordered to hide the message (or `None` if the message contains invalid characters or has a numeric value greater than or equal to `52!`).
```
```if:elixir
Which takes a string containing a message, and returns a list of strings representing a deck of playing cards ordered to hide the message (or `nil` if the message contains invalid characters or has a numeric value greater than or equal to `52!`).
```

* ```python
decode(deck)
```
```if:java
Which takes a deck of playing cards as argument and returns the message hidden inside (or `null` if the deck contains invalid cards, more than one time a single card, or doesn't contain 52 cards).
```
```if:python
Which takes an array of Strings representing a deck of playing cards, and returns the message that is hidden inside (or `None` if the deck contains invalid cards, more than one time a single card, or doesn't contain 52 cards).
```
```if:elixir
Which takes a list of strings representing a deck of playing cards, and returns the message that is hidden inside (or `nil` if the deck contains invalid cards, more than one time a single card, or doesn't contain 52 cards).
```

Each card name, in a deck, is written with a two characters String: the rank followed by the suit. So the first arrangement of the deck is represented like:

`AC 2C 3C 4C 5C 6C 7C 8C 9C TC JC QC KC` for the Clubs  
`AD 2D 3D 4D 5D 6D 7D 8D 9D TD JD QD KD` for the Diamonds  
`AH 2H 3H 4H 5H 6H 7H 8H 9H TH JH QH KH` for the Hearts  
`AS 2S 3S 4S 5S 6S 7S 8S 9S TS JS QS KS` for the Spades

For your convenience, a preloaded method allows you to easily print a deck to the output:

```python
printDeck(deck, unicode)
```

The first argument is the deck to print, the second one is a boolean value allowing you to choose between simple text or Unicode display. (For Unicode, you need to have a font, on your system, that contains the playing cards Unicode characters.)

---

# Examples

## Encoding

```python
playingCards.encode("ATTACK TONIGHT ON CODEWARS")
```

should return an array of 52 Strings containing:

```python
[
    "AC", "2C", "3C", "4C", "5C", "6C", "7C", "8C", "9C", "TC", "JC", "QC", "KC",
    "AD", "2D", "3D", "4D", "5D", "6D", "JD", "9D", "7S", "9S", "QD", "5S", "TH",
    "7D", "TS", "QS", "2H", "JS", "6H", "3S", "6S", "TD", "8S", "2S", "8H", "7H",
    "4S", "4H", "3H", "5H", "AS", "KH", "QH", "9H", "KD", "KS", "JH", "8D", "AH"
]
```

## Decoding

```python
playingCards.decode([
    "AC", "2C", "3C", "4C", "5C", "6C", "7C", "8C", "9C", "TC", "JC", "QC", "KC",
    "AD", "2D", "3D", "4D", "5D", "6D", "7D", "8D", "9D", "TD", "JD", "QD", "KD",
    "AH", "2H", "3H", "4H", "8H", "9S", "3S", "2S", "8S", "TS", "QS", "9H", "7H",
    "KH", "AS", "JH", "4S", "KS", "JS", "5S", "TH", "7S", "6S", "5H", "QH", "6H"
])
```

should return a String containing:

```python
"ATTACK APPROVED"
```

---

# Further readings

## Logarithm

With the logarithm function, we can know how many digits, in a numeral system of a certain base, are needed to represent a number. For example, `log base 2 of 52! = 225.58`, so we can store 225 bits of information in a deck of cards (and 226 bits are needed to represent the value of `52!`). Also, `log base 27 of 52! = 47.44`, so we can store [47](https://www.wolframalpha.com/input/?i=log+base+27+of+52!) characters of our alphabet in a deck of cards (and some message with 48 characters, but not all of them).

Difficulty: MEDIUM
Tags: Combinatorics

Game theory

Mathematics

Starter code:
def encode(self, message):
	

Solutions:
from math import factorial as fac
cards = ['AC', '2C', '3C', '4C', '5C', '6C', '7C', '8C', '9C', 'TC', 'JC', 'QC', 'KC', 'AD', '2D', '3D', '4D', '5D', '6D', '7D', '8D', '9D', 'TD', 'JD', 'QD', 'KD', 'AH', '2H', '3H', '4H', '5H', '6H', '7H', '8H', '9H', 'TH', 'JH', 'QH', 'KH', 'AS', '2S', '3S', '4S', '5S', '6S', '7S', '8S', '9S', 'TS', 'JS', 'QS', 'KS']
chars = ' ABCDEFGHIJKLMNOPQRSTUVWXYZ'
chars_len = len(chars)
facs = [1]
for x in range(1, 53, 1):
	facs.append(facs[-1] * x)

class PlayingCards:

	def encode(self, message):
		mlen = len(message)
		rem = 0
		for i in range(mlen):
			if message[i] not in chars:
				return None
			rem = rem + chars_len ** (mlen - i - 1) * chars.index(message[i])
		if rem >= facs[-1]:
			return None
		for i in range(1, 53):
			if rem < facs[i]:
				break
		remaining_cards = cards[53 - i - 1:]
		output_cards = cards[:53 - i - 1]
		for j in range(i - 1, -1, -1):
			idx = rem // facs[j]
			output_cards.append(remaining_cards.pop(idx))
			rem = rem % facs[j]
		return output_cards

	def decode(self, deck):
		if len(deck) != 52:
			return None
		remaining_cards = cards.copy()
		rem = 0
		for i in range(len(deck)):
			if deck[i] not in remaining_cards:
				return None
			idx = remaining_cards.index(deck[i])
			rem = rem + facs[51 - i] * idx
			remaining_cards.pop(idx)
		output_message = []
		if rem == 0:
			return ''
		while rem > 0:
			output_message.insert(0, chars[rem % chars_len])
			rem = rem // chars_len
		return ''.join(output_message)


import math

class PlayingCards:

	def encode(self, message):
		list_char = list(' ABCDEFGHIJKLMNOPQRSTUVWXYZ')
		list_card_val = list('A23456789TJQK')
		list_card_col = list('CDHS')
		list_card0 = [val + col for col in list_card_col for val in list_card_val]
		for c in message:
			if c not in list_char:
				return None
		sort_list_message = list(message)
		sort_list_message.sort()
		n = 0
		list_ind = []
		for (ii, c) in enumerate(message):
			ind = list_char.index(c)
			n += ind * 27 ** (len(message) - 1 - ii)
		if n >= math.factorial(52):
			return None
		dec = []
		n2 = n
		for ii in range(51, 0, -1):
			(n_i, n2) = (n2 // math.factorial(ii), n2 % math.factorial(ii))
			dec.append(n_i)
		dec.append(0)
		list_card = list_card0.copy()
		deck_code = []
		for ii in dec:
			deck_code.append(list_card[ii])
			list_card.remove(list_card[ii])
		return deck_code

	def decode(self, deck):
		list_char = list(' ABCDEFGHIJKLMNOPQRSTUVWXYZ')
		list_card_val = list('A23456789TJQK')
		list_card_col = list('CDHS')
		list_card0 = [val + col for col in list_card_col for val in list_card_val]
		test_deck = list(set(deck))
		test_deck.sort()
		test_list_card0 = list_card0.copy()
		test_list_card0.sort()
		if test_deck != test_list_card0:
			return None
		list_card = list_card0.copy()
		n = 0
		for (ii, c) in enumerate(deck[:-1]):
			ind = list_card.index(c)
			if ind > 0:
				n += ind * math.factorial(len(deck) - 1 - ii)
			list_card.remove(c)
		n2 = n
		if n == 0:
			return ''
		dec = []
		nchar = math.floor(math.log10(n) / math.log10(27))
		for ii in range(nchar, 0, -1):
			(n_i, n2) = (n2 // 27 ** ii, n2 % 27 ** ii)
			dec.append(n_i)
		dec.append(n2)
		message = ''.join([list_char[ii] for ii in dec])
		return message


import string

class PlayingCards:
	CHARSET = ' ' + string.ascii_uppercase
	DECK = [card + suit for suit in 'CDHS' for card in 'A23456789TJQK']
	DECKSIZE = len(DECK)
	MAX_FACT = 80658175170943878571660636856403766975289505440883277824000000000000

	def encode(self, msg: string):
		if any((c not in self.CHARSET for c in msg)):
			return None
		perm_index = sum((self.CHARSET.index(c) * pow(27, i) for (i, c) in enumerate(reversed(msg))))
		if perm_index >= self.MAX_FACT:
			return None
		frep = []
		i = 0
		while perm_index:
			i += 1
			frep.append(perm_index % i)
			perm_index = perm_index // i
		frep.reverse()
		left_part = self.DECK[:self.DECKSIZE - len(frep)]
		right_part = self.DECK[self.DECKSIZE - len(frep):]
		return left_part + [right_part.pop(pos) for pos in frep]

	def decode(self, input_deck: list):
		if any((card not in self.DECK for card in input_deck)) or len(set(input_deck)) != self.DECKSIZE:
			return None
		j = 0
		perm_index = 0
		while j < self.DECKSIZE:
			i = 1
			factor = 1
			while i < self.DECKSIZE - j:
				factor *= i
				i += 1
			i = 0
			index = self.DECK.index(input_deck[j])
			while i < j:
				if self.DECK.index(input_deck[i]) < self.DECK.index(input_deck[j]):
					index -= 1
				i += 1
			perm_index += index * factor
			j += 1
		message = ''
		i = 0
		while perm_index:
			i += 1
			message = self.CHARSET[perm_index % 27] + message
			perm_index = perm_index // 27
		return message


import math
import numpy as np
d = {' ': 0, 'A': 1, 'B': 2, 'C': 3, 'D': 4, 'E': 5, 'F': 6, 'G': 7, 'H': 8, 'I': 9, 'J': 'A', 'K': 'B', 'L': 'C', 'M': 'D', 'N': 'E', 'O': 'F', 'P': 'G', 'Q': 'H', 'R': 'I', 'S': 'J', 'T': 'K', 'U': 'L', 'V': 'M', 'W': 'N', 'X': 'O', 'Y': 'P', 'Z': 'Q'}
s_deck = ['AC', '2C', '3C', '4C', '5C', '6C', '7C', '8C', '9C', 'TC', 'JC', 'QC', 'KC', 'AD', '2D', '3D', '4D', '5D', '6D', '7D', '8D', '9D', 'TD', 'JD', 'QD', 'KD', 'AH', '2H', '3H', '4H', '5H', '6H', '7H', '8H', '9H', 'TH', 'JH', 'QH', 'KH', 'AS', '2S', '3S', '4S', '5S', '6S', '7S', '8S', '9S', 'TS', 'JS', 'QS', 'KS']

class PlayingCards:

	def encode(self, message):
		if message == '':
			message = ' '
		if len(message) > 52:
			return None
		meslis = [str(x) for x in message]
		for i in range(len(meslis)):
			if meslis[i] in d:
				None
			else:
				return None
		bnum = ''.join([str(d[x]) for x in message])
		num = int(bnum, 27)
		if num >= math.factorial(52):
			return None
		fact = []
		for j in range(1, 53):
			fact.insert(0, num % j)
			num = int(num // j)
		final = ['#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#']
		s_deckc = s_deck.copy()
		for v in range(0, 52):
			final[v] = s_deckc[fact[v]]
			del s_deckc[fact[v]]
		return final

	def decode(self, deck):
		s_deckc = s_deck.copy()
		if len(deck) != 52:
			return None
		dfact = []
		numb = 0
		for i in range(52):
			if deck[i] in s_deckc:
				None
			else:
				return None
			fact = int(s_deckc.index(deck[i]))
			del s_deckc[fact]
			numb += fact * math.factorial(51 - i)
		if numb == 0:
			return ''
		basetws = np.base_repr(numb, base=27)
		btws_list = [str(x) for x in basetws]
		r_d = {str(value): str(key) for (key, value) in d.items()}
		word = []
		for i in range(len(btws_list)):
			word.append(r_d[btws_list[i]])
		return ''.join(word)


class PlayingCards:

	def __init__(self):
		self.alphabet = ' ABCDEFGHIJKLMNOPQRSTUVWXYZ'
		self.factorials = [1]
		self.deck = ['AC', '2C', '3C', '4C', '5C', '6C', '7C', '8C', '9C', 'TC', 'JC', 'QC', 'KC', 'AD', '2D', '3D', '4D', '5D', '6D', '7D', '8D', '9D', 'TD', 'JD', 'QD', 'KD', 'AH', '2H', '3H', '4H', '5H', '6H', '7H', '8H', '9H', 'TH', 'JH', 'QH', 'KH', 'AS', '2S', '3S', '4S', '5S', '6S', '7S', '8S', '9S', 'TS', 'JS', 'QS', 'KS']
		for i in range(1, 52):
			self.factorials.append(self.factorials[-1] * i)

	def encode(self, message):
		code = self.numeral(message)
		copy = self.deck.copy()
		facts = self.factorials.copy()
		encrypted = []
		for letter in message:
			if letter not in self.alphabet:
				return None
			else:
				pass
		if code > self.factorials[-1] * 52 - 1:
			return None
		for i in range(len(self.deck)):
			(x, code) = divmod(code, facts.pop())
			encrypted.append(copy[x])
			copy.remove(copy[x])
		return encrypted

	def decode(self, deck):
		copy = self.deck.copy()
		facts = self.factorials.copy()
		row = 0
		for i in range(len(self.deck)):
			try:
				if deck[i] in copy:
					x = copy.index(deck[i])
					row += facts.pop() * x
					copy.remove(deck[i])
				else:
					return None
			except:
				return None
		message = self.string(int(row), 27)
		if message == ' ':
			return ''
		else:
			return message

	@staticmethod
	def numeral(string):
		return sum([27 ** i * (ord(char) - 64) for (i, char) in enumerate(string.replace(' ', '@')[::-1])])

	def string(self, n, base):
		if n < base:
			return self.alphabet[n]
		else:
			return self.string(n // base, base) + self.alphabet[n % base]


import itertools as it
import math
import string

class PlayingCards:

	def __init__(self):
		self.cards = ['AC', '2C', '3C', '4C', '5C', '6C', '7C', '8C', '9C', 'TC', 'JC', 'QC', 'KC', 'AD', '2D', '3D', '4D', '5D', '6D', '7D', '8D', '9D', 'TD', 'JD', 'QD', 'KD', 'AH', '2H', '3H', '4H', '5H', '6H', '7H', '8H', '9H', 'TH', 'JH', 'QH', 'KH', 'AS', '2S', '3S', '4S', '5S', '6S', '7S', '8S', '9S', 'TS', 'JS', 'QS', 'KS']
		self.alphabet = [' '] + [letter for letter in string.ascii_uppercase]

	def message_to_cypher_value(self, clear_string):
		alphabet_size = len(self.alphabet)
		output = 0
		for letter in clear_string:
			output = output * alphabet_size + self.alphabet.index(letter)
		return output

	def encode(self, message):
		for letter in message:
			if letter not in self.alphabet:
				return None
		remaining_cards = list(self.cards)
		n = self.message_to_cypher_value(message)
		if n > math.factorial(52) - 1:
			return None
		output = []
		while remaining_cards:
			permutations_per_card = math.factorial(len(remaining_cards)) // len(remaining_cards)
			current_index = n // permutations_per_card
			output.append(remaining_cards.pop(current_index))
			n -= current_index * permutations_per_card
		return output

	def deck_to_cypher_value(self, deck):
		remaining_cards = list(self.cards)
		output = 0
		for card in deck:
			permutations_per_card = math.factorial(len(remaining_cards)) // len(remaining_cards)
			current_index = remaining_cards.index(card)
			output += current_index * permutations_per_card
			remaining_cards.pop(current_index)
		return output

	def decode(self, deck):
		printDeck(deck, False)
		if len(deck) > len(self.cards):
			return None
		if len(set(deck)) < len(self.cards):
			return None
		for card in deck:
			if not card in self.cards:
				return None
		n = self.deck_to_cypher_value(deck)
		alphabet_size = len(self.alphabet)
		output = []
		while n > 0:
			output.append(n % alphabet_size)
			n = (n - output[-1]) // alphabet_size
		return ''.join([self.alphabet[x] for x in output[::-1]])


from math import factorial
stack = ['AC', '2C', '3C', '4C', '5C', '6C', '7C', '8C', '9C', 'TC', 'JC', 'QC', 'KC', 'AD', '2D', '3D', '4D', '5D', '6D', '7D', '8D', '9D', 'TD', 'JD', 'QD', 'KD', 'AH', '2H', '3H', '4H', '5H', '6H', '7H', '8H', '9H', 'TH', 'JH', 'QH', 'KH', 'AS', '2S', '3S', '4S', '5S', '6S', '7S', '8S', '9S', 'TS', 'JS', 'QS', 'KS']
numeral = ' ABCDEFGHIJKLMNOPQRSTUVWXYZ'

class PlayingCards:

	def encode(self, message):
		(tom, tim, s, deck, new) = ([], 1, 0, stack.copy(), [])
		try:
			for (i, x) in enumerate(message):
				s += numeral.index(x) * len(numeral) ** (len(message) - i - 1)
		except ValueError:
			return None
		while s >= 1:
			tom.insert(0, s % tim)
			s = s // tim
			tim += 1
		if len(tom) > 52:
			return None
		while len(tom) != 52:
			tom.insert(0, 0)
		for x in tom:
			new.append(deck[x])
			deck.remove(deck[x])
		return new

	def decode(self, new):
		(yo, haha, plus, deck) = ([], 0, [], stack.copy())
		if len(new) != 52:
			return None
		try:
			while new:
				if yo or deck.index(new[0]) != 0:
					yo.append(deck.index(new[0]))
				deck.remove(new[0])
				new.remove(new[0])
		except ValueError:
			return None
		for (i, x) in enumerate(yo):
			haha += x * factorial(len(yo) - i - 1)
		for x in range(100000000):
			if 27 ** x > haha:
				break
		for e in reversed(range(x)):
			for b in range(27):
				if (b + 1) * 27 ** e > haha:
					haha -= b * 27 ** e
					plus.append(b)
					break
		return ''.join([numeral[x] for x in plus])


from math import factorial

class PlayingCards:

	def __init__(self):
		self.chars = ' ABCDEFGHIJKLMNOPQRSTUVWXYZ'
		self.ranks = 'A23456789TJQK'
		self.suits = 'CDHS'
		self.cards = [rank + suit for suit in self.suits for rank in self.ranks]

	def encode(self, message):
		if any((ch not in self.chars for ch in message)):
			return None
		char_vals = {v: i for (i, v) in enumerate(self.chars)}
		perm_ind = sum((int(self.n_to_base_27(char_vals[ch]) * 27 ** (len(message) - i - 1)) for (i, ch) in enumerate(message)))
		if perm_ind >= factorial(52):
			return None
		return self.card_permutation(perm_ind)

	def decode(self, deck):
		if not all((card in self.cards for card in deck)) or len(set(deck)) != 52:
			return None
		val_chars = {i: v for (i, v) in enumerate(self.chars)}
		lex_index = 0
		for i in range(len(deck)):
			s = 0
			for (j, v) in enumerate(deck[i + 1:]):
				if self.cards.index(v) < self.cards.index(deck[i]):
					s += 1
			lex_index += s * factorial(len(deck) - i - 1)
		char_arr = []
		while lex_index:
			(lex_index, i) = divmod(lex_index, 27)
			char_arr.append(val_chars[i])
		return ''.join(char_arr[::-1])

	@staticmethod
	def n_to_base_27(n):
		if n == 0:
			return 0
		d = []
		while n:
			d.append(n % 27)
			n //= 27
		return int(''.join(map(str, d[::-1])))

	def card_permutation(self, k):
		factorial_arr = [1]
		for i in range(2, 52):
			factorial_arr.append(factorial_arr[-1] * i)
		permutation = []
		res = list(range(52))
		while factorial_arr:
			factorial = factorial_arr.pop()
			(n, k) = divmod(k, factorial)
			permutation.append(res[n])
			res.remove(res[n])
		permutation.append(res[0])
		return [self.cards[i] for i in permutation]


class PlayingCards:
	CARDS = ['AC', '2C', '3C', '4C', '5C', '6C', '7C', '8C', '9C', 'TC', 'JC', 'QC', 'KC', 'AD', '2D', '3D', '4D', '5D', '6D', '7D', '8D', '9D', 'TD', 'JD', 'QD', 'KD', 'AH', '2H', '3H', '4H', '5H', '6H', '7H', '8H', '9H', 'TH', 'JH', 'QH', 'KH', 'AS', '2S', '3S', '4S', '5S', '6S', '7S', '8S', '9S', 'TS', 'JS', 'QS', 'KS']
	S = len(CARDS)

	def __init__(self):
		self.f = [0] * self.S
		self.f[0] = 1
		for k in range(1, self.S):
			self.f[k] = self.f[k - 1] * k
		self.CARD_index = {c: self.CARDS.index(c) for c in self.CARDS}

	def baseEncode(self, s):
		r = 0
		for c in s:
			r *= 27
			r += ord(c) % 32
		return r

	def baseDecode(self, n):
		s = ''
		while n:
			(n, c) = divmod(n, 27)
			s = (chr(64 + c) if c else ' ') + s
		return s

	def permute(self, n):
		S = 52
		p = [0] * S
		for k in range(S):
			(p[k], n) = divmod(n, self.f[S - 1 - k])
		for k in range(S - 1, 0, -1):
			for j in range(k - 1, -1, -1):
				if p[j] <= p[k]:
					p[k] += 1
		return [self.CARDS[i] for i in p]

	def encode(self, message):
		if not all((c == ' ' or 'A' <= c <= 'Z' for c in message)):
			return
		n = self.baseEncode(message)
		if n >= 80658175170943878571660636856403766975289505440883277824000000000000:
			return
		p = self.permute(n)
		return p

	def decode(self, deck):
		if set(deck) != set(self.CARDS):
			return
		(i, j) = (0, 1)
		perm = [self.CARD_index[c] for c in deck]
		for p in range(50, -1, -1):
			s = 0
			for q in range(p + 1, 52):
				if perm[p] > perm[q]:
					s += 1
			i += s * self.f[j]
			j += 1
		return self.baseDecode(i)


class PlayingCards:
	codex = tuple((n + suit for suit in 'CDHS' for n in 'A23456789TJQK'))
	ix = ' ABCDEFGHIJKLMNOPQRSTUVWXYZ'
	xi = {ch: i for (i, ch) in enumerate(ix)}

	@staticmethod
	def encode(s):
		q = 0
		tiers = PlayingCards.get_tiers()
		for ch in s:
			if ch not in PlayingCards.xi:
				return
			q *= 27
			q += PlayingCards.xi[ch]
		if q >= 52 * tiers[0]:
			return
		for (i, x) in enumerate(tiers):
			if q >= x:
				break
		codex = list(PlayingCards.codex[:i])
		cdx = list(PlayingCards.codex[i:])
		for n in tiers[i:]:
			v = q // n
			q %= n
			codex.append(cdx.pop(v))
		return codex + cdx

	@staticmethod
	def decode(r):
		if set(r) != set(PlayingCards.codex):
			return
		q = 0
		tiers = PlayingCards.get_tiers()
		for (i, card) in enumerate(r):
			if card != PlayingCards.codex[i]:
				break
		tiers = tiers[i:]
		cdx = list(PlayingCards.codex[i:])
		for (card, tier) in zip(r[i:], tiers):
			j = cdx.index(card)
			q += j * tier
			cdx.pop(j)
		s = ''
		while q:
			v = q % 27
			q //= 27
			s += PlayingCards.ix[v]
		return s[::-1]

	@staticmethod
	def get_tiers():
		r = []
		c = 1
		for i in range(1, 52):
			c *= i
			r.append(c)
		return r[::-1]