"Exercise: AtbashCipher"

atbashCipher: aString decode: isDecoding
"Create an implementation of the atbash cipher, an ancient encryption system created in the Middle East.
    The Atbash cipher is a simple substitution cipher that relies on transposing all the letters in the alphabet such that the resulting alphabet is backwards. The first letter is replaced with the last letter, the second with the second-last, and so on.
    An Atbash cipher for the Latin alphabet would be as follows:
    Plain:  abcdefghijklmnopqrstuvwxyz
    Cipher: zyxwvutsrqponmlkjihgfedcba
    It is a very weak cipher because it only has one possible key, and it is a simple mono-alphabetic substitution cipher. However, this may not have been an issue in the cipher's time.
    Ciphertext is written out in groups of fixed length, the traditional group size being 5 letters, leaving numbers unchanged, and punctuation is excluded. This is to make it harder to guess things based on word boundaries.
    All text will be encoded as lowercase letters."
"(self new atbashCipher: 'test' decode: false) >>> 'gvhg'"
"(self new atbashCipher: 'x123 yes' decode: false) >>> 'c123b vh'"
"(self new atbashCipher: 'gvhg' decode: true) >>> 'test'"
"(self new atbashCipher: 'gsvjf rxpyi ldmul cqfnk hlevi gsvoz abwlt' decode: true) >>> 'thequickbrownfoxjumpsoverthelazydog'"
| alphabet reversed sanitized transcoded |
    alphabet := Character alphabet.
    reversed := alphabet reversed.
    sanitized := aString
        select: [ :each | each isAlphaNumeric ]
        thenCollect: #asLowercase.
    transcoded := String streamContents: [ :out |
        sanitized do: [ :ch |
            | idx |
            idx := alphabet indexOf: ch.
            idx = 0
                ifTrue: [ out nextPut: ch ]
                ifFalse: [ out nextPut: (reversed at: idx) ] ] ].
    isDecoding ifTrue: [ ^ transcoded ].
    ^ String streamContents: [ :out |
        | input |
        input := ReadStream on: transcoded.
        out << (input next: 5).
        [ input atEnd ] whileFalse: [ out space << (input next: 5) ] ]


"Tests"
testAtbashCipher
| result |

  "Encode tests"
  result := task atbashCipher: 'yes' decode: false.
  self assert: result equals: 'bvh'.

  result := task atbashCipher: 'no' decode: false.
  self assert: result equals: 'ml'.

  result := task atbashCipher: 'OMG' decode: false.
  self assert: result equals: 'lnt'.

  result := task atbashCipher: 'O M G' decode: false.
  self assert: result equals: 'lnt'.

  result := task atbashCipher: 'mindblowingly' decode: false.
  self assert: result equals: 'nrmwy oldrm tob'.

  result := task atbashCipher: 'Testing,1 2 3, testing.' decode: false.
  self assert: result equals: 'gvhgr mt123 gvhgr mt'.

  result := task atbashCipher: 'Truth is fiction.' decode: false.
  self assert: result equals: 'gifgs rhurx grlm'.

  result := task atbashCipher: 'The quick brown fox jumps over the lazy dog.' decode: false.
  self assert: result equals: 'gsvjf rxpyi ldmul cqfnk hlevi gsvoz abwlt'.

  "Decode tests"
  result := task atbashCipher: 'vcvix rhn' decode: true.
  self assert: result equals: 'exercism'.

  result := task atbashCipher: 'zmlyh gzxov rhlug vmzhg vkkrm thglm v' decode: true.
  self assert: result equals: 'anobstacleisoftenasteppingstone'.

  result := task atbashCipher: 'gvhgr mt123 gvhgr mt' decode: true.
  self assert: result equals: 'testing123testing'.

  result := task atbashCipher: 'gsvjf rxpyi ldmul cqfnk hlevi gsvoz abwlt' decode: true.
  self assert: result equals: 'thequickbrownfoxjumpsoverthelazydog'.

  result := task atbashCipher: 'vc vix    r hn' decode: true.
  self assert: result equals: 'exercism'.

  result := task atbashCipher: 'zmlyhgzxovrhlugvmzhgvkkrmthglmv' decode: true.
  self assert: result equals: 'anobstacleisoftenasteppingstone'