class SolitaireDeck:
  ALPHA="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  def __init__(self,deck=None):
    """Initiates the deck of cards. Deck can be a list containing values 1-54."""
    if not deck:                  # If we didn't get a deck...
      deck = range(1,55)          # ...we initiate an unkeyed deck. [1,2,3, ... ,54]
    self.deck = deck              # Store the deck-list in our object variable self.deck
  
  def next(self):  
    """Returns the next value in the keystream."""  
    d=self.deck                    # Use a local reference to the deck object to save typing.
    while True:                    # Loop until we get an output card that is not a Joker.
      # --- Move Jokers ---------------------------------------------------------------------
      for n in [53,54]:            # Loop over both Jokers (JokerA=53, JokerB=54).
        i=d.index(n)               # Get the index of the current Joker.
        d.pop(i)                   # Remove the Joker from the deck.
        i+=n-52                    # Move Joker A one step and Joker B two steps.
        if i>53:                   # If the new index is greater than 53...
          i-=53                    # ...wrap the index to get to the top of the deck.
        d.insert(i,n)              # Insert the Joker at the new index.
      # --- Triple cut ----------------------------------------------------------------------
      a,b=d.index(53),d.index(54)  # Get the locations of the both jokers. 
      if a>b:                      # If b is closer to the top...
        a,b=b,a                    # ...switch the two variables.
      d[:]=d[b+1:]+d[a:b+1]+d[:a]  # Perform the triple cut using some ninja list slicing.
      # --- Count cut -----------------------------------------------------------------------
      v=min(d[-1],53)              # Get the value of the bottom card in the deck (max 53).
      d[:-1] = d[v:-1]+d[:v]       # Perform the count cut using some more ninja slicing.
      # --- Find output card ----------------------------------------------------------------
      v=min(d[0],53)               # Get the value of the top card (max 53).
      if d[v]<53:                  # If we have an output value less than 53...
        return d[v]                # ...return that as the next key in the stream.

  def key(self,key):
    """Keys the deck using a keyword (A-Z)."""
    d=self.deck                    # Use a local reference to the deck object to save typing.
    key=key.strip().upper()        # Remove leading and trailing spaces, make it upper case.
    for c in key:                  # Loop through all characters.
      if c in self.ALPHA:          # Is it a valid character?
        self.next()                # Perform a normal keystream round.
        v=self.ALPHA.index(c)+1    # Get the characters value 1-26
        d[:-1]=d[v:-1]+d[:v]       # Perform a count cut using the character value.
  
  def format(self,text):
    """Formats the text in groups of five, padded with X."""
    text=text.strip().upper()      # Remove leading and trailing spaces, make it upper case.
    r=[]                           # Create a list for the result.
    while len(text):               # Loop as long as we have any text.
      p=text[:5]                   # Get the first 5 characters.
      text = text[5:]              # Remove the 5 characters.
      if len(p)<5:                 # Did we get less than 5 characters?
        p=p+"X"*(5-len(p))         # Pad out with X
      r.append(p)                  # Add group to result.
    return " ".join(r)             # Return the result as string.

  def _encdec(self,text,mod):
    """The actual encryption/decryption algorithm."""
    text=text.strip().upper()      # Remove leading and trailing spaces, make it upper case.
    r=[]                           # Create a list for the result.
    for c in text:                 # Loop through all characters.
      if c in self.ALPHA:          # Is it a valid character?
        k=self.next()              # Get the next key in stream.
        v=self.ALPHA.index(c)+1    # Get the value of character. (1-26)
        v=(v+(k*mod))%26           # Add or subtract key from value. Modulus 26 to get 1-26.
        r.append(self.ALPHA[v-1])  # Append the encrypted/decrypted character.
        # The 4 lines above can also be written as a one-liner.
        # r.append(self.ALPHA[(((self.ALPHA.index(c)+1)+(self.next()*mod))%26)-1]) 
    return "".join(r)

  def enc(self,text):
    """Encrypts a text."""
    return self._encdec(text,1)    # Tell algorithm to encrypt (add key to character)

  def dec(self,text):
    """Decrypts a text."""
    return self._encdec(text,-1)   # Tell algorithm to decrypt (subtract key from character).

def main(args):
    print ""
    print "Test 1: First 10 keys from an unkeyed deck:"
    d = SolitaireDeck()
    r = []
    for i in range(10):
        r.append(str(d.next()))
    print "   ",",".join(r)

    print ""
    print "Test 2: Encryption/Decryption with an unkeyed deck." 
    d = SolitaireDeck()
    ctext = d.format("AAAAAAAAAA")
    etext = d.format(d.enc(ctext))
    d = SolitaireDeck()
    dtext = d.format(d.dec(etext))
    print "   Clear text:",ctext
    print "   Encrypted text:",etext
    print "   Decrypted text",dtext

    print ""
    print "Test 3: First 15 keys from a deck keyed with \"FOO\"." 
    d = SolitaireDeck()
    d.key("FOO")
    r = []
    for i in range(15):
        r.append(str(d.next()))
    print "   ",",".join(r)

    print ""
    print "Test 4: Encryption/Decryption with a deck keyed with \"FOO\"." 
    d = SolitaireDeck()
    d.key("FOO")
    ctext = d.format("AAAAAAAAAAAAAAA")
    etext = d.format(d.enc(ctext))
    d = SolitaireDeck()
    d.key("FOO")
    dtext = d.format(d.dec(etext))
    print "   Clear text:",ctext
    print "   Encrypted text:",etext
    print "   Decrypted text",dtext

    print ""
    print "Test 5: Encryption/Decryption with a deck keyed with \"CRYPTONOMICON\"." 
    d = SolitaireDeck()
    d.key("CRYPTONOMICON")
    ctext = d.format("SOLITAIRE")
    etext = d.format(d.enc(ctext))
    d = SolitaireDeck()
    d.key("CRYPTONOMICON")
    dtext = d.format(d.dec(etext))
    print "   Clear text:",ctext
    print "   Encrypted text:",etext
    print "   Decrypted text",dtext
    print ""

if __name__ == '__main__': 
    import sys
    main(sys.argv[1:])

